oslab oranges 一個操作系統的實現 實驗五 讓操作系統走進保護模式


實驗目的:

• 如何從軟盤讀取並加載一個Loader程序到操作

系統,然后轉交系統控制權

• 對應章節:第四章

實驗內容:

1. 向軟盤鏡像文件寫入一個你指定的文件,手

工讀取在磁盤中的信息

2. 在軟盤中找到指定的文件,讀取其扇區信息

3. 將指定文件裝入指定內存區,並執行

4. 學會在bochs中使用xxd讀取反匯編信息

完成本次實驗要思考的問題:

1.FAT12格式是怎樣的?

2.如何讀取一張軟盤的信息

3.如何在軟盤中找到指定的文件

4.如何在系統引導過程中,從讀取並加載一個可執行文件

到內存,並轉交控制權?

5.為什么需要這個Loader程序不包含dos系統調用?

關鍵技術:

引導扇區,loader與控制權轉交。

一個操作系統從開機到開始運行,大 致經歷“引導→加載內核入內存→跳入保護模式→開始執行內核”這樣一個過程。也就是說,在內核開始執行之前不但要加載內 核,而且還有准備保護模式等一系列工作,如果全都交給引導扇區來做,512字節很可能是不夠用的,所以,把這個過程交給另外的模塊來完成,我們把這個模塊叫做Loader。引導扇區負責把Loader加載入內存並且把控制權交給它,其他工作交給 Loader來做,因為它沒有512字節的限制,將會靈活得多。

在這里,為了操作方便,把軟盤做成FAT12格式,這樣對Loader以及今后的Kernel(內核)的操作將會非常簡單易行。

實驗步驟:

1. 向軟盤鏡像文件寫入一個你指定的文件,手工讀取在磁盤中的信息

(1)修改引導扇區

增加BPB等信息以被識別。修改boot.asm,生成boot.bin,寫入已引導扇區。(boot.asm在第一章出現)

 

把生成的Boot.bin寫入磁盤引導扇區,運行的效果沒有變,仍然會是圖1.1的樣子。但是,現在的軟盤已經能夠被DOS以及 Linux識別了,我們已經可以方便地往上添加或刪除文件了。

修改bochsrc:

修改vgaromimage對應的文件位置,以你的實際安裝位置為准

注釋掉keyboard_mapping一行

增加display_library: sdl

 

 

 

 

 

 

(2)一個簡單的loader

Loader.asm。編譯為loader.bin

 

 

 

(3)讀軟盤,根目錄部分得loader起始扇區號

為加載loader.bin到軟盤,需要讀軟盤。

核心思想為修改boot.asm,引導扇區,使其功能改為讀軟盤,尋找loader.bin

bios中斷 int 13h讀軟盤。

 

 

 

中斷需要的參數不是原來提到的從第0扇區開始的扇區號,而是柱面號、磁頭號以及在當前柱面上的扇區號3個分量,所以需要我們自己來轉換一下。對於1.44MB的軟盤來講,總共有兩面(磁頭號0和1),每面80個磁道(磁道號0~79),每個 磁道有18個扇區(扇區號1~18)。下面的公式就是軟盤容量的由來: 2×80×18×512=1.44MB

於是,磁頭號、柱面(磁道)號和起始扇區號可以用圖所示的方法來計算。

注意如Q=0,1,2,3,4,0與1為柱面0,在兩面為磁道0.0為磁頭0,1為磁頭1.

可知寫軟盤時先寫一個柱面,上下磁道都寫滿了再切換柱面。

 

 

 

對應,寫讀軟盤函數到boot.asm。

 

 

 

 

由於上述代碼用到堆棧,故有初始化堆棧,初始化ss和esp

 

 

 

之后寫查找loader.bin的函數

 

 

 

 

 

 

遍歷根目錄區所有的扇區,將每一個扇區加載入內存,然后從中尋找文件名為Loader.bin的條目,直到找到為止。找到的那一刻,es:di是指向條目中字母N后面的那個字符。其中宏定義與變量

 

 

 

 

 

 

