实模式汇编代码header.S——无用的bootsect

3.2 实模式汇编代码header.S

如上所述,第一和第二部分是实模式代码,这些代码来自于汇编程序arch/x86/boot/header.Sc程序arch/x86/boot/main.c

 

再从加载开始说,如上所述,vmlinuz保护模式的代码加载到0x100000开始的位置。而实模式的代码,因为被加载的位置不要求是固定的,也就是上面文档中看到的:

                Kernel setup                 |        The kernel real-mode code.

                Kernel boot sector

它们的位置是X,其不确定性是受Boot loader,也就是grub大小的影响。

 

3.2.1 无用的bootsect代码

所以,我们先来看header.S的代码,第46行:

 

  46        .global bootsect_start

  47 bootsect_start:

  48

  49        # Normalize the start address

  50        ljmp    $BOOTSEG, $start2

 

这是bootsect开始代码,也就是vmlinuz第一个512字节的源代码。

 

我们看到$BOOTSEG是在源代码的第28行定义的BOOTSEG=0x07C0。没错,这个ljmp的意思就是说的是跳转到07C0的偏移start2处。还记得我们BIOS一节说过,BIOS不管你是内核还是bootloader,总会一来把第一个块加载到内存然后执行0x07c0处的代码。那么如果从这里开始执行,就说明vmlinuz是被BIOS直接加载过来的,这是不允许的,因为现在linux都需要经过一个特定的bootloader,如前面大篇幅介绍的GRUB这也就是前面说的bootsect有点特殊的地方,就是说它并没打算用来执行,是个死代码。所以万一它被作为bootsectBIOS直接执行,那么就直接提示reboot。可以拿vmware实验一下:

        dd if=/boot/vmlinuz-2.6.34.1 of=vm.img bs=512 count=1

然后用vm.img作为软盘启动。程序就会跳到$start2处:

 

  52 start2:

  53        movw    %cs, %ax

  54        movw    %ax, %ds

  55        movw    %ax, %es

  56        movw    %ax, %ss

  57        xorw    %sp, %sp

  58        sti

  59        cld

  60

  61        movw    $bugger_off_msg, %si

  62

 

我们看到,start2首先将将ds,es,ss全设置为cs,其内容为0x07C0;然后将sp设置成0、开中断sticld清方向标志最后把下面bugger_off_msg 符号的偏移地址放在si寄存器中。继续走:

 

  63 msg_loop:

  64        lodsb

  65        andb    %al, %al

  66        jz      bs_die

  67        movb    $0xe, %ah

  68        movw    $7, %bx

  69        int     $0x10

  70        jmp     msg_loop

  71

 

63行的msg_looplodsb指令把si指向的源串的内容逐步装入al另一个指令stosb是将al中数据装入di指向的地址中64行,当取完的时候,al=0,此时操作andb后方向为0,执行下句跳转语句到bs_die。当然,al不为0的时候,执行69Video中断服务指令,其中AH = 0EhAL = 对应字符(8位),BL = Color (only in graphic mode)

 

  72 bs_die:

  73        # Allow the user to press a key, then reboot

  74        xorw    %ax, %ax

  75        int     $0x16

  76        int     $0x19

  77

  78        # int 0x19 should never return.  In case it does anyway,

  79        # invoke the BIOS reset code...

  80        ljmp    $0xf000,$0xfff0

  81

  82        .section ".bsdata", "a"

  83 bugger_off_msg:

  84        .ascii  "Direct booting from floppy is no longer supported./r/n"

  85        .ascii  "Please use a boot loader program instead./r/n"

  86        .ascii  "/n"

  87        .ascii  "Remove disk and press any key to reboot . . ./r/n"

  88        .byte   0

  89

  90

  91        # Kernel attributes; used by setup.  This is part 1 of the

  92        # header, from the old boot sector.

  93

 

上面这些代码的作用就是打印消息并等待重启,就不多说了。在这里顺便说一下,grubboot_func中的big_linux_boot里,描述了实际上grubstage2将内核的bootsectsetup实模式代码载入到地址0x90000后。也就是说,这里就是对应Grub0x90000的证据。由于我们setup.ld链接脚本里规定的setup.bin入口点是_start,那么grub加载vmlinuz后将跳过前512个字节,0x200个字节的,直接跳转到地址0x90200处执行的。我们还是来关注真正的setup代码了,从偏移内核映像vmlinuz0x0200开始。

 

那么grub怎么执行这条代码呢?grub会执行jmp_far(0x20, 0); 也就是跳过vmlinuz0x200个字节,也就是跳到vmlinuz实模式代码_start执行了,因为这样就略过了前512字节的bootsect。这也就是一开始我们说bootsect.S消失了的秘密。

 

实际上,jmp_far就是grub中一条指令,ljmp。再看看第105行的注释,这就是ljmp跳到的位置,即_start,也就是进入“中世纪时代”。

你可能感兴趣的:(vmware,linux,汇编,header,video,attributes)