4. BootROM与第一阶段引导

在RISC-V启动链中,BootROM是芯片上电后执行的第一段不可修改的固件,通常固化在SoC内部掩膜ROM或受保护的安全存储区。它承担着“把系统从裸硬件状态带到可运行下一阶段引导程序”的职责。与U-Boot SPL、OpenSBI等后续阶段不同,BootROM代码量极小、功能高度受限,却决定了整个启动链的起点是否可靠。理解BootROM的作用、复位向量、硬件初始化以及加载SPL/FSBL的基本流程,是掌握OpenSBI与RISC-V启动链开发的基础。

一、BootROM的核心作用

BootROM的首要作用是提供确定性的复位入口。RISC-V处理器在复位后,PC会被设置为一个固定的复位向量地址,该地址通常指向BootROM的起始位置。由于此时DDR尚未初始化、缓存可能未使能、栈指针未设置,BootROM必须运行在极简环境中,通常直接使用芯片内部的SRAM或TCM作为临时存储。

BootROM的主要职责可以概括为以下几点:

  • 建立最小运行环境:设置栈指针、清零BSS段、配置必要的CSR寄存器,使C代码可以运行。
  • 硬件初始化:完成时钟、PLL、电源域、复位控制器、引脚复用等基础配置,为后续阶段准备硬件条件。
  • 启动介质探测:根据启动模式引脚或eFuse配置,选择SPI Flash、SD/eMMC、UART、USB等启动源。
  • 加载并校验下一阶段:从启动介质读取SPL/FSBL镜像,进行签名或校验和验证,然后跳转执行。
  • 安全启动支撑:在安全启动场景中,BootROM是信任根,负责验证第一级可执行镜像的签名。

BootROM是启动链的信任根和第一段代码,它不负责完整系统启动,而是负责“把控制权安全、正确地交给下一阶段”。

二、复位向量与上电执行路径

RISC-V规范定义了复位时处理器的行为:复位向量地址由实现决定,通常通过硬件引脚或配置寄存器选择。例如,许多RISC-V SoC将复位向量设置为0x1000或0x80000000等固定地址。BootROM必须位于该地址处,且第一条指令通常是跳转到初始化代码。

上电后的典型执行路径如下:

  1. 处理器复位,PC指向复位向量。
  2. BootROM入口执行最简汇编初始化,设置栈、关闭中断、初始化时钟。
  3. 跳转到BootROM的C入口,完成硬件自检和启动介质选择。
  4. 从启动介质加载SPL/FSBL到内部SRAM。
  5. 校验镜像完整性,设置参数(如设备树地址、启动模式),跳转到SPL入口。

需要注意的是,BootROM阶段通常不初始化DDR,因为DDR训练耗时且依赖具体板级参数。因此,SPL/FSBL必须足够小,能够装入内部SRAM,并在自身阶段完成DDR初始化。

三、硬件初始化与启动介质选择

BootROM的硬件初始化范围因芯片而异,但通常包括:

  • 时钟与PLL:将CPU时钟从低速晶振切换到PLL输出,提高执行速度。
  • 电源与复位:释放相关电源域,确认复位源,记录启动原因。
  • 引脚复用:配置启动介质相关引脚,如SPI、SD、UART。
  • 存储控制器:初始化SPI控制器或SD控制器,使其可读取镜像。
  • 安全模块:初始化TRNG、SHA、RSA等安全引擎,用于镜像校验。

启动介质选择通常由一组启动模式引脚(strap pins)或eFuse值决定。BootROM读取这些配置后,按优先级尝试启动源。例如,若SPI Flash启动失败,可能回退到UART下载模式。这种回退机制在开发和量产中都非常重要。

启动阶段 运行位置 主要任务 是否可修改
BootROM 芯片内部ROM 复位入口、基础硬件初始化、加载SPL 否,固化
SPL/FSBL 内部SRAM DDR初始化、加载OpenSBI/U-Boot 是,可升级
OpenSBI DDR M模式运行时、SBI服务、跳转S模式 是,可升级
U-Boot DDR 加载内核、设备树、根文件系统 是,可升级

四、加载SPL/FSBL的基本流程

BootROM加载SPL/FSBL的过程通常遵循“探测—读取—校验—跳转”四步。以SPI Flash启动为例,BootROM会从Flash的固定偏移处读取镜像头,解析镜像大小、加载地址、入口地址和校验信息,然后将镜像复制到内部SRAM,最后跳转到入口地址。

一个典型的镜像头结构可能包含以下字段:

struct boot_image_header {
    uint32_t magic;        // 魔数,如 0x53424F4F
    uint32_t version;      // 镜像版本
    uint32_t load_addr;    // 加载地址
    uint32_t entry_addr;   // 入口地址
    uint32_t image_size;   // 镜像大小
    uint32_t checksum;     // 校验和
    uint8_t  signature[];  // 可选签名
};

BootROM在跳转前还会传递启动参数,常见方式包括:

  • 通过寄存器a0/a1传递hart ID和设备树地址。
  • 通过内部SRAM固定地址传递启动信息结构体。
  • 通过CSR或内存映射寄存器传递启动模式。

在开发阶段,可以通过UART输出BootROM日志,确认启动介质、镜像加载地址和校验结果。若BootROM无日志,可借助JTAG在复位向量处设置断点,单步跟踪执行路径。

五、常见实现与误区复盘

不同厂商的BootROM实现差异较大。例如,SiFive的BootROM通常从SPI Flash加载FSBL;全志D1的BootROM支持SPI、SD和USB FEL模式;StarFive JH7100的BootROM则从QSPI Flash加载SPL。尽管细节不同,但核心流程一致:复位向量→基础初始化→介质选择→镜像加载→校验→跳转。

常见误区包括:

  • 误以为BootROM会初始化DDR:大多数BootROM不初始化DDR,SPL必须自行完成DDR训练。
  • 忽略镜像头对齐要求:部分BootROM要求镜像按扇区或4KB对齐,否则加载失败。
  • 混淆复位向量与入口地址:复位向量是CPU第一条指令地址,入口地址是SPL的起始地址,两者不同。
  • 未处理启动失败回退:量产产品应配置回退启动源,避免单一介质损坏导致变砖。

复盘时,建议从复位向量开始,逐级确认BootROM日志、镜像头、校验结果和跳转地址。只有确保每一级都正确交接,OpenSBI和后续系统才能稳定启动。

RISC-V BootROM 第一阶段引导流程 复位向量 PC = 0x1000 BootROM 入口 汇编初始化 硬件初始化 时钟/电源/引脚 启动介质选择 SPI / SD / UART 加载 SPL/FSBL 镜像 读取镜像头 → 复制到 SRAM 解析 load_addr / entry_addr 镜像校验 Checksum / 签名验证 失败则回退启动源 跳转到 SPL 入口 传递 hart ID / DTB 地址 a0 / a1 寄存器 SPL / FSBL 阶段 DDR 初始化与训练 加载 OpenSBI / U-Boot BootROM 关键特征 固化在芯片内部,不可修改 运行在内部 SRAM,不依赖 DDR 代码量小,功能高度受限 安全启动的信任根 支持启动失败回退机制 注意:BootROM 不初始化 DDR DDR 训练由 SPL/FSBL 完成