RISC-V特权模式及切换

news2025/5/31 21:02:31

1 RISC-V特权模式基本概念

1.1 RISC-V特权模式介绍

RISC-V 指令集架构(ISA)采用多特权级别设计作为其核心安全机制,通过层次化的权限管理实现系统资源的隔离与保护。该架构明确定义了四个层次化的特权模式,按照权限等级由高至低依次为:

  • 机器模式(Machine Mode,M-mode) - 最高特权级别,负责硬件初始化和底层安全控制
  • 虚拟机监控模式(Hypervisor Mode,H-mode) - 可选模式,用于虚拟化扩展
  • 监管者模式(Supervisor Mode,S-mode) - 操作系统内核运行级别,管理系统资源
  • 用户模式(User Mode,U-mode) - 最低权限级别,仅允许用户程序运行

和ARM的EL0-EL3分级类似,RISC-V这种分级特权架构为系统设计提供了灵活的权限控制方案,各模式间通过严格的权限边界实现安全隔离,满足从嵌入式系统到云计算平台的不同安全需求。每个特权级别都配备专属的寄存器组和控制机制,确保执行环境的完整性和安全性。

  1. Machine Mode (M-mode)
    机器模式是RISC-V最高特权级别,可以访问所有内存区域(包括内存、外设和CSR寄存器),执行所有的特权指令和处理所有的中断和异常;一般会在系统启动BootLoader阶段使用。

  2. Hypervisor Mode (H-mode)
    虚拟机模式是RISC-V次高特权级别模式,依赖与硬件虚拟化扩展(H扩展),一般应用在服务器级别,日常实际接触并不多(后面基本不会涉及);该模式支持管理虚拟机的内存和资源和处理虚拟机的异常和中断。

  3. Supervisor Mode (S-mode)
    监督者模式是 中等特权级别的模式,该模式依赖与M-mode的支持,可以访问内核及硬件资源,处理系统调用的部分异常和中断,一般在运行操作系统内核(如Linux)、管理任务调度、管理内存映射(如页表)和控制用户程序等场景下使用。

  4. User Mode (U-mode)
    用户模式是最低特权级别的模式,正常情况下仅访问用户内存区域,允许运行用户程序,无法直接访问硬件或修改系统状态的,也是无法执行CSR特权指令;在触发异常时通过ecall切换到特权模式(如S-mode 或 M-mode)请求内核服务

1.2 常见的设计

常用的RISC-V芯片架构中,M 模式是权限最高的特权等级,也是 RISC-V Spec 中明确规定必须要实现的特权等级,其他三个特权等级是可选的,处理器设计厂商可根据产品的功能定位和成本效益进行选择性实现,在典型的嵌入式应用场景中,对于不需要运行Linux等复杂操作系统的微控制器,为优化功耗控制和芯片面积,往往仅实现Machine模式,或者选择性地实现Machine/User两种模式组,一般情况下:

  • 单片机(运行RTOS):通常支持M模式和U模式,或者只有M模式
  • 通用SOC(运行linux):通常支持M、S、U模式

在一个典型 Linux 系统中,用户态应用程序跑在 U 模式,内核跑在 S 模式,而 M 模式一般是 OpenSBI/U-Boot、Bootloader启动阶段在用。

另外:
RISC-V 手册中有提到过一个 Debug Mode,可以理解为比M模式权限更高的特权等级,用于支持芯片调试。关于这个模式不再这里介绍,想要了解,资料可参考riscv-debug-release.pdf

2 特权模式切换

对于嵌入式工程师来说,对Linux的操作系统更熟悉些,所以为了更好的理解,这里从Linux用户态、内核态角度,对比理解硬件的特权模式。

2.1 Linux操作系统和特权关系

一般来说用户程序都运行在用户态,当它们需要切换到内核态以获得更高权限时,需要向操作系统申请;而操作系统内核和设备驱动程序则默认就运行在内核态。

Linux操作系统的用户态和内核态就分别对应到RISC-V处理器的特权等级就是:用户态对应U-Mode,内核态对应S-Mode。

2.2 Linux用户态、内核态的切换

