天天看點

深入了解 GNU GRUB - 02 boot.S 2.1 相關BIOS例程

轉載注明出處(cppgp: http://blog.csdn.net/cppgp )

2. boot.S: GRUB引導第一步

boot.S位于目錄boot/i386/pc/。這部分指令被加載到0x7C00~0x7DFF。主要工作包括:配置寄存器;設定堆棧;檢測引導盤;檢測引導盤讀取模式;讀取另一扇區指令。這個過程用到幾個BIOS例程,并且對主引導記錄(MBR, Master Boot Record)結構有很大的依賴。是以boot.S我們分作四步進行較長的描述:

1)    相關BIOS例程

2)    主引導記錄MBR結構

3)    boot.S代碼結構

4)    boot.S詳細注釋

在本節最後,我們實作一個簡單的boot.S,這個boot.S僅僅通過BIOS例程,向終端輸出文本。

2.1 相關BIOS例程

boot.S用到五個磁盤相關的BIOS例程。分别是:探測磁盤擴充讀支援、LBA方式讀、CHS參數擷取、CHS方式讀、磁盤複位。磁盤讀取出現LBA/CHS兩種模式(其實LBA又分為28-bit、48-bit、64-bit三種),是因為在磁盤的發展曆史上,由于對磁盤容量的錯誤估量,導緻容量屏障的出現,而不得不進行接口更改、擴充。曆史上出現的容量屏障主要有四次:

1)    504MiB限制。也稱作實體CHS尋址限制。早期的IDE/ATA磁盤接口規定柱面(C=Cylinder)、磁頭(H=Head)、扇區(S=Sector)的位寬分别為C/H/S=16/4/8;而BIOS INT 13 H/02H讀調用接口規定C/H/S=10/8/6。兩者結合取位寬較少者,磁盤尋址參數C/H/S=10/4/6。扇區的起始編号是1而不是0,每扇區資料512位元組,是以可尋址容量為210*24*(26-1)*512=528482304bytes=504MiB。

2)    7.88GiB限制。也稱作邏輯CHS尋址限制。從1) 可以看出,BIOS INT 13 H/02H接口磁頭H有4位是空閑的,是以邏輯上可以擴充磁頭H為8位(而實際上連接配接到IDE/ATA時,磁頭H有4位映射到柱面C,或者映射到柱面C和扇區S)。在BIOS接口上看,現在C/H/S尋址範圍擴充到210*28*(26-1)*512=8455716864bytes=7.88GiB。

3)    128GiB限制。也稱作LBA-28bits限制。在 2) 中,BISO接口的CHS尋址已經到了極限,無法再擴充了,而IDE/ATA是28 位(IDE/ATA内部C/H/S=16:4:8共28位)的,理論尋址容量為228*512=128GB,是以出現了LBA (Logical Block Addressing) 尋址。LBA是一個一維位址,從0~2N-1,其中N是位址寬度,在這裡是28,而LBA到CHS的轉換由BIOS和磁盤完成。28位LBA尋址容量為228*512=128GiB。

4)    2TiB限制。為了提供更大範圍的尋址,Western Digital和Phoenix Technologies制定了EDD (BIOS Enhanced Disk Drive Services) 标準。它使用64位LBA尋址,同時也支援48位和28位尋址。48位LBA尋址容量為128PiB,而64位LBA尋址容量更是高達8ZiB,無論48位LBA或者64位LBA,在目前或可以預見的将來應該是足夠的。但是曆史悠久的MBR中儲存有磁盤分區表DPT,而分區表中分區絕對起始扇區和分區總扇區數都是32位的,是以對于傳統分區的磁盤,最大尋址範圍由這兩個32位值決定,大小為232*512=2TiB。為了解決該問題,引進了GPT (GUID Partition Table) 和EFI (Extensible Firmware Interface) 技術,本文不對其做較長的描述,有興趣的讀者可以Google相關主題。

在磁盤容量的計算上,軟體按照1024進行機關換算,而廠商按照1000進行機關換算,是以上述的504MiB限制、7.85GiB限制、128GiB限制又稱為522MB限制、8.46GB限制、137GB限制。

除了上述限制,曆史上還出現過一些軟體導緻的容量屏障,例如一些BIOS BUG或檔案系統體系導緻的容量屏障,本文 不再對其做進一步描述,有興趣可以Google相關細節。BIOS例程及容量屏障描述參考如下網絡資源:

http://en.wikipedia.org/wiki/INT_13H

http://en.wikipedia.org/wiki/INT_10H

http://www.pcguide.com/ref/hdd/bios/index.htm

2.1.1磁盤擴充探測: INT 13H, AH=41H

檢測磁盤擴充讀(LBA/CHS)支援情況。較長的描述如下。

參數:

寄存器        描述

AH=0x41    擴充檢測函數序号

DL        驅動器編号(第一塊硬碟為0x80,第二塊為0x81,依次類推)

BX        0x55AA

結果:

寄存器        描述

CF        支援清零,不支援置1

AH        錯誤碼或者主版本号

BX        0x55AA

CX        接口支援掩碼

        1 – 使用打包結構體存取裝置

        2 – 驅動器加鎖和彈出

        4 – 支援增強型磁盤驅動器(EDD)

使用AT&T文法實作這個調用的例子如下(預設使用0x80作為驅動器):

boot_driver:

.byte 0x80

movb boot_driver, %dl

movb $0x41, %ah

movw $0x55aa, %bx

int $0x13

movb boot_driver, %dl

jc chs_mode

cmpw $0xaa55, %bx

jne chs_mode

andw $1, %cx

lba_mode:

// Do LBA operation

Jmp end

chs_mode:

// Do CHS operation

end:

// Do Normal Works

調用結束後,如果CF置1、或者BX不等于0xAA55、或者CX不等于1,都表示不支援LBA,是以進入CHS處理。注意,BIOS調用可能更改掉DL寄存器值,是以需要重置。

2.1.2 LBA模式讀: INT 13H, AH=42H

LBA模式的讀采用打包的資料結構作為參數。

參數:

寄存器        描述

AH=42H    擴充讀函數序号

DL        驅動器編号(第一塊硬碟為0x80,第二塊為0x81,依次類推)

DS:SI        segment:offset指針,指向磁盤位址包DAP (Disk Address Packet)

DAP結構體的格式描述如下:

偏移量        大小        描述

00H        1 Byte        DAP大小=16=0x10

01H        1 Byte        未用,必須置0

02H~03H    2 Bytes        需要讀的扇區數

(有些BIOS限制不能超過127扇區)

04H~07H    4 Bytes        segment:offset指針,指向記憶體緩沖區,讀取到的

扇區内容放置在該緩沖區

08H~0FH    8 Bytes        需要讀的連續扇區的起始扇區編号

(第一個扇區的編号是0)

結果:

寄存器        描述

CF        失敗置1,成功清零

AH        傳回碼

使用AT&T文法實作這個調用的例子如下(假定我們使用0x80作為驅動器,從第2個扇區開始連續讀取4個扇區):

boot_driver:

.byte 0x80

sector_start:

.long 2, 0

sector_num:

.word 4

dap:

/* reserved 16 bytes to hold dap, or disk address packet */

. = dap + 0x10

read_buffer:

/* reserved 512*4bytes to save data */

. = read_buffer + 512*4

movb boot_driver, %dl

movw $dap, %si

xorw %ax, %ax

movw %ax, %ds

movw %ax, 4(%si)

movb $0x0010, (%si)

movw sector_num, %ax

movw %ax, 2(%si)

movw $read_buffer, 6(%si)

movl sector_start, %eax

movl %eax, 8(%si)

movl sector_start+4, %eax

movl %eax, 12(%si)

movb $0x42, %ah

int $0x13

jc lba_fail

// Do LBA-read success operations

Jmp end

lba_fail:

// Do LBA-read fail operations

end:

// Do Normal Works

調用結束後,通過檢測CF來判斷是否成功。需要注意位元組序的問題,80x86采用小端序。

2.1.3 CHS參數擷取: INT 13H, AH=08H

擷取驅動器CHS參數。如下。

參數:

寄存器        描述

AH=0x08    驅動器CHS參數讀取函數序号

DL        驅動器編号(第一塊硬碟為0x80,第二塊為0x81,依次類推)

BX        0x55AA

結果:

寄存器        描述

CF        成功清零,失敗置1

AH        傳回碼

DL        驅動器号

DH        邏輯磁頭最大索引值(number_of-1因為索引從0開始)

CX        邏輯柱面索引最大值和邏輯扇區數

        邏輯柱面最大索引=number_of-,因為索引從0開始

        邏輯扇區數=number_of,因為索引從1開始

柱面占10位,CH表示柱面低8位,CL高2位表示柱面9~10位

扇區占6位,CL低6位表示扇區

使用AT&T文法實作這個調用的例子很簡單:

boot_driver:

.byte 0x80

movb boot_driver, %dl

movb $0x08, %ah

int $0x13

jc chs_para_fail

// Do CHS-para success operations

jmp end

chs_para_fail:

// Do CHS-para fail operation

end:

