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 ROM | 0x0000_0000 | 64 KB | 固化启动代码,不可写 |
| 片上 SRAM | 0x0010_0000 | 256 KB | 早期栈、SPL 运行区 |
| 外设寄存器 | 0x4000_0000 | 512 MB | UART、GPIO、时钟控制器 |
| DDR 主存 | 0x8000_0000 | 1~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
四、上电复位到操作系统加载的完整流程
下面这张图把处理器、内存、启动介质和各级引导程序串成一条完整信号与数据流,便于对照实际板卡排查问题。
五、案例与常见误区
某 Cortex-A53 板卡上电后串口无任何输出。排查发现 Boot ROM 按拨码从 SD 卡启动,但 SD 卡第一分区没有写入 SPL,Boot ROM 读取失败后进入死循环。重新烧写 SPL 后,串口出现 DDR 初始化日志,问题定位在启动介质而非处理器。
另一个常见误区是忽略内存映射中的保留区。内核启动时若把 reserved-memory 区域当作普通内存使用,会覆盖 TrustZone 或固件数据,导致随机崩溃。正确做法是在设备树中显式声明 reserved-memory,并在 U-Boot 的 bootargs 中避免重叠。