《ucore lab1 練習4》實驗報告

2021-09-29 02:31:15 字數 4336 閱讀 8365

首先要介紹一下 對於bootloader訪問硬碟時都是lba模式的pio方式,也就是說所有的i/o操作都是通過cpu訪問硬碟的i/o位址暫存器完成。作業系統位於第乙個硬碟上,而訪問第乙個硬碟的扇區可以設定i/o埠0x1f0~0x1f7來改變位址暫存器實現。下述**所顯示的即為0x1f0~0x1f7所對應的功能:

i/o位址

功能0x1f0

讀資料,當0x1f7不為忙狀態時,可以讀

0x1f1

可獲得詳細的錯誤資訊

0x1f2

與讀寫的扇區數量,每次讀寫前,都需要表明要讀寫幾個扇區

0x1f3

如果是lba格式,就是讀lba引數的0~7位

0x1f4

如果是lba格式,就是讀lba引數的8~15位

0x1f5

如果是lba格式,就是讀lba引數的16~23位

0x1f6

第0~3位:如果是lba模式就是24-27位 第4位:為0主盤;為1從盤

0x1f7

狀態和命令暫存器。操作時先給命令,再讀取,如果不是忙狀態就從0x1f0埠讀資料

實際操作中,需要知道怎樣與硬碟互動。閱讀材料中同樣給出了答案:所有的io操作是通過cpu訪問硬碟的io位址暫存器完成。硬碟共有8個io位址暫存器,其中第1個儲存資料,第8個儲存狀態和命令,第3個儲存要讀寫的扇區數,第4~7個儲存要讀寫的起始扇區的編號(共28位)。了解這些資訊,就不難程式設計實現啦。

bootloader讀取扇區的功能是在boot/bootmain.c的readsect函式中實現的,先貼**:

static void

waitdisk

(void)

/* readsect - read a single sector at @secno into @dst */

static void

readsect

(void *dst, uint32_t secno)

一般主機板有2個ide通道,每個通道可以接2個ide硬碟。訪問第乙個硬碟的扇區可設定io位址暫存器0x1f0-0x1f7實現的,具體引數見下表。一般第乙個ide通道通過訪問io位址0x1f0-0x1f7來實現,第二個ide通道通過訪問0x170-0x17f實現。每個通道的主從盤的選擇通過第6個io偏移位址暫存器來設定。從outb()可以看出這裡是用lba模式的pio(program io)方式來訪問硬碟的。從磁碟io位址和對應功能表可以看出,該函式一次只讀取乙個扇區。

readseg簡單包裝了readsect,可以從裝置讀取任意長度的內容。

static void

readseg

(uintptr_t va, uint32_t count, uint32_t offset)

}

根據**可以得出讀取硬碟扇區的步驟:

首先從原理上分析載入流程。

根據elf header和program header表的資訊,我們便可以將elf檔案中的所有segment逐個載入到記憶體中

elf定義:

/* file header */

struct elfhdr

;

在這裡我們只需要關注其中的幾個引數,e_magic,是用來判斷讀出來的elf格式的檔案是否為正確的格式;e_phoff,是program header表的位置偏移;e_phnum,是program header表中的入口數目;e_entry,是程式入口所對應的虛擬位址。

巨集定義:
#define elfhdr          ((struct elfhdr *)0x10000) 

#define sectsize 512

bootloader載入os的功能是在bootmain函式中實現的,先貼**:
void

bootmain

(void

)struct proghdr *ph,

*eph;

// elf頭部有描述elf檔案應載入到記憶體什麼位置的描述表,

// 先將描述表的頭位址存在ph

ph =

(struct proghdr *)(

(uintptr_t)elfhdr + elfhdr-

>e_phoff)

; eph = ph + elfhdr-

>e_phnum;

// 按照描述表將elf檔案中資料載入記憶體

for(

; ph < eph; ph ++

)// elf檔案0x1000位置後面的0xd1ec位元被載入記憶體0x00100000

