Linux/ARM物理内存探测(2)-uBoot atags
Linux/ARM物理内存的探测不同于Linux/X86,在Arm
Linux 物理内存探测 (1) – CMDLINE一文中有提及过。在本文中,重点谈下Kernel如何解析uBoot传过来的内存参数。tag数据结构是uBoot构建的,其数据结构分布图如下:
与内存有关的两个tag,一个是mem32,另外一个是cmd. 关于cmd这个tag,和build Kernel是的config_cmdline作用是一样的,但是具体选择哪一个还是两者都拷贝在一起,是根据.config的配置情况。对于cmdline的parse,在Arm Linux 物理内存探测 (1) – CMDLINE 有所描述,这里不再重复。
在Kernel中处理uBoot传递的tag参数的过程如下
start_kernel->setup_arch->setup_machine_tags:
843 static struct machine_desc * __init
setup_machine_tags(unsigned int nr)
844
{
845
struct tag *tags = (struct tag *)&init_tags;
846
struct machine_desc *mdesc = NULL, *p;
847
char *from = default_command_line;//Kernel 's data
848
849
init_tags.mem.start = PHYS_OFFSET;
850
851
/*
852
* locate machine in the list of supported machines.
853
*/
854
for_each_machine_desc(p)
855 if (nr == p->nr) {
856 printk("Machine:
%s\n", p->name);
857 mdesc = p;
858 break;
859 }
860
861
if (!mdesc) {
862 early_print("\nError:
unrecognized/unsupported machine ID"
863 " (r1 =
0x%08x).\n\n", nr);
864 dump_machine_table(); /* does
not return */
865
}
866
867
if (__atags_pointer)
868 tags =
phys_to_virt(__atags_pointer);
869
else if (mdesc->atag_offset)
870 tags = (void *)(PAGE_OFFSET +
mdesc->atag_offset);
871
872
#if defined(CONFIG_DEPRECATED_PARAM_STRUCT)
873
/*
874
* If we have the old style parameters, convert them to
875 * a tag list.
876
*/
877
if (tags->hdr.tag != ATAG_CORE)
878 convert_to_tag_list(tags);
879
#endif
880
881
if (tags->hdr.tag !=
ATAG_CORE) {
882
#if defined(CONFIG_OF)
883 /*
884 * If CONFIG_OF is set, then
assume this is a reasonably
885 * modern system that should
pass boot parameters
886 */
887 early_print("Warning:
Neither atags nor dtb found\n");
888
#endif
889 tags = (struct tag
*)&init_tags;
890
}
891
892
if (mdesc->fixup)
893 mdesc->fixup(tags,
&from, &meminfo);
894
895
if (tags->hdr.tag == ATAG_CORE) {
896 if (meminfo.nr_banks != 0)
897 squash_mem_tags(tags);
898 save_atags(tags);
899 parse_tags(tags);
900
}
901
902
/* parse_early_param needs a boot_command_line */
903
strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);
904
905
return mdesc;
906
}
tag参数存放在r2中从uBoot传递到Kernel中,并被放在__atags_pointer中。在895行~900行,如果是tag数据结构,则会进行tags的parse.
start_kernel->setup_arch->setup_machine_tags->parse_tags:
748 /*
749 *
Parse all tags in the list, checking both the global and architecture
750 *
specific tag tables.
751 */
752
static void __init parse_tags(const struct tag *t)
753
{
754
for (; t->hdr.size; t = tag_next(t))
755 if (!parse_tag(t))
756 printk(KERN_WARNING
757 "Ignoring
unrecognised tag 0x%08x\n",
758
t->hdr.tag);
759
}
从754行的for循环,可以看出其会对tags进行遍历,对每一个tag都进行一次parse_tag。
start_kernel->setup_arch->setup_machine_tags->parse_tags->parse_tag:
729 /*
730 *
Scan the tag table for this tag, and call its parse function.
731 *
The tag table is built by the linker from all the __tagtable
732 *
declarations.
733 */
734
static int __init parse_tag(const struct tag *tag)
735
{
736
extern struct tagtable __tagtable_begin, __tagtable_end;
737
struct tagtable *t;
738
739
for (t = &__tagtable_begin; t < &__tagtable_end; t++)
740 if (tag->hdr.tag ==
t->tag) {
741 t->parse(tag);
742 break;
743 }
744
745
return t < &__tagtable_end;
746
}
__tagtable_begin~__tagtable_end是什么呢?以mem参数为例,arch/arm/kernel/setup.c中定义了关于mem tag结构的定义。
651 static int __init parse_tag_mem32(const
struct tag *tag)
652
{
653
return arm_add_memory(tag->u.mem.start, tag->u.mem.size);
654
}
655
656
__tagtable(ATAG_MEM, parse_tag_mem32);
656行的宏为:
188 #define __tag __used
__attribute__((__section__(".taglist.init")))
189 #define __tagtable(tag, fn) \
190 static const struct tagtable
__tagtable_##fn __tag = { tag, fn }
188行表示采用__tag属性的变量将被连接器放在.taglist.init的section中。
arch/arm/kernel/vmlinux.lds.S
153
.init.tagtable : {
154 __tagtable_begin = .;
155 *(.taglist.init)
156 __tagtable_end = .;
157
}
153~157行表示该section的前后地址分别为__tagtable_begin和__tagtable_end.也就是说parse_tag将会遍历这个section.对于每一个从uBoot中传递tag参数列表的每一个tag,Kernel都会遍历所有的。taglist.init段标识的struct
tagtable数据结构,判断是否其tag表示是否相等,若相等则调用对应注册的parse函数。以“mem”为例,如果是memory的tag,则会调用parse_tag_mem32。
parse_tag_mem32的651~656行,是将tag对应的属性传递给arm_add_memory,记录到meminfo中。
注意一点:由于Kernel先处理atags中的ATAG_MEM类型参数,后处理cmdline中的mem参数,因此,若atags.ATAG_MEM和cmdline都存在的话,以cmdline为准。这是因为,在后处理的cmdline中,将之前解析的atags中的mem参数给销毁了:
537 /*
538 * Pick out the memory size. We look for mem=size@start,
539 * where start and size are "size[KkMm]"
540 */
541 static int __init early_mem(char *p)
542 {
543 static int usermem __initdata = 0;
544 unsigned long size;
545 phys_addr_t start;
546 char *endp;
547
548 /*
549 * If the user specifies memory size, we
550 * blow away any automatically generated
551 * size.
552 */
553 if (usermem == 0) {
554 usermem = 1;
555 meminfo.nr_banks = 0;
556 }
557
558 start = PHYS_OFFSET;
559 size = memparse(p, &endp);
560 if (*endp == '@')
561 start = memparse(endp + 1, NULL);
562
563 arm_add_memory(start, size);
564
565 return 0;
566 }
567 early_param("mem", early_mem);
注意一点:由于Kernel先处理atags中的ATAG_MEM类型参数,后处理cmdline中的mem参数,因此,若atags.ATAG_MEM和cmdline都存在的话,以cmdline为准。这是因为,在后处理的cmdline中,将之前解析的atags中的mem参数给销毁了:
537 /*
538 * Pick out the memory size. We look for mem=size@start,
539 * where start and size are "size[KkMm]"
540 */
541 static int __init early_mem(char *p)
542 {
543 static int usermem __initdata = 0;
544 unsigned long size;
545 phys_addr_t start;
546 char *endp;
547
548 /*
549 * If the user specifies memory size, we
550 * blow away any automatically generated
551 * size.
552 */
553 if (usermem == 0) {
554 usermem = 1;
555 meminfo.nr_banks = 0;
556 }
557
558 start = PHYS_OFFSET;
559 size = memparse(p, &endp);
560 if (*endp == '@')
561 start = memparse(endp + 1, NULL);
562
563 arm_add_memory(start, size);
564
565 return 0;
566 }
567 early_param("mem", early_mem);
但是,atags同时也会传cmdline参数,这个和build Kernel是的cmdline参数是不矛盾的。具体取其一,还是两者都有效,取决于如何配置Linux Kernel.
参考:
1. http://blog.csdn.net/linyt/article/details/6627664
评论
发表评论