// Do Normal Works

調用結束後,通過檢測CF來判斷參數擷取調用是否成功。

2.1.4 CHS模式讀: INT 13H, AH=02H

CHS模式讀扇區。需要注意緩沖區沒有超出該段尋址範圍。

參數:

寄存器        描述

AH=0x02    CHS模式讀扇區函數序号

AL        需讀取扇區數

CX        柱面和扇區,柱面占10位,扇區占6位

CH表示柱面低8位,CL高2位表示柱面9~10位

CL低6位表示扇區(從1~63)

DH        磁頭

DL        驅動器編号

第一軟碟為0x00,第二軟碟為0x01,依次類推

第一硬碟為0x80,第二硬碟為0x81,依次類推

ES:BX        segment:offset緩沖區位址指針

結果:

寄存器        描述

CF        成功清零,失敗置1

AH        傳回碼

AL        實際讀取的扇區數

CHS讀存在7.88GiB容量限制,現在幾乎沒有硬碟使用這種模式讀,并且也很少有人使用軟碟(你還有軟驅嗎?),是以CHS讀扇區隻是作為殘留機制存在,很少用到。

使用AT&T文法實作這個調用的例子如下,因為要用到移位等,彙編代碼看起來稍微有點麻煩。這裡我們依然假定使用0x80作為驅動器,以C/H/S=0/0/1開始連續讀取4個扇區。另外,需要用到2.1.3描述的CHS參數擷取的結果。

boot_driver:

.byte 0x80

cylinder_start:

.word 0

head_start:

.byte 0

sector_start:

.byte 1

sector_num:

.word 4

chs_sectors:

.byte 0

chs_heads:

.word 0

chs_cylinders:

.word 0

read_buffer:

/* reserved 512*4bytes to save data */

. = read_buffer + 512*4

movb boot_driver, %dl

movb $0x08, %ah

int $0x13

jc chs_para_fail

/* saved the CHS parameters */

/*

* dh : numbers_of_heads-1, 8-bits

* cx : numbers_of_cylinders-1, number_of_sectors

*/

movzbl %dh, %eax

incw %ax

movw %ax, chs_head

movzbw %dl, %dx

shlw $2, %dx

movb %ch, %al

movb %dh, %ah

incw %ax

movw %ax, chs_cylinders

movzbw %dl, %ax

shrw $2, %ax

movw %ax, chs_sectors

/* CHS read */

movb sectors_start, %al

movb %al, %cl

movw cylinder_start, %ax

movb %al, %ch

xorb %al, %al

shrw $2, %ax

orb %al, %cl

movb head_start, %al

movb %al, %dh

movb boot_drive, %dl

xorw %ax, %ax

movw %ax, %es

movw $read_buffer, %bx

movb sector_num, %al

movb $2, %ah

int $0x13

jc chs_read_fail

// Do CHS-read success operations

jmp end

chs_para_fail:

// Do CHS-para fail operations

jmp end

chs_read_fail:

// Do CHS-read fail operations

jmp end

end:

// Do Normal Works

CHS讀結束後,通過CF判斷是否成功。

2.1.5 複位磁盤驅動器: INT 13H, AH=00H

複位磁盤驅動器。如下。

參數:

寄存器        描述

AH=0x00    驅動器複位函數序号

DL        驅動器編号(第一塊硬碟為0x80,第二塊為0x81,依次類推)

結果:

寄存器        描述

CF        成功清零,失敗置1

AH        狀态

使用AT&T文法實作這個調用的例子非常簡單:

boot_driver:

.byte 0x80

xorb %ah, %ah

movb boot_driver, %dl

int $0x13

jc reset_fail

// Do Disk Reset success operations

jmp end

reset_fail:

// Do Disk Reset fail operations

end:

// Do Normal Works

通過CF判斷複位是否成功。軟碟的讀取和CHS模式的讀

2.1.6 字元輸出: INT 10H, AH=0EH

BIOS使用INT 10H圖形服務,可以用來設定圖形模式、輸出字元或字元串、以及基本圖形(圖形模式下讀取/寫入像素)。

AH=0EH提供用來向終端輸出單一字元。如下。

參數:

寄存器        描述

AH=0x0E    電傳輸出函數序号

AL=Character    需要輸出的字元

BL=Color    輸出字元顔色,隻對圖形終端有效

結果:

無傳回

使用AT&T文法實作這個調用的例子如下:

char_value:

.byte ‘A’

char_color:

.byte 0

movb char_value, %al

movb char_color, %bl

movb $0x0E, %ah

int $0x10

INT 10H, AH=0EH調用無傳回值。