3.1 特权级模型与 hart 抽象


3.1 特权级模型与 hart 抽象

本节摘要:RISC-V 以机器、监督、用户三个特权级划分软硬件权责,机器级是唯一强制实现的地基;hart(硬件线程)则是规范对"独立执行流"的正式抽象。本节拆解特权级的切换机制——记录来路、改写目标、一条指令降级——并在裸机环境里完成一次真实的降级演示。

在复位向量上的第一微秒

想象示波器接在复位信号上,屏幕捕捉到释放瞬间的前几个时钟周期:pc 被硬件置成实现定义的复位向量,此刻的处理器拥有最高权力——它能碰所有 CSR、能响应与屏蔽全部中断、能配置物理内存保护。这个状态就是机器特权级(M-mode)。规范只保证这一级的存在:一颗最简 RISC-V 芯片可以只有机器级,跑个裸机循环就心满意足;但只要你想跑操作系统,就必须再实现监督级(S-mode);要让用户程序与内核隔离,还要用户级(U-mode)。特权级越高权力越大、能访问的 CSR 越多;低级代码执行高级指令会立刻触发非法指令异常——边界不是礼貌性的,是硬件强制的。

hart 这个词需要单独澄清。规范行文里几乎不用"CPU 核"而用 hart(hardware thread):一个独立取指、独立维护全套特权状态的执行流。单核单线程芯片有一个 hart;八核芯片有八个 hart,各自持有私有的 mstatus、mepc、mcause——理解这种 CSR 私有性非常重要,你在 hart 零上读到的一堆寄存器值,对 hart 一毫无意义。软件想知道自己在哪个 hart 上,读 mhartid 即可,这是多核启动时分辨"我是谁"的标准动作。

切换的三步舞:xret 与前级字段

特权级切换有上浮与下降两个方向,机制却不对称。上浮(陷入)由硬件自动完成:异常或中断发生时,处理器把返回地址写进当前级的 epc,原因写进 cause,特权级在 mstatus 里自动升档、自动关中断,然后按向量寄存器跳去处理程序——软件不参与,全程几个周期。下降(返回)则由软件发起,用一条 xret 指令完成,但降去哪一级不由这条指令直接指定,而是查 mstatus 里的前级字段:mret 看 MPP 两位,sret 看 SPP 一位。

这个"记录来路、查表回程"的设计乍看绕,实际很巧。陷入时硬件把旧特权级存进 MPP,mret 时自然弹回原级——这是最常见用法。但 MPP 是软件可写的,于是出现了经典的"伪造来路"技巧:固件想启动一段用户态代码,先把 MPP 手动写成用户级、把入口地址写进 mepc,再执行 mret,处理器便"假装"从用户态返回,一头扎进你安排的入口。裸机与教学内核的降级全靠这一手。整套机制可画成一张状态机图:

读图注意两个不对称:下行全靠 xret 加可写的字段,上行全靠硬件自动路由;而"谁接住上浮"由委托寄存器裁决,那是下一节的主角。

工程现场:把裸机程序降到用户态

背景:一块只有 M-mode 轮询程序的测试板,我们想验证用户级隔离确实生效——让一段代码降到 U-mode,然后故意越权,看硬件拦不拦。

操作:先在汇编里铺好现场。

la t0, user_entry # 目标入口 csrw mepc, t0 # 伪造成从用户态返回的目的地 li t1, 0x00000800 # MPP 编码 01 即用户级 csrs mstatus, t1 # 把 MPP 两位置为用户级 li t2, 0x00001800 csrc mstatus, t2 # 先清掉 MPP 原值,避免残留 csrw mtvec, t0_trap # 布好机器级陷入向量,接住越权 mret # 一键降级,pc 落在 user_entry

user_entry 里放一条只有监督级才能执行的 sfence.vma。预期行为:处理器立刻抛非法指令异常,硬件自动升回机器级,pc 落进 t0_trap。

结果:在调试器里单步,mret 后特权级成功降为用户级;随后那条越权指令触发的异常如约而至,读 mcause 得到值为二,即非法指令;mepc 指向那条 sfence.vma,mtvec 派上用场,处理程序顺利接盘。

解读:这个实验把三件事钉死在直觉里:其一,特权级是硬件状态而非软件标签,降级只是一条指令加两个字段;其二,越权即异常,隔离边界由硬件守夜,不依赖任何软件自觉;其三,mcause 与 mepc 是排错的第一现场——异常处理器开口问的两个问题"出什么事了、在哪儿出的事",答案都在 CSR 里。

变式:把 MPP 写成监督级再做同样实验,sfence.vma 会被顺利放行——指令合法与否取决于当前级,这一条在排查"同一段代码两块板子一跑一炸"时是首要检查项。另一个变式是双 hart 板:在两个 hart 上各跑一次降级流程,观察 mstatus 读数互不干扰,体会 CSR 的私有粒度。

hart 数量与启动顺序

多 hart 系统还有一课要上:上电时所有 hart 可能同时从复位向量冲出来,固件必须立刻分辨彼此、只放一个去初始化全局资源,其余的停在原地等待唤醒——通常靠读 mhartid 分工,用内存里的标志位协调。这个"抢跑与守序"问题在第三章末节的启动排错里会以真实故障的形态再遇一次,这里先记住原则:多 hart 环境里,任何"大家都来初始化"的代码都是隐患。

本节要点回顾

  • 三级权责:机器级保底、监督级跑内核、用户级跑应用,越权即硬件异常,不靠软件自律;
  • hart 是执行流单位:CSR 按 hart 私有,mhartid 是多核启动分辨身份的标准读数;
  • 上行自动、下行手动:陷入由硬件完成全套装填,返回靠 xret 查前级字段;
  • 伪造来路是标准技巧:写 mepc、置 MPP、执行 mret,三步完成任意降级,裸机内核的必会动作;
  • mcause 与 mepc 是第一现场:任何异常排错都从这两只寄存器开始读。

特权级的骨架立住了,但骨架上还挂着整套神经系统:异常往哪儿跳、中断谁能开、事务怎么委托——下一节把 CSR 体系与陷入处理一次讲透。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U