1. 程式人生 > >mit-6.828 Lab01:Booting a PC Part2 理論知識

mit-6.828 Lab01:Booting a PC Part2 理論知識

# Part 2 [TOC]
## 學習理論知識 ### 反彙編 把機器語言轉換為組合語言程式碼 ### 扇區 對於PC來說,軟盤,硬碟都可以被劃分為一個個大小為512位元組的區域,叫做**扇區**。一個扇區是一次磁碟操作的最小粒度。每一次讀取或者寫入操作都必須是一個或多個扇區。如果一個磁碟是可以被用來啟動作業系統的,就把這個磁碟的第一個扇區叫做**啟動扇區**。當BIOS找到一個可以啟動的軟盤或硬碟後,它就會把這512位元組的啟動扇區載入到記憶體地址0x7c00~0x7dff這個區域內。 ### BIOS 啟動過程總結 - 當**計算機加電**後,一般不直接執行作業系統,而是執行系統初始化軟體完成基本IO初始化和引導載入功能。簡單地說,系統初始化軟體就是在作業系統核心執行之前執行的一段小軟體。通過這段小軟體,我們可以初始化硬體裝置、建立系統的記憶體空間對映圖,從而將系統的軟硬體環境帶到一個合適的狀態,以便為最終呼叫作業系統核心準備好正確的環境。最終引導載入程式把作業系統核心映像載入到RAM中,並將系統控制權傳遞給它。 - 對於絕大多數計算機系統而言,作業系統和應用軟體是存放在磁碟(硬碟/軟盤)、光碟、EPROM、ROM、Flash等可在掉電後繼續儲存資料的儲存介質上。**計算機啟動後**,*CPU一開始會到一個特定的地址開始執行指令,這個特定的地址存放了系統初始化軟體,負責完成計算機基本的IO初始化,這是系統加電後執行的第一段軟體程式碼。*對於Intel 80386的體系結構而言,PC機中的系統初始化軟體由**BIOS** (Basic Input Output System,即基本輸入/輸出系統,其本質是一個固化在主機板Flash/CMOS上的軟體)和位於軟盤/硬碟引導扇區中的OS Boot Loader(在ucore中的bootasm.S和bootmain.c)一起組成。BIOS實際上是被固化在計算機ROM(只讀儲存器)晶片上的一個特殊的軟體,為上層軟體提供最底層的、最直接的硬體控制與支援。更形象地說,BIOS就是PC計算機硬體與上層軟體程式之間的一個"橋樑",負責訪問和控制硬體。 - 以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 1. 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*** 2.
3. 擴充套件記憶體:1M以上的記憶體定址空間 - 這裡面絕大部分記憶體區域只能在**保護模式**下才能定址到, - 但有一部分既可以在保護模式下,也可以在真實模式下定址,這就是我們前面提到過的地址100000h--10ffefh之間的這塊記憶體,為了表明其特殊性,我們把這塊有趣的記憶體區叫做**“高階記憶體”**。 - (如果當初IBM把上位記憶體區的東西放在低端,就沒有這麼多麻煩了) 4. **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一樣。
** 5. 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 - 該檔案的目的: 1. start CPU, switch to 32-bit protected mode(啟動CPU 並且最終轉到32-bit 保護模式) 2. BIOS loads code from first sector of the hard disk into memory at physical addr 07xc00 3. executing in real mode (%cs=0, %ip=7c00) - 步驟: 1. 初始化重要的segment registers,全部初始化為0 2. 16位指令下,遮蔽中斷,初始化段暫存器 3. 開啟A20 gate,停止取模運算,將高位的空間也可訪問 4. 利用bootstrap GDT轉換到protected mode 5. 跳轉到32-bit模式下的下一個指令 6. 然後在32-bit 保護模式下,設定保護模式的暫存器 7. 設定stack pointer 然後呼叫main.c執行main.c裡面的bootmain函式 - 關於開啟A20 gate的程式碼部分解析: ```assembly 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,名稱是控制器讀取狀態暫存器。下面是它各個位的含義。   ![img](https://images2015.cnblogs.com/blog/809277/201601/809277-20160108221030887-1147554079.jpg)   所以16~18號指令是在不斷的檢測bit1。bit1的值代表輸入緩衝區是否滿了,也就是說CPU傳送給控制器的資料,控制器是否已經取走了,如果CPU想向控制器傳送新的資料的話,必須先保證這一位為0。所以這三條指令會一直等待這一位變為0,才能繼續向後執行。   當0x64埠準備好讀入資料後,現在就可以寫入資料了,所以19~20這兩條指令是把0xd1這條資料寫入到0x64埠中。當向0x64埠寫入資料時,則代表向鍵盤控制器804x傳送指令。這個指令將會被送給0x60埠。   ![img](https://images2015.cnblogs.com/blog/809277/201601/809277-20160108221500184-1601361742.jpg)   通過圖中可見,D1指令代表下一次寫入0x60埠的資料將被寫入給804x控制器的輸出埠。可以理解為下一個寫入0x60埠的資料是一個控制指令。   然後21~24號指令又開始再次等待,等待剛剛寫入的指令D1,是否已經被讀取了。   如果指令被讀取了,25~26號指令會向控制器輸入新的指令,0xdf。通過查詢我們看到0xDF指令的含義如下   ![img](https://images2015.cnblogs.com/blog/809277/201601/809277-20160108222031012-642740036.jpg)   這個指令的含義可以從圖中看到,使能A20線,代表可以進入保護模式了。 > boot.S & main.c 程式碼分析連結: > > https://www.cnblogs.com/fatsheep9146/p/5115086.html
#### - boot/mian.c - boot.S & main.c存在磁碟第一個扇區 - 第二個扇區開始儲存kernel - 核心需為ELF格式 - Boot up steps: 1. CPU啟動後,載入BIOS進入記憶體並執行它 2. BIOS初始化裝置、一系列中斷準備、讀取第一個扇區的boot device到記憶體並跳到該處 3. 從boot.S開始控制,它建立**保護模式**+1個stack,以便C程式碼可以跑,然後呼叫bootmain()函式 4. bootmain()函式讀取核心並跳到核心 - segment 和 sector的關係:一個segment包含多個sector - readsect(void *dst, uint32_t offset) - readseg(uchar *pa, uint count, uint offset) 它的功能從註釋上來理解是,把距離核心起始地址offset個偏移量儲存單元作為起始,將它和它之後的count位元組的資料讀出送入以pa為起始地址的記憶體物理地