调试 AX615 长电机型时,我用内核自带的 initrd 启动方式,把根文件系统挂载到内存,替代 SDK 默认的将 rootfs 分区直接挂载物理盘为 squashfs 的做法。后者在 OTA 升级 rootfs 分区时容易触发 squashfs 错误,导致升级失败、系统变砖。这个过程中踩到的问题相当经典,本文先梳理 bootargs 编译进 U-Boot 后却不生效的来龙去脉。

01 问题的起因:Bug 背后
本来以为就是改下 project.mak 里的一个变量赋值——把 KERNEL_BOOTARGS 拼好,编译烧录,收工。
KERNEL_BOOTARGS := "$(OS_MEM) cmmpool=$(CMM_POOL_PARAM) console=ttyS0,115200n8 \
loglevel=8 earlycon=uart8250,mmio32,0x4880000 \
root=/dev/ram0 rw rootfstype=$(ROOTFS_TYPE) init=/linuxrc \
mtdparts=spi3.0:$(FLASH_PARTITIONS) \
initrd=$(ROOTFS_START_ADDR),$(ROOTFS_PARTITION_SIZE) $(CMMRC_PARAM)"
结果内核一起来,initrd= 整段直接消失。内核自然无法挂载文件系统,直接报错。
Kernel command line: mem=68M ... rootfstype=squashfs init=/linuxrc mtdparts=... boot_reason=panic
02 先怀疑变量没赋值成功:开始排查
第一反应是:KERNEL_BOOTARGS 这个变量是不是根本没赋上值?加一行调试打印试试:
$(info # KERNEL_BOOTARGS: $(KERNEL_BOOTARGS))
编译输出如下:
# KERNEL_BOOTARGS: "mem=68M ... mtdparts=spi3.0:...8M(rootfs)... initrd=0x43c00000,8M "
变量值好好的,initrd=0x43c00000,8M 明明白白在里头。那就奇了怪了。
03 不对劲的地方:发现问题
把编译打印的内容和实际内核命令行放一起比对,差异立刻浮现:
| 差异项 |
编译打印的 KERNEL_BOOTARGS |
实际内核命令行 |
| mtdparts 里 rootfs 大小 |
8M(rootfs) |
8192K(rootfs) |
| 末尾参数 |
无 |
boot_reason=panic |
格式不同,还凭空冒出来一个新参数。
如果实际命令行就是编译时写进 CONFIG_CMDLINE 的那份,它应该和编译打印一字不差才对。但 8M 变成了 8192K,还多出来 boot_reason=panic——这说明实际跑起来的命令行根本不是编译进去的那份,而是 U-Boot 运行时传进来的。
U-Boot 的 bootargs 环境变量把内核默认命令行整个盖掉了。
04 翻 U-Boot 源码:找源头
顺着这个思路去扒代码,在 axera_boot.c 的 flash_raw_read 函数里找到了 bootargs 的处理逻辑:
int flash_raw_read(const char *part_name, void *dest)
{
...
case STORAGE_TYPE_NOR:
#if !defined(CONFIG_MTD_SPI_NAND) && defined(CONFIG_SPI_FLASH)
bootargs =
env_get("bootargs");
printf("========= bootargs:%s\n", bootargs);
if(NULL == bootargs) {
bootargs = BOOTARGS_SPINOR;
printf("========= BOOTARGS_SPINOR:%s\n", BOOTARGS_SPINOR);
#ifdef SUPPPORT_DDR_AUTO_ADJUST
if (iram_misc_info->chip_type.chiptype == AX615DV201 ||
iram_misc_info->chip_type.chiptype == AX615QV201){
bootargs = BOOTARGS_SPINOR_L2;
}
#endif
mtdparts = MTDPARTS_SPINOR;
env_set("bootargs", bootargs);
env_set("mtdparts", mtdparts);
env_save();
} else {
mtdparts = strstr(bootargs , "mtdparts");
if (NULL != mtdparts) {
mtdparts = strdup(mtdparts);
strtok(mtdparts, " ");
env_set("mtdparts", mtdparts);
free(mtdparts);
} else {
mtdparts = MTDPARTS_SPINOR;
env_set("mtdparts", mtdparts);
}
}
mtdids = env_get("mtdids");
env_set("mtdids", MTDIDS_SPINOR);
if (NULL == mtdids) {
env_set("mtdids", MTDIDS_SPINOR);
}
read_len = flash_read_from_nor(part_name, dest);
#endif
break;
}
return read_len;
}
逻辑本身不算复杂:
- env 里没有 bootargs → 使用编译期的
BOOTARGS_SPINOR,写入 env 并持久化。
- env 里已经有 bootargs → 直接沿用现有值,只从中提取
mtdparts,bootargs 本身一个字都不动。
05 问题出在这里:找到根因
回顾一下整个过程:之前某次启动时,env 里还没有 bootargs,代码用当时的默认值(已经包含更新过的 root=,但还没有 initrd=)写进了 env,saveenv 把它持久化到了 flash 里。
后来我在源码里加上了 initrd=,重新编译烧录——但 env 分区里存的还是旧 bootargs。于是每次启动都走进 else 分支,直接用旧值。新编译的 BOOTARGS_SPINOR 根本没有任何执行机会。
我改的是源码里的默认值,实际跑的是 env 里缓存的旧值。这俩压根不是一个东西。
06 修改方法:清除 env 中的 bootargs 数据
进入 U-Boot 命令行,清掉缓存:
env delete bootargs
saveenv
reset
重启后就会走进 if (NULL == bootargs) 分支,用最新编译的默认值重新初始化 env。
再验证一下:
Kernel command line: mem=68M ... mtdparts=spi3.0:...8M(rootfs)... initrd=0x43c00000,8M boot_reason=panic
initrd=0x43c00000,8M 回来了。
这个设计其实有它的道理:
虽然坑,但逻辑不是乱写的:
- 出厂第一次启动:env 是空的,用编译默认值初始化,保证系统能起来。
- 后续启动:用户可能在 U-Boot 命令行里用
setenv bootargs 自定义过参数,代码不应该用默认值去覆盖用户的修改。
问题在于:开发者改源码和用户改 env,走的是同一条路径。代码区分不了"这个 env 是用户手动改的"和"这个 env 是上次自动缓存的"。
产品开发阶段这个机制反而添乱——你以为改了源码已经生效,实际跑的还是几个版本前的缓存值。
07 踩过这个坑之后的几条经验
- 改了 bootargs 相关源码,先清 env 再验证:
env delete bootargs; saveenv; reset,别直接看启动日志就下结论。
- 编译打印和实际命令行对一下:如果格式、参数数量不一致,说明存在运行时覆盖。优先排查 U-Boot env 和设备树的
/chosen 节点。
CONFIG_CMDLINE 不是唯一来源:U-Boot 的 bootargs 环境变量、设备树 bootargs 属性、内核默认命令行三者有优先级,通常 U-Boot 传入的优先级最高。
- env 分区是持久化的:重新烧录 kernel 或 U-Boot 不会自动清掉 env,除非专门擦除了 env 分区或执行了
env default -a。
08 文末小结:扩展知识
initrd=0x43c00000,8M 里的 8M 后缀,大部分架构用 memparse 解析是支持的。如果遇到大小识别异常,改成字节数 0x800000 更稳妥。
另外确认一下 0x43c00000 这个地址没有和其他内存区域冲突——我们的 mem=68M 加 cmmpool 从 0x44400000 开始,0x43c00000~0x44400000 这段是空闲的,没问题。但如果后续调整了内存布局,这个地址必须同步改。
💡 温馨提示
这个坑说大不大,但排查路线挺典型的:从"变量没赋值对"到"运行时覆盖"再到"env 缓存机制",每一步都得靠对比和证据说话。如果你同样在 AX615 上调启动流程,希望这篇记录能帮你少走点弯路。
类似的内核启动与参数传递问题,在云栈社区也有不少开发者讨论,遇到疑难杂症不妨去找找思路。