由於在讀取過程中打印一些字符串,我們需要一個函數來做這項工作。為了節省代碼長度,字符串的長度都設為9字節,不夠則用空格補齊,這樣就相當於一個二維數組,定位的時候通過數字就可以了。顯示字符串的函數DispStr,調用它的時候只要保證寄存器dh的值是字符串的序號就可以了。 

 

 

 

(4)寫入boot.bin,loader.bin到軟盤並反匯編調試

寫入:

 

 

 

但此時boot.bin只是找到了loader.bin,運行不會有效果,所以加斷點反匯編調試。

b 0x7c00 是因為bios把boot sector加載到0x7c00處。見boot.asm

 

 

 

N 單步執行,遇到函數則跳過。這里跳過了BPB

U 反匯編。/45 為count,反匯編的指令個數。用help x可以查看信息

 

 

 

 

 

 

 

 

 

然后書上b 0x7cb4是在boot.bin的jmp $處下斷點,根據實際情況(0x7cad處是jmp.-2,jmp $),我在b 0x7cad處下斷點,然后

x /32xb es:di - 16 ←查看es:di 前后的內存

x /13xcb es:di - 11 ←容易發現es:di 前乃我們要找的文件名

sreg ←查看es

r查看di

 

 

 

 

 

 

可見拷貝成功。

(5)根據(3)讀根目錄得到的扇區號,讀FAT將loader加載到內存

繼續修改boot.asm。

現在我們已經有了Loader.bin的起始扇區號,我們需要用這個扇區號來做兩件事:一件是把起始扇區裝入內存,另一件則是通過它找到FAT中的項,從而找到Loader占用的其余所有扇區。

在這里,我們把Loader裝入內存的BaseOfLoader:OffsetOfLoader處

寫一個函數來找到FAT中的項。函數的輸入就是扇區號,輸出則是其對應的FAT項的值 

 

 

 

新增加了宏SectorNoOfFAT1,它與前面提到的RootDirSectors、SectorNoOfRootDirectory等宏一起,與FAT12有關的幾個數字我們都定義成了宏,而不是在程序中進行計算。一方面,這是為縮小引導扇區代碼考慮;

另一方面,這些數字一般情況下是不會變的,寫代碼計算它們其實是一種浪費。

由於一個FAT項可能跨越兩個扇區,所以在代碼中一次總是讀兩個扇區,以免在邊界發生錯誤。

之后加載loader

 

 

 

新的宏DeltaSectorNo。根據下面的例子來看,文件RIVER.TXT對應的目錄條目中的開始簇號是2。實際上,開始簇號是2對應的是數據區的第一個扇區。所以,我們需要有一個方法來計算簇號為X代表從引導扇區開始算起是第幾個扇區。 根目錄區占用RootDirSectors也即14個扇區,根目錄區的開始扇區號是19,於是用“X+RootDirSectors+19-2”來算出“33”這個正確的扇區號。所以,我們又定義了一個宏DeltaSectorNo為17(即19-2)來幫助計算正確的扇區號: DeltaSectorNo equ 17 

(6)loader移交控制權

上面的代碼調試通過后,我們就已經成功地將Loader加載入內存,下面讓我們來一個跳轉,開始執行Loader

 

 

 

(7)整理boot.asm 測試

為了在執行時實現更好的效果,增加如下代碼

2首先清屏,然后顯示字符串“Booting”。這樣,加載Loader時打印的圓點也會出現在這個字符串的后面。 屏幕上的圓點數目表明我們讀了幾個扇區就把Loader加載完畢。 

 

 

 

在加載完畢跳入loader前打印ready

 

 

 

更新引導扇區和loader。運行

修改bochsrc

 

 

 

 

 

 

運行,成功

 

 

 

 

 

 

 

 

(8)總結

Loader.bin本質上是個.COM文件,最大也不可能超過64KB。但是,我們已經成功突破512字節限制,這個進步無疑是巨大的。

Linux的的引導扇區代碼Boot.s比我們的代碼簡單,它直接把內核移動到目標內存。我們的代碼之所以復雜一些,是因為我們想和MSDOS的磁盤格式兼容,以便調試的時候容易一些。比如現在,我們就完全可以把第3章中的代碼pmtest9.asm編譯一下,將編譯后的二進制命名為Loader.bin並復制到剛剛引導過 的軟盤中覆蓋掉原來簡陋的Loader.bin。你會發現程序馬上可以執行,結果如圖所示。 

 

 

 

