【发布时间】:2017-04-26 09:32:37
【问题描述】:
我正在尝试使用 NASM 编写引导加载程序,因此,我发现 OSDEV 非常有用。但是,在设置分页、加载我的 GDT 或转换(我直接来自实模式)过程中的某个地方,出现了导致机器重新启动的错误。我的代码基于Long mode OSDEV article。这就是我所拥有的对这个问题很重要的内容:
GDT
gdt_start:
.gdt_null: equ $-gdt_start ; mandatory null descriptor
dw 0
dw 0
db 0 ; define double word
db 0
db 0
db 0
.gdt_code: equ $-gdt_start ; code segment
; base = 0x0, limif = 0xffff
; 1st flags: (present)1 (privilege)00 (descriptor type)1 -> 1001
; type flags: (code)1 (conforming)0 (readable)1 (accessed)0 -> 1010
; 2nd flags: (granularity)1 (32 bit default)1 (64 bit seg)0 (AVL)0 -> 1100
dw 0 ; limit (0-15)
dw 0 ; base (0-15)
db 0 ; base (16-23)
db 10011010b ; 1st/type flags
db 00100000b ; 2nd flags, limit (16-19)
db 0 ; base (bits 24-31)
.gdt_data: equ $-gdt_start ; data segment descriptor
; type flags (code)0 (expand down)0 (writable)1 (accessed)0 -> 0010
dw 0 ; limit
dw 0 ; base
db 0 ; base
db 10010010b ; 1st/type flags
db 00000000b ; 2nd flags, limit
db 0 ; base
gdt_end:
gdt_descriptor:
dw gdt_end - gdt_start - 1 ; size of GDT
dq gdt_start
CODE_SEG equ gdt_start.gdt_code
DATA_SEG equ gdt_start.gdt_data
分页和切换:
%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
我的 A20 测试代码:
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 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
jmp fin
【问题讨论】:
-
请展示一个可验证的最小完整示例。
-
@MichaelPetch 我唯一要做的其他事情是检查 OSDEV 文章中提到的 cpuid/longmode,如果 A20 线尚未打开,我将启用它。当我调用“switch_to_lm”标签时会出现问题。你想要什么“最小的完整可变答案”?
-
允许任何人重现问题的最少且完整的代码量,包括用于链接/编译/组装等的命令。目测我没有看到问题,我非常积极地参与关于 SO 的 OSDEV 问题。有时我不要求一个最小的完整示例,我花费了不必要的时间和几十个 cmets 来发现与所提供的代码无关的事情是一个问题。如果您将代码放在 Github 上或通过电子邮件将您的文件存档发送给我,我可以测试一下,我可能会更快地发现您的问题。我的电子邮件地址是 mpetch@capp-sysware.com。
-
如果您不希望生成一个最小的、完整的可验证示例,我建议您使用 BOCHS 内部调试器来单步调试您的代码,查看 GDT、寄存器和内存中的结构。查找崩溃的位置等。使用调试器是我可以推荐的最佳技能。 BOCHS 内部调试器确实可以理解在实模式下运行的代码,这是有益的。
-
感谢您的澄清。我会发邮件给你,因为我在 bitbucket 上的 repo 是私有的,我不想改变它哈哈。
标签: x86 x86-64 paging bootloader osdev