Home  >  Article  >  Database  >  FreeBSD操作系统的boot2阶段

FreeBSD操作系统的boot2阶段

WBOY
WBOYOriginal
2016-06-07 15:24:521154browse

FreeBSD操作系统的boot2阶段 2007-04-27 来源:FreeBSD 作者: 关键词: 系统FreeBSDBSD操作SD 也许你想知道,为什么 boot2 是在 boot0 之后,而不是在boot1之后。事实上,也有一个512字节的文件 boot1 存放在目录 /boot 里,那是用来从一张软盘引导系统的。从

FreeBSD操作系统的boot2阶段

2007-04-27      来源:FreeBSD         作者:

关键词:   系统      FreeBSD      BSD      操作      SD   

也许你想知道,为什么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 中的代码不能使用诸如<span>open()</span> <span>read()</span> 之类的例程函数,因为内核还没有被加载。而应当扫描硬盘,读取文件系统结构,找到文件/boot/loader ,用BIOS的功能将它读入内存,然后从其入口点开始执行之。

除此之外,boot2 还可提示用户进行选择,loader可以从其它磁盘、系统单元、分区装载。

boot2 的二进制代码用特殊的方式产生:

<span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><tt>sys/boot/i386/boot2/Makefile</tt>
<br>
boot2: boot2.ldr boot2.bin ${BTX}/btx/btx<br>
    btxld -v -E ${ORG2} -f bin -b ${BTX}/btx/btx -l boot2.ldr /<br>
        -o boot2.ld -P 1 boot2.bin<br>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>

这个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,这是系统调用关口。

  • 两个系统调用<span>exec</span> <span>exit</span> 的定义如下:

    <span><span><tt>sys/boot/i386/btx/lib/btxsys.s:</tt>
    <br>
            .set INT_SYS,0x30       # 中断号<br>
    #<br>
    # System call: exit<br>
    #<br>
    __exit:     xorl %eax,%eax          # BTX系统调用0x0<br>
            int $INT_SYS            #<br>
    #<br>
    # System call: exec<br>
    #<br>
    __exec:     movl $0x1,%eax          # BTX系统调用0x1<br>
            int $INT_SYS            #<br>
    </span>
    </span>
    

BTX建立全局描述符表(Global Descriptor Table, GDT):

<span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><tt>sys/boot/i386/btx/btx/btx.s:</tt>
<br>
gdt:        .word 0x0,0x0,0x0,0x0       # 以空为入口<br>
        .word 0xffff,0x0,0x9a00,0xcf    # SEL_SCODE<br>
        .word 0xffff,0x0,0x9200,0xcf    # SEL_SDATA<br>
        .word 0xffff,0x0,0x9a00,0x0 # SEL_RCODE<br>
        .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA<br>
        .word 0xffff,MEM_USR,0xfa00,0xcf# SEL_UCODE<br>
        .word 0xffff,MEM_USR,0xf200,0xcf# SEL_UDATA<br>
        .word _TSSLM,MEM_TSS,0x8900,0x0 # SEL_TSS<br>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>

客户的代码和数据始于地址MEM_USR(0xa000),选择符(selector) SEL_UCODE指向客户的数据段。选择符 SEL_UCODE 拥有第3级描述符权限(Descriptor Privilege Level, DPL),这是最低级权限。但是INT 0x30 指令的处理程序存储于另一个段里,这个段的选择符SEL_SCODE (supervisor code)由有着管理级权限。正如代码建立IDT(中断描述符表)时进行的操作那样:

<span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span>       mov $SEL_SCODE,%dh      # 段选择符<br>
init.2:     shr %bx             # 是否处理这个中断?<br>
        jnc init.3          # 否<br>
        mov %ax,(%di)           # 设置处理程序偏移量<br>
        mov %dh,0x2(%di)        # 设置处理程序选择符<br>
        mov %dl,0x5(%di)        # 设置 P:DPL:type<br>
        add $0x4,%ax            # 下一个中断处理程序<br>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>

所以,当客户调用 <span>__exec()</span> 时,代码将被以最高权限执行。这使得内核可以修改保护模式数据结构,如分页表(page tables)、全局描述符表(GDT)、中断描述符表(IDT)等。

boot2 定义了一个重要的数据结构:struct bootinfo 。这个结构由 boot2 初始化,然后被转送到loader,之后又被转入内核。这个结构的部分项目由boot2 设定,其余的由loader设定。这个结构中的信息包括内核文件名、BIOS提供的硬盘柱面/磁头/扇区数目信息、BIOS提供的引导设备的驱动器编号,可用的物理内存大小,envp 指针(环境指针)等。定义如下:

<span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><tt>/usr/include/machine/bootinfo.h</tt>
<br>
struct bootinfo {<br>
    u_int32_t   bi_version;<br>
    u_int32_t   bi_kernelname;      /* 用一个字节表示 * */<br>
    u_int32_t   bi_nfs_diskless;    /* struct nfs_diskless * */<br>
        /* 以上为常备项 */<br>
#define bi_endcommon    bi_n_bios_used<br>
    u_int32_t   bi_n_bios_used;<br>
    u_int32_t   bi_bios_geom[N_BIOS_GEOM];<br>
    u_int32_t   bi_size;<br>
    u_int8_t    bi_memsizes_valid;<br>
    u_int8_t    bi_bios_dev;        /* 引导设备的BIOS单元编号 */<br>
    u_int8_t    bi_pad[2];<br>
    u_int32_t   bi_basemem;<br>
    u_int32_t   bi_extmem;<br>
    u_int32_t   bi_symtab;      /* struct symtab * */<br>
    u_int32_t   bi_esymtab;     /* struct symtab * */<br>
        /* 以下项目仅高级bootloader提供 */<br>
    u_int32_t   bi_kernend;     /* 内核空间末端 */<br>
    u_int32_t   bi_envp;        /* 环境 */<br>
    u_int32_t   bi_modulep;     /* 预装载的模块 */<br>
};<br>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>

boot2 进入一个循环等待用户输入,然后调用<span>load()</span> 。如果用户不做任何输入,循环将在一段时间后结束,<span>load()</span> 将会装载缺省文件(/boot/loader )。函数 <span>ino_t lookup(char *filename)</span> <span>int xfsread(ino_t inode, void *buf, size_t nbyte)</span> 用来将文件内容读入内存。/boot/loader 是一个ELF格式二进制文件,不过它的头部被换成了a.out格式中的struct exec 结构。<span>load()</span> 扫描loader的ELF头部,装载/boot/loader 至内存,然后跳转至入口执行之:

<span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><span><tt>sys/boot/i386/boot2/boot2.c:</tt>
<br>
    __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),<br>
       MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part),<br>
       0, 0, 0, VTOP(&bootinfo));<br>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>
</span>


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn