读书频道 > 系统 > linux > 深度探索Linux操作系统系统构建和原理解析
3.1.1 一级推进系统——setup.bin
2013-09-28 15:26:29     我来说两句 
收藏    我要投稿   
全书一共8章:第1章介绍了如何准备工作环境。在第2章中构建了编译工具链,这是后面构建操作系统各个组件的基础。在这一章中,不仅详细讲解了工具链的构建过程,而且还通过对编译链接过程的探讨,深入讨论了工具链  立即去当当网订购

在进行内核初始化时,需要一些信息,如显示信息、内存信息等。曾经,这些信息由工作在实模式下的setup.bin通过BIOS获取,保存在内核中的变量boot_params中,变量boot_params是结构体boot_params的一个实例。如setup.bin中收集显示信息的代码如下:
linux-3.7.4/arch/x86/boot/video.c:

static void store_video_mode(void)
{
    struct biosregs ireg, oreg;
    ...
    initregs(&ireg);
    ireg.ah = 0x0f;
    intcall(0x10, &ireg, &oreg);
    ...
    boot_params.screen_info.orig_video_mode = oreg.al & 0x7f;
    boot_params.screen_info.orig_video_page = oreg.bh;
}

store_video_mode首先调用函数intcall获取显示方面的信息,并将其保存在boot_params的screen_info中。intcall是调用BIOS中断的封装,0x10是BIOS 提供的显示服务(Video Service)的中断号,代码如下:
linux-3.7.4/arch/x86/boot/bioscall.S:

intcall:
    /* Self-modify the INT instruction.  Ugly, but works. */
    cmpb    %al, 3f
    je  1f
    movb    %al, 3f
    jmp 1f      /* Synchronize pipeline */
1:
    ...
    .byte   0xcd        /* INT opcode */
3:  .byte   0
    ...

在代码中我们并没有看到熟悉的调用BIOS中断的身影,如“int $0x10”,但是我们看到了一个特殊的字符——0xcd。正如其后面的注释所言,0xcd就是x86汇编指令INT的机器码,如表3-1所示。

表3-1 x86 INT指令说明(部分)
序号 操作码(Opcode) 指令(Instruction) 操作数编码方式(Op/En) 描 述
1 CD ib INT imm8 B 跟在操作码后面的8位立即数指定中断号

根据x86的INT指令说明,0xcd后面跟着的1字节就是BIOS中断号,这就是上面代码中标号为3处分配1字节的目的。

在函数intcall的开头,首先比较寄存器al中的值与标号3处占用的1字节,若相等则直接向前跳转至标号1处,否则将寄存器al中的值复制到标号3处的1个字节空间。那么寄存器al中保存的是什么呢?

在默认情况下,GCC使用栈来传递参数。但是我们可以使用关键字“__attribute__(regparm(n))”修饰函数,或者通过向GCC传递命令行参数“-mregparm=n”来指定GCC使用寄存器传递参数,其中n表示使用寄存器传递参数的个数。在编译setup.bin时,kbuild使用了后者,编译脚本如下所示:
linux-3.7.4/arch/x86/boot/Makefile:
KBUILD_CFLAGS   := ... -mregparm=3 ...

如此,函数的第一个参数通过寄存器eax/ax传递,第二个参数通过ebx/bx传递,等等,而不是通过栈传递了。因此,上面的寄存器al中保存的是函数intcall的第一个参数,即BIOS中断号。

在完成信息收集后,setup.bin将CPU切换到保护模式,并跳转到内核的保护模式部分执行。如我们前面讨论的,setup.bin作为一级推进系统,即将结束历史使命,所以内核将setup.bin收集的保存在setup.bin的数据段的变量boot_params复制到vmlinux的数据段中。

但是随着新的BIOS标准的出现,尤其是EFI的出现,为了支持这些新标准,开发者们制定了32位启动协议(32-bit boot protocol)。在32位启动协议下,由Bootloader实现收集这些信息的功能,内核启动时不再需要首先运行实模式部分(即setup.bin),而是直接跳转到内核的保护模式部分。因此,在32位启动协议下,不再需要setup.bin收集内核初始化时需要的相关信息。但是这是否意味着可以彻底放弃setup.bin呢?

事实上,除了收集信息功能外,setup.bin被忽略的另一个重要功能就是负责在内核和Bootloader之间传递信息。例如,在加载内核时,Bootloader需要从setup.bin中获取内核是否是可重定位的、内核的对齐要求、内核建议的加载地址等。32位启动协议约定在setup.bin中分配一块空间用来承载这些信息,在构建映像时,内核构建系统需要将这些信息写到setup.bin的这块空间中。所以,虽然setup.bin已经失去了其以往的作用,但还不能完全放弃,其还要作为内核与Bootloader之间传递数据的桥梁,而且还要照顾到某些不能使用32位启动协议的场合。

点击复制链接 与好友分享!回本站首页
分享到: 更多
您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:3.1 内核映像的组成
下一篇:3.1.2 二级推进系统——内核非压缩部分
相关文章
图文推荐
3.3.6 GNOME的软件管
3.3.5 GNOME的文件管
3.3.4 GNOME的窗口管
3.3.3 收藏夹和快捷
排行
热门
文章
下载
读书

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训
版权所有: 红黑联盟--致力于做最好的IT技术学习网站