第1章 课程导入:学习目标、核心概念与整体框架——理解用户态、监督态、机器态三种特权级的基本作用

RISC-V 指令集架构(ISA)自诞生以来,以其简洁、开放、模块化的设计理念,迅速成为嵌入式系统、边缘计算乃至高性能处理器领域的重要选择。与 x86/ARM 等商业架构不同,RISC-V 将“特权级(Privilege Levels)”作为整个软件栈安全与隔离的基石。无论是运行 Linux 等复杂操作系统,还是面向裸机(Bare-Metal)的实时控制,理解用户态(U-Mode)、监督态(S-Mode)、机器态(M-Mode)三种特权级的职责边界与切换机制,都是进行系统级开发、驱动移植、安全加固和性能调优的前提。本章作为整个课程的导入,将首先明确学习目标,厘清核心概念,并搭建起贯穿后续 29 章的总体知识框架,重点剖析三种特权级的基本作用与协作关系。

RISC-V 三种特权级隔离与协作模型 机器态(M-Mode)— 最高权限,硬件信任根 核心职责:复位/启动、异常入口、CSR 读写、物理内存保护(PMP)、中断委托 典型组件:Boot ROM、OpenSBI(M-Mode 固件)、Trusted Firmware 权限级别:3(最高) 监督态(S-Mode)— 操作系统内核驻留层 核心职责:虚拟内存管理(MMU/TLB)、进程调度、系统调用入口、中断处理 典型组件:Linux Kernel、RTOS 内核(若启用 MMU)、Hypervisor(HS 模式) 权限级别:1(中) 用户态(U-Mode)— 应用程序运行环境 核心职责:受限指令执行、无法直接访问硬件资源、通过 ecall 陷入内核 典型组件:用户进程、应用程序、运行时库(libc) 权限级别:0(最低) ecall sret ecall mret 隔离机制 PMP / MMU CSR 访问控制 切换方式 异常 / 中断 ecall / sret 硬件支持 CSR 寄存器组 异常向量表

1.1 学习目标与课程定位

本课程面向具备一定嵌入式基础或计算机体系结构知识的开发者,旨在帮助学员从“能跑代码”进阶到“理解系统为何如此运行”。完成本章及后续章节的学习后,学员应达到以下四个核心目标:

  • 目标一:建立特权级隔离的思维模型。 能够清晰阐述 U-Mode、S-Mode、M-Mode 的权限差异,理解为何不能允许应用程序直接操作硬件,以及这种隔离如何保障系统稳定性与安全性。
  • 目标二:掌握特权级切换的底层机制。 理解 ecall(环境调用)、ebreak(断点)、sret(从监督态返回)、mret(从机器态返回)等指令的作用,以及异常处理流程中硬件自动完成的现场保存与恢复动作。
  • 目标三:熟悉 RISC-V 特权架构的关键 CSR。 了解 mstatus、mepc、mcause、satp(页表基址寄存器)等控制状态寄存器的功能,为后续编写内核或移植驱动打下基础。
  • 目标四:具备系统级调试与安全分析能力。 当系统发生崩溃或非法访问时,能够通过分析特权级状态、异常原因寄存器(mcause/scause)快速定位问题。

本课程共 30 章,内容从基础概念逐步过渡到复杂实战。第 1 章作为总纲,将勾勒出完整的知识地图:第 2-5 章深入指令集与汇编;第 6-10 章聚焦内存管理与 MMU;第 11-15 章详解中断与异常处理;第 16-20 章探讨多核与缓存一致性;第 21-25 章涉及虚拟化与安全扩展;第 26-30 章则通过实际项目(如移植 Linux、编写 RTOS)串联全部知识点。

1.2 核心概念:特权级、CSR 与异常模型

在 RISC-V 规范中,特权级被定义为“软件执行环境所拥有的权限等级”。该架构原生支持三种特权级,其编号与名称如下表所示:

特权级编码 名称 缩写 主要用途 权限等级
00 用户态 U-Mode 运行应用程序 最低(0)
01 监督态 S-Mode 运行操作系统内核 中等(1)
10 机器态 M-Mode 运行固件、Bootloader、安全监控 最高(3)

