歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Unix知識 >> Unix教程 >> FreeBSD操作系統的boot2階段

FreeBSD操作系統的boot2階段

日期:2017/2/27 17:42:29   编辑:Unix教程
FreeBSD
  也許你想知道,為什麼boot2是在boot0之後,而不是在boot1之後。事實上,也有一個512字節的文件boot1存放在目錄/boot裡,那是用來從一張軟盤引導系統的。從軟盤引導時,boot1起著boot0對硬盤引導相同的作用:它找到boot2並運行之。
  
  你可能已經看到有一文件/boot/mbr。這是boot0的簡化版本。mbr中的代碼不會顯示菜單讓用戶選擇,而只是簡單的引導被標志的分區。
  
  實現boot2的代碼存放在目錄sys/boot/i386/boot2/裡,對應的可執行文件在/boot裡。在/boot裡的文件boot0和boot2不會在引導過程中使用,只有boot0cfg這樣的工具才會使用它們。boot0的內容應在MBR中才能生效。boot2位於可引導的FreeBSD分區的開始。這些位置不受文件系統控制,所以它們不可用ls之類的命令查看。
  
  boot2的主要任務是裝載文件/boot/loader,那是引導過程的第三階段。在boot2中的代碼不能使用諸如open()和read()之類的例程函數,因為內核還沒有被加載。而應當掃描硬盤,讀取文件系統結構,找到文件/boot/loader,用BIOS的功能將它讀入內存,然後從其入口點開始執行之。
  
  除此之外,boot2還可提示用戶進行選擇,loader可以從其它磁盤、系統單元、分區裝載。
  
  boot2 的二進制代碼用特殊的方式產生:
  
  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表示引導擴展器(BooT eXtender)是給程序(稱為客戶(client))提供保護模式環境、並與客戶程序相鏈接的一段代碼。所以boot2是一個BTX客戶,使用BTX提供的服務。
  
  工具btxld是鏈接器,它將兩個二進制代碼鏈接在一起。btxld(8)和ld(1)的區別是ld通常將兩個目標文件鏈接成一個動態鏈接庫或可執行文件,而btxld則將一個目標文件與BTX鏈接起來,產生適合於放在分區首部的二進制代碼,以實現系統引導。
  
  boot0執行跳轉至BTX的入口點。然後,BTX將處理器切換至保護模式,並准備一個簡單的環境,然後調用客戶。這個環境包括:
  
  虛擬8086模式。這意味著BTX是虛擬8086的監視程序。實模式指令,如pushf, popf, cli, sti, if,均可被客戶調用。
  
  建立中斷描述符表(Interrupt Descriptor Table, IDT),使得所有的硬件中斷可被缺省的BIOS程序處理。建立中斷0x30,這是系統調用關口。
  
  兩個系統調用exec和 exit的定義如下:
  
  sys/boot/i386/btx/lib/btxsys.s:
    .set INT_SYS,0x30    # 中斷號
  #
  # System call: exit
  #
  __exit:   xorl %eax,%eax     # BTX系統調用0x0
    int $INT_SYS      #
  #
  # System call: exec
  #
  __exec:   movl $0x1,%eax     # BTX系統調用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
  客戶的代碼和數據始於地址MEM_USR(0xa000),選擇符(selector) SEL_UCODE指向客戶的數據段。選擇符 SEL_UCODE 擁有第3級描述符權限(Descriptor Privilege Level, DPL),這是最低級權限。但是INT 0x30 指令的處理程序存儲於另一個段裡,這個段的選擇符SEL_SCODE (supervisor code)由有著管理級權限。正如代碼建立IDT(中斷描述符表)時進行的操作那樣:
  
    mov $SEL_SCODE,%dh   # 段選擇符
  init.2:   shr %bx       # 是否處理這個中斷?
    jnc init.3     # 否
    mov %ax,(%di)      # 設置處理程序偏移量
    mov %dh,0x2(%di)    # 設置處理程序選擇符
    mov %dl,0x5(%di)    # 設置 P:DPL:type
    add $0x4,%ax      # 下一個中斷處理程序
  所以,當客戶調用 __exec()時,代碼將被以最高權限執行。這使得內核可以修改保護模式數據結構,如分頁表(page tables)、全局描述符表(GDT)、中斷描述符表(IDT)等。
  
  boot2 定義了一個重要的數據結構:struct bootinfo。這個結構由 boot2 初始化,然後被轉送到loader,之後又被轉入內核。這個結構的部分項目由boot2設定,其余的由loader設定。這個結構中的信息包括內核文件名、BIOS提供的硬盤柱面/磁頭/扇區數目信息、BIOS提供的引導設備的驅動器編號,可用的物理內存大小,envp指針(環境指針)等。定義如下:
  
  /usr/include/machine/bootinfo.h
  struct bootinfo {
  u_int32_t  bi_version;
  u_int32_t  bi_kernelname;   /* 用一個字節表示 * */
  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;    /* 引導設備的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;   /* 內核空間末端 */
  u_int32_t  bi_envp;    /* 環境 */
  u_int32_t  bi_modulep;   /* 預裝載的模塊 */
  };
  boot2 進入一個循環等待用戶輸入,然後調用load()。如果用戶不做任何輸入,循環將在一段時間後結束,load() 將會裝載缺省文件(/boot/loader)。函數 ino_t lookup(char *filename)和int xfsread(ino_t inode, void *buf, size_t nbyte) 用來將文件內容讀入內存。/boot/loader是一個ELF格式二進制文件,不過它的頭部被換成了a.out格式中的struct exec結構。load()掃描loader的ELF頭部,裝載/boot/loader至內存,然後跳轉至入口執行之:
  
  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));
Copyright © Linux教程網 All Rights Reserved