基于ARM架构的嵌入式系统启动流程深度剖析

嵌入式系统上电后,并不是处理器立即执行操作系统。复位信号释放之后,芯片需要先确定启动介质,再完成片上资源和外部内存的基本初始化,随后由不同阶段的引导程序接力加载系统镜像。任何一个环节的地址、时钟、存储布局或安全校验出现问题,都可能导致系统停留在黑屏、无串口输出或反复复位状态。

下面以 Cortex-A53 处理器运行 Linux 的典型路径为主线,梳理从 BootROM 到用户空间的启动过程,并结合 Cortex-A 与 Cortex-M 在启动方式上的差异,说明各阶段的职责、判断条件和调试边界。需要强调的是,启动链会因芯片厂商、存储介质、镜像格式和安全启动策略而变化,文中的阶段划分用于建立通用分析框架,不能直接替代某一具体 SoC 的启动手册。

启动流程的本质

启动流程的核心,是让处理器从一个资源极少、运行环境尚未建立的状态,逐步进入能够加载操作系统的完整环境。

上电初期,时钟、引脚复用、内存控制器和外部 DDR 通常还没有完成配置。此时,处理器不能假设所有硬件模块已经处于可用状态,也不能直接依赖操作系统提供的内存管理和驱动能力。因此,芯片必须依靠一段固化在内部的启动代码,先完成最基本的判断和加载工作。

这一过程主要涉及两个问题:

  1. 硬件如何进入可运行状态
    处理器需要从复位状态开始,逐步建立时钟、引脚和内存等运行条件。


  2. 程序在外部内存尚未初始化时如何执行
    在 DDR 控制器完成配置之前,早期代码通常只能运行在 BootROM 或片上 SRAM 中。BootROM 负责加载后续引导代码,后续引导代码再完成外部内存初始化。


从软件视角看,Bootloader 的基本职责包括初始化处理器和内存、准备必要的硬件环境、加载内核镜像,并将控制权交给 Linux 内核。Linux 内核文档也将初始化 RAM、初始化串口、准备启动参数、加载 initramfs 以及调用内核映像列为引导程序需要完成的关键工作。

Cortex-A 与 Cortex-M 的启动差异

Cortex-A 和 Cortex-M 都属于 Arm 架构,但两者面向的系统复杂度不同,启动流程不能简单等同。

Cortex-A 系统一般需要运行 Linux 等操作系统,启动链中往往包含 BootROM、SPL、U-Boot、可信执行环境或安全固件、内核以及根文件系统等多个部分。处理器还可能涉及 MMU、缓存、异常级别、外部 DDR 和复杂的设备树或启动参数传递。

Cortex-M 则更多用于裸机程序或实时操作系统。其启动入口通常与向量表、复位处理函数和 C 运行时初始化有关,启动代码完成基础寄存器和运行环境设置后,便可能进入 main 函数。它不一定需要类似 Cortex-A Linux 系统那样的多级 Bootloader、外部 DDR 初始化和根文件系统加载。

因此,分析启动问题时,首先要确认处理器内核类型和系统软件形态。不能把 Cortex-A Linux 的启动链直接套用到 Cortex-M,也不能因为 Cortex-M 可以较快进入应用代码,就推断 Cortex-A 系统同样不需要分阶段初始化。

Cortex-A53 Linux 的典型启动链

以下流程以 Cortex-A53 + Linux 为例。不同平台的镜像名称、存储偏移、加载地址和安全校验方式可能不同,实际开发应以对应 SoC 的 BootROM 说明和启动介质布局为准。

图片[1]-基于ARM架构的嵌入式系统启动流程深度剖析-极牧

阶段一:BootROM

BootROM 是芯片内部固化的启动代码,可以理解为处理器上电后的第一段软件。它通常由芯片厂商在芯片设计或生产阶段固化,开发者一般不能修改其实现,只能通过启动引脚、熔丝配置或外部介质布局影响其行为。

触发条件