現在的Loader僅僅是個Loader,它不是操作系統內核,也不能當做操作系統內核。我們希望自己的操作系統內核至少應該可以在Linux下用GCC編譯鏈接,要不然,永遠用匯編一點一點地寫下去實在是太痛苦了。

那么,現在我們假設已經有了一個內核,Loader肯定要加載它入內存,而且內核開始執行的時候肯定已經在保護模式下了,所以,Loader要做的事情至少有兩件:

加載內核入內存。 跳入保護模式。

 

 2.在軟盤中找到指定的文件,讀取其扇區信息

1.(3)讀軟盤,根目錄部分得loader起始扇區號

 3.將指定文件裝入指定內存區,並執行

1.(5)根據(3)讀根目錄得到的扇區號,讀FAT將loader加載到內存

4.學會在bochs中使用xxd讀取反匯編信息

1.(4)寫入boot.bin,loader.bin到軟盤並反匯編調試

完成本次實驗要思考的問題:

1.FAT12格式是怎樣的?

FAT12 是DOS時代就開始使用的文件系統(File System),直到現在仍然在軟盤上使用。

幾乎所有的文件系統都會把磁盤划分為若干層次以方便組織和管理,這些層次包括:

扇區(Sector):磁盤上的最小數據單元。

簇(Cluster):一個或多個扇區。

分區(Partition):通常指整個文件系統。

引導扇區是整個軟盤的第0個扇區,在這個扇區中有一個很重要的數據結構

叫做BPB(BIOS ParameterBlock),引導扇區的格式如表所示,其中名稱以BPB_開頭的域屬於BPB,以BS_開頭的域不屬於BPB, 只是引導扇區(Boot Sector)的一部分。

 

 

 

緊接着引導扇區的是兩個完全相同的FAT表,每個占用9個扇區。第二個FAT之后是根目錄區的第一個扇區。根目錄區的后面是數據區,如圖所示。

 

 

 

要把Loader復制到軟盤上並讓引導扇區找到並加載它,來看一下引導扇區通過怎樣的步驟才能找到文件,以及如何能夠把文件內容全都讀出來並放進內存里。 為簡單起見,我們規定Loader只能放在根目錄中,而根目錄信息存放在FAT2后面的根目錄區中。

根目錄區。 根目錄區位於第二個FAT表之后,開始的扇區號為19,它由若干個目錄條目(Directory Entry)組成,條目最多有BPB_RootEntCnt個。由於根目錄區的大小是依賴於BPB_RootEntCnt的,所以長度不固定。

根目錄區中的每一個條目占用32字節,格式如表所示。

主要定義了文件的名稱、屬性、大小、日期以及在磁盤中的位置。

 

 

 

舉例:

創建一個虛擬軟盤,假設是x.img,把它作為FreeDos的B盤,格式化后就可以往其中添加文件和目錄了(比如使用FreeDos 里的edit.exe)。這樣,當我們想查看它的格式時,只需用二進制查看器打開x.img就可以。

通過FreeDos在這張虛擬軟盤中添加以下幾個文本文件:

RIVER.TXT,內容為riverriverriver。

FLOWER.TXT,內容為300個單詞flower,用來測試文件跨越扇區的情況。(可以先建一個小文件,最后再把它改長,這 樣可以讓它對應的簇不連續,便於觀察和理解。)

TREE.TXT,內容為treetreetree。

再添加一個HOUSE目錄,然后在目錄nHOUSE下添加兩個文本文件: 

179CAT.TXT,內容為catcatcat。

DOG.TXT,內容為dogdogdog。

由於根目錄區從第19扇區開始,每個扇區512字節,所以其第一個字節位於偏移19*512=9728=0x2600處。用二 進制查看器來看看x.img的偏移0x2600處是什么,如下

 

 

 

RIVER.TXT為例,它的各項值如表所示。

 

 

 

