主引導程序控制權的交接(六)

        我們在上節博客中學習瞭如何進行主引導程序的 512 字節的擴展,那麼我們本節就繼續來學習下如何進行控制權的交接。就是控制權由主引導程序交由下一個將要執行的程序,類似於嵌入式中的 uboot 在啓動內核的時候將控制權由 uboot 交由 kernel。下來我們先來看看 BootLoader 的內存佈局,如下所示

圖片.png

        我們看到在 0x7c00 前還有一段預留的空間,那麼這段空間就是用來存放棧信息的。在主引導程序的 512 字節之後,緊接着就是 Fat 表,它大小爲 4kb。從 0x9000 地址之後便全部爲 Loader 了,也就是我們交由控制權的地方了。我們來看看通過 FAT 表是如何來加載文件內容的,如下圖所示

圖片.png

        我們看到,先指定 FAT 表的地址,然後指定 DIR_FstClus 成員的入口地址,再間接賦給 dx 寄存器。這個 0xFF7 是哪來的呢?在我們之前用 Qt 編寫的代碼中就指定了這個大小,這是規定的,後面的流程也是我們之前實現過的流程。我們就來做個實驗:1、在虛擬軟盤中創建體積較大的文本文件(Loader);2、將 Loader 的內容加載到 BaseOfLoader 地址處;3、打印 Loader 中的文本(用來判斷加載是否完全)。具體源碼如下

start:
    mov ax, cs
    mov ss, ax
    mov ds, ax
    mov es, ax
    mov sp, BaseOfStack
    
    mov ax, RootEntryOffset
    mov cx, RootEntryLength
    mov bx, Buf
    
    call ReadSector
    
    mov si, Target
    mov cx, TarLen
    mov dx, 0
    
    call FindEntry
    
    cmp dx, 0
    jz output
    
    mov si, bx ; 將起始地址放到 si 中
    mov di, EntryItem
    mov cx, EntryItemLength
    
    call MemCpy
    
    ; 計算 Fat 表所佔用的內存
    mov ax, FatEntryLength
    mov cx, [BPB_BytsPerSec]
    mul cx ; 將所佔用的內存大小結果保存到 ax 中
    mov bx, BaseOfLoader
    sub bx, ax ; bx 就是 Fat 表在內存中的起始位置了
    
    mov ax, FatEntryOffset
    mov cx, FatEntryLength
    
    call ReadSector
    
    mov dx, [EntryItem + 0x1A] ; 獲取目標起始處的位置
    mov si, BaseOfLoader
    
loading:
    mov ax, dx
    add ax, 31
    mov cx, 1
    push dx
    push bx
    mov bx, si
    call ReadSector
    pop bx
    pop cx
    call FatVec
    cmp dx, 0xFF7
    jnb output
    add si, 512
    jmp loading

output:
    mov bp, BaseOfLoader
    mov cx, [EntryItem + 0x1c]
    call Print
    
last:
    hlt
    jmp last

        我們來編譯看看

圖片.png

        我們看到 make 直接報錯,原因是整個主引導程序的大小超出 512 字節的範圍了。那麼此時我們該怎麼辦呢?我們就有必要將之前的 push 和 pop 入棧出棧的操作進行刪除了,那麼我們之前爲何要這樣做呢?是爲了遵守彙編代碼的約定,有操作相關寄存器的值就要進行入棧出棧操作。那麼我們這塊內存已經不夠,因此沒必要進行這個操作了。我們將下面的入棧出棧操作進行刪除,但是要在 FindEntry 這個函數保留 cx 寄存器的入棧出棧的操作,原因是下面不停在改變 cx 寄存器的值。我們在 find 操作中,call MemCmp 操作前後有必要再加上 si 寄存器的入棧出棧操作。我們修改完再來進行 make 看看,是否還會出問題

圖片.png

        我們看到已經編譯成功,我們來 bochs 調試下,看看 loader 文本的內容是什麼

圖片.png

        我們看到打印了好多的 D.T.Software。我們來掛載下 data.img 看看 loader 文本的內容是否如此

