Linux系统移植⑨:uboot启动流程详解-bootz启动Linux过程
bootz
是 U-Boot 中用于启动 Linux 内核的命令,专为处理 zImage(压缩内核映像) 设计。
启动 Linux 的完整过程:
1. 加载内核与相关文件
U-Boot 先将以下文件加载到内存指定地址:
- zImage:压缩的 Linux 内核映像(位于内存地址
KERNEL_ADDR
)。 - 设备树二进制文件(DTB):硬件描述文件(位于
FDT_ADDR
)。 - initramfs(可选):初始内存文件系统(位于
INITRD_ADDR
)。
2. 执行 bootz
命令
bootz [内核地址] [initramfs地址]:[大小] [设备树地址]
-
启动命令:
bootz ${KERNEL_ADDR} ${INITRD_ADDR}:${INITRD_SIZE} ${FDT_ADDR}
若无需 initramfs:
bootz ${KERNEL_ADDR} - ${FDT_ADDR}
3. zImage 自解压
- U-Boot 跳转到
KERNEL_ADDR
,执行 zImage 头部的小型解压程序。 - 解压程序将内核解压到预设的 物理内存地址(如 ARM 架构通常为
0x8000
)。
4. 传递启动参数
U-Boot 通过寄存器/设备树传递关键参数:
架构 | 寄存器 | 参数说明 |
---|---|---|
ARM | r0 | 0(保留) |
r1 | 机器 ID(旧)或 0xFFFFFFFF (新) | |
r2 | 设备树地址(FDT_ADDR ) | |
RISC-V | a0 | 设备树地址 |
a1 | 0(保留) |
设备树(DTB) 包含 CPU、内存、外设等硬件信息,内核据此初始化硬件。
5. 内核初始化
Linux 内核接管后执行:
- 硬件初始化:基于 DTB 设置 CPU、内存控制器、时钟等。
- 驱动加载:识别并初始化存储、网络等设备驱动。
- 挂载根文件系统:
- 若有 initramfs,将其作为临时根文件系统。
- 否则直接挂载
root=
参数指定的文件系统(如/dev/mmcblk0p2
)。
6. 用户空间启动
- 内核启动第一个用户进程
/init
(位于 initramfs 或根文件系统)。 init
进程加载系统服务(如 systemd 或 SysVinit)。- 最终进入登录界面或指定应用。
关键依赖
- zImage 格式:必须为
gzip
压缩的Image
文件(非 uImage)。 - 设备树支持:现代内核强制要求 DTB(无 DTB 的内核无法启动)。
- 地址对齐:内核/DTB 的加载地址需符合 CPU 架构要求(如 ARM 需 4KB 对齐)。
do_bootz源码如下:
其内部主要函数调用关系如下:
do_bootz
-> bootz_start
-> do_bootm_states 阶段为BOOTM_STATE_START
-> bootm_start 对images全局变量清零,
-> images->ep = 0X80800000
->bootz_setup 判断zImage是否正确
-> bootm_find_images
-> boot_get_fdt 找到设备树,然后将设备树起始地址和长度,写入到images的ft_addr和ft_len成员变量中。
-> bootm_disable_interrupts 关闭中断相关
-> images.os.os = IH_OS_LINUX; 表示要启动Linux系统
-> do_bootm_states 状态BOOTM_STATE_OS_PREP 、BOOTM_STATE_OS_FAKE_GO 、BOOTM_STATE_OS_GO,
-> bootm_os_get_boot_func 查找Linux内核启动函数。找到Linux内核启动函数do_bootm_linux,赋值给boot_fn。
-> boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images); 就是do_bootm_linux。
-> boot_prep_linux 启动之前的一些工作,对于使用设备树来说,他会将Bootargs传递给Linux内核,通过设备树完成。也就是向Linux内核传参。
-> boot_selected_os BOOTM_STATE_OS_GO, do_bootm_linux
-> do_bootm_linux,BOOTM_STATE_OS_GO
-> boot_jump_linux
-> machid= gd->bd->bi_arch_number;
-> void (kernel_entry)(int zero, int arch, uint params);
-> kernel_entry = (void ()(int, int, uint))images->ep; 0X80800000。
-> announce_and_cleanup 输出Starting kernel……
-> kernel_entry(0, machid, r2); 启动Linux内核。 Uboot的最终使命,启动Linux内核。