Linux内核在嵌入式板上启动时,会经历一个复杂的过程,两次从U-Boot读取内存信息:一次通过ATAG链,一次通过命令行参数。如果这两组数据不一致,内核需要按优先级处理。这就像你准备出发旅行时,行李既被打在行李袋里(ATAG链),又被单独装在一个手提包中(cmdline),你需要决定先看哪个,哪个的数据更重要。 01 在内核启动前,U-Boot把准备传递给内核的参数打包成ATAG(ARM平台上的ACPI表)链表。在board_init()中指定了一个地址(CFG_BOOT_PARAMS),作为存放这些参数的内存缓冲区。在do_bootm_linux()中,这个链表被组装好。 02 这个过程包含两个关键步骤: 2.1 生成链表头:setup_start_tag()生成一个ATAG_CORE类型的起始标记,通常内容为空。 2.2 写入内存信息:setup_memory_tags()通过循环调用__tagtable(ATAG_MEM, parse_tag_mem32),往链表末尾添加ATAG_MEM标记,告知内核哪些物理地址范围属于可用DRAM。 03 当内核启动时,在start_kernel()->setup_arch()->parse_tags()中,它首先解析ATAG链中的内存信息。然后在boot_command_line里找到"mem="参数并二次处理。如果cmdline中指定了内存大小,就会清除之前由ATAG填充的bank数组。 04 在这个双解析机制中,优先级是cmdline高于ATAG链。如果cmdline里存在"mem="参数,之前tag里记录的所有内存bank都会被覆盖重写。如果板子只用了一部分DRAM而tag只涵盖这部分区域时,若cmdline给出了更大的值,内核仍会按cmdline执行。 05 为了灵活调整内存大小而不改动U-Boot或环境变量(风险高且容易失败),可以在启动路径找到bootargs字符串(通常位于.data+COMMAND_LINE_SIZE),然后调用自定义函数hikio_fix_meminfo()。在这个函数中找到"mem="并清空原有值再追加所需值(比如"72M")。同时更新meminfo.bank[0].size字段。 06 这次改动已经在海思平台验证可行: 修改后的cmdline会被重新传递给内核并生效,这样就不需要改动U-Boot来改变管理内存的大小了。通过理解这个双解析机制可以启发嵌入式开发中的安全高效实践。 最后小结一下:理解同一物理内存不同声明方式的优先级非常重要;谨慎修改U-Boot固件版本可能带来启动失败风险;通过动态补足cmdline可以快速调整内存管理范围,既安全又高效。