在Linux移植之内核启动过程start_kernel函数简析中已经指出了start_kernel函数的调用层次,这篇主要是对具体的tag参数列表进行解析。
1、内存参数ATAG_MEM参数解析
2、命令行参数ATAG_CMDLINE解析,以传入的命令参数bootargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0为列:
1)、noinitrd参数解析过程,当你没有使用ramdisk启动系统的时候,你需要使用noinitrd这个参数,但是如果使用了的话,就需要指定initrd=r_addr,size, r_addr表示initrd在内存中的位置,size表示initrd的大小。
2)、root=/dev/mtdblock3参数解析过程
3)、init=/linuxrc参数解析过程
4)、 console=ttySAC0参数解析过程
start_kernel
setup_arch //解析UBOOT传入的启动参数
setup_command_line //解析UBOOT传入的启动参数
do_early_param //解析early参数,uboot中没传这个参数
unknown_bootoption//解析到了命令行参数,saved_root_name在这块初始化
console_init();//控制台初始化
rest_init
kernel_thread
kernel_init
prepare_namespace //解析命令行参数解析成功挂接在哪个分区
mount_root//挂接根文件系统
init_post
//执行应用程序
1、内存参数ATAG_MEM参数解析
看到archarmkernelSetup.c文件,在setup_arch函数里看到如下几行,首先根据内核启动时第一阶段得到的machine_arch_type,取得mdesc结构体,这个结构体在Linux移植之内核启动过程引导阶段分析已经介绍过,这里主要关心的是boot_params参数,里面存放的是tag参数列表的存放地址,然后将取得的物理地址转换为虚拟地址供后面使用tag。
776 console_init();//控制台初始化
777 archarmkernelSetup.c
778
779 setup_processor();//设置处理器相关的一些设置
780 mdesc = setup_machine(machine_arch_type);//获得开发板的machine_desc结构
781 machine_name = mdesc->name;//取得开发板的名称
782
783 if (mdesc->soft_reboot)
784 reboot_setup('s');
785
786 if (mdesc->boot_params)//确定uboot传入的启动参数的地址
787 tags = phys_to_virt(mdesc->boot_params);//将启动参数的物理地址转换为虚拟地址
setup_arch函数继续往下看
109 static struct meminfo meminfo __initdata = { 0, };
798 if (tags->hdr.tag == ATAG_CORE) {//ATAG_CORE为tag标记列表的开始
799 if (meminfo.nr_banks != 0)//如果已经在内核中定义了meminfo结构
780 squash_mem_tags(tags);//则忽略内存tag
781 parse_tags(tags);//解释每个tag
782 }
其中meminfo就是处理完ATAG_MEN参数后,将里面的内容放去meninfo中,它的结构定义在includeasm-armSetup.h 中
207 struct meminfo {
208 int nr_banks;
209 struct membank bank[NR_BANKS];
210 };
接着继续看parse_tags函数,它也位于archarmkernelSetup.c中
733 static void __init parse_tags(const struct tag *t)
734 {
735 for (; t->hdr.size; t = tag_next(t))//循环取出tag列表,然后处理
736 if (!parse_tag(t)) //处理取出的tag列表
737 printk(KERN_WARNING
738 'Ignoring unrecognised tag 0x%08xn',
739 t->hdr.tag);
740 }
接着分析parse_tag函数,它同样位于archarmkernelSetup.c中
715 static int __init parse_tag(const struct tag *tag)
716 {
717 extern struct tagtable __tagtable_begin, __tagtable_end;
718 struct tagtable *t;
719
720 for (t = &__tagtable_begin; t < &__tagtable_end; t++)//从.taglist.init段找出符合的处理tag列表的结构
721 if (tag->hdr.tag == t->tag) {//找到符合的tag
722 t->parse(tag);//调用相应的处理tag的函数处理
723 break;
724 }
725
726 return t < &__tagtable_end;//t<&__tagtable_end说明找到了tag
727 }
parse_tag会从.taglist.init段找出符合的tag,然后调用相应的处理函数处理。tagtable 的结构如下,它位于includeasm-armSetup.h 中
171 struct tagtable {
172 __u32 tag;//处理的tag值
173 int (*parse)(const struct tag *);//处理函数
174 };
我们需要的是处理ATAG_MEN参数的函数,搜搜ATAG_MEN,在archarmkernelSetup.c中找到了parse_tag_mem32处理ATAG_MEN参数的函数。它的功能就是取出内存的开始地址与大小信息后存放在meminfo结构中
614 static int __init parse_tag_mem32(const struct tag *tag)
615 {
616 if (meminfo.nr_banks >= NR_BANKS) {
617 printk(KERN_WARNING
618 'Ignoring memory bank 0x%08x size %dKBn',
619 tag->u.mem.start, tag->u.mem.size / 1024);
620 return -EINVAL;
621 }
622 arm_add_memory(tag->u.mem.start, tag->u.mem.size);//取出内存的开始地址与大小信息后存放在meminfo结构中
623 return 0;
624 }
625
626 __tagtable(ATAG_MEM, parse_tag_mem32);//解析ATAG_MEM列表,函数为parse_tag_mem32
再看到__tagtable,同样位于includeasm-armSetup.h中。主要就是将tagtable 这个结构体放在.taglist.init段
188 #define __tag __used __attribute__((__section__('.taglist.init')))
189 #define __tagtable(tag, fn)
190 static struct tagtable __tagtable_##fn __tag = { tag, fn }
到这里就分析完了tag列表中ATAG_MEM参数的处理,接下去分析ATAG_CMDLINE参数的处理。
2、命令行参数ATAG_CMDLINE解析
找到与ATAG_CMDLINE参数的过程与前面ATAG_MEM参数一样的流程就不分析了,直接找到处理ATAG_CMDLINE参数的函数,它位于archarmkernelSetup.c中。它只是简单的将tag->u.cmdline.cmdline的内容复制到default_command_line中。
702 static int __init parse_tag_cmdline(const struct tag *tag)
703 {
704 strlcpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE);//简单的将tag的内容复制到字符串default_command_line中
705 return 0;
706 }
707
708 __tagtable(ATAG_CMDLINE, parse_tag_cmdline);
接着看到default_command_line,它定义在archarmkernelSetup.c中,它的大小为1024字节
114 static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;
它初始化为CONFIG_CMDLINE,位于includelinuxAutoconf.h中
374 #define CONFIG_CMDLINE 'root=/dev/hda1 ro init=/bin/bash console=ttySAC0'
所以拷贝之后
default_command_line[] = 'noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0'
继续往下看default_command_line,在archarmkernelSetup.c下的setup_arch函数中:其中parse_cmdline是对位于.early_param.init段的内容进行前期的初始化。相应的命令有:cachepolicy=、nocache、nowb、ecc=、initrd=、mem=等等,我们的参数没有涉及到这类命令,所以不去细细的分析这个函数了。
809 memcpy(boot_command_line, from, COMMAND_LINE_SIZE);//form指向default_command_line,将default_command_line中的内容拷贝到boot_command_line中
810 boot_command_line[COMMAND_LINE_SIZE-1] = '