從cpu加電到加載OS內核的詳細過程(清華大學ucore-lab1總結一)


      結合最近學習清華的OS課,先用“人話”來高度抽象的描述一下我自己的理解。CPU在系統加電也就是我們按下電源開關后,開始初始化他的寄存器,主要是cs和eip(本文基於x86架構),然后在ROM中找到一個叫BIOS(Basic Input Output System),加載到RAM中然后開始執行他,他在進行完設備的自檢和初始化之后,就根據他自己內部的“我該去哪個設備啟動加載程序”表,將其中第一個設備的主引導扇區加載到內存中來,也就是將系統的控制權轉交給了這個程序,然后他在正確的引用了bootloader,也就是我們常說的“引導程序”,這個引導程序就會在內存的I/O區域中讀入硬盤(os的位置)扇區的信息,得到操作系統內核的ELF文件,加載到內存中來,然后將控制權轉交給OS,就大功告成了。


(一)關於BIOS的那些事

  1、我們的第一條指令的地址從何而來?

  cpu在加電之后首先會初始化寄存器,主要是CF寄存器和EIP寄存器,因為他們合起來組成了第一條指令的線性地址。

  The address FFFFFFF0H is beyond the 1-MByte addressable range of the processor while in real-address mode. The processor is initialized to this starting address as follows. The CS register has two parts: the visible segment selector part and the hidden base address part. In real-address mode, the base address is normally formed by shifting the 16-bit segment selector value 4 bits to the left to produce a 20-bit base address. However, during a hardware reset, the segment selector in the CS register is loaded with F000H and the base address is loaded with FFFF0000H. The starting address is thus formed by adding the base address to the value in the EIP register (that is, FFFF0000 + FFF0H = FFFFFFF0H).

【參考IA-32 Intel Architecture Software Developer’s Manual Volume 3: System Programming Guide Section 9.1.4】

  由於386的段base address不是像8086/8088那樣直接存儲在段寄存器中然后左移四位形成,而是通過在段寄存器中存入段選擇子(也叫段選擇符、段選擇器),然后再查找GDT或LDT獲取段的基地址和段界限以及屬性等信息——為了避免每次都存儲器訪問時都進行上述的過程,在處理器的內部特地為每個段配置了一個76位的高速緩沖寄存器,稱其為段描述符高速緩沖寄存器,這些寄存器對程序員是透明的(不可見的)。然后最開始的CS寄存器提供的基地址就是從這兒得來的。

  2、BIOS都做了啥?

  第一條指令是一個跳轉執行,跳轉到BIOS的首地址去執行,系統將控制權轉交給BIOS。BIOS首先進行硬件的自檢和初始初始化之后,會選擇一個啟動設備(例如軟盤、硬盤、光盤等),並且讀取該設備的第一扇區(即主引導扇區或啟動扇區)到內存一個特定的地址0x7c00處,然后CPU控 制權會轉移到那個地址繼續執行。至此BIOS的初始化工作做完了,進一步的工作交給了bootloader。

  加載bootloader:

  bootloader的中文譯名叫“引導程序”,他所在硬件的扇區(512B)叫“引導扇區(boot sector)”,這里簡單的介紹下硬盤的扇區結構。

  硬盤是有很多個512bytes的扇區組成的,這些扇區又進一步划分為多個“分區”,每個分區擁有的扇區在物理上必須是連續的,每個分區的第一個扇區成為起始扇區或者引導扇區。主引導扇區(boot sector)位於硬盤的0磁頭0柱面1扇區,由主引導記錄(Master Boot Record)分區表(Disk Partition Table)組成。其中主引導記錄的作用就是根據分區表檢查分區是否正確以及確定哪個分區為引導分區,並在程序結束后將引導分區的起始扇區加載到內存中。

  

  每個起始扇區(包括主引導扇區)的最后兩個字節都是“0X55AA”作為其結束的標志。


 

(一)關於bootloader的那些事  

  1、bootloader都干了啥

  • 切換到保護模式,啟用分段機制
  • 讀磁盤中ELF執行文件格式的ucore操作系統到內存
  • 顯示字符串信息
  • 把控制權交給ucore操作系統

  2、實模式和保護模式

  一篇博文搞懂一切:http://www.cnblogs.com/immortal-worm/p/5867418.html

  3、關於A20Gate

  這里只從一個較高的抽象級給大家介紹一下這個概念。(A20Gate就是的cpu從低到高0計數開始數的第20根地址線)

  首先在實模式下,我們知道cpu只有20根地址線可以用來尋址,也就是說理論上我們最大的尋址范圍是2^20也就是1111 1111 1111 1111 1111=1M的空間,第21位是1,后面的全是0。但是實際上我們都知道x86是通過【段寄存器*4+EIP寄存器】來得到線性地址的。很明顯這樣做的最大尋址空間就變成0xffff*4 + 0xffff = 0x10ffef也就是1 0000 1111 1111 1110 1111——這個值是大於理論上的1M的,那么在實模式下,我們對於超出1M的地址,引入了一種叫wrap-around的技術,即對於的超出的部分以1M位模進行取余,這樣就不會出現越界的情況。此時我們不難發現在實模式下A20Gate是處於關閉狀態的,原因很簡單,因為它是第21根地址線,而實模式只打開了20根。

  那么現在bootloader要將實模式轉換到保護模式下,首先要做的就是打開A20Gate,這里涉及到和鍵盤的8042芯片交互,在了解具體的打開方式之前,要先儲備一點x86的IO端口相關的知識。

  基礎知識之IO端口:http://www.cnblogs.com/immortal-worm/p/5867690.html

 

  多數PC都使用鍵盤控制器(8042芯片)來處理A20Gate。

  從理論上講,打開A20Gate的方法是通過設置8042芯片輸出端口(64h)的2nd-bit,但事實上,當你向8042芯片輸出端口進行寫操作的時候,在鍵盤緩沖區中,或許還有別的數據尚未處理,因此你必須首先處理這些數據。

 

  流程如下:

 

   1. 禁止中斷;

 

   2. 等待,直到8042 Inputbuffer為空為止;

 

   3. 發送禁止鍵盤操作命令到8042Input buffer;

 

   4. 等待,直到8042 Inputbuffer為空為止;

 

   5. 發送讀取8042 OutputPort命令;

 

   6. 等待,直到8042 Outputbuffer有數據為止;

 

   7. 讀取8042 Outputbuffer,並保存得到的字節;

 

   8. 等待,直到8042 Inputbuffer為空為止;

 

   9. 發送Write 8042Output Port命令到8042 Input buffer;

 

   10. 等待,直到8042 Inputbuffer為空為止;

 

   11. 將從8042 OutputPort得到的字節的第2位置1(OR 2),然后寫入8042 Input buffer;

 

   12. 等待,直到8042 Inputbuffer為空為止;

 

   13. 發送允許鍵盤操作命令到8042Input buffer;

 

   14. 打開中斷。

  4、配置GDT表

  GDT表的詳細介紹在前文保護模式中已經給出,現在我們只要用lgdt指令將gdt表的位置加載到dgtr寄存器中即可。

  5、使能保護模式

  386之后x86架構的cpu設置了控制寄存器,其中CR0的D0位決定了cpu訪問內存的模式。在打開了A20Gate、配置好了GDT表之后,將其D0位置1即可。

   6、從硬盤中讀取ELF格式的操作系統

  在使CPU進入到保護模式之后,就要開始從硬盤的相應位置去讀取ELF格式的OS內核了,這個環節主要分兩步,一個是從硬盤中讀出ELF文件,然后從ELF文件的header進入讀取整個os的內核。


免責聲明!

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



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