无法按预期进入 32 位保护模式

问题描述

最近,我决定尝试为我的基本操作系统内核使用 32 位代码而不是 16 位代码。我试图进入 32 位保护模式,但它似乎无法正常工作。我使用 NASM 进行构建,使用 qemu 进行调试,但是当我调试它时,qemu 窗口开始出现很多故障。

而且它似乎每半秒都会出现一致的突发故障。

这是我的内核代码

bits 32
org 0x7E00

 ; attempting to enter 32 bit protected mode
cli
lgdt [gdtr]
mov eax,cr0
or al,1
mov cr0,eax
jmp 08h:kernel_main

gdt_start:
    dq 0x0
gdt_code:
    dw 0xFFFF
    dw 0x0
    db 0x0
    db 10011010b
    db 11001111b
    db 0x0
gdt_data:
    dw 0xFFFF
    dw 0x0
    db 0x0
    db 10010010b
    db 11001111b
    db 0x0
gdt_end:
gdtr:
    dw gdt_end - gdt_start
    dd gdt_start

 ; main kernel code

kernel_main:
     ; display hi in grey at top of screen
    mov dword [0xb8000],0x07690748
    hlt
    ret
times 1024-($-$$) db 0

它应该在屏幕顶部以灰色显示“Hi”,但这似乎不起作用。
我做了一些测试,发现这可能与我尝试进入 32 位保护模式的方式有关。我在网上到处搜索,但找不到任何有用的东西。

解决方法

仅将您的 bits 32 指令放在保护模式代码实际开始的位置。一旦发生这种情况,从 gdt_data 选择器加载段寄存器。您可能还想将 ESP 寄存器的高 16 位清零。

为了节省一些空间,您可以将 GDT 指针放在 NULL 描述符上。
请注意正确的 GDT 限制是 gdt_end - gdt_start - 1

bits 16
org 0x7E00

    ; attempting to enter 32 bit protected mode
    cli
    lgdt  [gdtr]
    mov   eax,cr0
    or    al,1
    mov   cr0,eax
    jmp   08h:kernel_main

gdt_start:
gdtr:
    dw gdt_end - gdt_start - 1
    dd gdt_start
    dw 0
gdt_code:
    dw 0xFFFF
    dw 0
    db 0
    db 10011010b
    db 11001111b
    db 0
gdt_data:
    dw 0xFFFF
    dw 0
    db 0
    db 10010010b
    db 11001111b
    db 0
gdt_end:

bits 32

kernel_main:
    ; main kernel code
    mov   eax,0x10
    mov   ds,eax 
    mov   es,eax
    mov   fs,eax
    mov   gs,eax
    mov   ss,ax
    movzx esp,sp

    ; Display hi in grey at top of screen
    mov   dword [0xB8000],0x07690748
    jmp   $

times 1024-($-$$) db 0
,

在远跳转到保护模式后,您必须初始化段寄存器。 把你的 kernel_main 改成这样

kernel_main:
    mov eax,0x10
    mov ds,eax 
    mov es,eax
    mov fs,eax
    mov gs,eax
    mov ss,eax

     ; Display hi in grey at top of screen
    mov dword [0xb8000],0x07690748
    hlt
    ret