如果数据段非零,则保护模式将失败

时间:2015-01-16 08:32:14

标签: assembly operating-system nasm boot bootloader

在处理一个非常简单的引导程序时,我发现在运行到保护模式之前为数据段(ds)分配内容会产生处理器故障。

这段代码很好用:

[BITS 16]
[ORG 0x7c00]

xor ax,ax
mov ds,ax

cli
lgdt [gdt_descriptor]  

mov  eax, cr0       
or eax,1              
mov  cr0, eax

jmp CODE_SEG:now

[BITS 32]
now:
    jmp $

    db 0
gdt_start:
gdt_null:
    dd 0x0
    dd 0x0
gdt_cs:
    dw 0xFFFF ; Limit
    dw 0x0000 ; Base
    db 0x0000    ; Base 23:16
    db 10011011b
    db 11011111b
    db 0x0000
gdt_ds:
    dw 0xFFFF ; Limit
    dw 0x0000 ; Base
    db 0x0000    ; Base 23:16
    db 10010011b
    db 11011111b
    db 0x0000
gdt_end
gdt_descriptor:
    dw gdt_end - gdt_start - 1
    dd gdt_start

CODE_SEG equ gdt_cs - gdt_start
DATA_SEG equ gdt_ds - gdt_start

times 510-($-$$) db 0  ; fill sector w/ 0's
db 0x55          ; req'd by some BIOSes
db 0xAA

这个让处理器重启:

[BITS 16]
[ORG 0x7c00]

mov ax,0x10 ;<-- Pre-assigning data segment
mov ds,ax

cli
lgdt [gdt_descriptor]  

mov  eax, cr0       
or eax,1              
mov  cr0, eax

jmp CODE_SEG:now

[BITS 32]
now:
    jmp $

    db 0
gdt_start:
gdt_null:
    dd 0x0
    dd 0x0
gdt_cs:
    dw 0xFFFF ; Limit
    dw 0x0000 ; Base
    db 0x0000    ; Base 23:16
    db 10011011b
    db 11011111b
    db 0x0000
gdt_ds:
    dw 0xFFFF ; Limit
    dw 0x0000 ; Base
    db 0x0000    ; Base 23:16
    db 10010011b
    db 11011111b
    db 0x0000
gdt_end
gdt_descriptor:
    dw gdt_end - gdt_start - 1
    dd gdt_start

CODE_SEG equ gdt_cs - gdt_start
DATA_SEG equ gdt_ds - gdt_start

times 510-($-$$) db 0  ; fill sector w/ 0's
db 0x55          ; req'd by some BIOSes
db 0xAA

我用NASM编译了这个,我用VMWARE运行它。

为什么会这样?

1 个答案:

答案 0 :(得分:2)

问题不是您在进入保护模式之前设置ds。问题是您在执行ds指令之前设置了lgdt

lgdt指令还访问数据段中的内存,因此ds在执行时需要是正确的值。当您更改ds时,您更改了您尝试加载的GDT的有效地址。然后,当您在该GDT中进入带有cs段的保护模式时,GDT条目是伪造的,并且处理器生成了异常。最后,因为你还没有设置IDT,处理器加倍,然后三重故障并重新启动。

让我们说gdt_descriptor代码为0x40字节,这意味着它在加载引导加载程序时位于0000:7C40。当ds为零(您的第一个示例)时,lgdt [gdt_descriptor]指令会尝试从(0x0 << 4) + 0x7C40 == 0x7C40的基数/限制加载GDT。但是,当您将ds设置为0x10时,您现在正在尝试从(0x10 << 4) + 0x7C40 == 0x7D40的基数/限制加载GDT,这不是您想要的。

因此,您可以在进入保护模式之前将ds设置为0x10 - 只是在您跳远之前不要进行任何内存访问(即lgdt)进入PM。