2. RISC-V架构基础

OpenSBI 是运行在 RISC-V 平台上的 M 模式固件,它向下管理硬件、向上为 S 模式操作系统提供 SBI 接口。要读懂 OpenSBI 的启动流程、异常处理和内存布局,必须先建立对 RISC-V 架构基础的整体认知。本节从指令集、特权级、CSR、异常中断、内存模型五个维度展开,为后续启动链分析打地基。

一、RISC-V 指令集特点

RISC-V 采用模块化设计,基础指令集固定且精简,扩展指令集按需组合。这种“基础 + 扩展”的方式让同一套软件栈可以适配从微控制器到服务器处理器的不同实现。

  • RV32/RV64/RV128:以 XLEN 表示通用寄存器位宽,OpenSBI 常见于 RV64。
  • I 基础整数指令:加载、存储、算术、分支、跳转等,是所有实现的最小集合。
  • M/A/F/D/C 扩展:乘除、原子、单双精度浮点、压缩指令,操作系统与固件常依赖 A 与 C。
  • 特权指令:mret、sret、wfi、sfence.vma 等,是模式切换与内存管理的关键。
  • CSR 访问指令:csrr、csrw、csrs、csrc,用于读写控制和状态寄存器。

与 x86 的复杂指令集不同,RISC-V 指令长度固定(基础为 32 位,C 扩展可压缩为 16 位),译码简单,非常适合用硬件流水线实现。对固件开发者而言,这意味着启动代码中大量使用 CSR 操作和特权指令,而不是依赖复杂的指令前缀。

二、特权级架构

RISC-V 定义了三个特权级:M(Machine)、S(Supervisor)、U(User)。M 模式权限最高,负责固件和平台管理;S 模式通常运行操作系统内核;U 模式运行用户进程。OpenSBI 运行在 M 模式,通过 SBI 调用为 S 模式提供服务。

M 模式 → S 模式 → U 模式,权限逐级降低;异常和中断可逐级委托,减少高特权级介入。

模式切换依赖 mret 和 sret 指令,切换目标由 mepc/sepc 指定,状态由 mstatus/sstatus 控制。OpenSBI 启动时会配置 mstatus 的 MPP 字段,使 mret 后进入 S 模式。

三、CSR 寄存器

CSR(Control and Status Register)是 RISC-V 特权架构的核心。每个 CSR 有 12 位地址,按读写权限分为只读、读写等类型。OpenSBI 启动阶段会大量读写 CSR 来完成环境初始化。

CSR地址作用
mstatus0x300全局状态,含 MIE、MPP、MPRV 等
misa0x301指令集架构信息,含 XLEN 与扩展位
mie / mip0x304 / 0x344中断使能与挂起
mtvec0x305M 模式异常/中断入口基址
mepc0x341异常返回地址
mcause0x342异常/中断原因编码
medeleg / mideleg0x302 / 0x303异常/中断委托给 S 模式
satp0x180S 模式页表基址与模式

下面是一段典型的 CSR 读取代码,用于判断当前是否支持某些扩展:

csrr t0, misa        # 读取 misa
li   t1, (1 << 12)   # 检查 M 扩展位
and  t2, t0, t1
bnez t2, has_mul

四、异常与中断机制

RISC-V 将异常(同步)和中断(异步)统一为“陷阱”(trap)。发生陷阱时,硬件自动保存返回地址到 mepc,原因写入 mcause,并跳转到 mtvec 指定的入口。OpenSBI 的 M 模式陷阱处理程序负责处理平台级中断,并把部分异常委托给 S 模式。

  • 异常:指令非法、访存错误、断点、环境调用(ecall)等,由当前指令触发。
  • 中断:软件中断、定时器中断、外部中断,由 mie/mip 控制。
  • 委托:通过 medeleg/mideleg 把 S 模式可处理的陷阱下放,减少 M 模式切换开销。
  • 入口模式:mtvec 低两位决定直接模式或向量模式,OpenSBI 通常使用直接模式。
调试启动问题时,优先检查 mcause 和 mepc:前者告诉你“为什么陷入”,后者告诉你“从哪里陷入”。

五、内存模型与地址空间

RISC-V 采用弱内存模型(RVWMO),允许硬件在不违反数据依赖的前提下重排访存顺序。多核场景下必须使用 fence 指令保证可见性,例如 fence rw,rw。OpenSBI 在启动多核、传递参数时会用到 fence 与原子指令。

地址空间方面,M 模式默认使用物理地址;S 模式可通过 satp 开启分页,把虚拟地址翻译为物理地址。OpenSBI 运行在 M 模式,通常直接访问物理地址,并为 S 模式准备页表基址。典型的内存布局如下:

0x8000_0000  +------------------+
             | OpenSBI 固件     |
0x8002_0000  +------------------+
             | 设备树 DTB       |
0x8004_0000  +------------------+
             | 内核镜像         |
             +------------------+
             | 可用内存         |
             +------------------+

理解地址空间划分,是分析 OpenSBI 如何把控制权交给内核、如何保留自身内存区域的前提。

六、综合关系图

下图把指令集、特权级、CSR、异常中断和内存模型串联起来,展示它们在 OpenSBI 启动链中的位置关系。

RISC-V 架构基础与 OpenSBI 启动链关系 指令集层 RV64I + M/A/F/D/C 扩展 · 特权指令 mret/sret/wfi/sfence.vma · CSR 访问 csrr/csrw 特权级架构 M 模式:OpenSBI 固件 S 模式:操作系统内核 U 模式:用户进程 CSR 寄存器 mstatus / misa / mtvec mepc / mcause / mie / mip medeleg / mideleg / satp 异常与中断 同步异常:ecall / 非法指令 异步中断:软件/定时器/外部 委托:medeleg / mideleg 内存模型与地址空间 RVWMO 弱内存模型 · fence 保证顺序 · M 模式物理地址 · S 模式 satp 分页 典型布局:0x8000_0000 OpenSBI → DTB → 内核镜像 → 可用内存 OpenSBI 启动关键动作 读取 misa 判断扩展 → 配置 mstatus/mtvec → 设置 medeleg/mideleg → 准备 satp 与页表 → mret 进入 S 模式

七、常见误区与复盘

误区一:认为 M 模式也能直接使用虚拟地址。实际上 M 模式默认走物理地址,分页只对 S/U 模式生效。
误区二:忽略 mstatus.MPP 的设置,导致 mret 后仍停留在 M 模式。
误区三:在多核启动时忘记 fence,导致参数传递出现可见性问题。

复盘建议:在 OpenSBI 启动早期打印 misa、mstatus、mtvec 的值,确认扩展支持、特权级配置和陷阱入口是否符合预期;再检查 medeleg/mideleg 的委托位,确保 S 模式能独立处理常见异常。掌握这些基础后,后续分析 OpenSBI 的 sbi_init、sbi_hart_init 和平台初始化流程会顺畅很多。