X86 为实模式到64位长模式开关设置分页
我正在尝试使用NASM编写一个引导加载程序,因此,我发现OSDEV非常有用。但是,在设置分页、加载我的GDT或转换(im直接来自实模式)的过程中,出现了一个错误,导致机器重新启动。我的代码是基于。这就是我所拥有的对这个问题很重要的东西: GDT 寻呼和交换机:X86 为实模式到64位长模式开关设置分页,x86,x86-64,paging,bootloader,osdev,X86,X86 64,Paging,Bootloader,Osdev,我正在尝试使用NASM编写一个引导加载程序,因此,我发现OSDEV非常有用。但是,在设置分页、加载我的GDT或转换(im直接来自实模式)的过程中,出现了一个错误,导致机器重新启动。我的代码是基于。这就是我所拥有的对这个问题很重要的东西: GDT 寻呼和交换机: %include "gdt.ns" ;test/enable A20 line call test_a20 fin: cmp ax, 1 je enabled call enable_A20 enabled: ;switch c
%include "gdt.ns"
;test/enable A20 line
call test_a20
fin:
cmp ax, 1
je enabled
call enable_A20
enabled:
;switch
call switch_to_lm
jmp $
switch_to_lm:
;
; SET UP PAGING!!!!!
;
;no previous paging defined so the below code is unnecessary
;mov eax, cr0
;and eax, 01111111111111111111111111111111b
;mov cr0, eax
;clear tables
mov edi, 0x1000
mov cr3, edi
xor eax, eax
mov ecx, 4096
rep stosd
mov edi, cr3
;set up new tables
mov DWORD [edi], 0x2003
add edi, 0x1000
mov DWORD [edi], 0x3003
add edi, 0x1000
mov DWORD [edi], 0x4003
add edi, 0x1000
mov ebx, 0x00000003
mov ecx, 512
.setEntry:
mov DWORD [edi], ebx
add ebx, 0x1000
add edi, 8
loop .setEntry
;enable PAE bit in CR4
mov eax, cr4
or eax, 1<<5
mov cr4, eax
;switch from REAL MODE
;set long mode bit
mov ecx, 0xc0000080
rdmsr
or eax, 1<<8
wrmsr
;enable paging
mov eax, cr0
or eax, 1<<31
mov cr0, eax
lgdt [gdt_descriptor]
jmp CODE_SEG:init_lm
[bits 64]
init_lm:
cli
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov ebp, 0x90000
mov esp, ebp
call BEGIN_LM
在代码中,
test_a20
函数有问题。特别是您有以下代码:
mov al, byte [es:di]
push ax
mov byte [es:di], 0x00
mov byte [ds:si], 0xff
cmp byte [es:di], 0xff
pop ax
mov byte [ds:si], al
pop ax
mov byte [es:di], al
您似乎在堆栈上按下AX的一个值,然后弹出2个。这将打乱堆栈,寄存器将被错误恢复,包括DS和标志。您可能一直试图通过不使用ret
返回来规避此错误。相反,您使用jmp-fin
跳转到call test_a20
指令之后的一个点
看来您正试图使用。您会注意到缺少以下几行:
mov al, byte [ds:si]
push ax
如果修改test_a20
函数以添加缺少的行并使用ret
它应该如下所示:
test_a20:
pushf
push ds
push es
push di
push si
cli
xor ax, ax
mov es, ax
not ax
mov ds, ax
mov di, 0x0500
mov si, 0x0510
mov al, byte [es:di]
push ax
mov al, byte [ds:si]
push ax
mov byte [es:di], 0x00
mov byte [ds:si], 0xff
cmp byte [es:di], 0xff
pop ax
mov byte [ds:si], al
pop ax
mov byte [es:di], al
mov ax, 0
je test_exit
mov ax, 1
test_exit:
pop si
pop di
pop es
pop ds
popf
ret
此更改将修复DS寄存器被销毁以及页面中的后续内存访问导致代码无法正常工作的问题。您还必须修改页面启用代码以启用保护模式。此代码:
;enable paging
mov eax, cr0
or eax, 1<<31
mov cr0, eax
;启用分页
mov-eax,cr0
或者eax,1请给出一个最小的完整的可验证示例。@MichaelPetch我所做的唯一其他工作是检查OSDEV文章中提到的cpuid/longmode,如果A20行尚未打开,我将启用它。当我调用“switch_to_lm”标签时,问题就出现了。你想要什么“最小完整可变答案”?允许任何人重现问题的最小完整代码量,包括用于链接/编译/组装等的命令。仔细观察,我看不出问题所在,我在OSDEV问题中非常活跃。当我不要求提供一个最小的完整示例时,我会花费不必要的时间和数十条注释来发现与所提供的代码无关的事情是一个问题。如果你把你的代码放在Github上,或者给我发电子邮件,给我一份你的文件存档,我可以测试一下,我可能会更快地发现你的问题。我的电子邮件地址是mpetch@capp-sysware.com。如果您不希望生成最小完整的可验证示例,我建议使用BOCHS内部调试器逐步完成代码,检查GDT、寄存器和内存中的结构。找到它崩溃的地方等。使用调试器是我能推荐的最好的技巧。BOCHS内部调试器确实理解以实模式运行的代码,这是有益的。感谢您的澄清。我会给你发电子邮件,因为我在比特桶上的回购协议是私人的,我不想改变它哈哈。
test_a20:
pushf
push ds
push es
push di
push si
cli
xor ax, ax
mov es, ax
not ax
mov ds, ax
mov di, 0x0500
mov si, 0x0510
mov al, byte [es:di]
push ax
mov al, byte [ds:si]
push ax
mov byte [es:di], 0x00
mov byte [ds:si], 0xff
cmp byte [es:di], 0xff
pop ax
mov byte [ds:si], al
pop ax
mov byte [es:di], al
mov ax, 0
je test_exit
mov ax, 1
test_exit:
pop si
pop di
pop es
pop ds
popf
ret
;enable paging
mov eax, cr0
or eax, 1<<31
mov cr0, eax
;enable paging
mov eax, cr0
or eax, (1<<31) | (1<<0)
mov cr0, eax