org 0x7c00
BOOTDRIVE equ 0x9000
flatcode equ 0x0008
flatdata equ 0x0010
flatstack equ 0x0010
[bits 16]
section .text
bspstart:
xor ax,ax ;some BIOS required
mov ds,ax
mov byte [BOOTDRIVE],dl
mov ax,0x8000
mov ss,ax
mov sp,0
mov cx,2000 ;80x25*2
xor si,si
mov di,1
mov ax,0xb800
mov ds,ax
cleanscreen:
mov byte [ds:si],0x0
add si,2
mov byte [ds:di],0xf
add di,2
loop cleanscreen
xor ax,ax
mov ds,ax ;restore ax to load GDTR
Read_Disk_INT0x13: ;Read/Write Harddisk/Floppy provided by BIOS INT 0x13 ;cannot use in 32bits mode and 64bits mode
mov ah,0x2 ;function ID 0x2 read_sector; 0x3 write_sector
mov al,0x2 ;count of read/write sector
mov ch,0x0 ;location(cylider)
mov dh,0x0 ;location()
mov cl,0x2 ;location(sector)
mov byte dl,[BOOTDRIVE] ;type of drive(0x0~0x7f floppy drive;0x80~0xff hard drive)
mov bx,0x0
mov es,bx ;es:bx target memory area
mov bx,0x7e00
int 13h
cli ;after int 0x13 inst execute, the IF bit was enabled. we must to disable it.
jc Read_Disk_Error
Read_Disk_OK:
lgdt [GDT_PROP]
in al,0x92
or al,2
out 0x92,al
mov eax,0x1
mov cr0,eax
jmp dword flatcode:bsp_protected_mode_init_segreg
[bits 32]
bsp_protected_mode_init_segreg:
mov ax,flatdata
mov ds,ax
mov ax,flatstack
mov ss,ax
mov esp,0x00200000
loadpage:
mov eax,flatdata
mov ds,eax
mov es,eax
mov esi,temp_pt
mov edi,0x00100000
mov ecx,temp_pt_end - temp_pt
cld
rep movsb
mov esi,temp_pdt
mov edi,0x00101000
mov ecx,temp_pdt_end - temp_pdt
cld
rep movsb
mov esi,temp_pdpt
mov edi,0x00102000
mov ecx,temp_pdpt_end - temp_pdpt
cld
rep movsb
mov esi,temp_pml4
mov edi,0x00103000
mov ecx,temp_pml4_end - temp_pml4
cld
rep movsb
bsp_init_long_mode:
call checkcpuid
call checkia32e
mov ax,flatdata
mov ds,ax
mov eax,0x20
mov cr4,eax
mov eax,0x00103000 ;pml4
mov cr3,eax
mov ecx,0xc0000080
rdmsr
or eax,1 << 8
wrmsr
cli
mov eax,0x80000001
mov cr0,eax
jmp dword flatcode:bsp_long_mode
temp_pt:
dq 0x00000000000b8001
dq 0x00000000000b8001
dq 0x0000000000000001
dq 0x0000000000000001
dq 0x0000000000000001
dq 0x0000000000000001
dq 0x0000000000104001 ;stack at 0x00104000
dq 0x0000000000007001 ;bootloader
temp_pt_end:
temp_pdt:
dq 0x0000000000100001
temp_pdt_end:
temp_pdpt:
dq 0x0000000000101001
temp_pdpt_end:
temp_pml4:
dq 0x0000000000102001
temp_pml4_end:
GDT_PROP:
dw GDT_TABLE_32_END - GDT_TABLE_ENTRY_32 - 1
dd GDT_TABLE_ENTRY_32
GDT_TABLE_ENTRY_32:
dq 0x0000000000000000 ;Empty Entry
dq 0x00cf9a000000ffff ;code
dq 0x00cf92000000ffff ;data&stack
GDT_TABLE_32_END:
GDT_PROP_64:
dw GDT_TABLE_64_END - GDT_TABLE_ENTRY_64 - 1
dq GDT_TABLE_ENTRY_64
GDT_TABLE_ENTRY_64:
dq 0x0000000000000000
dq 0x00af9a000000ffff
dq 0x00af92000000ffff
GDT_TABLE_64_END:
[bits 16]
BIOS_13H_ERROR:
db 'PizzaLoader:(0x0) Disk Error! System Halted.'
Read_Disk_Error:
mov ax,0x0
mov ds,ax
xor si,si
mov bx,BIOS_13H_ERROR
mov cx,44
Print_Disk_Error:
mov dx,0x0
mov ds,dx
mov byte al,[ds:bx] ;read BIOS_13H_ERROR
add bx,1
mov dx,0xb800
mov ds,dx
mov byte [ds:si],al ;write 0xb8000
add si,2
loop Print_Disk_Error
jmp cpuhlt
cpuhlt:
hlt
times 510 - ($ - $$) db 0x0
db 0x55, 0xaa
[bits 32]
NO_CPUID_ERROR:
db 'PizzaLoader:(0x1)The Processor does not support CPUID instruction, System Halted.'
checkcpuid:
pushfd
pop eax
mov ecx,eax
xor eax,1<<21
push eax
popfd
pushfd
pop eax
push ecx
popfd
xor eax,ecx
jz nocpuid
ret
nocpuid:
mov ax,flatdata
mov ds,ax
mov esi,0x000b8000
mov ebx,NO_CPUID_ERROR
mov ecx,81
Print_no_CPUID_Error:
mov byte al,[ebx] ;read NO_CPUID_ERROR
add ebx,1
mov byte [esi],al ;write 0xb8000
add esi,2
loop Print_no_CPUID_Error
hlt
NO_IA32E_ERROR:
db 'PizzaLoader:(0x2)The Processor does not support 64-bits mode, System Halted.'
checkia32e:
mov eax, 0x80000000 ; Set the A-register to 0x80000000.
cpuid ; CPU identification.
cmp eax, 0x80000001 ; Compare the A-register with 0x80000001.
jb noia32e ; It is less, there is no long mode.
ret
noia32e:
mov ax,flatdata
mov ds,ax
mov esi,0x000b8000
mov ebx,NO_IA32E_ERROR
mov ecx,76
Print_no_IA32E_Error:
mov byte al,[ebx] ;read NO_CPUID_ERROR
add ebx,1
mov byte [esi],al ;write 0xb8000
add esi,2
loop Print_no_IA32E_Error
hlt
[bits 64]
bsp_long_mode:
lgdt [GDT_PROP_64]
mov rax,flatdata
mov ds,rax
mov ss,rax
mov sp,0x00006000
call Load_Kernel
hlt
Load_Kernel:
mov rsi,0x00000000
mov rbx,loading_kernel_Message
mov rcx,21
Print_Loading_Kernel_Message:
mov byte al,[rbx] ;read message
add rbx,1
mov byte [rsi],al ;write 0x000b8000
add rsi,2
loop Print_Loading_Kernel_Message
ret
loading_kernel_Message:
db 'Loading Mio Kernel...'
[bits 64]
restart:
mov dx,0xcf9
mov al,0xe
out dx,al
这是我的完整代码,运行结果如下:
Kn’chmf?Lhn?Jdqmdk———
文本输出的 ASCII 代码比应有的低 1,因此在字母表中比应有的早 1 个字母。应为“正在加载...”
加载 64 位 GDT 后,重新加载 DS 和 SS,但不加载 CS。您需要使用新的代码段选择器 0x8 进行远跳转才能真正进入 64 位模式。(这也意味着
[BITS 64]
应该在该跳转之后进行。)或者,正如我看到您刚刚发表的评论一样,
lgdt [GDT_PROP_64]
在之前移动jmp dword flatcode:bsp_long_mode
。目前,您想要的 64 位代码正在 32 位模式下执行。正如 Peter Cordes 推断的那样,您想要的 64 位指令上的 REX 前缀在 32 位模式下解码为
dec eax
,导致您的字节在存储到视频内存之前减少。然后还有另一个错误:
mov sp,0x00006000
应该是mov rsp, ...
,否则您只加载低 16 位而忽略高 48 位,当您的call
指令尝试推送到堆栈时,会导致页面错误(因此是三重错误)。并且由于根据您的评论,您希望堆栈位于页面 0x6000(即虚拟地址 0x6000-0x6fff),并且堆栈向下增长,因此您实际上希望初始化rsp
为0x7000
。(
mov esp, 0x7000
具有相同的效果,因为在 64 位模式下 32 位寄存器写入是零扩展的,但看起来很奇怪。)以下版本的代码对我有用: