逐行解讀Linux0.11系統引導(一)

本文內容的重點是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加載引導扇區

  1. 什麼是引導扇區:引導扇區就是磁盤等存儲外設的第一個物理扇區。這個扇區又叫MBR(Master Boot Record,主引導記錄)
    Linux0.11的代碼就從這裏開始。
  2. BIOS會把指定磁盤的第一個物理扇區的512字節內容複製到內存的0x07C00處,這是一個約定,所有的BIOS都會這麼做。
    這個時候CS:IP就指向物理地址0x07c00,下一步CPU就會運行從磁盤讀來的第一條指令,也就是Linux0.11的第一條指令。至此,第一部分的引導就完成了。

3. Linux的代碼第一次被CPU運行

  1. 當第一句Linux的代碼被運行,BIOS就結束了它加載MBR的使命。從此,計算機的控制權就交給了Linux。
  2. 前面提到這時候處於實模式,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

(注意註釋符是!不是

  1. 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
  1. 在第一段代碼把自己在內存中的位置重新安排好之後,就要開始裝載開始提到的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:
  1. 下一步是讀取磁盤參數,用到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
  1. 在屏幕上顯示一些信息,使用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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章