Uboot在设置启动命令的时候使用的是Tag方式,也就是内核现在期望使用的参数传递方式。还有一种引导设置方式,就是采用2.2以及以前版本使用的参数设置方式,2.4和2.6内核为了兼容之前版本参数设置,对老版本参数数据进行了解析,转换成了内部tag方式。这样我们完全可以使用老版本的参数传递方式。
Setup.h文件中定义了老版本参数传递结构:
#define COMMAND_LINE_SIZE 1024 // 命令行最多1024字节
/* This is the old deprecated way to pass parameters to the kernel */
struct param_struct {
union {
struct {
unsigned long page_size; // 内存的页面大小
unsigned long nr_pages; // 内存页面数量
unsigned long ramdisk_size; // RAM disk配置, 可以不用
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]; // 命令行启动参数
};
在整个结构中,最关键的部分是u1.s.page_size,u1.s.nr_pages和commandline域。这三个域也是必须设置的域!
代码如下:
#define LINUX_MACHINE_ID 406 // 见后面说明
#define LINUX_PAGE_SHIFT 12 // 页面大小4K
#define LINUX_PAGE_SIZE (1<
int do_bootzimage(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
int i;
u32 addr;
char *cmdline = getenv ("bootargs"); // 获得命令行参数
void (*run)(int zero, int arch); // 定义引导内核的函数原型
// 设置引导参数所处的位置
struct param_struct *params = (struct param_struct *)0xa0000100;
// 设置内核加载地址
if (argc<2)
addr = load_addr; // 默认的加载地址
else
// 调用命令时参数传递了地址, 那么就使用指定的地址
addr = simple_strtoul(argv[1], NULL, 16);
// 接下来将结构的数据清零, 不用的全部清零, 防止出错
for(i=0; i<(sizeof(struct param_struct)>>2); i++)
((u32 *)params)[i] = 0;
// 设置u1.s.page_size和u1.s.nr_pages参数, 这两个参数由内核自动解析.
params->u1.s.page_size = LINUX_PAGE_SIZE;
params->u1.s.nr_pages = (0x04000000 >> LINUX_PAGE_SHIFT);
// 拷贝命令行参数到命令位置
memcpy(params->commandline, cmdline, strlen(cmdline));
run = (void (*)(int, int))addr;
// 执行内核程序, 传递两个参数进去, 第2个参数指定了ARCH值, 必须与
// 内核配置的相同, 否则会出现”Error: a”错误!!
run(0, LINUX_MACHINE_ID);
}
// 最后定义bootzImage命令,实现函数是do_bootzimage。
U_BOOT_CMD(
bootzimage, 2, 1, do_bootzimage,
"bootzimage - boot zImage from ram.\n",
" [addr] boot zImage directoly. "
);
这里我们在引导的时候使用了老版本的参数传递方式,下面时Linux内核在进行参数解析时的代码,可以看到,内核将这些参数自动转换成能够识别的类型:
内核(2.6.9)的init/main.c文件中的start_kernel()函数中调用了setup_arch()函数:
asmlinkage void __init start_kernel(void)
{
char * command_line;
extern struct kernel_param __start___param[], __stop___param[];
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/
lock_kernel();
page_address_init();
printk(linux_banner);
setup_arch(&command_line); // 这个函数中将对传递的参数进行解析
setup_per_cpu_areas();
…
setup_arch()函数位于体系结构相关的代码中,本例中存在于arch/arm/kernel/setup.c文件中。
以下是该函数的部分内容
…
struct tag *tags = (struct tag *)&init_tags;
…
// 判断是不是tag的ATAG_CORE, 老式的参数是不能满足要求的, 如果是老式的
// 参数那么将会执行convert_to_tag_list(tags)将老式参数转换成新的参数类型.
if (tags->hdr.tag != ATAG_CORE)
convert_to_tag_list(tags);
convert_to_tag_list()函数实现在arch/arm/kernel/compat.c文件中,该函数随后调用build_tag_list()函数进行参数重组,具体可以参考内核的源代码。
(本文章发表于psbec的个人blog,未经本人许可,不得用于商业用途。任何个人、媒体、其他网站不得私自抄袭;网络媒体转载请注明出处,增加原文链接,否则属于侵权行为。如有任何问题,请留言或者发邮件给psbec,地址)
阅读(1466) | 评论(0) | 转发(0) |