bootsect.s 预备——Linux-0.11 剖析笔记(一)
文章目錄
- boot 目錄下文件介紹
- 16 位代碼是什么意思
- 計算機啟動過程
- Linux 0.11 啟動過程
boot 目錄下文件介紹
boot 目錄中一共有三個文件,都是用匯編語言寫的,如下圖(圖來自趙炯的書)
我是在 https://mirrors.edge.kernel.org/pub/linux/kernel/Historic/old-versions/ 下載的 Linux-0.11 的源碼。
再提供一個下載地址:http://oldlinux.org/Linux.old/
打開 boot 文件夾,和上圖對比一下。
修改日期差了 8 個小時,應該是不同時區導致的。
這 3 個文件雖然都是匯編程序,但卻使用了兩種語法格式。 bootsect.s 和 setup.s 是實模式下運行的 16 位代碼程序,使用類似于 Intel 匯編語法,并需要使用 Intel 8086 匯編編譯器(as86)和連接器(ld86),這一點在 Makefile 里面有體現:
...AS86 =as86 -0 -a LD86 =ld86 -0...boot/setup: boot/setup.s$(AS86) -o boot/setup.o boot/setup.s$(LD86) -s -o boot/setup boot/setup.oboot/bootsect: boot/bootsect.s$(AS86) -o boot/bootsect.o boot/bootsect.s$(LD86) -s -o boot/bootsect boot/bootsect.o而 head.s,則使用 AT&T 匯編格式, 運行在保護模式下,需要用 GNU 的 gas 進行編譯、gld 進行鏈接。Makefile 里面有體現:
AS =gas LD =gldLinus 當時使用兩種匯編編譯器的主要原因在于對于 Intel x86 處理器系列來講,那時的 GNU 編譯器僅支持 i386 及以后的 CPU,如果不用特殊方法,就無法編譯出在實模式下運行的程序。
直到 1994 年以后發布的 GNU as 匯編器才開始支持編譯 16 位代碼(使用 .code16 偽指令)。
16 位代碼是什么意思
有人會問,這里的 16 位代碼到底是什么意思?實模式和保護模式有何區別?
最早的 8086 處理器,寄存器和數據線都是 16 位,地址線是 20 位,可以尋址 1MB 的內存。16 位的寄存器如何表示 20 位的物理地址呢?
答案是 :物理地址 = 段基址<<4 + 段內偏移
段基址和段內偏移都是 16 位的,16 位的段基址左移 4 位,再加上段內偏移,就構成了 20 位的物理地址。
后來,有了 80286,它的寄存器和數據線也是 16 位,地址線變成了 24 位。在尋址上,開創了新方法。尋址時,段寄存器保存的值不再是段基址,而是段選擇子(selector),其中高 13 位指向描述符表(descriptor table)的條目;低 2 位則定義了請求權限,從 0 到 3;剩下的 1 位表示是使用全局描述符表(GDT)還是局部描述符表(LDT)。描述符表的條目為 8 字節長,其中包括 24 位的段起始物理地址,這個 24 位段起始物理地址再加上 16 位的段內偏移,就構成了訪問內存的物理地址。為了和 8086 那種尋址模式區別開,就把原先的稱為“實模式”,現在的這種稱作“保護模式”。當然,為了兼容,80286 及以后的處理器是支持實模式的。
我認為,16 位代碼是指在實模式下工作的代碼,在這種模式下,寄存器和數據線都是 16 位,于是稱為 16 位代碼。
你有沒有想過這樣一個問題——計算機是怎么開始執行我們寫的指令的?這就需要了解計算機的啟動過程。
計算機啟動過程
對于 32 位的 x86 處理器,加電后,處于實模式,段寄存器 CS 的內容是 0xF000,IP 寄存器的內容為 0xFFF0,按照實模式地址的合成方法,得到的地址就是 0xFFFF0;另外,在剛啟動的時候,處理器會將地址線 A20~A31 強制為高電平,所以,32 根地址線發出的物理地址就是 0xFFFF_FFF0,這個地址存放的就是 BIOS 的第一條指令。
在 Bochs 上調試的時候,可以看到第一條指令是:
jmpf 0xf000:e05b上電后,BIOS 需要做的工作有很多,比如
1)初始化各種主板芯片組
2)初始化鍵盤控制器
3)初始化中斷向量 ,中斷服務例程.
4)初始化 VGA BIOS 控制器
5)顯示 BIOS 的版本和公司名稱
6)掃描軟驅和各種介質容量
7)讀取 CMOS 的啟動順序配置,并檢測啟動裝置是否正常
8)調用 INT 19h
對于匯編語言的學習,需要了解最后兩個步驟,(7)和(8)做的工作是:
BIOS 按照啟動順序,選擇排在第一位的儲存設備,讀取該設備的第一個扇區(大小是 512B)到內存 0x7c00(物理地址)處,然后檢查這 512 個字節的最后兩個字節是不是 0x55 和 0xAA,如果是則表明這個設備可以用于啟動,這個扇區就是主引導扇區;如果不是,則繼續嘗試啟動順序中的下一個設備。
如果確實是可以啟動的設備,則用一個華麗的 JMP 指令跳到 0x0000:0x7c00 處執行。
jmp 0x0000:0x7c00Linux 0.11 啟動過程
如圖所示,可以分為幾個階段:
另外,網上還有一張圖,也很好。
因為當時 system 模塊的長度不會超過 512KB,所以 bootsect.s 把 system 模塊讀入到從內存地址 0x10000 處并不會覆蓋自己。
有人會問,bootsect.s 為什么不把 system 模塊直接加載到物理地址 0x0000 處,而是先加載到 0x10000,再通過 setup.s 把 system 模塊移動到物理內存起始位置處,這是何苦呢?
這是因為 setup.s 代碼執行的時候,需要用到 ROM BIOS 中的中斷調用來獲取機器的一些參數,例如顯卡模式、硬盤參數表等,而 BIOS 中斷向量表存放在物理內存開始處,大小為 1KB。必須等使用完 BIOS 中斷調用后,才能“過河拆橋”。
參考資料
【1】《Linux 內核完剖析》,趙炯,械工業出版社(ISBN:9787111180326)
【2】維基百科「 Intel 80286 」詞條
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的bootsect.s 预备——Linux-0.11 剖析笔记(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 大数字类
- 下一篇: setup.s 解读——Linux-0.