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数据结构,则会进行tagsparse.
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.initsection中。
arch/arm/kernel/vmlinux.lds.S
153         .init.tagtable : {
154                 __tagtable_begin = .;
155                 *(.taglist.init)
156                 __tagtable_end = .;
157         }
153157行表示该section的前后地址分别为__tagtable_begin__tagtable_end.也就是说parse_tag将会遍历这个section.对于每一个从uBoot中传递tag参数列表的每一个tagKernel都会遍历所有的。taglist.init段标识的struct tagtable数据结构,判断是否其tag表示是否相等,若相等则调用对应注册的parse函数。以“mem”为例,如果是memorytag,则会调用parse_tag_mem32
parse_tag_mem32651~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);
但是,atags同时也会传cmdline参数,这个和build Kernel是的cmdline参数是不矛盾的。具体取其一,还是两者都有效,取决于如何配置Linux Kernel.

参考:
1. http://blog.csdn.net/linyt/article/details/6627664

评论

此博客中的热门博文

Linux/ARM Page Table Entry 属性设置分析

提交了30次才AC ---【附】POJ 2488解题报告

笔记