二. 編寫mbr,讓機器啓動起來

mbr簡介

大家都知道,在我們按下電腦電源的時候,首先啓動的BIOS(基本輸入輸出系統),那麼BIOS又是如何被啓動的呢,誰來喚醒他呢,它又在何處運行呢。要了解這些的話,首先得介紹一下我們實模式的內存佈局

實模式的內存佈局

mark

圖中的內容我們現在只需要關注紅色框出來的地方,可以看到BIOS的入口地址處只有16BYTE的空間,很顯然,這一小塊空間肯定存放的不是數據,只能是指令了,圖中也寫的很明顯了


jmp f000:e05b

也就是跳轉到了(f000 << 4) + e05b = fe05b處,這裏的段基址左移四位的原因是,在實模式下段基址寄存器只有16位,想一下,16位的寄存器最多訪問2^16=64KB的空間,我們想訪問實模式下1MB的空間的話就需要將段基址左移4位,自然就可以訪問到1MB的空間了,這麼做的原因也是出於兼容性而採取的曲線救國方式,雖然我們現在的OS都已經到了64位,它也還得向下兼容不是嗎

當我們的電腦加電的一瞬間cs:ip就會被強制置位f000:e05b了,接下來就對內存,顯卡等外設進行檢查,做好它的初始化工作之後就完成它的任務了,在最後的時候,BIOS會通過絕對遠跳


jmp 0:0x7c00

將接力棒交由MBR來加載我們的內核,我們初步的工作就是編寫MBR。在進行內核加載之前,我們先通過MBR打印一些字符,來驗證我們之前所說是否正確

編寫MBR驗證程序


;主引導程序

;---------------------



SECTION MBR vstart=0x7c00 ;程序開始的地址

    mov ax, cs            ;使用cs初始化其他的寄存器

    mov ds, ax            ;因爲是通過jmp 0:0x7c00到的MBR開始地址

    mov es, ax            ;所以此時的cs爲0,也就是用0初始化其他寄存器

    mov ss, ax            ;此類的寄存器不同通過立即數賦值,採用ax中轉

    mov fs, ax

    mov sp, 0x7c00  ;初始化棧指針



;清屏利用0x10中斷的0x6號功能

;------------------------

    mov ax, 0x600

    mov bx, 0x700

    mov cx, 0

    mov dx, 0x184f



    int 0x10

    ;獲取光標位置

;---------------------

    mov ah, 3   ; 3號子功能獲取光標位置

    mov bh, 1   ; bh寄存器存儲帶獲取光標位置的頁號,從0開始,此處填1可以看成將光標移動到最開始

    int 0x10



;打印字符串

;------------------

    mov ax, message

    mov bp, ax



    mov cx, 6    ;字符串長度,不包括'\0'

    mov ax, 0x1301

    mov bx, 0x2



    int 0x10



    jmp $



    message db "My MBR"

    times 510-($-$$) db 0 

    db 0x55, 0xaa

這段代碼通過0x10號中斷直接操控顯卡,達到打印字符串的目的

編寫好後通過


nasm -o mbr.bin mbr.S

dd if=mbr.bin of=/home/ba/bochs/hd60M.img bs=512 count=1 conv=notrunc

對我們的彙編代碼進行編譯並寫入之前創建的磁盤中,接下來運行bochs,應該可以看到如下結果

mark

現在我們通過bochs的調試看一下程序到底是怎麼執行的,和我們之前所說的是否一致

mark

這幅圖是在我們開啓bochs時顯示的結果,很明顯可以看到他的cs:ip寄存器的值和我們之前所說的結果一致,在這裏進行跳轉之後接下來肯定就是一系列的初始化工作了,我們跳過這些初始化的工作,直接進入到MBR執行的開始位置,也就是地址0x7c00處

mark

可以看到,左邊是bochs初始化完成之後的輸出,這是已經運行到了0x7c00後的結果,看紅框標記的地方,有沒有感覺很熟悉,這就是我們mbr的第一行代碼啦,接下來就會按照我們所寫的那樣,清屏,打印了。

