硬件: TQ2440
软件: uboot1.1.6+ Linux2.6.25
一、 uboot 将参数copy到0x30000100处
1. 在uboot1.1.6/lib_arm/boot_zImage.c中
对于kernel来说,uboot传的参数是放在0x30000100处,并没有通过寄存器(r0,r1,r2...)传递参数地址.
二、 kernel 将参数从0x30000100处读取出来
2. zImage读取uboot参数过程
说明: 虽然进入内核之后会先解压, 对于TQ2440来说, 还是解压到0x30008000,而且寄存器没有变化,所以可以忽略解压的过程,可以认为内核是直接在0x30008000运行了.
2.1 进入start_kernel -->setup_arch
2.2 setup_machine函数
2.3 在arch/arm/kernel/compat.c中
在arch/arm/kernel/compat.c中
2.4 在 arch/arm/kernel/setup.c中
ATAG_CMDLINE的初始化函数是parse_tag_cmdline
, L739 t
-
>
parse
(
tag
)
;
就调用了这个
三、 kernel 解析参数
3.1 在 arch/arm/kernel/setup.c中
3.2 在init/main中
start_kernel--> parse_early_param
3.3 在init/main中
start_kernel --> parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param, &unknown_bootoption);
综上所述:
还综上个屁啊, 这个参数传递也太简单了. uboot 会把要传给内核的参数copy到0x30000100处; 内核的setup_arch函数会直接到0x30000100把参数copy出来,然后进行解析.
软件: uboot1.1.6+ Linux2.6.25
一、 uboot 将参数copy到0x30000100处
1. 在uboot1.1.6/lib_arm/boot_zImage.c中
- 151 int boot_zImage(ulong from, size_t size)
- 152 {
- #define LINUX_KERNEL_OFFSET 0x8000
- 158 boot_mem_base = 0x30000000;
- 161 to = boot_mem_base + LINUX_KERNEL_OFFSET;
- 163 ret = copy_kernel_img(to, (char *)from, size); // 将内核从nand_flash拷到内存中, to=0x30008000
- 171 if (*(ulong *)(to + 9*4) != LINUX_ZIMAGE_MAGIC) ; // 检查zImage标志, 0x30008000+36处是zImage的标志
- 180 setup_linux_param(boot_mem_base + LINUX_PARAM_OFFSET); // 将参数拷到内存0x30000100处
- 183 mach_type = MACH_TYPE_S3C2440; // MACH_TYPE_S3C2440=168
- 188 call_linux(0, mach_type, to); // 启动内核
- 190 return 0;
- 191 }
-
- 106 static void setup_linux_param(ulong param_base)
- 107 {
- 108 struct param_struct *params = (struct param_struct *)param_base; // param_base=0x30000100; //将struct param_struct 着重显示一下
- 109 char *linux_cmd;
- 112 memset(params, 0, sizeof(struct param_struct));
- 113
- 114 params->u1.s.page_size = LINUX_PAGE_SIZE;
- 115 params->u1.s.nr_pages = (DRAM_SIZE >> LINUX_PAGE_SHIFT);
- 116
- 117 /* set linux command line */
- 118 linux_cmd = getenv ("bootargs");
- 119 if (linux_cmd == NULL) {
- 120 printk("Wrong magic: could not found linux command line\n");
- 121 } else {
- 122 memcpy(params->commandline, linux_cmd, strlen(linux_cmd) + 1); //将bootargs拷到0x30000100+offset(commandline)处
- 124 }
- 125 }
-
- 75 void call_linux(long a0, long a1, long a2) //a0=0, a1=MACH_TYPE_S3C2440, a2=0x30008000
- 76 {
- 77 local_irq_disable();
- 78 cache_clean_invalidate();
- 79 tlb_invalidate(); //启动内核前的必要准备工作,关irq, 清cache以及tlb
- 80
- 81 __asm__(
- 82 "mov r0, %0\n" //L82-L98启动内核: mov pc,r2即跳到0x30008000处执行
- 83 "mov r1, %1\n" //此时寄存器状态: r0=0, r1=MACH_TYPE_S3C2440, r2=0x30008000
- 84 "mov r2, %2\n" //其中有用的寄存器只有r1=MACH_TYPE_S3C2440
- 85 "mov ip, #0\n" //r0必须为0, 内核会把r2当成tagsList,检查后发现不对,就把r2赋成0,对内核来说这两个寄存器都没有多大用途
- 86 "mcr p15, 0, ip, c13, c0, 0\n" /* zero PID */
- 87 "mcr p15, 0, ip, c7, c7, 0\n" /* invalidate I,D caches */
- 88 "mcr p15, 0, ip, c7, c10, 4\n" /* drain write buffer */
- 89 "mcr p15, 0, ip, c8, c7, 0\n" /* invalidate I,D TLBs */
- 90 "mrc p15, 0, ip, c1, c0, 0\n" /* get control register */
- 91 "bic ip, ip, #0x0001\n" /* disable MMU */
- 92 "mcr p15, 0, ip, c1, c0, 0\n" /* write control register */
- 93 "mov pc, r2\n"
- 94 "nop\n"
- 95 "nop\n"
- 96 : /* no outpus */
- 97 : "r" (a0), "r" (a1), "r" (a2)
- 98 : "r0","r1","r2","ip"
- 99 );
- 100 }
二、 kernel 将参数从0x30000100处读取出来
2. zImage读取uboot参数过程
说明: 虽然进入内核之后会先解压, 对于TQ2440来说, 还是解压到0x30008000,而且寄存器没有变化,所以可以忽略解压的过程,可以认为内核是直接在0x30008000运行了.
2.1 进入start_kernel -->setup_arch
- void __init setup_arch(char **cmdline_p)
- 789 {
- 790 struct tag *tags = (struct tag *)&init_tags;
- 791 struct machine_desc *mdesc;
- 792 char *from = default_command_line;
- 795 mdesc = setup_machine(machine_arch_type); //见下文2.2: 会调用head-common.S中的lookup_machine_tye,这个跟arch/arm/head.S中的调用是一样的
- 796 machine_name = mdesc->name; //查找到正确的machine_desc结构体之后,在machine_desc中就有了boot_params的地址(0x30000100)
- 801 if (__atags_pointer)
- 802 tags = phys_to_virt(__atags_pointer);
- 803 else if (mdesc->boot_params)
- 804 tags = phys_to_virt(mdesc->boot_params); //将物理地址(boot_params)转为虚拟地址,己经开启MMU了啊,亲. P->V(0x30000100-> 0xc0000100)
- 805
- 810 if (tags->hdr.tag != ATAG_CORE)
- 811 convert_to_tag_list(tags); //见下文2.3: 将tags转为tagslist(可以理解为taglist_init),
- //同时获取uboot中setup_linux_param传的参数
- //taglist的hdr.tag依次为ATAG_CORE ATAG_RAMDISK ATAG_INITRD ATAG_SERIAL ATAG_REVISION ATAG_CMDLINE ATAG_NONE
- 818 if (tags->hdr.tag == ATAG_CORE) {
819 if (meminfo.nr_banks != 0)
820 squash_mem_tags(tags); //no exec
821 save_atags(tags);
822 parse_tags(tags); //见下文2.4 :将参数copy到default_command_line中,from指向default_command_line,所以现在的from就是uboot的参数
823 }
824
825 init_mm.start_code = (unsigned long) &_text;
826 init_mm.end_code = (unsigned long) &_etext;
827 init_mm.end_data = (unsigned long) &_edata;
828 init_mm.brk = (unsigned long) &_end;
829
830 memcpy(boot_command_line, from, COMMAND_LINE_SIZE); //将参数copy到全局变量boot_cmmand_line中
831 boot_command_line[COMMAND_LINE_SIZE-1] = '\0';
832 parse_cmdline(cmdline_p, from); //见下文3.1: 解析参数
833 paging_init(&meminfo, mdesc);
834 request_standard_resources(&meminfo, mdesc);
- }
2.2 setup_machine函数
- static struct machine_desc * __init setup_machine(unsigned int nr)
- {
- struct machine_desc *list;
- list = lookup_machine_type(nr); //这个lookup_machine_type就是 arch/arm/kernel/head.S中的 __lookup_machine_type,只不过对C进行了一点封装
- return list;
- }
-
2.2.1 在arch/arm/mach/arch.h中
- #define MACHINE_START(_type,_name) \
- static const struct machine_desc __mach_desc_##_type \
- __used \
- __attribute__((__section__(".arch.info.init"))) = { \
- .nr = MACH_TYPE_##_type, \
- .name = _name,
- #define MACHINE_END \
- }
对结构体machine_desc进行初始化
MACHINE_START(S3C2440, "SMDK2440")
- /* Maintainer: Ben Dooks <ben@fluff.org> */
- .phys_io = S3C2410_PA_UART,
- .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
- .boot_params = S3C2410_SDRAM_PA + 0x100, //boot_param=0x30000100
- .init_irq = s3c24xx_init_irq,
- .map_io = smdk2440_map_io,
- .init_machine = smdk2440_machine_init,
- .timer = &s3c24xx_timer,
- MACHINE_END
- #define MACHINE_START(_type,_name) \
2.3 在arch/arm/kernel/compat.c中
- 215 void __init convert_to_tag_list(struct tag *tags)
- 216 {
- 217 struct param_struct *params = (struct param_struct *)tags; //最终再次见到struct param_struct结构体了
- 218 build_tag_list(params, ¶ms->u2);
- 219 }
- 95 static void __init build_tag_list(struct param_struct *params, void *taglist) //这个参数params就是0x3000100的虚拟地址
- 96 {
- 201 tag = tag_next(tag);
- 202 tag->hdr.tag = ATAG_CMDLINE;
- 203 tag->hdr.size = (strlen(params->commandline) + 3 +
- 204 sizeof(struct tag_header)) >> 2;
- 205 strcpy(tag->u.cmdline.cmdline, params->commandline); //获取uboot的setup_linux_param中的参数
213 }
- start_kernel-->setup_arch-->parse_tags
- 在convert_to_tag_list(tags)中,己经把taglist的hdr.tag依次初始化为:
- ATAG_CORE
- ATAG_RAMDISK
- ATAG_INITRD
- ATAG_SERIAL
- ATAG_REVISION
- ATAG_CMDLINE
- ATAG_NONE
- 下面就按照list的内容依次调用其初始化函数
- 733 static int __init parse_tag(const struct tag *tag)
- 734 {
- 735 extern struct tagtable __tagtable_begin, __tagtable_end;
- 736 struct tagtable *t;
- 737
- 738 for (t = &__tagtable_begin; t < &__tagtable_end; t++)
- 739 if (tag->hdr.tag == t->tag) {
- 740 t->parse(tag);
- 741 break;
- 742 }
- 743
- 744 return t < &__tagtable_end;
- 745 }
-
- 751 static void __init parse_tags(const struct tag *t)
- 752 {
- 753 for (; t->hdr.size; t = tag_next(t)) //会调用head-common.
- 754 if (!parse_tag(t))
- 755 printk(KERN_WARNING
- 756 "Ignoring unrecognised tag 0x%08x\n",
- 757 t->hdr.tag);
- 758 }
- 759
- 720 static int __init parse_tag_cmdline(const struct tag *tag)
- 721 {
- 722 strlcpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE); //tag->u.cmdline.cmdline就是uboot的参数,现在copy到default_command_line中
- 723 return 0;
- 724 }
- 725
- 726 __tagtable(ATAG_CMDLINE, parse_tag_cmdline);
- 727
三、 kernel 解析参数
3.1 在 arch/arm/kernel/setup.c中
- 这个函数只会解析,几个特定的参数,如: "mem=" "initrd=" "ecc=" nowb nocache "cachepolicy="(没有等等,就这几个),其它的参数,它老人家就不管了
- 505 static void __init parse_cmdline(char **cmdline_p, char *from)
- 506 {
- 507 char c = ' ', *to = command_line;
- 508 int len = 0;
- 509
- 510 for (;;) {
- 511 if (c == ' ') {
- 512 extern struct early_params __early_begin, __early_end;
- 513 struct early_params *p;
- 514
- 515 for (p = &__early_begin; p < &__early_end; p++) {
- 516 int len = strlen(p->arg);
- 517
- 518 if (memcmp(from, p->arg, len) == 0) {
- 519 if (to != command_line)
- 520 to -= 1;
- 521 from += len;
- 522 p->fn(&from);
- 523
- 524 while (*from != ' ' && *from != '\0')
- 525 from++;
- 526 break;
- 527 }
- 528 }
- 529 }
- 530 c = *from++;
- 531 if (!c)
- 532 break;
- 533 if (COMMAND_LINE_SIZE <= ++len)
- 534 break;
- 535 *to++ = c;
- 536 }
- 537 *to = '\0';
- 538 *cmdline_p = command_line;
- 539 }
3.2 在init/main中
start_kernel--> parse_early_param
- 476 void __init parse_early_param(void)
- 477 {
- 478 static __initdata int done = 0;
- 479 static __initdata char tmp_cmdline[COMMAND_LINE_SIZE];
- 480
- 481 if (done)
- 482 return;
- 483
- 484 /* All fall through to do_early_param. */
- 485 strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
- 486 parse_args("early options", tmp_cmdline, NULL, 0, do_early_param);
- 487 done = 1;
- 488 }
3.3 在init/main中
start_kernel --> parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param, &unknown_bootoption);
- 130 int parse_args(const char *name,
131 char *args,
132 struct kernel_param *params,
133 unsigned num,
134 int (*unknown)(char *param, char *val))
135 {
136 char *param, *val;
137
138 DEBUGP("Parsing ARGS: %s\n", args);
139
140 /* Chew leading spaces */
141 while (*args == ' ')
142 args++;
143
144 while (*args) {
145 int ret;
146 int irq_was_disabled;
147
148 args = next_arg(args, ?m, &val);
149 irq_was_disabled = irqs_disabled();
150 ret = parse_one(param, val, params, num, unknown);
151 if (irq_was_disabled && !irqs_disabled()) {
152 printk(KERN_WARNING "parse_args(): option '%s' enabled "
153 "irq's!\n", param);
154 }
155 switch (ret) {
156 case -ENOENT:
157 printk(KERN_ERR "%s: Unknown parameter `%s'\n",
158 name, param);
159 return ret;
160 case -ENOSPC:
161 printk(KERN_ERR
162 "%s: `%s' too large for parameter `%s'\n",
163 name, val ?: "", param);
164 return ret;
165 case 0:
166 break; - 167 default:
168 printk(KERN_ERR
169 "%s: `%s' invalid for parameter `%s'\n",
170 name, val ?: "", param);
171 return ret;
172 }
173 }
174
175 /* All parsed OK. */
176 return 0;
177 }
综上所述:
还综上个屁啊, 这个参数传递也太简单了. uboot 会把要传给内核的参数copy到0x30000100处; 内核的setup_arch函数会直接到0x30000100把参数copy出来,然后进行解析.