如果数据段不为零,保护模式将失败
Protected mode fails if data segment is non-zero
在开发一个非常简单的引导加载程序时,我发现在 运行 进入保护模式之前将某些内容分配给数据段 (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 运行 编译了它。
为什么会这样?
问题不在于您在进入保护模式之前设置 ds
。问题是您在执行 lgdt
指令之前设置 ds
。
lgdt
指令也是访问数据段中的内存,所以ds
执行时需要是一个正确的值。当您更改 ds
时,您更改了您尝试加载的 GDT 的有效地址。然后,当您使用该 GDT 中的 cs
段进入保护模式时,GDT 条目是伪造的,并且处理器生成异常。最后,由于您尚未设置 IDT,处理器出现两次、三次故障并重新启动。
假设 gdt_descriptor
是代码中的 0x40 字节,这意味着加载引导加载程序时它位于 0000:7C40
。当 ds
为零(您的第一个示例)时,lgdt [gdt_descriptor]
指令将尝试从 (0x0 << 4) + 0x7C40 == 0x7C40
处的 base/limit 加载 GDT。但是,当您将 ds
设置为 0x10
时,您现在正试图从位于 (0x10 << 4) + 0x7C40 == 0x7D40
的 base/limit 加载 GDT,这不是您想要的。
因此您可以在进入保护模式之前将 ds
设置为 0x10
- 只是在您进入 PM 之前不要进行任何内存访问(即 lgdt
) .
在开发一个非常简单的引导加载程序时,我发现在 运行 进入保护模式之前将某些内容分配给数据段 (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 运行 编译了它。
为什么会这样?
问题不在于您在进入保护模式之前设置 ds
。问题是您在执行 lgdt
指令之前设置 ds
。
lgdt
指令也是访问数据段中的内存,所以ds
执行时需要是一个正确的值。当您更改 ds
时,您更改了您尝试加载的 GDT 的有效地址。然后,当您使用该 GDT 中的 cs
段进入保护模式时,GDT 条目是伪造的,并且处理器生成异常。最后,由于您尚未设置 IDT,处理器出现两次、三次故障并重新启动。
假设 gdt_descriptor
是代码中的 0x40 字节,这意味着加载引导加载程序时它位于 0000:7C40
。当 ds
为零(您的第一个示例)时,lgdt [gdt_descriptor]
指令将尝试从 (0x0 << 4) + 0x7C40 == 0x7C40
处的 base/limit 加载 GDT。但是,当您将 ds
设置为 0x10
时,您现在正试图从位于 (0x10 << 4) + 0x7C40 == 0x7D40
的 base/limit 加载 GDT,这不是您想要的。
因此您可以在进入保护模式之前将 ds
设置为 0x10
- 只是在您进入 PM 之前不要进行任何内存访问(即 lgdt
) .