前言
RISC-V 的启动流程在内核文档与各固件实现中散落着大量"约定俗成":boot ROM 把权限交给谁、多核从哪个 hart 先跑、内核入口寄存器里放什么、设备树里哪些节点必须存在。这些约定如果只存在于各家的实现里,操作系统与固件就无法保证互操作------换一个厂商的芯片,内核可能就起不来。
启动与运行时规范(Boot and Runtime Specification,BRS)正是把这些约定固化为文档的产物。它由 RISC-V International 的操作系统工作组维护,定义了从上电到操作系统运行的完整交接协议。本文以 BRS 的三条主线------hart 状态管理、启动阶段交接、设备树要求------展开解析,并说明它与 Profile 机制的配合关系。
一、BRS 在规范体系中的位置
回看系列平台规范篇给出的三层体系:ISA 扩展定义指令、Profile 划定基线、平台规范约定硬件接口。BRS 属于平台规范一层的核心成员,与 AIA(中断架构)、SBI(监督者二进制接口)共同构成软件可见的平台契约:
| 规范 | 约定对象 | 主要消费者 |
|---|---|---|
| BRS | 启动流程、hart 状态、交接约定 | 固件与操作系统 |
| SBI | S 模式到 M 模式的调用接口 | 内核与 SBI 实现 |
| AIA | 中断控制器架构 | 内核与设备驱动 |
| Profile | 指令集基线 | 编译器与发行版 |
四者的分工可以概括为:Profile 决定"能执行什么指令",SBI 决定"怎么请求高特权服务",AIA 决定"中断怎么到达",BRS 决定"系统怎么从上电走到内核入口"。
值得注意的是 BRS 的前身是独立的 Boot Flow Specification,后并入统一平台规范体系。其约束对象是"符合 RISC-V 平台规范要求的系统",而非全部 RISC-V 硬件------深度定制的嵌入式设备可以不遵守,但通用系统的固件与内核都必须遵守。
二、hart 状态机与 HSM 扩展
2.1 四种状态
BRS 将每个 hart 的生命周期定义为状态机,由 SBI 的 HSM(Hart State Management)扩展管理:
| 状态 | 含义 | 迁入条件 |
|---|---|---|
| STARTED | 正常运行 | 启动完成或 sbi_hsm_hart_start 成功 |
| STOPPED | 停止,可被再次启动 | 初始状态或 sbi_hsm_hart_stop |
| STARTING | 正在启动中 | 收到 start 请求、尚未跳入目标地址 |
| RESUMENS_PENDING | 挂起恢复中(非 Sret 路径) | 收到 resume 请求 |
这套状态机解决的核心问题是多核启动的确定性。经典做法由一个引导 hart(boot hart)先运行固件完成初始化,其余 hart 处于 STOPPED;操作系统侧通过 SBI 调用按需拉起二级 hart,而不是依赖"所有核同时从 boot ROM 出发自由竞争"------后者会带来初始化竞态与功耗失控。
2.2 启动交接的寄存器约定
BRS 规定了每一级交接时通用寄存器的含义,其中最重要的两处在 SBI 与内核之间:
a0:当前 hart 的 hartida1:设备树(DTB)的物理地址- 其余寄存器为未指定值,内核不得依赖
这条约定解释了启动篇中内核入口代码的第一段汇编为什么先保存 a0/a1------它们是固件与内核之间唯一有保证的通信通道。此外 satp/sie/sstatus 等关键 CSR 的初始状态也由 BRS 逐项定义,内核据此可以不写冗余的清零代码。
三、启动阶段的划分与交接
BRS 将启动流程划分为两级:boot ROM 与固件,随后固件(含 SBI 实现)与操作系统之间按 SBI 约定交接。每个阶段的职责边界如下:
| 阶段 | 职责 | 交接动作 |
|---|---|---|
| boot ROM | 片上不可改写代码,加载下一级镜像 | 校验后跳转,无统一寄存器约定 |
| 固件(M 模式) | DRAM 初始化、PMP 配置、SBI 服务部署 | 按 BRS 约定进入 S 模式 |
| bootloader / 内核 | 操作系统启动 | 消费 a0/a1 约定 |
U-Boot 等引导程序位于固件与内核之间,BRS 对其不做强制约束------它承接 SBI 的交接约定,再以相同约定转交内核,是纯粹的"二传手"角色。
对裸机与 RTOS 开发者同样重要的是:即使不使用 SBI,遵循 BRS 的寄存器与内存约定也能保证代码在符合平台规范的芯片上直接运行。这解释了为何 QEMU 的 -bios none 直启内核模式仍然要求内核自带 DTB 地址处理逻辑。
四、设备树的强制约定
BRS 与设备树规范共同规定了操作系统可见的最低设备描述集,缺失任何一项都可能导致内核启动失败或设备不可见:
| 节点/属性 | 作用 | 缺失的后果 |
|---|---|---|
/cpus/cpu@N |
每个核的描述 | 核不可见、调度异常 |
mmu-type |
sv39/sv48/sv57 | 页表模式选择错误 |
isa |
该核的扩展集合 | 能力探测失效 |
timebase-frequency |
时间基准频率 | 时钟与调度全错 |
/memory |
可用物理内存范围 | 内存识别失败 |
interrupt-parent |
中断控制器挂接 | 中断不可用 |
其中 isa 字符串是平台规范篇所述运行时探测的设备树侧对应物:内核从中获知该 hart 支持的扩展集合,再与 riscv_hwprobe 接口配合完成能力画像。两处信息应当一致,不一致属于平台实现缺陷。
多核场景下,BRS 还要求设备树中每个 cpu 节点携带 device_type、reg(hartid)与 status 属性,固件有责任在启动早期修正设备树与实际拓扑的一致性------例如部分 hart 被 fuse 禁用的芯片,固件应将这些节点标记为不可用。
五、设备树与 ACPI 的双轨
BRS 描述的启动路径以设备树为中心,但这不是唯一路径。面向服务器场景的 RISC-V 平台规范(ServerReady)走 ACPI + UEFI 路线:固件以 UEFI 标准流程启动,系统描述由 ACPI 表(含 GTDT、MADT、SRAT、PPTT)承担,设备树仅作为启动早期的过渡。
两条路径的选择依据:
| 维度 | 设备树 | ACPI |
|---|---|---|
| 适用场景 | 嵌入式、SoC、定制板卡 | 服务器、通用系统 |
| 描述来源 | 静态二进制 + 固件修正 | 固件运行时生成 |
| 动态热插拔 | 支持有限 | 原生支持 |
| 工具链 | 板级 DTS 编写 | ACPICA 生态 |
对开发者的实际影响在于:同一份内核镜像在两条路径下的启动入口、能力描述来源、中断拓扑获取方式均不同。嵌入式开发者以设备树为主,服务器方向的适配工作则以 ACPI 表核对为核心。
六、QEMU 下的验证
BRS 的约定可以在 QEMU 上逐项观察:
bash
qemu-system-riscv64 -machine virt -smp 4 -m 2G \
-bios default -kernel Image -append "root=/dev/vda rw console=ttyS0"
启动日志中可核对三个要点:
bash
# 1. SBI 版本与 HSM 扩展的存在
dmesg | grep -i "sbi"
# 2. 实际在线的 hart 数(HSM 拉起的二级核)
cat /sys/devices/system/cpu/online
# 3. 设备树约定的完整性别------逐项检查 BRS 必需属性
ls /proc/device-tree/cpus/
cat /proc/device-tree/cpus/timebase-frequency
第一条输出中的 SBI specification 版本与 SBI implementation 信息,对应 BRS/SBI 双规范在当前固件中的实现状态;第三条则直接验证了第四章的强制约定清单。将 -smp 4 改为 -smp 1 重复实验,可以观察到 HSM 在单核场景下的退化行为,加深对 boot hart 概念的理解。
结语
BRS 把启动流程从"各家固件的私有约定"收敛为"平台的公共契约":HSM 状态机让多核启动有了确定性的顺序,寄存器约定让固件与内核的交接不再依赖实现细节,设备树强制项让操作系统能力探测有了可靠输入。与 Profile 的指令集基线相配合,两者共同构成"能执行什么"与"怎么启动起来"的完整规范底座。
后续可展开的议题包括指针完整性扩展在系统软件中的落地方式,以及 IOMMU 与 AIA 在直通场景下的协同细节。