BootROM 通常在以下条件满足后开始执行:

  • 上电复位,即 Power-On Reset(POR);
  • 外部硬件复位信号释放;
  • 芯片完成必要的复位状态建立。

BootROM 并不等同于完整的 Bootloader。它的主要任务是完成最小化的硬件判断,并找到可以继续执行的下一阶段代码。

启动介质选择

芯片通常会根据启动引脚电平、熔丝位(eFUSE)或其他固定配置,选择启动来源。常见介质包括:

  • SD 卡;
  • eMMC;
  • UART 等下载或恢复接口。

启动介质的具体种类和优先级由芯片设计决定,不能仅根据某一平台的经验推断到其他 SoC。若启动模式配置错误,即使镜像内容正确,BootROM 也可能无法读取预期位置。

加载下一阶段代码

BootROM 找到启动介质后,会从约定的位置读取一级引导代码,并将其加载到片上 SRAM 中执行。部分平台将这一级代码称为 SPL,即 Secondary Program Loader。

此时外部 DDR 往往尚未完成初始化,因此 BootROM 和 SPL 的代码大小、运行地址和数据占用都受到片上 SRAM 容量限制。以输入案例中的配置为例,部分芯片片上 SRAM 的容量可能为 64KB,例如 MP157 的 SYSRAM;但这一数值并不适用于所有平台。

安全校验

部分高端芯片的 BootROM 支持对后续镜像进行验证,例如使用 RSA-2048 和 SHA-256 完成签名或摘要校验。安全启动开启后,镜像不仅要能够被读取,还必须满足芯片规定的信任链和签名要求。

这意味着启动失败不一定是存储读写错误,也可能是镜像签名、密钥配置、版本限制或安全状态不匹配。排查时应区分“无法读取镜像”和“读取后校验不通过”这两类问题。

阶段二:SPL

SPL 是运行在片上 SRAM 中的早期引导程序,主要作用是把处理器从最小运行环境带入可以加载完整 Bootloader 的状态。

运行条件

SPL 执行时,代码通常仍然位于片上 SRAM。外部 DDR 可能尚未初始化,MMU 也可能尚未启用,因此 SPL 必须严格控制代码规模、栈空间和临时数据的使用。

这也是 SPL 与完整 U-Boot 的重要区别:SPL 并不负责提供完整的交互式引导功能,而是优先完成后续启动所必需的硬件初始化。

关键初始化工作

SPL 通常需要根据具体 SoC 完成以下工作:

  1. 配置时钟树
    包括时钟源、分频器和 PLL 等。时钟配置错误可能导致处理器运行异常,也可能影响外设和 DDR 的时序。


  2. 配置引脚复用
    存储控制器、串口和其他外设能否正常工作,取决于相关引脚是否切换到正确功能。引脚复用错误时,软件逻辑可能已经执行,但外部设备仍然没有有效信号。


  3. 初始化 DDR
    DDR 是后续 U-Boot、内核和文件系统运行的主要内存空间。初始化过程通常涉及控制器配置、时序参数和硬件校准。DDR 初始化失败时,系统可能表现为随机死机、代码执行不稳定或加载镜像后异常跳转。


  4. 准备下一阶段镜像
    SPL 会从指定启动介质读取完整的 U-Boot 或其他后续引导镜像,将其放入可执行的内存区域,然后跳转执行。


不同芯片对上述步骤的组织方式并不相同。以 OMAP-L138 等平台为例,启动配置可以通过 AIS 文件提供 PLL、DDR、PSC 和 PINMUX 等初始化参数;这类机制属于特定平台的启动实现,不能直接视为所有 Cortex-A SoC 的通用格式。

SPL 阶段的判断重点

如果串口完全没有输出,问题可能发生在 BootROM、启动模式识别或 SPL 早期初始化阶段;如果能够看到 SPL 的输出,但在 DDR 初始化后停止,则应重点检查 DDR 参数、供电、时钟和板级连接。

