视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
FreeBSD操作系统的boot2阶段
2020-11-09 15:41:34 责编:小采
文档


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 中的代码不能使用诸如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,0x00,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));


    下载本文
    显示全文
    专题