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

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的體系結構而言,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
  1. 擴充套件記憶體:1M以上的記憶體定址空間

    • 這裡面絕大部分記憶體區域只能在保護模式下才能定址到,
    • 但有一部分既可以在保護模式下,也可以在真實模式下定址,這就是我們前面提到過的地址100000h--10ffefh之間的這塊記憶體,為了表明其特殊性,我們把這塊有趣的記憶體區叫做“高階記憶體”
    • (如果當初IBM把上位記憶體區的東西放在低端,就沒有這麼多麻煩了)
  2. 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一樣。
  3. 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的程式碼部分解析:

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 程式碼分析連結:

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為起始地址的記憶體實體地址處。