调试时不宜一开始就把问题归因于 Linux。只要完整 U-Boot 还没有稳定运行,内核和根文件系统就不是优先排查对象。

阶段三:完整 Bootloader

当 SPL 完成片上资源和外部 DDR 初始化后,系统通常会进入功能更完整的 Bootloader,例如 U-Boot。

完整 Bootloader 的职责不再局限于“让硬件醒来”,还包括:

  • 读取和解析启动配置;
  • 访问更复杂的存储设备;
  • 加载内核镜像;
  • 加载设备树;
  • 按需要加载 initramfs 或其他启动组件;
  • 准备内核所需的启动参数;
  • 将控制权交给 Linux 内核。

在一些平台上,完整 U-Boot 可能与其他固件共同组成镜像。Rockchip 平台的资料将启动链划分为 BootROM、U-Boot TPL/SPL、U-Boot、ATF/TEE、内核和根文件系统等阶段,并使用不同镜像承载这些组件。由此可见,启动链中的镜像名称和组合关系具有明显的平台特征。

启动参数的传递

Bootloader 不只是把内核代码复制到内存中,还需要向内核传递必要的硬件和启动信息。具体传递方式与架构、内核版本和平台实现有关,内容可能包括内存布局、串口信息、设备树或初始文件系统位置。

如果内核能够开始执行,但很快出现内存、串口或设备识别异常,应检查 Bootloader 传递的地址和参数是否与内核实际配置一致。

阶段四:Linux 内核启动

Bootloader 完成准备后,会跳转到内核入口。此时,系统的主导权从 Bootloader 转交给 Linux 内核。

内核启动阶段通常需要完成:

  • 建立处理器运行环境;
  • 初始化内存管理;
  • 初始化中断、时钟和基本驱动框架;
  • 识别设备树或其他硬件描述信息;
  • 初始化存储和文件系统相关组件;
  • 挂载初始根文件系统;
  • 准备进入用户空间。

Bootloader 与内核之间的接口必须匹配。内核并不负责替 Bootloader 完成所有早期硬件初始化,尤其是与平台强相关的 DDR、启动介质和部分时钟配置。因此,如果 Bootloader 没有正确初始化内存,内核通常无法通过后续初始化自行修复这一问题。

内核阶段的故障一般比 BootROM 阶段更容易观察,因为此时可能已经能够输出串口日志。但日志出现的位置只能说明代码执行到了某个阶段,不能直接证明前面的所有硬件配置都完全正确。

阶段五:根文件系统挂载

内核完成基础初始化后,还需要找到并挂载根文件系统。根文件系统可以来自启动介质,也可以由初始内存文件系统提供,具体取决于平台配置和启动方案。

根文件系统通常包含:

  • 用户空间程序;
  • 动态库;
  • 配置文件;
  • 设备节点;
  • 启动脚本或服务程序。

如果内核已经启动,却出现找不到根文件系统、无法挂载设备或无法执行初始化程序等信息,排查重点应转向存储设备识别、分区或镜像布局、根文件系统格式,以及 Bootloader 传递的根文件系统参数。

这一阶段与“内核是否启动”需要区分。内核成功运行并不意味着系统已经具备可用的用户空间。

阶段六:用户空间启动

根文件系统挂载后,系统会执行用户空间的初始化程序,并继续启动系统服务和应用程序。至此,设备才从“完成内核启动”进入“具备实际功能”的状态。

用户空间启动失败的表现可能包括:

  • 内核日志正常,但系统无法进入登录界面;
  • 初始化程序无法执行;
  • 关键动态库缺失;
  • 启动脚本或服务配置错误;
  • 应用程序启动后立即退出。

这类问题通常不应再从 BootROM 或 SPL 入手,而应检查根文件系统内容、文件权限、初始化配置和应用依赖关系。按照启动阶段划分故障范围,可以减少在错误层级反复修改代码的情况。

启动镜像与存储布局

启动链的每一阶段都依赖明确的镜像布局。一个典型的启动介质可能依次存放引导加载器、可信固件、内核镜像和根文件系统,但具体偏移和镜像名称由平台决定。

