Bootloader与内核的交互

 
Bootloader与内核的交互是单向的,Bootloader将各类参数传给内核。由于它们不能同时运行,传递办法只有一个:Bootloader将参数放在某个约定的地方之后,再启动内核,内核启动后从这个地方获得参数。
除了约定好参数存放的地址外,还要规定参数的结构。Linux 2.4.x 以后的内核都期望以标记列表(tagged list)的形式来传递启动参数。标记,就是一种数据结构;标记列表,就是挨着存放的多个标记。标记列表以标记ATAG_CORE 开始,以标记ATAG_NONE 结束。标记的数据结构为tag,它由一个tag_header结构和一个联合(union)组成。tag_header结构表示标记的类型及长度,比如是表示内存还是表示命令行参数等。对于不同类型的标记使用不同的联合(union),比如表示内存时使用tag_mem32,表示命令行时使用tag_cmdline。数据结构tagtag_header定义在Linux内核源码的arch/arm/include/asm/setup.h头文件中:
 
struct tag_header {
      u32 size;
      u32 tag;
};
 
struct tag {
      struct tag_header hdr;
      union {
            struct tag_core               core;
            struct tag_mem32          mem;
            struct tag_videotext       videotext;
            struct tag_ramdisk         ramdisk;
            struct tag_initrd             initrd;
            struct tag_serialnr          serialnr;
            struct tag_revision         revision;
            struct tag_videolfb         videolfb;
            struct tag_cmdline         cmdline;
 
          /*
          * Acorn specific
          */
           struct tag_acorn             acorn;
 
         /*
          * DC21285 specific
          */
           struct tag_memclk         memclk;
      } u;
};
 
 
下面以设置内存标记、命令行标记为例说明参数的传递:
 
(1)设置标记 ATAG_CORE。
 
标记列表以标记 ATAG_CORE开始,假设Bootloader与内核约定的参数存放地址为0x30000100,则可以以如下代码设置标记 ATAG_CORE:
 
params = (struct tag *) 0x30000100;
 
params->hdr.tag            = ATAG_CORE;
params->hdr.size           = tag_size (tag_core);
params->u.core.flags     = 0;
params->u.core.pagesiz = 0;
params->u.core.rootdev = 0;
params                        = tag_next (params);
 
其中,tag_next定义如下,它指向当前标记的末尾:
 
#define tag_next(t)      ((struct tag *)((u32 *)(t) + (t)->hdr.size))
 
 
(2)设置内存标记。
 
假设开发板使用的内存起始地址为0x3000,0000,大小为0x0400,0000,则内存标记可以如下设置:
 
params->hdr.tag            = ATAG_MEM;
params->hdr.size           = tag_size (tag_mem32);
params->u.mem.start     = 0x30000000;
params->u.mem.size      = 0x4000000;
params                         = tag_next (params);
 
(3)设置命令行标记。
 
命令行就是一个字符串,它被用来控制内核的一些行为。比如“root=/dev/mtdblock2 init=/linuxrc console=ttySAC0”表示根文件系统在MTD2分区上,系统启动后执行的第一个程序为/linuxrc,控制台为ttySAC0,即第一个串口。
命令行可以在Bootloader中通过命令设置好,然后如下构造标记传给内核:
 
char *p = "root=/dev/mtdblock2 init=/linuxrc console=ttySAC0";
 
params->hdr.tag = ATAG_CMDLINE;
params->hdr.size = (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;
strcpy (params->u.cmdline.cmdline, p);
params = tag_next (params);
 
(4)设置标记ATAG_NONE。
 
标记列表以标记ATAG_NONE结束,如下设置:
 
params->hdr.tag = ATAG_NONE;
params->hdr.size = 0;
 
 
对于ARM架构的CPU,都是通过lib_arm/bootm.c中的do_bootm_linux函数来启动内核。这个函数中,设置标记列表,最后通过“theKernel (0, bd->bi_arch_number, bd->bi_boot_params)”调用内核。其中,theKernel指向内核存放的地址(对于ARM架构的CPU,通常是0x30008000),bd->bi_arch_number就是前面board_init函数设置的机器类型ID,而bd->bi_boot_params就是标记列表的开始地址。
命令行标记、内存标记由U-Boot中的setup_memory_tags、setup_commandline_tag函数实现,它们都是在lib_arm/bootm.c中定义。一般而言,设置这两个标记就可以了,在配置文件include/configs/smdk2410.h中增加如下两个配置项即可:
 
#define CONFIG_SETUP_MEMORY_TAGS      1
#define CONFIG_CMDLINE_TAG                    1
 
注:
 
       对于 linux 2.4版本,使用struct param_struct传递内核参数,见linux源码中arch/arm/kernel/compat.c。2.6版本目前也支持这种格式。
 
/*
 * Usage:
 * - do not go blindly adding fields, add them at the end
 * - when adding fields, don't rely on the address until
 *    a patch from me has been released
 * - unused fields should be zero (for future expansion)
 * - this structure is relatively short-lived - only
 *    guaranteed to contain useful data in setup_arch()
 *
 * This is the old deprecated way to pass parameters to the kernel
 */
struct param_struct {
    union {
              struct {
                  unsigned long page_size;                     /* 0 */
                   unsigned long nr_pages;                      /* 4 */
                  unsigned long ramdisk_size;                /* 8 */
                  unsigned long flags;                            /* 12 */
#define FLAG_READONLY 1
#define FLAG_RDLOAD      4
#define FLAG_RDPROMPT 8
           unsigned long rootdev;                        /* 16 */
           unsigned long video_num_cols;           /* 20 */
           unsigned long video_num_rows;          /* 24 */
           unsigned long video_x;                       /* 28 */
           unsigned long video_y;                       /* 32 */
           unsigned long memc_control_reg;        /* 36 */
           unsigned char sounddefault;                 /* 40 */
           unsigned char adfsdrives;                    /* 41 */
           unsigned char bytes_per_char_h;          /* 42 */
           unsigned char bytes_per_char_v;          /* 43 */
           unsigned long pages_in_bank[4];        /* 44 */
           unsigned long pages_in_vram;             /* 60 */
           unsigned long initrd_start;                   /* 64 */
           unsigned long initrd_size;                    /* 68 */
           unsigned long rd_start;                        /* 72 */
           unsigned long system_rev;                   /* 76 */
           unsigned long system_serial_low;        /* 80 */
           unsigned long system_serial_high;        /* 84 */
           unsigned long mem_fclk_21285;          /* 88 */
       } s;
              char unused[256];
    } u1;
    union {
              char paths[8][128];
              struct {
                  unsigned long magic;
                  char n[1024 - sizeof(unsigned long)];
              } s;
    } u2;
    char commandline[COMMAND_LINE_SIZE];
};