uboot传递内核参数全解析(转)

转自http://forum.byr.edu.cn/wForum/boardcon.php?bid=317&id=3584&ftype=3 

标  题: uboot传递内核参数全解析(原创)
发信站: 北邮人论坛 (Tue Jan 20 17:54:27 2009), 站内

一:启动参数的传递过程
启动参数是包装在数据结构里的,在linux kernel启动的时候,bootloader把这个数据结构拷贝到某个地址,
在改动PC跳向内核接口的同时,通过通用寄存器R2来传递这个地址的值,下面这句话就是uboot跳向linux
kernel的代码(bootm命令)
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
thekernel其实不是个函数,而是指向内核入口地址的指针,把它强行转化为带三个参数的函数指针,会把三个
参数保存到通用寄存器中,实现了向kernel传递信息的功能,在这个例子里,会把R0赋值为0,R1赋值为机器号
R2赋值为启动参数数据结构的首地址
因此,要向内核传递参数很简单,只要把启动参数封装在linux预定好的数据结构里,拷贝到某个地址(一般
约定俗成是内存首地址+100dex)

二:启动参数的数据结构
启动参数可保存在两种数据结构中,param_struct和tag,前者是2.4内核用的,后者是2.6以后的内核更期望用的
但是,到目前为止,2.6的内核也可以兼容前一种结构,两种数据结构具体定义如下(arm cpu):

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];
};

param_struct只需要设置cmmandline,u1.s.page_size,u1.s.nr_pages三个域,具体使用可参见下面的例子
对于tag来说,在实际使用中是一个struct tag组成的列表,在tag->tag_header中,一项是u32 tag(重名,注意类型)
其值用宏ATAG_CORE,ATAG_MEM,ATAG_CMDLINE,ATAG_NONE等等来表示,此时下面union就会使用与之相关的数据结构
同时,规定tag列表中第一项必须是ATAG_CORE,最后一项必须是ATAG_NONE,比如在linux代码中,找到启动参数之后
首先看tag列表中的第一项的tag->hdr.tag是否为ATAG_CORE,如果不是,就会认为启动参数不是tag结构而是param_struct
结构,然后调用函数来转换.在tag->tag_header中,另一项是u32 size,表示tag的大小,tag组成列表的方式就是
指针+size,实际使用中用tag_next (params).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;
        struct tag_acorn    acorn;        //Acorn specific
    struct tag_omap   omap;     //OMAP specific
        struct tag_memclk    memclk;   //DC21285 specific
    } u;
};

需要注意的是,这两个数据结构在uboot中和linux中分别有定义,这个定义必须一直才能正常传递参数
如果实际使用中不一致的话就不能正常传递,可以自行修改

三:通过两种数据结构传递参数的具体例子

1:例子一:通过param_struct让uboot中的go命令可以传递参数
分析:go的代码在common/cmd_boot.c中,里面并没有拷贝启动参数的代码,转向内核的时候也没有传送
启动参数所在的地址,因此添加如下代码用于拷贝参数,可以看到,对于param_struct只需要设置cmmandline
u1.s.page_size,u1.s.nr_pages三个域
                char *commandline = getenv("bootargs");
        struct param_struct *lxy_params=(struct param_struct *)0x80000100;

        printf("setup linux parameters at 0x80000100/n");
        memset(lxy_params,0,sizeof(struct param_struct));
        lxy_params->u1.s.page_size=(0x1<<12); //4K 这个是必须有的,否则无法启动
        lxy_params->u1.s.nr_pages=(0x4000000)>>12; //64M 这个是必须有的,否则无法启动
        memcpy(lxy_params->commandline,commandline,strlen(commandline)+1);
        printf("linux command line is: /"%s/"/n",lxy_params->commandline);

然后还要向内核传递参数地址,将下面一行代码修改:
rc = ((ulong (*)(int, char *[]))addr) (--argc, &argv[1]);  //需要被修改的代码
rc = ((ulong(*)(int,int,uint))addr) (0, gd->bd->bi_arch_number,gd->bd->bi_boot_params);//修改之后的代码


2:例子二:bootm命令中通过拷贝tag传递参数
为方便阅读,进行了少许修改,但功能不变,该函数参数为存放启动参数的地址
static void setup_linux_tag(ulong param_base)
{
 struct tag *params = (struct tag *)param_base;
 char *linux_cmd;
 char *p;

 memset(params, 0, sizeof(struct tag));

 /* step1: setup start tag */
 params->hdr.tag = ATAG_CORE;
 params->hdr.size = tag_size(tag_core);
 params->u.core.flags = 0;
 params->u.core.pagesize = LINUX_PAGE_SIZE;
 params->u.core.rootdev = 0;
 params = tag_next(params);

 /* step2: setup cmdline tag */
 params->hdr.tag = ATAG_CMDLINE;
 linux_cmd = getenv("bootargs");
 /* eat leading white space */
 for (p=linux_cmd; *p==' '; p++) {/* do nothing */;}
 params->hdr.size = (sizeof(struct tag_header)+strlen(linux_cmd)+1+4) >> 2;
 memcpy(params->u.cmdline.cmdline, linux_cmd, strlen(linux_cmd)+1);
 params = tag_next(params);

 /* step3: setup end tag */
 params->hdr.tag = ATAG_NONE;
 params->hdr.size = 0;
}


四:其他
在uboot中,进行设置tag的函数都在lib_arm/armlinux.c中,在这些函数前面是有ifdef的
#if defined (CONFIG_SETUP_MEMORY_TAGS) || /
    defined (CONFIG_CMDLINE_TAG) || /
    defined (CONFIG_INITRD_TAG) || /
    defined (CONFIG_SERIAL_TAG) || /
    defined (CONFIG_REVISION_TAG) || /
    defined (CONFIG_LCD) || /
    defined (CONFIG_VFD)

因此,如果你的bootm命令不能传递内核参数,就应该是在你的board的config文件里没有对上述的
宏进行设置,定义一下即可

你可能感兴趣的:(U-BOOT)