Linux/ARM物理内存探测(1) - CMDLINE


ARM Linux中物理内存的探测并不像X86那样复杂。在X86中,物理内存的探测完全依赖于BIOS中的E820数据结构。对于ARM,物理内存的探测,其实谈不上“探测”,完全靠手工指定。指定的方法有两种:第一种是通过uBootKernel中传递参数,第二种是通过Kernel中的CONFIG_CMDLINE配置项。本文对CMDLINE中传递的的mem参数进行总结。

Linux Kernel 设置了各个可能参数的名字和处理函数。比如物理内存的参数名字是”mem””early_mem”
arch/arm/kernel/setup.c
567 early_param("mem", early_mem);

567行的宏展开为:
251 #define early_param(“mem”, early_mem)                                    \
252         __setup_param(“mem”, early_mem, early_mem, 1)

238 #define __setup_param(str, unique_id, fn, early)                        \
239         static const char __setup_str_early_mem[] __initconst __aligned(1) = “mem”; \
241         static struct obs_kernel_param __setup_early_mem  __used __section(.init.setup) __attribute__((aligned((sizeof(long)))))        \
244                 = { __setup_str_ early_mem, early_mem, 1 }

定义了一个obs_kernel_param结构体,其结构体的定义为:
226 struct obs_kernel_param {
227         const char *str;
228         int (*setup_func)(char *);
229         int early;
230 };

以上,238244行,定义了物理内存的名字处理函数。即:
static struct obs_kernel_param __setup_early_mem  __used __section(.init.setup) __attribute__((aligned((sizeof(long)))))        \
= { __setup_str_ early_mem, early_mem, 1 }
名字是”mem”,处理函数是”early”.

下面,介绍一下Kernel是怎样利用cmdline中物理内存的参数来设置使用的物理内存的。

cmdline中,定义物理内存的方式为:mem=size@start。这个cmdline是在.config文件中指定的CONFIG_CMDLINE
arch/arm/kernel/setup.c:
start_kernel->setup_arch()
{
944         parse_early_param();
946         sort(&meminfo.bank, meminfo.nr_banks, sizeof(meminfo.bank[0]), meminfo_cmp, NULL);//将所有的bank按照start的值进行排序。
}
start_kernel->setup_arch-> parse_early_param
409 /* Arch code calls this early on, or if not, just before other parsing. */
410 void __init parse_early_param(void)
411 {
412         static __initdata int done = 0;
413         static __initdata char tmp_cmdline[COMMAND_LINE_SIZE];
414
415         if (done)
416                 return;
417
418         /* All fall through to do_early_param. */
419         strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
420         parse_early_options(tmp_cmdline);//Done nothing
421         done = 1;
422 }

start_kernel->setup_arch-> parse_early_param-> parse_early_options
404 void __init parse_early_options(char *cmdline)
405 {
406         parse_args("early options", cmdline, NULL, 0, do_early_param);
407 }
406行的parse_args循环调用do_early_param止到cmdline中的各个参数遍历完。
start_kernel->setup_arch-> parse_early_param-> parse_early_options->do_early_param:
385 /* Check for early params. */
386 static int __init do_early_param(char *param, char *val)
387 {
388         const struct obs_kernel_param *p;
389
390         for (p = __setup_start; p < __setup_end; p++) {
391                 if ((p->early && parameq(param, p->str)) ||
392                     (strcmp(param, "console") == 0 &&
393                      strcmp(p->str, "earlycon") == 0)
394                 ) {
395                         if (p->setup_func(val) != 0)
396                                 printk(KERN_WARNING
397                                        "Malformed early option '%s'\n", param);
398                 }
399         }
400         /* We accept everything at this stage. */
401         return 0;
402 }
对于cmdline中的mem参数,调用early_mem:
arch/arm/kernel/setup.c
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 }
558行~561行,如果mem=256M,那么默认的@PHYS_OFFSETsize的值为256>>20.
563行,将cmdline中的mem参数的值传给kernel中。
508 int __init arm_add_memory(phys_addr_t start, unsigned long size)
 509 {
 510         struct membank *bank = &meminfo.bank[meminfo.nr_banks];
 511
 512         if (meminfo.nr_banks >= NR_BANKS) {
 513                 printk(KERN_CRIT "NR_BANKS too low, "
 514                         "ignoring memory at 0x%08llx\n", (long long)start);
 515                 return -EINVAL;
 516         }
 517
 518         /*
 519          * Ensure that start/size are aligned to a page boundary.
 520          * Size is appropriately rounded down, start is rounded up.
 521          */
 522         size -= start & ~PAGE_MASK;
 523         bank->start = PAGE_ALIGN(start);
 524         bank->size  = size & PAGE_MASK;
 525
 526         /*
 527          * Check whether this memory region has non-zero size or
 528          * invalid node number.
 529          */
 530         if (bank->size == 0)
 531                 return -EINVAL;
 532
 533         meminfo.nr_banks++;
 534         return 0;
 535 }
522~524行来看,startsize都需要页对齐,然后记录到meminfo中。

评论

此博客中的热门博文

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

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

笔记