本文內容的重點是x86硬件架構下Linux0.11的引導代碼部分,不討論BIOS本身及其加載,簡略介紹BIOS加載引導扇區的部分。
和現在流行的UEFI不同,這個古老的系統由BIOS引導。
BIOS是英文"Basic Input Output System"的縮略詞,直譯過來後中文名稱就是"基本輸入輸出系統"。——百度百科
1. 加載硬盤數據之前
我們都知道,CPU只能執行內存中的代碼。如果代碼在硬盤等外設上面,就需要先將其加載進內存再執行。
在Windows下,當我們雙擊exe程序希望運行它的時候,操作系統會幫我們把硬盤中的程序讀入內存然後運行。在按下計算機電源鍵的時候,操作系統自身也需要"被"加載進內存。這個操作可以分爲幾個步驟,第一步的加載就是由BIOS完成的,這時CPU處於實模式。
- 注:所有x86 CPU在剛上電時都運行在“實模式”下,這個時候的CPU就相當於一個8086,運行16位代碼並擁有20位尋址能力。
2. BIOS加載引導扇區
- 什麼是引導扇區:引導扇區就是磁盤等存儲外設的第一個物理扇區。這個扇區又叫MBR(Master Boot Record,主引導記錄)
Linux0.11的代碼就從這裏開始。 - BIOS會把指定磁盤的第一個物理扇區的512字節內容複製到內存的0x07C00處,這是一個約定,所有的BIOS都會這麼做。
這個時候CS:IP就指向物理地址0x07c00,下一步CPU就會運行從磁盤讀來的第一條指令,也就是Linux0.11的第一條指令。至此,第一部分的引導就完成了。
3. Linux的代碼第一次被CPU運行
- 當第一句Linux的代碼被運行,BIOS就結束了它加載MBR的使命。從此,計算機的控制權就交給了Linux。
- 前面提到這時候處於實模式,MBR中放的是一段8086的機器碼,這段程序是由彙編語言寫成的,源文件是
/boot/bootsect.s
。他的作用是將另外兩批Linux代碼,分別是4個扇區的/boot/setup.s
和240個扇區的system模塊複製進入內存,本篇文章介紹到屏幕上顯示“Loading system …”爲止,後面的內容將在下一篇文章中介紹。
以下是這個文件的開頭幾行,每一個數據的作用將在後面說明。
SYSSIZE = 0x3000
SETUPLEN = 4 ! nr of setup-sectors
BOOTSEG = 0x07c0 ! original address of boot-sector
INITSEG = 0x9000 ! we move boot here - out of the way
SETUPSEG = 0x9020 ! setup starts here
SYSSEG = 0x1000 ! system loaded at 0x10000 (65536).
ENDSEG = SYSSEG + SYSSIZE ! where to stop loading
(注意註釋符是!
不是;
)
bootsect.s
的前幾條指令如下,它首先做的事是把自身(位於0x07c00,也就是段地址爲BOOTSEG
,偏移量爲0)複製到物理地址0x90000(段地址爲INITSEG
,偏移量爲0)處。需要說明以下rep
命令,它的作用是重複執行下面一條命令直到CX寄存器爲0,每執行一次會將CX的值減1。可以看到執行了256次“字傳送”操作,也就是移動了512個字節(一個扇區)的數據。
start:
mov ax,#BOOTSEG
mov ds,ax
mov ax,#INITSEG
mov es,ax
mov cx,#256
sub si,si
sub di,di
rep
movw
下面有一個關鍵的jmpi的指令,它將CS的值置爲INITSEG
也就是0x9000,IP的值置爲標號go所標記的指令處的偏移量。這個指令使程序跳轉到新複製到的位置繼續執行。然後重置數據段地址寄存器和附加段地址寄存器的值爲INITSEG
的值,也就是0x9000。並且將棧頂指針指向0x9FF00
jmpi go,INITSEG
go: mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,#0xFF00 ! arbitrary value >>512
- 在第一段代碼把自己在內存中的位置重新安排好之後,就要開始裝載開始提到的4個扇區的Linux程序了。這個裝載操作要藉助BIOS提供的中斷服務程序:直接磁盤服務(Direct Disk Service——INT 13H)的02H功能。如果操作成功則跳轉到標號
ok_load_setup:
處,否則使用功能號00H將磁盤系統復位後重新讀取磁盤數據。
02H功能:從硬盤讀取數據:
- AH存放功能號02H,AL存放要讀取的扇區數量
- ES:BX表示了緩衝區的地址,即複製的目標地址,BX就是緩衝區的偏移量
- CH存放柱面號,CL存放讀取的起始扇區
- DH存放磁頭號,DL存放驅動器號,00H~7FH:軟盤;80H~0FFH:硬盤
02H功能執行完成會影響CF標誌位:
- CF=0——操作成功,AH=00H,AL=傳輸的扇區數
- CF=1——操作失敗,AH=狀態代碼(略)
00H功能:磁盤系統復位:
- AH存放功能號00H
- DL存放驅動器號,00H~7FH:軟盤;80H~0FFH:硬盤
00H功能執行完成後會影響CF標誌位:
- CF=0——操作成功,AH=00H
- CF=1——操作失敗,AH=狀態代碼
load_setup:
mov dx,#0x0000 ! drive 0, head 0
mov cx,#0x0002 ! sector 2, track 0
mov bx,#0x0200 ! address = 512, in INITSEG
mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors
int 0x13 ! read it
jnc ok_load_setup ! ok - continue
mov dx,#0x0000
mov ax,#0x0000 ! reset the diskette
int 0x13
j load_setup
ok_load_setup:
- 下一步是讀取磁盤參數,用到int 13H的08H功能,從代碼中也能看出來Linux0.11是存在軟盤中的
08H功能:讀取磁盤參數
- AH存放功能號08H ,
- DL存放驅動器號,00H~7FH:軟盤;80H~0FFH:硬盤
00H功能執行完成後會影響CF標誌位:
- CF=1——操作失敗,AH=狀態代碼,
- CF=0——操作成功:
BL存儲讀取到的磁盤容量01H — 360K,02H — 1.2M,03H — 720K ,04H — 1.44M
CH存儲柱面數的低8位
CL的位7-6存儲柱面數的高2位
CL的位5-0存儲扇區數
DH存儲磁頭數
DL存儲驅動器數
ES:DI表示磁盤驅動器參數表地址
ok_load_setup:
! Get disk drive parameters, specifically nr of sectors/track
mov dl,#0x00
mov ax,#0x0800 ! AH=8 is get drive parameters
int 0x13
mov ch,#0x00
seg cs
mov sectors,cx
mov ax,#INITSEG
mov es,ax
sectors:
.word 0
- 在屏幕上顯示一些信息,使用int 10H,這段信息就是經典的“Loading system …”
! Print some inane message
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10
mov cx,#24
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#msg1
mov ax,#0x1301 ! write string, move cursor
int 0x10
! 作者注:這些數據放在源文件末尾,爲方便閱讀,放在這裏
msg1:
.byte 13,10
.ascii "Loading system ..."
.byte 13,10,13,10