讀取硬盤

前面通過打印字符串對開機啓動過程做了個小小的驗證,接下來需要讓我們的MBR讀取硬盤啦,因爲加載kernel的話肯定需要從硬盤中讀入數據


; 主引導程序

;---------------------



%include "boot.inc"



SECTION MBR vstart=0x7c00 ;程序開始的地址

    mov ax, cs            ;使用cs初始化其他的寄存器

    mov ds, ax            ;因爲是通過jmp 0:0x7c00到的MBR開始地址

    mov es, ax            ;所以此時的cs爲0,也就是用0初始化其他寄存器

    mov ss, ax            ;此類的寄存器不同通過立即數賦值,採用ax中轉

    mov fs, ax

    mov sp, 0x7c00  ;初始化棧指針,sp也就是32位下的esp



;清屏利用0x10中斷的0x6號功能

;------------------------

    mov ax, 0x600

    mov bx, 0x700

    mov cx, 0

    mov dx, 0x184f



    int 0x10



;獲取光標位置

;---------------------

    mov ah, 3   ; 3號子功能獲取光標位置

    mov bh, 1   ; bh寄存器存儲帶獲取光標位置的頁號,從0開始,此處填1可以看成將光標移動到最開始

    int 0x10



;打印字符串`

;------------------

    mov ax, message

    mov bp, ax



    mov cx, 6

    mov ax, 0x1301

    mov bx, 0x2



    int 0x10



    message db "My MBR"



    mov eax, LOADER_START_SECTOR    ;起始扇區的lba地址

    mov bx, LOADER_BASE_ADDR        ;寫入的地址

    mov cx, 1                       ;讀入的扇區數

    call rd_disk_m_16



    jmp LOADER_BASE_ADDR



;讀取n個扇區

;---------------------

rd_disk_m_16:       ;eax=扇區號,cx=讀入的扇區數,bx=將數據寫入的內存地址

    mov esi, eax    ;備份eax和cx

    mov di, cx



;設置要讀取的扇區數

    mov dx, 0x1f2

    mov al, al

    out dx, al

    mov eax, esi



;將lba地址存入0x1f3-0x1f6



    ;lba地址0-7位寫入端口0x1f3

    mov dx, 0x1f3

    out dx, al



    ;lba地址8-15位寫入端口0x1f4

    mov cl, 8

    shr eax, cl

    mov dx, 0x1f4

    out dx, al



    ;lba地址16-23位寫入端口0x1f5

    shr eax, cl

    mov dx, 0x1f5

    out dx, al





    shr eax, cl

    and al, 0x0f

    or al, 0xe0

    mov dx, 0x1f6

    out dx, al



;向0x1f7端口寫入讀命令

    mov dx, 0x1f7

    mov al, 0x20

    out dx, al

.not_ready:

    nop

    in al, dx

    and al, 0x88 ;第4位爲1表示硬盤控制器已經準備號數據傳輸,第7位爲1表示硬盤忙

    cmp al, 0x08

    jnz .not_ready



;從0x1f0端口讀數據

    mov ax, di

    mov dx, 256

    mul dx

    mov cx, ax

    mov dx, 0x1f0

.go_on_read:

    in ax, dx

    mov [bx], ax

    add bx, 2

    loop .go_on_read

    ret



    times 510-($-$$) db 0 

    db 0x55, 0xaa

這是boot.inc中的內容


LOADER_BASE_ADDR equ 0x900

LOADER_START_SECTOR equ 0x2

先看看程序的執行流程

  1. 從0x7c00入口處進入mbr

  2. 打印My MBR

  3. 爲讀取磁盤操作傳遞參數,包括讀入的扇區數,讀取的數據寫入的內存地址

  4. 將讀取到的數據寫入0x900,並跳到此處去執行

MBR中的內容差不多就多了,接下來的工作就是逐步完善內核。

發佈了92 篇原創文章 · 獲贊 11 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章