![](/img/trans.png)
[英]Why doesn't GRUB2 load the diskboot.img to address 0x8000 directly?
[英]Can't jump or call kernel loaded at 0x8000
我正在嘗試開發一個操作系統。 設計是這樣的:我有一個加載在0x7c00的引導程序,它加載第二階段並在0x7e00跳轉到它。 第二階段也處於實模式並且執行許多操作,例如加載gdt,啟用A20並切換到保護模式。 它還在0x8000處加載一個非常簡單的32位內核。 現在的問題是我無法調用或jmp到0x8000,因為內核似乎沒有加載(我在VirtualBox中進行了內存轉儲)。 我已經在第二階段完成了FAR JMP來設置CS寄存器。 我在VirtualBox中測試我的操作系統。
我的啟動加載程序的代碼:
org 0x7c00
bits 16
Start:
jmp Reset
bpbOEM DB "SKULLOS "
bpbBytesPerSector: DW 512
bpbSectorsPerCluster: DB 1
bpbReservedSectors: DW 1
bpbNumberOfFATs: DB 2
bpbRootEntries: DW 224
bpbTotalSectors: DW 2880
bpbMedia: DB 0xF0
bpbSectorsPerFAT: DW 9
bpbSectorsPerTrack: DW 18
bpbHeadsPerCylinder: DW 2
bpbHiddenSectors: DD 0
bpbTotalSectorsBig: DD 0
bsDriveNumber: DB 0
bsUnused: DB 0
bsExtBootSignature: DB 0x29
bsSerialNumber: DD 0xa0a1a2a3
bsVolumeLabel: DB "MOS FLOPPY "
bsFileSystem: DB "SKFS "
Set:
mov al , 02h
mov ah , 00h
int 10h
jmp Print
Print:
mov al , 'A'
mov bl , 0Fh
mov cx , 01h
mov ah , 09h
int 10h
jmp Reset
Reset:
; mov dl , 0x00
mov [0x500] , dl
mov ah , 0x00
int 0x13
jc Reset
mov ax , 0x7E0
mov es , ax
xor bx , bx
mov ah , 0x02
mov al , 1
mov ch , 0
mov cl , 2
mov dh , 0
mov dl , [0x500]
int 0x13
jmp 0x0000 :0x7e00
times 510-($-$$) db 0
db 0x55
db 0xAA
第二階段代碼:
org 0x7E00
bits 16
Start:
jmp Setup
;;;;;;;;;;;;;stack;;;;;;;;;;
Setup:
cli
xor ax , ax
mov ds , ax
mov es , ax
mov ax , 0x9000
mov ss , ax
mov sp , 0xFFFF
sti
jmp Set
;;;;;;;;;;;;;video;;;;;;;;;;;
Set:
mov al , 03h
mov ah , 00h
int 10h
mov ah , 09h
mov al , 'A'
mov bh , 00h
mov bl , 0x0F
mov cx , 01h
int 10h
jmp loadgdt
;;;;;;;;;;;;gdt;;;;;;;;;;;;;;;
gdt_start:
null:
dd 0
dd 0
code:
dw 0FFFFh
dw 0
db 0
db 10011010b
db 11001111b
db 0
data:
dw 0FFFFh
dw 0
db 0
db 10010010b
db 11001111b
db 0
end:
load: dw end - gdt_start -1
dd null
;;;;;;;;;;;;;loadgdt;;;;;;;;;;
loadgdt:
lgdt [load]
jmp A20
;;;;;;;;;;;;A20;;;;;;;;;;;;;;;
A20:
mov ax , 0x2401
int 0x15
jc A20
jmp Reset
;;;;;;;;;;;;;floppy;;;;;;;;;;;
Reset:
mov ah , 00h
mov dl , [0x500]
int 13h
jc Reset
jmp Read
Read:
mov ah , 02h
mov al , 01h
mov ch , 00h
mov cl , 03h
mov dh , 00h
mov dl , [0x500]
mov ax , 0x800
mov es , ax
xor bx , bx
int 13h
jc Read
jmp Begin
Begin:
mov ah , 09h
mov al , 'G'
mov bh , 00h
mov bl , 0x0F
mov cx , 01h
int 10h
jmp protected
;;;;;;;;;;;switching to protected;;;;
protected:
mov ah , 09h
mov al , 'P'
mov bh , 00h
mov bl , 0x0F
mov cx , 01h
int 10h
xor ax, ax
mov ds, ax
cli
mov eax, cr0
or eax , 1
mov cr0 , eax
jmp (code-gdt_start):transfer_control
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
bits 32
transfer_control:
mov ax, (data-gdt_start)
mov ds, ax
mov ss, ax
mov es, ax
mov esp, 90000h
mov [0xB8000], word 0x0F58 ; Print 'X'
call 0x8000
hlt
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
times 512-($-$$) db 0
內核代碼:
org 0x8000
bits 32
jmp Start
Start:
mov ax , 0x10
mov ds , ax
mov ss, ax
mov es, ax
mov esp, 90000h
mov [0xB8002], word 0x0F58 ; Print 'X'
ret
times 512-($-$$) db 0
目前,只打印一個'X'。 但是,應打印兩個“X”。 用於創建軟盤的命令:
dd seek=0 if=boot of=os.img
dd seek=1 if=second_stage of=os.img
dd seek=2 if=third_stage of=os.img
在第二階段,你加載第三階段這樣做:
Read:
mov ah , 02h ; Setup AH
mov al , 01h ; Setup AL
mov ch , 00h
mov cl , 03h
mov dh , 00h
mov dl , [0x500]
mov ax , 0x800 ; Destroy contents of AX
mov es , ax ; Setup ES=0x800
xor bx , bx
int 13h
我已經用問題標記了這些線條。 您有效地設置AX以准備讀取,然后使用0x800覆蓋值以設置ES 。 在設置AH和AL之前移動設置ES 。 將代碼修改為:
Read:
mov ax , 0x800
mov es , ax ; Setup ES=0x800
mov ah , 02h ; Setup AH
mov al , 01h ; Setup AL
mov ch , 00h
mov cl , 03h
mov dh , 00h
mov dl , [0x500]
xor bx , bx
int 13h
這可能會阻止您的第二階段正確加載第三階段。
在引導加載程序的末尾,您有:
db 0xAA
db 0x55
這是倒退,應該是:
db 0x55
db 0xAA
你可以把它寫成:
dw 0xAA55
問題似乎是在定義這些字節時沒有考慮小的字節序。
您正確跳過引導加載程序中的BIOS參數塊,但BPB需要從引導扇區的第4個字節開始。 jmp Reset
指令只有3個字節長。 您可以在跳轉后放置一個nop
,以便BPB從第4個字節開始。
更改:
jmp Reset
bpbOEM DB "SKULLOS "
至:
jmp short Reset
nop ; BPB needs to start at 4th byte (jmp instruction takes 3 bytes)
bpbOEM DB "SKULLOS "
mov sp , 0xFFFF
應該是mov sp, 0x0000
。 這只是一個小小的挑剔。 使堆棧位於WORD邊界(偶數地址)在8086處理器上表現更好。 由於你不是很長時間處於真實模式,所以根本不重要。 通常你會在你的情況下使用mov sp, 0x0000
,因為推送的第一個WORD將是0x9000:0xfffe,因為首先從SP中減去2,然后將WORD推入堆棧。 有效地,當SP = 0x0000時,堆棧將通過包裝到64k段的頂部開始。
你不需要JMP從標簽標注,如果標簽JMP之后是。 說明如下:
jmp Set
Set:
除了浪費空間和占用CPU周期之外別無所求。 我注意到你在很多地方都這樣做了。 這不是你問題的一部分,只是一個觀察。 FAR JUMP jmp (code-gdt_start):transfer_control
后跟標簽很好,因為它用於正確設置CS描述符(對於保護模式)
使用int 13h
進行磁盤訪問時,您應該使用BIOS傳遞給引導加載程序的引導驅動器號作為DL的值。 您的第一和第二階段的代碼如下:
mov dl , 00h
這總是假設您正在讀取第一張軟盤( A :)。 如果您想在軟盤驅動器A以外的啟動驅動器上使用您的代碼:您將要刪除它
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.