通常,用户程序只运行在用户态(User Mode),仅在以下三种情况下会切换到内核态(Kernel Mode)进行处理:

  • 系统调用:当应用程序需要访问操作系统服务(如文件读写、进程创建)时,通过 int 指令或 syscall 指令主动请求内核执行操作。
  • 异常:运行时若发生异常(如缺页异常、非法指令、除零错误等),CPU 会自动切换到内核态,由操作系统处理异常。
  • 外部中断:外设(如磁盘、网卡)完成任务后向 CPU 发送中断信号,CPU 根据中断向量表跳转到内核态处理事件。

这种机制通过限制用户程序的直接硬件访问权限,确保系统的安全性和稳定性,仅在必要时通过上述三种方式切换到内核态。

2.3 RISC-V特权模式切换

特权模式切换示意:
在这里插入图片描述

  1. 从高特权级别到低特权级别:

    • 通过异常返回指令(如MRET、SRET)返回到低特权级别
    • 处理器恢复之前保存的状态并切换到低特权级别
  2. 从低特权级别到高特权级别:

    • 通过异常(如系统调用、中断、页错误)或陷阱(trap)进入高特权级别
    • 处理器保存当前状态(如PC、寄存器)并切换到高特权级别

2.3.1 opensbi启动过程特权模式切换

以Linux启动过程中,opensbi阶段特权模式为例。
这里仅保留和切换相关的代码,一般opensbi阶段工作在M模式,完成初始化之后,将切到S模式。

sbi_hart_switch_mode(unsigned long arg0, unsigned long arg1,
             unsigned long next_addr, unsigned long next_mode,
             bool next_virt)
{
    // 1. 配置mstatus寄存器:设置MSTATUS.MPP=S模式,关闭M模式中断
    unsigned long mstatus = csr_read(CSR_MSTATUS);
    mstatus = INSERT_FIELD(mstatus, MSTATUS_MPP, next_mode); // MSTATUS.MPP = S模式
    mstatus = INSERT_FIELD(mstatus, MSTATUS_MPIE, 0);       // 禁用M模式中断
    csr_write(CSR_MSTATUS, mstatus);

    // 2. 设置mepc为S模式代码入口地址
    csr_write(CSR_MEPC, next_addr);

    // 3. 设置S模式相关寄存器
    csr_write(CSR_STVEC, next_addr);  // 设置S模式异常入口
    csr_write(CSR_SSCRATCH, 0);       // 清零S模式临时寄存器
    csr_write(CSR_SIE, 0);                             //关闭S模式中断使能
    csr_write(CSR_SATP, 0);                          //禁用S模式的分页机制

    // 4. 向a0/a1传递参数(例如设备树地址或启动参数)
    register unsigned long a0 asm("a0") = arg0;
    register unsigned long a1 asm("a1") = arg1;

    // 5. 执行mret切换到S模式,跳转到next_addr
    __asm__ __volatile__("mret" : : "r"(a0), "r"(a1));
    __builtin_unreachable();
}

这里再来简单看下流程

  1. ​​配置 mstatus​​:
    • 将 mstatus.MPP 设置为 PRV_S,表示 mret 后切换到S模式。
    • 关闭M模式中断(mstatus.MPIE = 0),确保切换时中断处于禁用状态。
  2. ​​设置 mepc​​:
    • 将目标S模式代码的入口地址写入 mepc,mret 后CPU跳转至此地址。
  3. 设置S模式相关寄存器
    • 设置S模式异常入口CSR_STVEC
    • 清空S模式临时寄存器
    • 关闭S模式中断使能(防止内核启动时被打断)
    • 禁用S模式分页机制
  4. 执行 mret​​:
    • CPU从M模式退出,特权级降为S模式。
    • 跳转到 mepc 指定的地址执行代码。
    • 通过 a0 和 a1 寄存器传递启动参数。

另外,Linux内核特权模式切换,在内核起来之后,就会拉起用户态程序,这是就需要把特权模式从S模式切换到U模式。
Linux内核支持多种架构,代码相对晦涩,riscv特权模式切换代码位于arch/riscv/kernel/entry.S,这里不再粘贴,(和M切S模式类似 ,感兴趣自行学习)

2.3.2 中断、异常、系统调用情况特权模式切换

前面2.2介绍了linux用户态(U-mode)如何进入内核态(M-mode),一般来说常见的就是陷入内核态,此时用户态程序在被异常、中断、系统调用等,将暂停当前的任务,切换到高特权级别。

  1. 中断/异常:
    中断本省就可以看做异常的一种,在触发后行为也比较类似,特权模式切换是被动的,这里放到一起。

切换流程:

  • 硬件行为:

    • 暂停当前指令,保存当前PC 到mepc/sepc(中断触发导致会禁用中断(mstatus.MIE/sstatus.SIE 清零))
    • 异常原因存入mcause/scause
    • 当前特权级存入mstatus 的MPP/SPP 字段
    • 根据中断或者异常类型跳转到mtvec(M 模式异常入口)或 stvec(S 模式异常入口)
  • 软件行为:

    • 保存寄存器上下文(手动)
    • 读取 mcause/scause 判断中断或者异常类型
    • 执行中断异常服务例程(如处理定时器、响应外设 / 终止进程、修复缺页)
    • 恢复上下文,执行 mret/sret 返回

具体的流程可以参考: ECLIC中断流程及实际应用 —— RISC-V中断机制(二)

  1. 系统调用流程
    系统调用是软件通过执行ecall指令主动请求进入搞特权级别模式,
  • 硬件行为:

    • 同步触发异常,保存 pc 到 mepc/sepc(指向 ecall 的下一条指令)
    • 设置 mcause/scause 为 ECALL_FROM_U_MODE(code:8)
    • 切换特权模式(通常到 S-mode)
    • 跳转到 stvec 指定的系统调用处理程序
  • 软件处理:

    • 保存上下文。
    • 从 a7 读取系统调用号,a0-a6 读取参数
    • 执行内核服务(如文件读写)
    • 将结果写入 a0,恢复上下文
    • 执行 sret 返回用户态

我们以 Linux 系统 sys_open 系统调用为例,我们看一下用户态程序(特权等级 0, U 模式)是怎么陷入到 Linux 内核(特权等级 1, S 模式)中执行系统调用的。

   22482:	eb8d               	bnez	a5,224b4 <__libc_open+0x64>
   22484:	03800893          	li	a7,56
   22488:	f9c00513          	li	a0,-100
   2248c:	8622               	mv	a2,s0
   2248e:	00000073           	ecall

陷入到内核态后,处理器从 STVEC 寄存器加载异常处理程序入口。在 Linux 内核初始化过程中 (arch/riscv/kernel/head.S),就已经通过 CSR 指令设置好了 STVEC 寄存器,指向 handle_exception 函数:

setup_trap_vector:
	/* Set trap vector to exception handler */
	la a0, handle_exception
	csrw CSR_TVEC, a0
              /*
	 * Set sup0 scratch register to 0, indicating to exception vector that
	 * we are presently executing in kernel.
	 */
	csrw CSR_SCRATCH, zero
	ret

handle_exception 最终会跳转到 handle_syscall,然后从 a7 寄存器中拿到系统调用编号,从 sys_call_table 中索引到最终系统调用处理函数 (arch/riscv/kernel/entry.S):

/* Check to make sure we don't jump to a bogus syscall number. */
	li t0, __NR_syscalls
	la s0, sys_ni_syscall
	/*
	 * Syscall number held in a7.
	 * If syscall number is above allowed value, redirect to ni_syscall.
	 */
	bgeu a7, t0, 1f
	/* Call syscall */
	la s0, sys_call_table
	slli t0, a7, RISCV_LGPTR
	add s0, s0, t0
	REG_L s0, 0(s0)
1:
	jalr s0

参考:
riscv-privileged-20240411.pdf
RISC-V特权模式与寄存器
RISC-V特权等级与Linux内核的启动

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2391802.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【深度学习】11. Transformer解析: Self-Attention、ELMo、Bert、GPT