值得注意的是,RISC-V 还定义了可选的 H 扩展(Hypervisor),用于支持虚拟化场景,此时 S-Mode 会被细分为 VS-Mode(虚拟化监督态)和 VU-Mode(虚拟化用户态),但基础隔离模型仍以 U/S/M 三级为核心。此外,部分低功耗微控制器(MCU)可能仅实现 M-Mode 和 U-Mode,省略 S-Mode,以简化硬件设计。

特权级的核心价值体现在以下三个维度:

  1. 指令集访问控制: 某些特权指令(如 mret、wfi(等待中断)、sfence.vma(内存屏障))只能在特定特权级下执行。例如,用户态执行 mret 会触发非法指令异常。
  2. CSR 访问控制: 每个 CSR 都有对应的特权级访问要求。例如,satp 寄存器(页表基址)只能在 S-Mode 或 M-Mode 下访问,U-Mode 访问将引发异常。
  3. 物理内存保护(PMP)与虚拟内存: M-Mode 通过 PMP 机制限制 S/U 模式对物理内存的访问范围;S-Mode 通过页表(Page Table)为 U-Mode 提供虚拟地址到物理地址的映射与权限控制。

核心原理: 特权级隔离的本质是“最小权限原则”。应用程序只拥有运行自身代码所需的权限,而操作系统内核拥有管理进程、内存和设备的权限,固件则拥有对硬件最底层的控制权。任何跨级操作都必须通过明确定义的“门”(即异常或中断)进行,从而避免恶意或故障的应用程序破坏整个系统。

1.3 整体框架:从复位到多任务运行的完整链路

理解三种特权级的协作关系,最直观的方式是追踪系统从上电复位到运行多个用户进程的完整流程。这一过程清晰地展示了特权级如何逐级“下放”权限,同时保留“回收”控制权的能力。

阶段一:M-Mode 启动(复位后)
处理器复位后,硬件强制将特权级设置为 M-Mode,并从 mtvec(机器态异常向量基址)指向的地址开始执行。此时运行的是 Boot ROM 或固化固件,负责初始化 DDR 控制器、时钟、串口等基础硬件。随后,固件加载 OpenSBI 或类似 M-Mode 运行时服务。

阶段二:进入 S-Mode(启动操作系统)
OpenSBI 完成硬件初始化后,会通过 mret 指令将特权级切换至 S-Mode,并跳转到操作系统内核入口(如 Linux 的 _start)。此时,mstatus.MPP 字段记录了返回目标特权级(S-Mode),mepc 寄存器保存了返回地址。

阶段三:S-Mode 创建用户进程
Linux 内核在 S-Mode 下完成内存管理、调度器初始化后,会创建第一个用户进程(init 进程)。内核通过设置 satp 寄存器加载进程页表,并执行 sret 指令,将特权级降至 U-Mode,同时跳转到用户代码入口。

阶段四:U-Mode 运行与系统调用
用户进程在 U-Mode 下运行,无法直接访问硬件。当进程需要读取文件或发送网络数据时,会执行 ecall 指令,触发环境调用异常。硬件自动将特权级提升至 S-Mode(或 M-Mode,取决于异常委托配置),跳转到内核的异常处理程序。内核完成服务后,再次执行 sret 返回 U-Mode。

这一“下放-回收-再下放”的循环,构成了现代操作系统运行的基础节律。下表总结了各阶段的关键动作与寄存器变化:

阶段 起始特权级 目标特权级 关键指令 核心 CSR
复位启动 无(硬件强制) M-Mode 无(硬件跳转) mtvec, mstatus.MPP
固件跳转内核 M-Mode S-Mode mret mepc, mstatus.MPP
创建用户进程 S-Mode U-Mode sret sepc, satp, sstatus.SPP
系统调用 U-Mode S-Mode ecall scause, sepc, stval
中断返回 S-Mode U-Mode sret sepc, sstatus.SPP

1.4 实战场景:基于 QEMU 的特权级观测实验

