mit-6.828 Lab01:Booting a PC Part2 理論知識
Part 2
目錄
學習理論知識
反彙編
把機器語言轉換為組合語言程式碼
扇區
對於PC來說,軟盤,硬碟都可以被劃分為一個個大小為512位元組的區域,叫做扇區。一個扇區是一次磁碟操作的最小粒度。每一次讀取或者寫入操作都必須是一個或多個扇區。如果一個磁碟是可以被用來啟動作業系統的,就把這個磁碟的第一個扇區叫做啟動扇區。當BIOS找到一個可以啟動的軟盤或硬碟後,它就會把這512位元組的啟動扇區載入到記憶體地址0x7c00~0x7dff這個區域內。
BIOS 啟動過程總結
-
當計算機加電後,一般不直接執行作業系統,而是執行系統初始化軟體完成基本IO初始化和引導載入功能。簡單地說,系統初始化軟體就是在作業系統核心執行之前執行的一段小軟體。通過這段小軟體,我們可以初始化硬體裝置、建立系統的記憶體空間對映圖,從而將系統的軟硬體環境帶到一個合適的狀態,以便為最終呼叫作業系統核心準備好正確的環境。最終引導載入程式把作業系統核心映像載入到RAM中,並將系統控制權傳遞給它。
-
對於絕大多數計算機系統而言,作業系統和應用軟體是存放在磁碟(硬碟/軟盤)、光碟、EPROM、ROM、Flash等可在掉電後繼續儲存資料的儲存介質上。計算機啟動後,CPU一開始會到一個特定的地址開始執行指令,這個特定的地址存放了系統初始化軟體,負責完成計算機基本的IO初始化,這是系統加電後執行的第一段軟體程式碼。
-
以Intel 80386為例,計算機加電後,CPU從實體地址0xFFFFFFF0(由初始化的CS:EIP確定,此時CS和IP的值分別是0xF000和0xFFF0))開始執行。在0xFFFFFFF0這裡只是存放了一條跳轉指令,通過跳轉指令跳到BIOS例行程式起始點。BIOS做完計算機硬體自檢和初始化後,會選擇一個啟動裝置(例如軟盤、硬碟、光碟等),並且讀取該裝置的第一扇區(即主引導扇區或啟動扇區)到記憶體一個特定的地址0x7c00處,然後CPU控制權會轉移到那個地址繼續執行。至此BIOS的初始化工作做完了,進一步的工作交給了bootloader。
-
⚠ 計算機加電後,首先處於 真實模式 ,經過boot loader轉換後切換到32-bit 保護模式
Boot loader啟動過程總結
BIOS將通過讀取硬碟主引導扇區到記憶體,並轉跳到對應記憶體中的位置執行bootloader。bootloader完成的工作包括:
- 切換到保護模式,啟用分段機制
- 讀磁碟中ELF執行檔案格式的作業系統到記憶體
- 顯示字串資訊
- 把控制權交給作業系統
對應實現檔案../boot/boot.S 和 ../boot/main.c
A20 gate
- 8088/8086只有20位地址線,按理它的定址空間是2^20,應該是1024KB,但PC機的定址結構是segment:offset,所以segment:offset所能表達的定址空間最大應為0ffff0h + 0ffffh = 10ffefh(大約1088kB)
- 當你用segment:offset的方式企圖定址100000h這個地址時,由於沒有實際的第21位地址線,你實際定址的記憶體是00000h的位置,如果你企圖定址100001h這個地址時,你實際得到的內容是地址00001h上的內容
- 這個事對實際使用幾乎沒有任何影響,但是後來就不行了,出現了80286,地址線達到了24位,使segment:offset定址100000h--10ffefh這將近64K的儲存器成為可能,為了保持向下相容,於是出現了A20 Gate
-
擴充套件記憶體:1M以上的記憶體定址空間
- 這裡面絕大部分記憶體區域只能在保護模式下才能定址到,
- 但有一部分既可以在保護模式下,也可以在真實模式下定址,這就是我們前面提到過的地址100000h--10ffefh之間的這塊記憶體,為了表明其特殊性,我們把這塊有趣的記憶體區叫做“高階記憶體”。
- (如果當初IBM把上位記憶體區的東西放在低端,就沒有這麼多麻煩了)
-
ROM和RAM的地址重疊
- 實際的記憶體條上地址都是連續的,採用技術手段把這段地址空間空出來給ROM 用,比浪費這384K記憶體的成本還要高 所以採用ROM和RAM的地址重疊
- 實際上,往往ROM並不能完全覆蓋整個384K區域,這樣就會有一些地址沒有被ROM佔用,那麼這部分地址上的RAM仍然是可以使用的。
- ROM Shadowing:
- RAM和ROM的效能是有很大差異的,RAM的存取速度要遠遠大於ROM,而且RAM可以32位存取,ROM通常只能16位
- 當機器加電後,先讓ROM有效,RAM無效,然後讀出ROM內容,再讓ROM無效,RAM有效,把讀出的ROM內容放到相同地址的RAM中,並把相應位置的RAM設定為只讀,這樣就把ROM搬到了RAM中,地址完全一樣,只是效能比使用ROM要高些,這塊RAM就好像ROM的Shadow一樣。
-
A20 gate:
-
出現80286以後,為了保持和8086的相容,需要使用第21根地址匯流排在設計上在第21條地址線(也就是A20)上做了一個開關,當這個開關開啟時,這條地址線和其它地址線一樣可以使用,當這個開關關閉時,第21條地址線(A20)恆為0
-
A20 gate在什麼時候需要開啟
-
在真實模式下要訪問高階記憶體區,這個開關必須開啟;
-
在保護模式下,由於使用32位地址線,如果A20恆等於0,那麼系統只能訪問奇數兆的記憶體,即只能訪問0--1M、2-3M、4-5M......,這顯然是不行的,所以在保護模式下,這個開關也必須開啟
-
-
PC如何實現A20 gate:
- 用8042晶片(控制鍵盤的單獨的微控制器),但與鍵盤毫無關係
-
參考資料:https://blog.csdn.net/jxth152913/article/details/52512663
讀boot/boot.S 和 boot/boot.c原始碼
- boot/boot.S
-
該檔案的目的:
- start CPU, switch to 32-bit protected mode(啟動CPU 並且最終轉到32-bit 保護模式)
- BIOS loads code from first sector of the hard disk into memory at physical addr 07xc00
- executing in real mode (%cs=0, %ip=7c00)
-
步驟:
- 初始化重要的segment registers,全部初始化為0
- 16位指令下,遮蔽中斷,初始化段暫存器
- 開啟A20 gate,停止取模運算,將高位的空間也可訪問
- 利用bootstrap GDT轉換到protected mode
- 跳轉到32-bit模式下的下一個指令
- 然後在32-bit 保護模式下,設定保護模式的暫存器
- 設定stack pointer 然後呼叫main.c執行main.c裡面的bootmain函式
-
關於開啟A20 gate的程式碼部分解析:
11 # Enable A20:
12 # For backwards compatibility with the earliest PCs, physical
13 # address line 20 is tied low, so that addresses higher than
14 # 1MB wrap around to zero by default. This code undoes this.
15 seta20.1:
16 inb $0x64,%al # Wait for not busy
17 testb $0x2,%al
18 jnz seta20.1
19 movb $0xd1,%al # 0xd1 -> port 0x64
20 outb %al,$0x64
21 seta20.2:
22 inb $0x64,%al # Wait for not busy
23 testb $0x2,%al
24 jnz seta20.2
25 movb $0xdf,%al # 0xdf -> port 0x60
26 outb %al,$0x60
這部分指令就是在準備把CPU的工作模式從真實模式轉換為保護模式。我們可以看到其中的指令包括inb,outb這樣的IO埠命令。所以這些指令都是在對外部裝置進行操作。根據下面的連結:
http://bochs.sourceforge.net/techspec/PORTS.LST
我們可以檢視到,0x64埠屬於鍵盤控制器804x,名稱是控制器讀取狀態暫存器。下面是它各個位的含義。
所以16~18號指令是在不斷的檢測bit1。bit1的值代表輸入緩衝區是否滿了,也就是說CPU傳送給控制器的資料,控制器是否已經取走了,如果CPU想向控制器傳送新的資料的話,必須先保證這一位為0。所以這三條指令會一直等待這一位變為0,才能繼續向後執行。
當0x64埠準備好讀入資料後,現在就可以寫入資料了,所以19~20這兩條指令是把0xd1這條資料寫入到0x64埠中。當向0x64埠寫入資料時,則代表向鍵盤控制器804x傳送指令。這個指令將會被送給0x60埠。
通過圖中可見,D1指令代表下一次寫入0x60埠的資料將被寫入給804x控制器的輸出埠。可以理解為下一個寫入0x60埠的資料是一個控制指令。
然後21~24號指令又開始再次等待,等待剛剛寫入的指令D1,是否已經被讀取了。
如果指令被讀取了,25~26號指令會向控制器輸入新的指令,0xdf。通過查詢我們看到0xDF指令的含義如下
這個指令的含義可以從圖中看到,使能A20線,代表可以進入保護模式了。
boot.S & main.c 程式碼分析連結:
- boot/mian.c
-
boot.S & main.c存在磁碟第一個扇區
-
第二個扇區開始儲存kernel
-
核心需為ELF格式
-
Boot up steps:
- CPU啟動後,載入BIOS進入記憶體並執行它
- BIOS初始化裝置、一系列中斷準備、讀取第一個扇區的boot device到記憶體並跳到該處
- 從boot.S開始控制,它建立保護模式+1個stack,以便C程式碼可以跑,然後呼叫bootmain()函式
- bootmain()函式讀取核心並跳到核心
-
segment 和 sector的關係:一個segment包含多個sector
-
readsect(void *dst, uint32_t offset)
-
readseg(uchar *pa, uint count, uint offset)
它的功能從註釋上來理解是,把距離核心起始地址offset個偏移量儲存單元作為起始,將它和它之後的count位元組的資料讀出送入以pa為起始地址的記憶體實體地址處。