2. 嵌入式Linux启动流程

在RK3568这类ARM64 SoC上,上电到进入用户空间并不是“一步到位”,而是由多个引导阶段接力完成。每个阶段运行在不同的存储介质、不同的内存环境、不同的权限级别中,职责边界非常清晰。理解这条链路,是后续排查“卡在串口无输出”“内核起来了但挂载rootfs失败”“U-Boot找不到内核”等问题的前提。

整体顺序可以概括为:BootROM → SPL(TPL/SPL)→ U-Boot(proper)→ Kernel → rootfs → init。前三个阶段属于“引导加载”,后两个阶段属于“操作系统启动”。下面这张图展示了RK3568典型启动链路中各阶段的运行位置、加载对象与交接关系。

RK3568 启动链路:从 BootROM 到 rootfs BootROM 片内固化代码 SRAM 运行 加载 SPL 不可修改 SPL / TPL 初始化 DDR 时钟 / 引脚 加载 U-Boot 体积小,SRAM 内 U-Boot DDR 中运行 加载 Kernel 传 dtb / bootargs 可交互命令行 Kernel 解压 / 自解压 驱动初始化 挂载 rootfs 启动 init rootfs /sbin/init 用户空间 应用服务 系统就绪 存储介质 BootROM 从 eMMC / SPI NOR / SD 的固定偏移读取 SPL SPL 从同一介质读取 U-Boot;U-Boot 从 boot 分区读取 Image + dtb 运行内存 BootROM / SPL 在片内 SRAM 运行;U-Boot、Kernel 在 DDR 运行 rootfs 可位于 eMMC 分区、NFS、initramfs 或 UBI 卷 关键交接点 ① BootROM→SPL:校验签名/头部,跳转到 SRAM 入口 ② SPL→U-Boot:DDR 初始化完成后搬运并跳转 ③ U-Boot→Kernel:设置 bootargs、传 dtb,跳转内核入口

各阶段职责与关键动作

BootROM 是芯片出厂固化的只读代码,位于片内 SRAM。它在上电复位后首先运行,根据启动引脚或 eFuse 配置选择启动介质,从固定偏移读取 SPL 镜像,校验头部信息后跳转执行。BootROM 不可修改,但可以通过启动模式引脚决定从 eMMC、SD 卡还是 SPI NOR 启动,这是“变砖”后仍能通过 SD 卡恢复的底层保障。

SPL(Secondary Program Loader) 是 U-Boot 编译产物中的第一阶段。它体量很小,能在 SRAM 中运行,核心任务是初始化 DDR 控制器、时钟和关键引脚,然后把完整的 U-Boot 从存储介质搬运到 DDR 并跳转。RK3568 的 DDR 初始化参数与具体颗粒型号相关,因此 SPL 通常由 Rockchip 提供的 DDR bin 与 U-Boot 打包生成。

U-Boot 是功能完整的引导程序,运行在 DDR 中,提供命令行交互、环境变量、网络、USB、文件系统读写等能力。它的核心职责是加载内核镜像(Image 或 zImage)、设备树(dtb)以及可选的 initramfs,并通过 bootargs 向内核传递根文件系统位置、控制台、内存布局等参数。

Kernel 被跳转后先进行自解压与早期初始化,随后探测并初始化 CPU、中断控制器、时钟、存储、网络等驱动。当内核完成基础初始化后,会根据 bootargs 中的 root= 参数挂载根文件系统,并执行其中的 init 程序。

rootfs 是用户空间的起点。内核挂载 rootfs 后执行 /sbin/init(或 systemd、busybox init),由它拉起服务、登录界面和应用进程,系统才算真正“可用”。

典型启动日志与阶段识别

串口日志是判断启动卡在哪一阶段的最直接依据。下面是一段 RK3568 典型启动日志的关键片段:

BootROM: 1.0
SPL: v2017.09
DDR: 2GB, 1560MHz
U-Boot 2017.09 (rk3568)
Hit any key to stop autoboot: 0
Loading kernel from mmc 0:1 ...
## Booting Android Image at 0x00280000 ...
Kernel: Image
Starting kernel ...
[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 5.10.110
[    2.345678] Run /sbin/init as init process

从日志可以清晰看到:BootROM 打印版本后交给 SPL,SPL 打印 DDR 信息后交给 U-Boot,U-Boot 打印 autoboot 倒计时并加载内核,内核打印版本与 init 进程启动。如果日志停在某一行不再前进,就能快速定位问题阶段。

各阶段职责速查表

阶段 运行位置 核心职责 常见故障现象
BootROM 片内 SRAM 选择启动介质,加载并校验 SPL 串口完全无输出
SPL 片内 SRAM 初始化 DDR/时钟,加载 U-Boot 打印 DDR 后卡死
U-Boot DDR 加载 Kernel/dtb,传 bootargs 找不到内核或 dtb
Kernel DDR 驱动初始化,挂载 rootfs Kernel panic,无法挂载根
rootfs DDR + 存储 启动 init,拉起用户空间 init 不存在或服务失败

案例:rootfs 挂载失败如何反推

假设串口日志显示内核已经启动,但最后停在:

[    3.120000] VFS: Cannot open root device "mmcblk0p5" or unknown-block(0,0)
[    3.120000] Please append a correct "root=" boot option
[    3.120000] Kernel panic - not syncing: VFS: Unable to mount root fs

这说明 Kernel 阶段已经运行,问题出在 rootfs 定位。排查顺序应为:先确认 U-Boot 环境变量中 bootargs 的 root= 是否与实际分区一致;再确认该分区是否已烧录 rootfs 镜像;最后检查内核是否使能了对应的 MMC 驱动与文件系统(ext4、squashfs 等)。这类问题不需要重新编译 BootROM 或 SPL,只需修正 bootargs 或重新烧录 rootfs。

提示:在 U-Boot 命令行中可用 printenv bootargs 查看当前内核参数,用 setenv bootargs ... 临时修改后 boot 验证,确认无误再写入环境变量保存。

常见误区与复盘

  • 误区一:认为 U-Boot 负责初始化 DDR。实际上 DDR 初始化在 SPL 阶段完成,U-Boot 运行时 DDR 已可用。若 DDR 参数错误,通常卡在 SPL 而非 U-Boot。
  • 误区二:把 rootfs 当成“内核的一部分”。rootfs 是独立镜像,内核只负责挂载它。内核与 rootfs 可以分别升级,但需保证驱动与文件系统格式匹配。
  • 误区三:忽略 bootargs 的传递。控制台、根分区、内存大小都依赖 bootargs,U-Boot 传错参数会直接导致内核启动异常。
  • 误区四:启动失败就重烧全部镜像。应先看串口日志定位阶段,再针对性修复,避免浪费时间。

掌握这条启动链路后,后续构建 rootfs、打包镜像、配置 U-Boot 环境变量时,就能清楚知道每个文件最终会被谁读取、在哪个阶段生效,从而把“系统起不来”拆解成可定位、可验证的具体环节。