为了将抽象概念具象化,我们通过一个简单的 QEMU 实验来观察特权级的实际切换过程。该实验不依赖具体硬件,仅需 QEMU 的 RISC-V 模拟器和 GNU 工具链。

实验背景与目标: 编写一段裸机程序,在 M-Mode 下初始化串口,然后切换到 S-Mode,最后尝试在 S-Mode 下执行一条仅在 M-Mode 下合法的指令(如 mret),观察异常的产生。

关键代码片段(汇编):

# 定义 CSR 地址
.equ MSTATUS, 0x300
.equ MEPC,    0x341
.equ MTVEC,   0x305

.section .text.init
.globl _start
_start:
    # 设置异常向量表(M-Mode)
    la t0, trap_handler
    csrw mtvec, t0

    # 设置 mstatus.MPP = 01 (S-Mode)
    li t0, 0x00001800   # MPP 字段位于 bit[12:11]
    csrs mstatus, t0

    # 设置 mepc 为 s_mode_entry
    la t0, s_mode_entry
    csrw mepc, t0

    # 切换到 S-Mode
    mret

s_mode_entry:
    # 此时处于 S-Mode
    # 尝试执行 mret(非法操作)
    mret
    # 若未触发异常,则执行到这里(实际不会)
    j .

trap_handler:
    # 异常处理:读取 mcause
    csrr t0, mcause
    # 此处可添加打印逻辑
    j .

分析判断过程: 程序首先在 M-Mode 下设置 mstatus.MPP 为 01(S-Mode),并将 mepc 指向 s_mode_entry。执行 mret 后,硬件将特权级切换至 S-Mode,并跳转到 s_mode_entry。随后,代码尝试在 S-Mode 下执行 mret。根据 RISC-V 规范,mret 是 M-Mode 特权指令,在 S-Mode 下执行会触发非法指令异常(mcause = 2)。处理器跳转到 trap_handler,此时特权级自动提升回 M-Mode。

结果与复盘: 通过 QEMU 的 -d in_asm,cpu 参数,可以观察到执行流的变化。该实验直观证明了:特权级切换不是软件“跳转”,而是硬件根据 CSR 配置和异常机制自动完成的。同时,也揭示了异常是特权级提升的唯一途径(除中断外)。

常见误区: 初学者常误以为可以通过修改 mstatus.MPP 后直接“跳转”到低特权级代码。实际上,mret 和 sret 是唯一合法的降级指令。直接使用 jal 跳转到低特权级代码会导致该代码仍以高特权级运行,破坏隔离性。

1.5 常见误区与学习建议

在理解特权级时,学员容易陷入以下三个典型误区,需要特别警惕:

  • 误区一:将特权级等同于“运行模式”。 特权级不是 CPU 的“性能模式”(如高性能/低功耗),而是权限边界。高特权级并不意味着运行更快,而是拥有更多指令和寄存器访问权。
  • 误区二:忽略异常委托(Interrupt Delegation)。 默认情况下,所有异常和中断都跳转到 M-Mode 处理。为了性能,RISC-V 允许通过 medeleg 和 mideleg 寄存器将部分异常委托给 S-Mode。不理解这一点,会导致在编写内核时发现中断未按预期进入 S-Mode。
  • 误区三:混淆 mepc 与 sepc 的保存时机。 当异常发生时,硬件会将当前指令地址保存到当前特权级对应的 *epc 寄存器(M-Mode 存 mepc,S-Mode 存 sepc)。如果异常从 U-Mode 陷入 S-Mode,则保存到 sepc;如果从 S-Mode 陷入 M-Mode,则保存到 mepc。混淆这两个寄存器会导致返回地址错误。

学习建议: 建议学员在学习本章后,立即动手编写一个最小化的“双模式切换”程序(M→S→U→S→M),并在 QEMU 中逐步调试。重点关注 mstatus 的 MPP/SPP 字段如何决定返回目标特权级。同时,阅读 RISC-V 特权规范(Volume II)的 3.1.6 节“Traps Taken in U-Mode”等章节,加深对硬件自动行为的理解。