系统启动(4)Grub

1621阅读 0评论2012-07-09 qtdszws
分类:LINUX

linux命令的最后调用了函数grub_loader_set,设置两个函数指针boot和unload,还有grub_loader_loaded变量
      grub_loader_set (grub_linux_boot, grub_linux_unload, 1);

void
grub_loader_set (grub_err_t (*boot) (void),
         grub_err_t (*unload) (void),
         int noreturn)
{
  if (grub_loader_loaded && grub_loader_unload_func)
    grub_loader_unload_func ();
 
  grub_loader_boot_func = boot;
  grub_loader_unload_func = unload;
  grub_loader_noreturn = noreturn;
 
  grub_loader_loaded = 1;
}

initrd命令加载initrd镜像

void
grub_rescue_cmd_initrd (int argc, char *argv[])
{
  grub_file_t file = 0;
  grub_ssize_t size;
  grub_addr_t addr_max, addr_min, addr;
  struct linux_kernel_header *lh;

  if (argc == 0)
    {
      grub_error (GRUB_ERR_BAD_ARGUMENT, "No module specified");
      goto fail;
    }
 
  if (!loaded)
    {
      grub_error (GRUB_ERR_BAD_ARGUMENT, "You need to load the kernel first.");
      goto fail;
    }

  lh = (struct linux_kernel_header *) grub_linux_tmp_addr;

  if (!(lh->header == grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)
    && grub_le_to_cpu16 (lh->version) >= 0x0200))
    {
      grub_error (GRUB_ERR_BAD_OS, "The kernel is too old for initrd.");
      goto fail;
    }

  /* Get the highest address available for the initrd.  */
  if (grub_le_to_cpu16 (lh->version) >= 0x0203)//0x020a
    {
      addr_max = grub_cpu_to_le32 (lh->initrd_addr_max);// ramdisk_max:    .long 0x7fffffff ->2G

      /* XXX in reality, Linux specifies a bogus value, so
     it is necessary to make sure that ADDR_MAX does not exceed
     0x3fffffff.  */
      if (addr_max > GRUB_LINUX_INITRD_MAX_ADDRESS)// 0x37ffffff
    addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS;
    }
  else
    addr_max = GRUB_LINUX_INITRD_MAX_ADDRESS;

//使用mem=限制了内存使用
  if (linux_mem_size != 0 && linux_mem_size < addr_max)
    addr_max = linux_mem_size;

  /* Linux 2.3.xx has a bug in the memory range check, so avoid
     the last page.
     Linux 2.2.xx has a bug in the memory range check, which is
     worse than that of Linux 2.3.xx, so avoid the last 64kb.  */
  addr_max -= 0x10000;

  if (addr_max > grub_os_area_addr + grub_os_area_size)
    addr_max = grub_os_area_addr + grub_os_area_size;//从0x100000开始的物理内存块大小

//最小地址,在command line后
  addr_min = (grub_addr_t) grub_linux_tmp_addr + GRUB_LINUX_CL_END_OFFSET;

  file = grub_file_open (argv[0]);
  if (!file)
    goto fail;

  size = grub_file_size (file);

  /* Put the initrd as high as possible, 4Ki aligned.  */
  addr = (addr_max - size) & ~0xFFF;

  if (addr < addr_min)//initrd太大
    {
      grub_error (GRUB_ERR_OUT_OF_RANGE, "The initrd is too big");
      goto fail;
    }

  if (grub_file_read (file, (void *)addr, size) != size)//读入
    {
      grub_error (GRUB_ERR_FILE_READ_ERROR, "Couldn't read file");
      goto fail;
    }

  lh->ramdisk_image = addr;//initrd起始地址
  lh->ramdisk_size = size;//大小
 
 fail:
  if (file)
    grub_file_close (file);
}


接下来就是boot命令,在commands/boot.c中
GRUB_MOD_INIT(boot)
{
  (void) mod;            /* To stop warning. */
  grub_register_command ("boot", grub_cmd_boot, GRUB_COMMAND_FLAG_BOTH,
             "boot", "Boot an operating system.", 0);
}

具体由grub_cmd_boot完成
static grub_err_t
grub_cmd_boot (struct grub_arg_list *state __attribute__ ((unused)),
           int argc, char **args __attribute__ ((unused)))
{
  if (argc)
    return grub_error (GRUB_ERR_BAD_ARGUMENT, "too many arguments");
 
  grub_loader_boot ();
 
  return 0;
}

kern/loader.c
grub_err_t
grub_loader_boot (void)
{
  if (! grub_loader_loaded)
    return grub_error (GRUB_ERR_NO_KERNEL, "no loaded kernel");

  if (grub_loader_noreturn)
    grub_machine_fini ();
 
  return (grub_loader_boot_func) ();
}

直接调用grub_loader_boot_func即grub_linux_boot,其实现在kern\i386\loader.S
/*
 * void grub_linux_boot_zimage (void)
 */
VARIABLE(grub_linux_prot_size)
    .long    0
VARIABLE(grub_linux_tmp_addr)
    .long    0
VARIABLE(grub_linux_real_addr)
    .long    0
VARIABLE(grub_linux_is_bzimage)
    .long    0
   
FUNCTION(grub_linux_boot)
    /* Must be done before zImage copy.  */
    call    EXT_C(grub_dl_unload_all)

    movl    EXT_C(grub_linux_is_bzimage), %ebx #一般都是bzImage
    test    %ebx, %ebx
    jne bzimage

    /* copy the kernel */
    movl    EXT_C(grub_linux_prot_size), %ecx
    addl    $3, %ecx
    shrl    $2, %ecx
    movl    $GRUB_LINUX_BZIMAGE_ADDR, %esi
    movl    $GRUB_LINUX_ZIMAGE_ADDR, %edi
    cld
    rep
    movsl

bzimage:
    movl    EXT_C(grub_linux_real_addr), %ebx #read_addr,应该是0x90000

    /* copy the real mode code */
    movl    EXT_C(grub_linux_tmp_addr), %esi#复制bootsect.S和setup到real addr
    movl    %ebx, %edi
    movl    $GRUB_LINUX_SETUP_MOVE_SIZE, %ecx #0x9100,执行command line的末尾
    cld
    rep
    movsb

    /* change %ebx to the segment address */
    shrl    $4, %ebx  #段地址 0x9000
    movl    %ebx, %eax # 0x9000
    addl    $0x20, %eax#段地址+0x20 = 相当于跳过512字节,到setup开头的_start
    movw    %ax, linux_setup_seg #ljump目标 0x9020:0

    /* XXX new stack pointer in safe area for calling functions */
    movl    $0x4000, %esp
    call    EXT_C(grub_stop_floppy)

    /* final setup for linux boot */
    call    prot_to_real # realmode.S中,保护模式到实模式
    .code16

    cli
    movw    %bx, %ss #0x9000
    movw    $GRUB_LINUX_SETUP_STACK, %sp #0x9000,后面的0x9100处是command line

    movw    %bx, %ds
    movw    %bx, %es
    movw    %bx, %fs
    movw    %bx, %gs

    /* ljmp */
    .byte    0xea #跳到setup的入口_start
    .word    0
linux_setup_seg:
    .word    0

至此grub的任务结束,开始setup的初始化
上一篇:系统启动(3)Grub
下一篇:系统启动(5)setup