kernel启动流程-start_kernel的执行_8.cpio initrd解包

本文承接之前文章,聚焦kernel启动时cpio initrd解包主要流程。介绍了rootfs挂载的三种方式,以与内核打包的initramfs为例说明populate_rootfs将initrd释放到rootfs的过程,包括文件处理的多种状态,还以GotName为例说明释放过程。

1. 前言

本专题文章承接之前《kernel启动流程_head.S的执行》专题文章,我们知道在head.S执行过程中保存了bootloader传递的启动参数、启动模式以及FDT地址等,创建了内核空间的页表,最后为init进程初始化好了堆栈,并跳转到start_kernel执行。
《kernel启动流程-start_kernel的执行_7.arch_call_rest_init》中提到kernel_init->do_basic_setup->do_initcalls会遍历执行所有的init函数,这其中会执行populate_rootfs函数,populate_rootfs会将initrd释放到rootfs的“/”目录,本文重点介绍start_kernel的cpio initrd解包的主要流程.

kernel版本:5.10
平台:arm64

2. rootfs挂载

rest_init
    \--kernel_thread(kernel_init, NULL, CLONE_FS);
			|--kernel_init
			|	  |--kernel_init_freeable
			|	  |      |--do_basic_setup
			|	  |      |      |--populate_rootfs 
			|     |      |             |   //情形1,通过释放根文件系统到kernel rootfs根目录/
			|     |      |             |--unpack_to_rootfs(__initramfs_start, __initramfs_size);
			|     |      |             |--if (!initrd_start || IS_ENABLED(CONFIG_INITRAMFS_FORCE)) goto done
			|     |      |             |   //情形2,通过dts传递的内存地址释放根文件系统到kernel rootfs根目录/
			|     |      |             |--unpack_to_rootfs((char *)initrd_start, initrd_end -</
<think>我们正在讨论的是mkbootimg命令,该命令用于构建Android boot镜像。用户提供了一个具体的命令示例,需要解释该命令的参数、作用以及可能的问题解决方案。 根据用户提供的命令: mkbootimg --base 0 --pagesize 2048 --kernel_offset 0xa2e80000 --ramdisk_offset 0xaae00000 --second_offset 0x00000000 --tags_offset 0x9ee00000 --kernel ${PRODUCT_IMG_DIR}/ivi_kernel.img --ramdisk ${PRODUCT_IMG_DIR}/ivi_dtb.img -o ${PRODUCT_IMG_DIR}/ivi_boot.img 注意:这里有一个需要注意的地方,通常ramdisk参数应该是一个ramdisk镜像(如cpio格式的镜像),但这里提供了一个dtb.img(设备树二进制文件)。这在某些特定平台(如海思)中可能是允许的,但需要特别注意。 下面,我们将逐一解释参数: 1. --base:表示内核加载的基地址,这里为0。但请注意,实际加载地址是base + kernel_offset。由于base为0,所以内核加载地址就是0xa2e80000。 2. --pagesize:页大小,这里为2048字节(2KB)。这个值必须与目标设备的闪存页大小一致,否则可能导致启动失败。 3. --kernel_offset:内核在内存中的偏移地址(相对于基地址base)。这里为0xa2e80000。由于base为0,所以内核加载地址就是0xa2e80000。 4. --ramdisk_offset:ramdisk在内存中的偏移地址,这里为0xaae00000。 5. --second_offset:第二引导加载程序的偏移地址,这里为0(表示没有第二引导加载程序)。 6. --tags_offset:内核启动参数(tags)的偏移地址,这里为0x9ee00000。 7. --kernel:指定内核镜像的路径。 8. --ramdisk:指定ramdisk镜像的路径。注意,这里实际上指向了一个dtb文件(设备树),而不是传统的ramdisk。这可能是特定平台的要求。 9. -o:输出文件,即生成的boot.img。 关于这个命令,我们需要关注以下几点: 1. 地址设置:必须确保这些地址在目标设备的内存映射中是有效的,并且不与其他组件(如U-Boot)冲突。 2. 设备树作为ramdisk:这种做法并不常见,通常ramdisk是一个文件系统镜像。但在一些平台(如海思)可能要求将设备树放在ramdisk的位置。这需要目标设备的引导加载程序(如U-Boot)能够识别这种格式。 3. 缺少命令行参数:在boot.img中,通常需要传递命令行参数(cmdline)给内核。但在这个命令中没有指定。如果需要,应该使用`--cmdline`参数。 常见问题及解决方案: 1. 生成的boot.img无法启动- 检查地址是否正确(特别是kernel_offset、ramdisk_offset、tags_offset)是否与目标设备的内存布局匹配。 - 检查pagesize是否正确(根据目标设备的闪存页大小设置)。 - 确认设备树文件是否正确(可以使用`fdtdump`工具检查)。 2. 缺少cmdline参数: - 如果内核启动需要额外的命令行参数,必须通过`--cmdline`参数传递。例如:`--cmdline 'console=ttyS0,115200n8 ...'` 3. 设备树文件作为ramdisk: - 确保目标设备的引导加载程序(通常是U-Boot)支持这种配置。如果不支持,可能需要调整U-Boot代码或使用传统的ramdisk(包含设备树文件)。 4. 环境变量未定义: - 命令中使用了环境变量`${PRODUCT_IMG_DIR}`,确保在执行命令前该变量已定义且指向正确的目录。 参考用户提供的引用资料: - 引用[1]:展示了使用unpack_bootimg解包boot.img的例子,其中提到了--header_version、--os_version等参数。但用户当前的mkbootimg命令中并没有使用这些参数。注意,不同版本的boot.img头部格式可能不同(如header version 3)。如果目标设备需要特定的头部版本,应该在mkbootimg命令中指定(例如:`--header_version 3`)。 - 引用[2]:提到了unyaffs2命令的页大小和备用区大小的设置。这提醒我们,不同的设备可能有不同的页大小和备用区大小,需要正确设置。 因此,对于用户提供的命令,如果需要支持header version 3,可能需要添加`--header_version 3`参数,否则可能生成的boot.img格式不被目标设备接受。 改进后的命令可能如下(假设需要header version 3): ``` mkbootimg --header_version 3 --base 0 --pagesize 2048 \ --kernel_offset 0xa2e80000 --ramdisk_offset 0xaae00000 \ --second_offset 0x00000000 --tags_offset 0x9ee00000 \ --kernel ${PRODUCT_IMG_DIR}/ivi_kernel.img \ --ramdisk ${PRODUCT_IMG_DIR}/ivi_dtb.img \ -o ${PRODUCT_IMG_DIR}/ivi_boot.img ``` 如果没有指定header version,则默认可能是0或1,而设备可能需要更高版本。 此外,用户可能还需要指定其他参数,如命令行参数(cmdline)。我们可以通过解包一个已知正常的boot.img来获取这些参数(如引用[1]所示)。 总结回答结构: 1. 命令的作用:生成boot.img 2. 参数详解(表格形式) 3. 特别说明:ramdisk参数实际是设备树文件 4. 可能的问题及解决方案 5. 改进建议(添加header version等) 生成相关问题: 1. 如何解包boot.img以验证其内容? 2. 如何确定正确的内存偏移地址? 3. 设备树文件(dtb)如何集成到boot.img中? 4. 如何指定内核命令行参数(cmdline)? 注意:在回答中,如果需要引用,则自然添加引用标识,如[^1]。 </think>### mkbootimg 命令详解与应用 #### 命令参数解析 | 参数 | 说明 | 示例值 | 关键点 | |------------------|-----------------------|------------------|------------------------------------------------| | `--base` | 内存加载基地址 | `0` | 内核运行的物理地址起点[^1] | | `--pagesize` | 闪存页大小 | `2048` | 必须与硬件实际页大小一致(常见 2048/4096/8192) | | `--kernel_offset` | 内核内存偏移 | `0xa2e80000` | 需避开 U-Boot/ATF 占用区域 | | `--ramdisk_offset` | RAM 磁盘偏移 | `0xaae00000` | **此处实际加载设备树文件**(非传统 ramdisk)[^1] | | `--tags_offset` | 启动参数偏移 | `0x9ee00000` | 存放内核 cmdline 等参数 | | `--kernel` | 内核镜像路径 | `${PRODUCT_IMG_DIR}/ivi_kernel.img` | zImage/uImage 格式 | | `--ramdisk` | 设备树文件路径 | `${PRODUCT_IMG_DIR}/ivi_dtb.img` | **特别注意:此处实为 dtb 文件** | | `-o` | 输出文件 | `${PRODUCT_IMG_DIR}/ivi_boot.img` | 最终生成的启动镜像 | #### 核心功能说明 1. **设备树集成机制**: ```mermaid graph TD A[内核镜像] -->|加载地址| C(kernel_offset) B[设备树文件] -->|伪装为ramdisk| D(ramdisk_offset) E[启动参数] -->|存放地址| F(tags_offset) C --> G{boot.img} D --> G F --> G ``` 此方案将设备树文件(`.dtb`)通过 `--ramdisk` 参数嵌入,是 HiSilicon 等平台的特定实现[^1] 2. **地址对齐要求**: - 所有偏移地址需满足:$$ \text{offset} \mod \text{pagesize} = 0 $$ - 示例验证:$$ 0xa2e80000 \mod 2048 = 0 $$(正确对齐) 3. **内存布局示例**: ``` +-----------------+ 0x00000000 | U-Boot | +-----------------+ 0x9ee00000 ← tags_offset | Kernel Params | +-----------------+ 0xa2e80000 ← kernel_offset | Kernel | +-----------------+ 0xaae00000 ← ramdisk_offset | Device Tree(dtb)| +-----------------+ ``` #### 常见问题解决 1. **启动卡在 "Starting kernel..."** - **原因**:内存地址冲突或页大小错误 - **解决**: - 通过 U-Boot 的 `bdinfo` 命令获取实际内存布局 - 验证页大小:`nand info` 查看硬件页尺寸[^2] - 确保所有偏移地址满足:$$ \text{offset} \mod \text{pagesize} \equiv 0 $$ 2. **设备树未生效** - **现象**:外设初始化失败 - **解决**: - 检查 dtb 文件有效性:`fdtdump ${PRODUCT_IMG_DIR}/ivi_dtb.img` - 确认 U-Boot 支持 dtb 伪装为 ramdisk 的机制 3. **环境变量错误** - **报错**:`Cannot open kernel file` - **解决**: - 验证 `${PRODUCT_IMG_DIR}` 是否定义:`echo ${PRODUCT_IMG_DIR}` - 使用绝对路径替代:`--kernel /abs/path/to/ivi_kernel.img` 4. **镜像版本兼容问题** - **报错**:`Invalid boot header version` - **解决**:添加版本参数(参考引用[1]): ```bash --header_version 3 --os_version 12.0.0 --os_patch_level 2023-05 ``` #### 最佳实践建议 1. **地址计算工具**: ```bash # 验证地址对齐 echo "0xa2e80000 % 2048" | bc # 应返回 0 ``` 2. **镜像解包验证**(参考引用[1]): ```bash unpack_bootimg --boot_img ivi_boot.img --out unpack/ \ --pagesize 2048 --header_version 3 ``` 3. **兼容性调整**: - 若目标设备页尺寸为 4096(常见于 eMMC): ```bash --pagesize 4096 --kernel_offset 0xA2E80000 ... # 需重新计算偏移地址确保对齐 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值