當我們尋找Loader時,只要發現文件名正確就認為它是我們要找的那一個文件。最后剩下最重要的信息DIR_FstClus,即文件開始簇號,它告訴我們文件存

放在磁盤的什么位置,從而讓我們可以找到它。由於一簇只包含一個扇區,所以簡化了計算過程,而且下文中說到“簇”的地方, 你也可以將它替換成“扇區”。

需要注意的是,數據區的第一個簇的簇號是2,而不是0或者1。 

RIVER.TXT的開始簇號就是2,也就是說,此文件的數據開始於數據區第一個簇。

計算根目錄區所占的扇區數:根目錄區條目最多有BPB_RootEntCnt個,扇區數假設根目錄區共占用RootDirSectors個扇區,則有:

之所以分子要加上(BPB_BytsPerSec—1),是為了保證此公式在根目錄區無法填滿整數個扇區時仍然成立。

 

 

 

在本例中,容易算出RootDirSectors=14。所以:數據區開始扇區號=根目錄區開始扇區號+14=19+14=33

33扇區的偏移量是0x4200(512×33),讓我們看一下這里的內容,如下: 

 

 

 

對於小於512字節的文件來說,FAT表用處不大,但如果文件大於512字節,我們需要FAT表來找到所有的簇(扇區)。FAT表有兩個,FAT2可看做是FAT1的備份,它們通常是一樣的。FAT1的開始扇區號是1,偏移為512字節(0x200),如下: 

 

 

 

12位稱為一個FAT項(FATEntry),代表一個簇。第 0個和第1個FAT項始終不使用,從第2個FAT項開始表示數據區的每一個簇,也就是說,第2個FAT項表示數據區第一個簇,依此類推。前文說過,數據區的第一個簇的簇號是2,和這里是相呼應的。

由於每個FAT項占12位,包含一個字節和另一個字節的一半。假設連續3個字節分別如圖所示,那么灰色框表示的是前一個FAT項(FATEntry1),BYTE1是FATEntry1的低8位,BYTE2的低4位是 FATEntry1的高4位;白色框表示的是后一個FAT項(FATEntry2),BYTE2的高4位是FATEntry2的低4位,BYTE3是FATEntry2的高8 位。

 

 

 

通常,FAT項的值代表的是文件下一個簇號,但如果值大於或等於0xFF8,則表示當前簇已經是本文件的最后一個簇。如果值為0xFF7,表示它是一個壞簇。

文件RIVER.TXT的開始簇號是2,對應FAT表中的值為0xFFF,表示這個簇已經是最后一個。 (FF 8F 00 注意是低地址,取ff和8F中的F)

我們來看一個長一點的文件FLOWER.TXT,它的DIR_FstClus值為3,對應第3個FAT項。結合我們打印出的FAT表內容我們知道,此FAT項值為0x008,也就是說,這個簇不是文件的最后一個簇,下一個簇號為8。我們再找到第8個FAT項,發現值為0x009, 接下來第9個FAT項值為0x00A,第0xA個FAT項值為0xFFF。所以,FLOWER.TXT占用了第3、8、9、10,共計4個簇。

這里需要注意一點,一個FAT項可能會跨越兩個扇區,這種情況在編碼實現的過程中要考慮在內。

2.如何讀取一張軟盤的信息

使用bios中斷 int 13h

3.如何在軟盤中找到指定的文件

先根據文件名遍歷根目錄區,找到起始扇區,然后根據起始扇區號查找對應FAT項

4.如何在系統引導過程中,從讀取並加載一個可執行文件

到內存,並轉交控制權?

在引導扇區boot sector中編寫函數,實現先根據文件名遍歷根目錄區,查找文件起始扇區號,然后根據起始扇區號找到對應起始FAT項,接着根據FAT項加載對應扇區到內存,直到加載完畢。(FAT值為FFF)

之后跳轉到加載內存的起始位置,開始執行,就移交了控制權。

5.為什么需要這個Loader程序不包含dos系統調用?

因為要用loader加載內核,跳入保護模式。

Loader調用的是bios的中斷。Dos是操作系統,要用loader裝入,裝入前無法調用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM