2015-01-16 77 views
1

在使用非常簡單的引導加載程序時,我發現在運行到保護模式之前將某些內容分配給數據段(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它。

爲什麼會發生這種情況?

+0

您正在加載具有錯誤值的'ds'。你爲什麼期望它工作? –

+0

如果文檔指定ds在加載保護模式的長跳轉之前必須爲非零值。除此之外,0x10是正確的值,跳進保護模式後,我可以將ds設置爲0x10,並且工作正常 – felknight

+0

@Felipe:Jonathon的答案正確;但只涉及你現在遇到的一個具體問題。您還有很多其他問題,包括依靠虛假信息(「ds必須在跳轉前不爲零」),不理解實模式段加載和保護模式段加載之間的差異,錯誤地假設BIOS使某些寄存器處於特定狀態甚至在引導加載程序的第一個512字節中切換到保護模式是一個設計錯誤。 – Brendan

回答

2

問題不是你在進入保護模式之前設置了ds。問題在於,在執行lgdt指令之前,您正在設置ds

lgdt指令也訪問數據段中的存儲器,因此ds在執行時需要正確的值。當您更改ds時,您更改了您嘗試加載的GDT的有效地址。然後,當您在該GDT中使用cs段輸入保護模式時,GDT條目是假的,並且處理器生成了一個異常。最後,由於您尚未設置IDT,因此處理器會雙擊,然後發生三重故障並重新啓動。

讓我們假設gdt_descriptor是0x40字節到您的代碼,這意味着它是在0000:7C40當您的引導程序加載。當ds爲零時(您的第一個示例),lgdt [gdt_descriptor]指令將嘗試從基址/限制加載GDT,格式爲(0x0 << 4) + 0x7C40 == 0x7C40。但是,如果將ds設置爲0x10,則您現在正在嘗試從基準/限制(0x10 << 4) + 0x7C40 == 0x7D40加載GDT,這不是您想要的。

因此,您可以在進入保護模式之前將ds設置爲0x10 - 直到跳到PM之後才執行任何內存訪問(即lgdt)。