2. RISC-V架构基础

Linux 内核并不是“凭空”运行在 CPU 上的,它必须与处理器架构建立一套明确的契约:内核如何进入、如何切换特权级、如何访问内存、如何保存上下文、如何响应中断。RISC-V 作为一套开放指令集架构(ISA),把这些契约以规范形式固定下来。理解 RISC-V 的指令集特点、寄存器组织、特权模式与内存模型,是判断“哪些代码需要移植、哪些只需配置”的关键前提。

一、核心原理:RISC-V 的四块基石

RISC-V 采用模块化设计,基础整数指令集为 RV32I / RV64I,其他能力通过扩展组合,例如 M(乘除)、A(原子)、F/D(浮点)、C(压缩)、V(向量)。Linux 移植最关心的是:RV64GC 是否可用、是否支持原子指令、是否具备 MMU 与 Sv39/Sv48 分页。没有 MMU 的 RISC-V 只能跑 NOMMU Linux 或裸机 RTOS,这与标准 Linux 移植路径完全不同。

寄存器组织方面,RISC-V 有 32 个通用整数寄存器 x0–x31,其中 x0 恒为 0。ABI 为它们定义了别名与调用约定:a0–a7 传参、a0–a1 返回、ra 保存返回地址、sp 栈指针、tp 线程指针、gp 全局指针、s0–s11 被调用者保存、t0–t6 调用者保存。Linux 内核在进程切换、异常入口、系统调用时,必须按这套约定保存和恢复寄存器。移植时若上下文保存顺序或栈帧布局写错,最典型的现象就是系统调用返回后用户态寄存器错乱、进程随机崩溃。

特权模式是 Linux 能运行的前提。RISC-V 定义 M(机器)、S(监管)、U(用户)三级特权。标准 Linux 运行在 S 模式,用户程序运行在 U 模式,M 模式通常由 OpenSBI 等固件占据,负责时钟、中断委托、PMP 配置和 SBI 调用。内核通过 CSR 寄存器完成模式切换与异常处理,例如 sstatus、sepc、scause、stvec、satp。移植时如果 M 模式固件没有正确委托中断或没有设置 PMP,S 模式内核将无法收到时钟中断,表现为系统启动后卡死或调度器不工作。

内存模型方面,RISC-V 采用弱内存序(RVWMO),普通 load/store 可能被重排,因此内核必须使用 fence、fence.i 以及原子指令来保证顺序与可见性。Linux 中的 mb()、rmb()、wmb()、smp_mb() 在 RISC-V 上会展开为相应 fence。移植驱动时若忽略这一点,DMA 描述符、自旋锁、无锁队列都可能出现“数据已写好但对方看不到”的隐蔽故障。

RISC-V 架构基础与 Linux 移植的对应关系 RISC-V 架构要素 指令集 RV64GC / M / A / F / D 压缩、原子、浮点扩展 寄存器组织 x0–x31 / ABI 别名 a0–a7、ra、sp、tp、gp 特权模式 M / S / U 三级 CSR、异常、中断委托 内存模型 RVWMO 弱内存序 fence / fence.i / 原子 决定 决定 决定 决定 Linux 移植关注点 编译与工具链 -march=rv64gc 原子/浮点指令可用性 上下文与栈帧 pt_regs 保存/恢复 switch_to、syscall 入口 启动与固件 OpenSBI / SBI 调用 PMP、中断委托、时钟 内存与并发 页表 Sv39/Sv48 屏障、自旋锁、DMA 设备树与平台描述 CPU 节点、中断控制器、时钟、串口、内存节点 riscv,cpu-intc / plic / clint / uart / memory 典型移植故障 无时钟中断、页表未启用、fence 缺失、PMP 拒绝访问 系统调用返回错乱、DMA 数据不可见 M 模式固件(OpenSBI)通过 SBI 为 S 模式 Linux 提供时钟、IPI、重启等能力

二、方法流程:从架构特性到移植动作

移植不是“改几个宏”,而是按架构契约逐层核对。建议按以下顺序推进:

  1. 确认 ISA 与扩展:读取设备树或 /proc/cpuinfo,确认 rv64imafdc 与 MMU 是否存在。
  2. 确认特权级与固件:确认内核运行在 S 模式,M 模式由 OpenSBI 提供 SBI 接口。
  3. 核对 CSR 与异常入口:检查 stvec、scause、sepc、sstatus 的初始化与保存逻辑。
  4. 核对内存与页表:确认 satp 分页模式、内核页表映射、DMA 一致性属性。
  5. 核对屏障与原子:检查驱动中 mb()、wmb()、atomic_* 是否按 RISC-V 语义实现。
  6. 核对设备树:CPU、CLINT、PLIC、UART、内存节点必须与硬件一致。

一个可用的最小检查命令示例:

# 查看 CPU 与 ISA 扩展
cat /proc/cpuinfo | grep -E "isa|mmu|hart"

# 查看内核启动时识别的 RISC-V 特性
dmesg | grep -i riscv

# 查看当前页表模式与内存布局
cat /proc/meminfo | head -5
cat /sys/kernel/debug/page_owner 2>/dev/null | head

三、案例场景:一个“卡在启动”的移植问题

某 RISC-V 开发板移植 Linux 时,内核解压后停在 Booting Linux on hart 0,串口不再输出。排查发现:M 模式固件没有把 MTIP(机器定时器中断)委托给 S 模式,也没有正确设置 mideleg 与 medeleg。S 模式内核虽然初始化了 sie 与 sstatus.SIE,但时钟中断始终无法到达,调度器无法启动,系统看起来“死机”。修复方式是在 OpenSBI 或固件中正确委托中断与异常,并在设备树中补充 riscv,ndev 与中断控制器节点。这个案例说明:特权模式与中断委托是 Linux 移植的“隐形开关”,不解决它,后面的驱动移植都无从谈起。

四、误区与复盘

常见误区 后果 正确做法
认为 RISC-V 只有一种“标准”配置 工具链与内核配置不匹配,编译失败或运行异常 明确 RV64GC、MMU、原子扩展是否具备
忽略 ABI 寄存器约定 系统调用返回后用户态寄存器错乱 按 a0–a7、ra、sp、s0–s11 约定保存上下文
把 M 模式代码直接搬进内核 权限异常、CSR 访问非法 通过 SBI 调用访问 M 模式资源
忽略弱内存序 DMA、锁、无锁队列偶发数据不一致 正确使用 fence、原子指令与内核屏障 API
设备树与硬件不一致 中断号错、串口无输出、内存大小错误 以硬件手册为准逐项核对设备树节点

复盘建议:把“指令集—寄存器—特权级—内存模型”当作四把尺子,每移植一个驱动或一个平台,都依次量一遍。凡是涉及上下文切换、中断、DMA、锁的代码,都要回到这四把尺子上确认,而不是只改配置宏。

总结来看,RISC-V 的指令集特点决定了工具链与内核配置,寄存器组织决定了上下文切换与系统调用入口,特权模式决定了内核与固件的分工,内存模型决定了并发与驱动代码的正确性。把这四者与 Linux 移植动作一一对应,才能在后续的启动流程、设备树、驱动移植中少走弯路。