我未能将cpu从真实模式切换到保护模式



我是根据Nick Blundell的一本书来做这件事的。我写了一个MBR程序,它首先在真实模式下运行,程序中的一些指令会将cpu切换到保护模式。首先我把GDT设置成这样:

gdt_start:
gdt_null:
    dd 0x0
    dd 0x0
gdt_code:
    dw 0xffff
    dw 0x0
    db 10011010b
    db 11001111b
    db 0x0
gdt_data:
    dw 0xffff
    dw 0x0
    db 0x0
    db 10010010b
    db 11001111b
    db 0x0
gdt_end:
gdt_descriptor :
    dw gdt_end - gdt_start - 1
    dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start

然后cpu运行以下指令:

   cli
    lgdt [gdt_descriptor]
    mov eax,cr0
    or eax,0x1
    mov cr0,eax ;this will set the cpu to protected-mode        
;jmp $  ;I use this instrction to find where is wrong
    jmp CODE_SEG:init_pm
jmp $
[bits 32]
init_pm:
jmp $
    mov ax,10
jmp $
    mov ds,eax
    mov ss,eax
jmp $
    mov es,ax
    mov fs,ax
    mov gs,ax
    mov ebp,0x90000
    mov esp,ebp
    call BEGIN_PM

指令jmp CODE_SEG:init_pm将导致cpu崩溃并重新启动。如果我将其更改为jmp init_pm,则以下指令mov ax,10将导致cpu崩溃并重新启动。书中说,切换操作需要一段很长的时间。

你能帮我做开关操作吗?

您的代码中存在以下几个问题:

  1. 您的gdt_code描述符中缺少基高位字的低位字节。只需在dw 0x0之后添加db 0x0
  2. Frank Kotler写道gdt_descriptor必须包含线性地址。是的,这是真的,但这并不是唯一需要线性地址的地方。您可以在任何代码之前使用ORG指令,也可以手动将原点添加到此字段
  3. 指令lgdt仍然使用ds寄存器来计算seg:off地址。在org下编写代码时,应该将其设置为零
  4. 远跳转到受保护模式还需要使用线性地址作为偏移量。请记住,此跳转是在兼容模式下执行的(因为它是在保护模式切换后执行的第一条指令)。它使用两个字节作为段选择器(冒号之前),仅使用两个字符作为偏移量(冒号之后)。这意味着您不应该尝试跳转到高于0xFFFF的地址。再次检查代码的来源
  5. 你说mov ax, 10指令会导致cpu崩溃并重新启动,但它使用了错误的值!我们需要的选择器位于偏移量16,即十六进制中的0x10。既然您定义了DATA_SEG equ gdt_data - gdt_start,那么您实际上应该编写mov ax, DATA_SEG

gdt_descriptor中,您有限制和地址。与大多数其他地址不同,这不是一个分段:偏移地址。它需要是一个线性地址。作为一个MBR,您可能已经将它从最初在0x7C00加载的位置移走了。您的gdt_descriptor(通常称为gdtr)中的地址需要是您现在所在位置的线性地址。你没有显示足够的代码来确定,但我怀疑你的问题就在那里。

最新更新