一:启动参数的传递过程
启动参数是包装在数据结构里的,在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文件里没有对上述的
宏进行设置,定义一下即可
启动参数是包装在数据结构里的,在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文件里没有对上述的
宏进行设置,定义一下即可