FreeBSD操作系統(tǒng)的boot2階段 2007-04-27 來源:FreeBSD 作者: 關(guān)鍵詞: 系統(tǒng)FreeBSDBSD操作SD 也許你想知道,為什么 boot2 是在 boot0 之后,而不是在boot1之后。事實上,也有一個512字節(jié)的文件 boot1 存放在目錄 /boot 里,那是用來從一張軟盤引導(dǎo)系統(tǒng)的。從
2007-04-27 來源:FreeBSD 作者:
關(guān)鍵詞: 系統(tǒng) FreeBSD BSD 操作 SD
也許你想知道,為什么boot2 是在boot0 之后,而不是在boot1之后。事實上,也有一個512字節(jié)的文件boot1 存放在目錄/boot 里,那是用來從一張軟盤引導(dǎo)系統(tǒng)的。從軟盤引導(dǎo)時,boot1 起著boot0 對硬盤引導(dǎo)相同的作用:它找到boot2 并運行之。
你可能已經(jīng)看到有一文件/boot/mbr 。這是boot0 的簡化版本。mbr 中的代碼不會顯示菜單讓用戶選擇,而只是簡單的引導(dǎo)被標(biāo)志的分區(qū)。
實現(xiàn)boot2 的代碼存放在目錄sys/boot/i386/boot2/ 里,對應(yīng)的可執(zhí)行文件在/boot 里。在/boot 里的文件boot0 和boot2 不會在引導(dǎo)過程中使用,只有boot0cfg 這樣的工具才會使用它們。boot0 的內(nèi)容應(yīng)在MBR中才能生效。boot2 位于可引導(dǎo)的FreeBSD分區(qū)的開始。這些位置不受文件系統(tǒng)控制,所以它們不可用ls 之類的命令查看。
boot2
的主要任務(wù)是裝載文件/boot/loader
,那是引導(dǎo)過程的第三階段。在boot2
中的代碼不能使用諸如open()
和read()
之類的例程函數(shù),因為內(nèi)核還沒有被加載。而應(yīng)當(dāng)掃描硬盤,讀取文件系統(tǒng)結(jié)構(gòu),找到文件/boot/loader
,用BIOS的功能將它讀入內(nèi)存,然后從其入口點開始執(zhí)行之。
除此之外,boot2 還可提示用戶進行選擇,loader可以從其它磁盤、系統(tǒng)單元、分區(qū)裝載。
boot2 的二進制代碼用特殊的方式產(chǎn)生:
sys/boot/i386/boot2/Makefile
boot2: boot2.ldr boot2.bin ${BTX}/btx/btx
btxld -v -E ${ORG2} -f bin -b ${BTX}/btx/btx -l boot2.ldr /
-o boot2.ld -P 1 boot2.bin
這個Makefile片斷表明btxld (8) 被用來鏈接二進制代碼。BTX表示引導(dǎo)擴展器(BooT eXtender)是給程序(稱為客戶(client))提供保護模式環(huán)境、并與客戶程序相鏈接的一段代碼。所以boot2 是一個BTX客戶,使用BTX提供的服務(wù)。
工具btxld 是鏈接器,它將兩個二進制代碼鏈接在一起。btxld (8) 和ld (1) 的區(qū)別是ld 通常將兩個目標(biāo)文件鏈接成一個動態(tài)鏈接庫或可執(zhí)行文件,而btxld 則將一個目標(biāo)文件與BTX鏈接起來,產(chǎn)生適合于放在分區(qū)首部的二進制代碼,以實現(xiàn)系統(tǒng)引導(dǎo)。
boot0 執(zhí)行跳轉(zhuǎn)至BTX的入口點。然后,BTX將處理器切換至保護模式,并準(zhǔn)備一個簡單的環(huán)境,然后調(diào)用客戶。這個環(huán)境包括:
虛擬8086模式。這意味著BTX是虛擬8086的監(jiān)視程序。實模式指令,如pushf, popf, cli, sti, if,均可被客戶調(diào)用。
建立中斷描述符表(Interrupt Descriptor Table, IDT),使得所有的硬件中斷可被缺省的BIOS程序處理。建立中斷0x30,這是系統(tǒng)調(diào)用關(guān)口。
兩個系統(tǒng)調(diào)用exec
和 exit
的定義如下:
sys/boot/i386/btx/lib/btxsys.s:
.set INT_SYS,0x30 # 中斷號
#
# System call: exit
#
__exit: xorl %eax,%eax # BTX系統(tǒng)調(diào)用0x0
int $INT_SYS #
#
# System call: exec
#
__exec: movl $0x1,%eax # BTX系統(tǒng)調(diào)用0x1
int $INT_SYS #
BTX建立全局描述符表(Global Descriptor Table, GDT):
sys/boot/i386/btx/btx/btx.s:
gdt: .word 0x0,0x0,0x0,0x0 # 以空為入口
.word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE
.word 0xffff,0x0,0x9200,0xcf # SEL_SDATA
.word 0xffff,0x0,0x9a00,0x0 # SEL_RCODE
.word 0xffff,0x0,0x9200,0x0 # SEL_RDATA
.word 0xffff,MEM_USR,0xfa00,0xcf# SEL_UCODE
.word 0xffff,MEM_USR,0xf200,0xcf# SEL_UDATA
.word _TSSLM,MEM_TSS,0x8900,0x0 # SEL_TSS
客戶的代碼和數(shù)據(jù)始于地址MEM_USR(0xa000),選擇符(selector) SEL_UCODE指向客戶的數(shù)據(jù)段。選擇符 SEL_UCODE 擁有第3級描述符權(quán)限(Descriptor Privilege Level, DPL),這是最低級權(quán)限。但是INT 0x30 指令的處理程序存儲于另一個段里,這個段的選擇符SEL_SCODE (supervisor code)由有著管理級權(quán)限。正如代碼建立IDT(中斷描述符表)時進行的操作那樣:
mov $SEL_SCODE,%dh # 段選擇符
init.2: shr %bx # 是否處理這個中斷?
jnc init.3 # 否
mov %ax,(%di) # 設(shè)置處理程序偏移量
mov %dh,0x2(%di) # 設(shè)置處理程序選擇符
mov %dl,0x5(%di) # 設(shè)置 P:DPL:type
add $0x4,%ax # 下一個中斷處理程序
所以,當(dāng)客戶調(diào)用 __exec()
時,代碼將被以最高權(quán)限執(zhí)行。這使得內(nèi)核可以修改保護模式數(shù)據(jù)結(jié)構(gòu),如分頁表(page tables)、全局描述符表(GDT)、中斷描述符表(IDT)等。
boot2 定義了一個重要的數(shù)據(jù)結(jié)構(gòu):struct bootinfo 。這個結(jié)構(gòu)由 boot2 初始化,然后被轉(zhuǎn)送到loader,之后又被轉(zhuǎn)入內(nèi)核。這個結(jié)構(gòu)的部分項目由boot2 設(shè)定,其余的由loader設(shè)定。這個結(jié)構(gòu)中的信息包括內(nèi)核文件名、BIOS提供的硬盤柱面/磁頭/扇區(qū)數(shù)目信息、BIOS提供的引導(dǎo)設(shè)備的驅(qū)動器編號,可用的物理內(nèi)存大小,envp 指針(環(huán)境指針)等。定義如下:
/usr/include/machine/bootinfo.h
struct bootinfo {
u_int32_t bi_version;
u_int32_t bi_kernelname; /* 用一個字節(jié)表示 * */
u_int32_t bi_nfs_diskless; /* struct nfs_diskless * */
/* 以上為常備項 */
#define bi_endcommon bi_n_bios_used
u_int32_t bi_n_bios_used;
u_int32_t bi_bios_geom[N_BIOS_GEOM];
u_int32_t bi_size;
u_int8_t bi_memsizes_valid;
u_int8_t bi_bios_dev; /* 引導(dǎo)設(shè)備的BIOS單元編號 */
u_int8_t bi_pad[2];
u_int32_t bi_basemem;
u_int32_t bi_extmem;
u_int32_t bi_symtab; /* struct symtab * */
u_int32_t bi_esymtab; /* struct symtab * */
/* 以下項目僅高級bootloader提供 */
u_int32_t bi_kernend; /* 內(nèi)核空間末端 */
u_int32_t bi_envp; /* 環(huán)境 */
u_int32_t bi_modulep; /* 預(yù)裝載的模塊 */
};
boot2
進入一個循環(huán)等待用戶輸入,然后調(diào)用load()
。如果用戶不做任何輸入,循環(huán)將在一段時間后結(jié)束,load()
將會裝載缺省文件(/boot/loader
)。函數(shù) ino_t lookup(char *filename)
和int xfsread(ino_t inode, void *buf, size_t nbyte)
用來將文件內(nèi)容讀入內(nèi)存。/boot/loader
是一個ELF格式二進制文件,不過它的頭部被換成了a.out格式中的struct exec
結(jié)構(gòu)。load()
掃描loader的ELF頭部,裝載/boot/loader
至內(nèi)存,然后跳轉(zhuǎn)至入口執(zhí)行之:
sys/boot/i386/boot2/boot2.c:
__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part),
0, 0, 0, VTOP(&bootinfo));
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com