转载_uboot传递内核参数全解析

来源:互联网 发布:淘宝编辑宝贝教程视频 编辑:程序博客网 时间:2024/06/08 16:34
一:启动参数的传递过程 
启动参数是包装在数据结构里的,在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文件里没有对上述的 
宏进行设置,定义一下即可