Transformer 神经网络 Self-Attention 的提出动机 传统的循环神经网络&#xff08;RNN&#xff09;处理序列信息依赖时间步的先后顺序&#xff0c;无法并行&#xff0c;而且在捕捉长距离依赖关系时存在明显困难。为了解决这些问题&#xff0c;Transformer 引入了 Self-Attent…

4060显卡什么水平 4060显卡参数介绍

NVIDIA的GeForce RTX 40系列显卡基于最新的Ada Lovelace架构&#xff0c;提供了前所未有的图形处理能力和效率。其中&#xff0c;RTX 4060定位中高端市场&#xff0c;针对那些寻求卓越性能同时又注重成本效益的用户群体。那么&#xff0c;4060显卡什么水平呢&#xff1f;本文将…

技术为器,服务为本:AI时代的客服价值重构

在智能化浪潮中&#xff0c;大语言模型的出现为客户服务行业注入了全新动能。然而技术创新的价值不在于技术本身&#xff0c;而在于其赋能服务的深度与广度。AI对于我们来说&#xff0c;如同发动机之于汽车&#xff0c;重要的不是引擎参数&#xff0c;而是整车带给用户的驾驶体…

EasyVoice:开源的文本转语音工具,让文字“开口说话“

名人说&#xff1a;博观而约取&#xff0c;厚积而薄发。——苏轼《稼说送张琥》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、EasyVoice是什么&#xff1f;1. 核心特性一览2. 技术架构概览 二、安装部署指南…

扫地机产品异物进入吸尘口堵塞异常检测方案

扫地机产品异物进入吸尘口堵塞异常的检测方案 文章目录 扫地机产品异物进入吸尘口堵塞异常的检测方案一.背景二.石头的音频异常检测的方案2.1 音频检测触发点2.1.1时间周期2.1.2根据清洁机器人清扫模式或清扫区域污渍类型,即当清扫模式为深度清洁模式 或清扫区域污渍类型为重度…

C++并集查找

前言 C图论 C算法与数据结构 本博文代码打包下载 基本概念 并查集&#xff08;Union-Find&#xff09;是一种用于处理动态连通性&#xff08;直接或间接相连&#xff09;的数据结构&#xff0c;主要支持两种操作&#xff1a;union 和 find。通过这两个基本操作&#xff0c;可…

git reset --hard HEAD~1与git reset --hard origin/xxx

git reset --hard HEAD~1与git reset --hard origin/xxx git reset --hard origin/xxx有时候会太长&#xff0c;手工输入略微繁琐&#xff0c;可以考虑&#xff1a; git reset --hard HEAD~1 替代。 或者使用这种方式 git reset撤销当前分支所有修改&#xff0c;恢复到最近一…

C++ RB_Tree

一、红黑树是什么&#xff1f;—— 带颜色标记的平衡二叉搜索树 红黑树是一种自平衡二叉搜索树&#xff0c;它在每个节点上增加了一个颜色属性&#xff08;红色或黑色&#xff09;&#xff0c;通过对颜色的约束来确保树的大致平衡。这种平衡策略被称为 "弱平衡"&…

kibana解析Excel文件,生成mapping es导入Excel

一、Excel转为CSV格式 在线免费网站&#xff1a;EXCEL转CSV - 免费在线将EXCEL文件转换成CSV (cdkm.com) 二、登录kibana 点击左边菜单栏找到Machine Learning&#xff0c; 进入后上面菜单选择Data Visualizer&#xff0c;然后上穿转好的csv格式的Excel 点击导入输入建立的m…

开疆智能Profinet转Profibus网关连接EC-CM-P1 PROFIBUS DP从站通讯模块配置案例

本案例是通过开疆智能Profibus转Profinet网关将正弦研发的Profibus从站模块连接的EM600变频器接入到西门子1200PLC的配置案例。 配置过程 1. 打开网关配置软件“”新建项目并添加模块PN2DPM并设置参数 2. 设置网关的Profibus参数。如站地址&#xff0c;波特率等。&#xff08;…

Oracle RMAN自动恢复测试脚本