// elf檔案0xf000位置後面的0x1d20位元被載入記憶體0x0010e000

// 根據elf頭部儲存的入口資訊,找到核心的入口((

void(*

)(void))

(elfhdr-

>e_entry &

0xffffff))

(); bad:

outw

(0x8a00

,0x8a00);

outw

(0x8a00

,0x8e00);

while(1

);}

輸入make debug啟動gdb,並在bootmain函式入口處即0x7d0d設定斷點,輸入c跳到該入口

單步執行幾次,執行到call readseg處,由於該函式會反覆讀取硬碟,為節省時間,可在下一條語句設定斷點,避免進入到readseg函式內部反覆執行迴圈語句。(或者直接輸入n即可,不用這麼麻煩)

執行完readseg後,可以通過x/xw 0x10000查詢elf header的e_magic的值,查詢結果如下,確實與0x464c457f相等,所以校驗成功。注意,我們的硬體是小端位元組序(這從asm檔案的彙編語句和二進位制**的對比中不難發現),因此0x464c45實際上對應字串"elf",最低位的0x7f字元對應del。

(gdb) x/xw 0x10000

0x10000

:0x464c457f

繼續單步執行,由0x7d2f mov 0x1001c,%eax可知elf header的e_phoff欄位將載入到eax暫存器,0x1001c相對0x10000的偏移為0x1c,即相差28個位元組,這與elf header的定義相吻合。執行完0x7d2f處的指令後,可以看到eax的值變為0x34,說明program header表在檔案中的偏移為0x34,則它在記憶體中的位置為0x10000 + 0x34 = 0x10034.查詢0x10034往後8個位元組的內容如下所示:

(gdb) x/

8xw 0x10034

0x10034

:0x00000001

0x00001000

0x00100000

0x00100000

0x10044

:0x0000dac4

0x0000dac4

0x00000005

0x00001000

struct proghdr 

;

program headers:

type offset virtaddr physaddr filesiz memsiz *** align

load 0x001000

0x00100000

0x00100000

0x0dac4

0x0dac4 r e 0x1000

load 0x00f000

0x0010e000

0x0010e000

0x00aac

0x01dc0 rw 0x1000

gnu_stack 0x000000

0x00000000

0x00000000

0x00000

0x00000 rwe 0x10

繼續單步執行,由0x7d34 movzwl 0x1002c,%esi可知elf header的e_phnum欄位將載入到esi暫存器,執行完x07d34處的指令後,可以看到esi的值變為3,這說明一共有3個segment。

後面是通過磁碟i/o完成三個segment的載入,不再贅述。

從硬碟讀了8個扇區資料到記憶體0x10000處,並把這裡強制轉換成elfhdr使用;

校驗e_magic欄位;

根據偏移量分別把程式段的資料讀取到記憶體中。

ucore lab1 練習5的實驗報告.

《ucore lab1 練習5》實驗報告

我們需要在lab1中完成kdebug.c中函式print stackframe的實現,可以通過函式print stackframe來跟蹤函式呼叫堆疊中記錄的返回位址。如果能夠正確實現此函式,可在lab1中執行 make qemu 後,在qemu模擬器中得到類似如下的輸出 請完成實驗,看看輸出是否與上...

MOOS ivp 實驗二 C 程式設計練習(1)

在moos ivp的第二個實驗中,主要任務是在linux系統中進行c 的程式設計練習。總結 主要記錄一些關於vim的相關操作以及linux中c 的相關程式實驗。基本上所有c 實驗的第一步都是構建乙個hello world檔案,此次實驗當然也不例外。1.先建立乙個文件用來編輯,使用 vim hello...

Week4 結對練習 團隊作業1

2017 10 14 10 00pm,以部落格發表日期為準。晚交 0分 遲交兩周以上 倒扣本次作業分數 抄襲 倒扣本次作業分數 閱讀calculator類,主要實現兩個運算元的加減乘除運算,結合calstring類,擴充套件calculator類實現四則混合運算。類的分析可用思維導圖,參考 附加題 ...