圖片.png

        那麼我們將 loader 文本的內容改爲我們剛纔編寫的 boot.asm 的內容,順便看看它的文件所佔內存是多大的,如果大於 512 字節還能正常進行讀取並顯示,那麼就說明我們所編寫的功能是沒有問題的。

圖片.png

        我們看到這個文本的內存是 8 kb,那麼它早就超過 512  字節了。看看最後打印的是不是 db 0x55, 0xaa,結果如下

圖片.png

        我們看到打印的確實是我們剛纔改的內容,也就證明了我們編寫的代碼是正確的。下來我們來講講由 boot 主引導程交由後的第一個程序 Loader:它的起始地址是 0x9000(org 0x9000),通過 int 0x10 號中斷在屏幕上打印字符串。在編寫 loader.asm 源碼之前,我們先來介紹下相關的彙編知識。我們在之前使用了 jz 指令,表示跳轉,其實 jxx 代表了一個指令族,功能是根據標誌位進行調整,具體如下

圖片.png

loader.asm 源碼如下

org 0x9000

begin:
    mov si, msg
    
print:
    mov al, [si]
    add si, 1
    cmp al, 0x00
    je end
    mov ah, 0x0E
    mov bx, 0x0F
    int 0x10
    jmp print

end:
    hlt
    jmp end
    
msg:
    db 0x0a, 0x0a
    db "Hello, D.T.OS!"
    db 0x0a, 0x0a
    db 0x00

        我們在上面的代碼中使用了 je,我們通過反彙編來看看它在內部是怎樣實現的,如下

圖片.png

        我們看到在編譯器的內部是將 je 指令當做 jz 指令使用了。我們將 loader.asm 編譯成 loader,然後將它拷貝至 data.img 中。看看運行的效果

圖片.png

        我們看到已經打印出 Hell, D.T.OS! ;我們再將此時的 data.img 放在 window 中,將它作爲軟盤在我們所創建的虛擬機上,看看效果

圖片.png

        我們看到已經成功輸出我們所打印的字符串了,雖然效果和我們之前所實現的是一樣的。但是此時已經發生了質的變化,此時的 loader.asm 文本大小不再有 512 字節的限制。換句話說,我們可以編寫更多的內核機制了。那麼我們在完成之後也要將之前的 makefile 重寫下,要不然每次都要手動編譯 loader.asm 程序。改動後的具體源碼如下

.PHONY : all clean rebuild

BOOT_SRC := boot.asm
BOOT_OUT := boot.bin

LOADER_SRC := loader.asm
LOADER_OUT := loader

IMG := data.img
IMG_PATH := /root/DT/wei

RM := rm -rf

all : $(BOOT_OUT) $(LOADER_OUT)
    @echo "Build Success ==> D.T.OS!"
    
$(IMG) :
    bximage $@ -q -fd -size=1.44
    
$(BOOT_OUT) : $(BOOT_SRC)
    nasm $^ -o $@
    dd if=$(BOOT_OUT) of=$(IMG) bs=512 count=1 conv=notrunc
    
$(LOADER_OUT) : $(LOADER_SRC)
    nasm $^ -o $@
    mount -o loop $(IMG) $(IMG_PATH)
    cp $@ $(IMG_PATH)/$@
    umount $(IMG_PATH)

clean :
    $(RM) $(IMG) $(BOOT_OUT) $(LOADER_OUT)
    
rebuild :
    @$(MAKE) clean
    @$(MAKE) all

        我們再來重新 make  下,看看效果

圖片.png

        我們將輸出字符串在  loader.asm 中改爲 Hello, world!看看效果

圖片.png

        我們看到已經成功改寫,那麼我們再來測試下對原來的功能有沒有造成什麼影響。將原來的 LOADER 在後面加個 a ,看看是否會因查找不到而輸出 No LOADER ... 提示性字符串

圖片.png

        我們看到已經輸出了提示性的字符串,證明我們添加的功能以及改寫的 makefile 對原來功能並沒有造成任何影響。通過今天對控制權交接的學習,總結如下:1、boot 需要在進行重構保證在 512 字節內完成功能;2、在彙編程序中儘量確保函數調用前後通用寄存器的狀態不變;3、boot 成功加載 loader 後將控制權轉移;4、loader 程序沒有代碼體積的限制。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章