Rockchip 的启动资料给出了一个示例布局,其中包括:

  • idbloader.img,用于承载 TPL/SPL;
  • u-boot.itbuboot.img,用于承载 U-Boot;
  • trust.img,用于承载 ATF/TEE;
  • boot.img,用于承载内核;
  • rootfs.img,用于承载根文件系统。

该布局只能作为理解分阶段镜像组织方式的参考,不能直接用于其他芯片。不同平台可能使用不同的镜像格式、固定偏移、加载地址和校验方式。实际分析时,应同时核对以下信息:

  1. BootROM 期望读取的介质和位置;
  2. SPL 或 TPL 的实际存放位置;
  3. U-Boot、可信固件和内核之间的加载关系;
  4. 内核、设备树和根文件系统的地址;
  5. 镜像是否经过压缩、打包或签名;
  6. 启动配置是否与当前硬件版本一致。

尤其要注意,存储介质中的“文件顺序”不一定等于代码执行顺序。BootROM 可能按照固定偏移读取镜像,Bootloader 也可能根据分区信息或启动参数定位后续组件。

启动问题的分层排查方法

启动故障的有效定位,关键不在于一次性检查所有模块,而在于先确认系统停在哪个阶段。

没有任何输出

如果串口完全没有输出,应优先确认:

  • 供电和复位是否正常;
  • 启动模式引脚或熔丝配置是否正确;
  • 启动介质是否可访问;
  • BootROM 是否支持当前镜像格式;
  • 镜像是否放置在芯片要求的位置;
  • 安全启动是否导致镜像校验失败。

此时不宜优先修改 Linux 配置,因为处理器可能尚未执行到内核。

SPL 有输出,DDR 初始化失败

如果能够看到 SPL 的早期日志,但系统在 DDR 初始化附近停止,应重点检查:

  • DDR 参数是否与实际颗粒匹配;
  • 时钟和供电是否稳定;
  • 相关引脚复用是否正确;
  • 板级布线和硬件连接是否满足设计要求;
  • 初始化代码是否超出片上 SRAM 的空间限制。

DDR 问题可能具有随机性,偶尔启动成功并不能证明配置已经可靠。

U-Boot 可以运行,内核无法启动

这类问题通常与内核镜像、设备树、加载地址、启动参数或内存布局有关。应确认:

  • 内核镜像是否完整;
  • 内核加载地址和入口地址是否匹配;
  • 设备树是否对应当前硬件;
  • Bootloader 是否传递了正确的内存和根文件系统信息;
  • 内核是否具备访问启动介质所需的驱动或配置。

内核启动,用户空间无法进入

如果已经看到内核初始化日志,问题通常转向根文件系统和用户空间。应检查根文件系统是否能够挂载、初始化程序是否存在、所需库文件是否完整,以及启动服务之间是否存在依赖错误。

结语

ARM 嵌入式系统的启动过程不是一段连续的“自动加载”,而是一条由 BootROM、SPL、完整 Bootloader、内核、根文件系统和用户空间共同组成的启动链。每个阶段都有明确的运行条件和职责边界:BootROM 负责选择并加载,SPL 负责建立基础硬件环境,Bootloader 负责组织系统镜像和启动参数,内核负责接管系统资源,用户空间则完成设备功能。

分析启动问题时,最重要的不是背诵某个平台的固定流程,而是确认三个问题:当前处理器执行到了哪一阶段、这一阶段依赖哪些硬件条件、下一阶段需要哪些镜像和参数。只有先划清边界,再结合芯片手册、镜像布局和串口日志定位,启动调试才不会陷入无关配置的反复尝试。

图片[2]-基于ARM架构的嵌入式系统启动流程深度剖析-极牧

图片[3]-基于ARM架构的嵌入式系统启动流程深度剖析-极牧

© 版权声明
THE END
喜欢就支持一下吧
点赞7 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容