说明 此恢复测试脚本&#xff0c;基于rman备份脚本文章使用的fullbak.sh做的备份。 数据库将被恢复到RESTORE_LO参数设置的位置。 在恢复完成后&#xff0c;执行一个测试sql,确认数据库恢复完成&#xff0c;数据库备份是好的。恢复测试数据库的参数&#xff0c;比如SGA大小都…

零基础设计模式——结构型模式 - 代理模式

第三部分&#xff1a;结构型模式 - 代理模式 (Proxy Pattern) 在学习了享元模式如何通过共享对象来优化资源使用后&#xff0c;我们来探讨结构型模式的最后一个模式——代理模式。代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问。 核心思想&#xff1a;为其…

架构意识与性能智慧的双重修炼

架构意识与性能智慧的双重修炼 ——现代软件架构师的核心能力建设指南 作者:蓝葛亮 🎯引言 在当今快速发展的技术环境中,软件架构师面临着前所未有的挑战。随着业务复杂度的不断增长和用户对性能要求的日益严苛,如何在架构设计中平衡功能实现与性能优化,已成为每个技术…

Dynamics 365 Business Central AI Sales Order Agent Copilot

#AI Copilot# #D365 BC 26 Wave# 最近很多客户都陆续升级到 Dynamics 365 Business Central 26 wave, Microsoft 提供一个基于Copilot 的Sales Order Agent&#xff0c;此文将此功能做个介绍. Explorer: 可以看到26版本上面增加了这样一个新图标。 Configuration: 配置过程…

RabbitMQ 与其他 MQ 的对比分析:Kafka/RocketMQ 选型指南(一)

一、引言 ** 在当今分布式系统大行其道的技术时代&#xff0c;消息队列作为分布式系统的关键组件&#xff0c;起着举足轻重的作用。它就像是一个可靠的信使&#xff0c;在不同的系统模块、服务之间传递信息&#xff0c;让各个部分能够高效、稳定地协同工作。消息队列能够实现系…

汽车EPS系统的核心:驱动芯片的精准控制原理

随着科技的飞速发展&#xff0c;电机及其驱动技术在现代工业、汽车电子、家用电器等领域扮演着越来越重要的角色。有刷马达因其结构简单、成本低廉、维护方便等优点&#xff0c;在市场上占据了一定的份额。然而&#xff0c;为了充分发挥有刷马达的性能&#xff0c;一款高效能、…

【Linux网络编程】传输层协议TCP,UDP

目录 一&#xff0c;UDP协议 1&#xff0c;UDP协议的格式 2&#xff0c;UDP的特点 3&#xff0c;面向数据报 4&#xff0c;UDP的缓冲区 5&#xff0c;UDP使用注意事项 6&#xff0c;基于UDP的应用层协议 二&#xff0c;对于报文的理解 三&#xff0c;TCP协议 1&…

基于Geotools的Worldpop世界人口tif解析-以中国2020年数据为例

目录 前言 一、Worldpop数据简介 1、数据来源 2、QGIS数据展示 3、元数据展示 二、GeoTools人口解析 1、Maven依赖引入 2、Tif人口计算 三、总结 前言 在当今数字化与信息化飞速发展的时代&#xff0c;地理空间数据的分析与应用已然成为诸多领域研究与决策的关键支撑。…

Unity3D仿星露谷物语开发55之保存游戏到文件

1、目标 将游戏保存到文件&#xff0c;并从文件中加载游戏。 Player在游戏中种植的Crop&#xff0c;我们希望保存到文件中&#xff0c;当游戏重新加载时Crop的GridProperty数据仍然存在。这次主要实现保存地面属性&#xff08;GridProperties&#xff09;信息。 我们要做的是…

【无标题】C++23新特性:支持打印volatile指针

文章目录 前言背景与问题C23的解决方案实现原理使用场景硬件开发多线程调试 总结 前言 在C开发中&#xff0c;volatile关键字常用于修饰变量&#xff0c;以确保编译器不会对这些变量进行优化&#xff0c;从而保证程序能够正确地与硬件交互或处理多线程环境下的特殊变量。然而&…