3. 硬件平台与启动流程

BSP(Board Support Package)的本质,是把一块具体硬件“翻译”成操作系统能够理解并驱动的软件集合。要做好 BSP,必须先看清硬件平台的三件事:处理器架构决定了指令集与异常模型,内存映射决定了地址空间如何划分,启动介质决定了第一行代码从哪里来。这三者串起来,就是从上电复位到操作系统加载的完整启动链路。

一、处理器架构:BSP 的“母语”

处理器架构是 BSP 移植的起点。以 ARM 为例,Cortex-A 系列面向应用处理器,具备 MMU、多级流水线和丰富的中断控制器(GIC);Cortex-M 系列面向微控制器,使用 NVIC 和向量表,通常不带 MMU。架构差异直接决定 BSP 的写法:A 系列需要配置页表、异常向量表基址(VBAR)、缓存与 MMU;M 系列则依赖向量表偏移寄存器(VTOR)和启动文件中的中断向量表。

除指令集外,还需关注字节序、对齐要求、异常等级(EL0~EL3)以及启动时的核心状态。例如 AArch64 上电后通常处于 EL3 或 EL2,BSP 的第一阶段代码必须在正确的异常等级下运行,才能安全地初始化时钟、内存和串口。

二、内存映射:地址空间的“地图”

内存映射把物理地址划分为若干区域:DDR 主存、片上 SRAM、外设寄存器、Boot ROM、保留区。BSP 开发者必须清楚每个区域的基地址、大小和属性(可缓存、可缓冲、设备属性)。典型 ARM SoC 的映射如下表所示。

区域典型基地址大小用途
Boot ROM0x0000_000064 KB固化启动代码,不可写
片上 SRAM0x0010_0000256 KB早期栈、SPL 运行区
外设寄存器0x4000_0000512 MBUART、GPIO、时钟控制器
DDR 主存0x8000_00001~4 GB内核、根文件系统、用户空间

内存映射一旦写错,最典型的现象是“串口无输出”或“内核解压后卡死”。因此 BSP 中通常用设备树(Device Tree)或板级头文件集中描述这些地址,避免散落在代码各处。

三、启动介质与启动链

常见启动介质包括 SPI NOR、eMMC、SD 卡、NAND 和网络(TFTP)。不同介质的读取方式不同:NOR 支持 XIP(就地执行),eMMC/SD 需要先初始化控制器再按块读取,NAND 需要处理坏块与 ECC。多数 SoC 采用多级启动:Boot ROM → SPL → U-Boot → Kernel。

Boot ROM  : 固化代码,按拨码/熔丝选择介质,加载 SPL 到 SRAM
SPL       : 初始化 DDR、时钟,加载 U-Boot 到 DDR
U-Boot    : 初始化外设,加载内核与设备树,传递 bootargs
Kernel    : 解压、建立页表、挂载根文件系统,启动 init
启动链的核心思想是“逐级放大”:Boot ROM 只认识最简单的介质,SPL 负责把 DDR 带起来,U-Boot 才有足够空间做复杂加载,内核最终接管整个系统。

四、上电复位到操作系统加载的完整流程

下面这张图把处理器、内存、启动介质和各级引导程序串成一条完整信号与数据流,便于对照实际板卡排查问题。

上电复位到操作系统加载的完整启动链路 处理器架构 Cortex-A / EL3 复位 异常向量表 VBAR 内存映射 Boot ROM / SRAM DDR 0x8000_0000 启动介质 SPI NOR / eMMC SD / NAND / TFTP Boot ROM 加载 SPL 到 SRAM SPL 初始化 DDR / 时钟 U-Boot 加载内核与 DTB Kernel 解压 / 挂载根文件系统 关键检查点 1. 复位后 PC 指向 Boot ROM,确认拨码/熔丝选择的启动介质正确。 2. SPL 阶段串口应打印 DDR 初始化信息,若卡死优先查 DDR 参数与时钟。 3. U-Boot 阶段确认 bootargs 中 console、root= 与内存映射一致。 4. 内核启动后若 panic,检查设备树中 memory 节点与 reserved-memory 是否冲突。

五、案例与常见误区

某 Cortex-A53 板卡上电后串口无任何输出。排查发现 Boot ROM 按拨码从 SD 卡启动,但 SD 卡第一分区没有写入 SPL,Boot ROM 读取失败后进入死循环。重新烧写 SPL 后,串口出现 DDR 初始化日志,问题定位在启动介质而非处理器。

另一个常见误区是忽略内存映射中的保留区。内核启动时若把 reserved-memory 区域当作普通内存使用,会覆盖 TrustZone 或固件数据,导致随机崩溃。正确做法是在设备树中显式声明 reserved-memory,并在 U-Boot 的 bootargs 中避免重叠。

不要跳过 SPL 直接调试内核。多数“内核起不来”的问题,根源在 SPL 阶段 DDR 未初始化或时钟配置错误。先让串口在 SPL 阶段稳定输出,再向上排查,效率最高。