riscv架构下Linux中断学习笔记
资料来源:https://www.cnblogs.com/dream397/p/15687184.html
https://riscv-rtthread-programming-manual.readthedocs.io/zh-cn/latest/zh_CN/2.html
https://www.riscv-mcu.com/quickstart-show-id-1.html#9
https://www.riscv-mcu.com/site/nuclei_interrupt_quickstart/#23-n
https://www.cnblogs.com/harrypotterjackson/p/17548837.html#_label12
https://rcore-os.cn/rCore-Tutorial-Book-v3/chapter2/4trap-handling.html
特权架构
RISCV架构下有三种特权级别,分别是Machine、Supervisor和User,简称M模式、S模式和U模式。M模式权限最高,在这个级别下的程序可以访问统统硬件和实验全部特权指令,可以直接利用物理地址进行访问,不颠末MMU;S模式一般用于运行操作体系,可以设置MMU利用虚拟地址;U模式一般是平凡应用程序利用,权限最低。在微控制器上利用的RISCV架构一般只有M模式,大概利用M和U两种模式,类似于cortex-m架构的定位;而在带MMU的芯片上,RISCV架构一般都利用M、S和U三种模式,如许通过“拼积木”的方式就可以让RISCV架构实用于各种场景了。
https://i-blog.csdnimg.cn/direct/552a3f66e75840c09131685cf4f65e0d.png
在RISCV中,默认产生中断和异常时,处理器自动切换到M模式处理,但通过中断托管允许某些中断和异常直接在S模式下处理。RISCV的架构计划就决定了必须要有程序运行在M模式下,来为S模式提供一些基础的服务,RISCV为此定义了SBI(Supervisor Binary Interface)接口规范,让运行在S模式下的操作体系在差异的RISCV处理器上都可以利用尺度的SBI接口来利用相应的功能。在RISCV中,通过“ecall”指令可以从低特权切换到高特权,在U模式下实验就切换到S模式,在S模式下调用就切换到M模式。
寄存器
RISC-V 架构提供 1 个只读寄存器 x0 和 31 个用户可修改的通用寄存器 x1 到 x31,全部寄存器都可以作为通用寄存器利用。其中 Caller 由调用者保存,Callee 由被调用者保存。
https://i-blog.csdnimg.cn/direct/9128df0f298f4a8b8fcf686da1fafae1.png
RISC-V 支持单精度浮点数和双精度浮点数,分别利用 32 位和 64 位来表示,分为两种类型:浮点暂时寄存器和浮点保存寄存器。
https://i-blog.csdnimg.cn/direct/ceb5d101f1d94fd698c65202019e0426.png
在RISCV架构计划中,有一系列的控制和状态寄存器( Control and Status Registers)简称CSR,能够反映和控制 CPU 当前的状态和实验机制,访问不存在的 CSR 将触发无效指令异常。在三种特权级别下都有其对应的CSR,比如m模式下的定名都为mxxxx,s模式下的都为sxxxx等等。用于设置异常向量表、设置页表基址、获取异常信息等等。
在呆板模式(Machine Mode)下CSR主要包罗以下六类:
[*]处理器信息相关:例如处理器的厂商信息,架构信息,核心数等等,是一个芯片自身的I固有信息。
[*]中断设置相关:例如中断开关以及中断入口等信息。
[*]中断响应相关:例如中断原因,中断返回地址等信息。
[*]存储器掩护相关:设置差异地址空间的存储器的访问属性,例如可读可写可实验等等。
[*]性能统计相关。
[*]调试接口相关。
跟异常相关的寄存器介绍跳转到此博客
异常和中断
在 RISC-V 中,中断(interrupt)和异常(exception)被统称为 trap。
RISC-V 中断被分为两类局部中断和全局中断:
[*]局部中断,算是内部中断,尺度是只规定了有两种,纵然体系定时器中断和软件中断。局部中断连接在 CLINT(Core Local Interruptor)上。
[*]全局中断,也就是所说的外部中断,其他外设统统都是外部中断。外部中断连接在 PLIC(Platform-Level Interrupt Controlle)上。
CLINT 和 PLIC 最大的区别在于,CLINT 没有仲裁,包罗 software 和 timer,一有中断马上响应,PLIC 需要一个仲裁决定谁先中断,存在个优先级的问题。
https://i-blog.csdnimg.cn/direct/26dce2f36c21448b83e7dccf2dc1a290.png
“定时器中断”是由一个独立的计时器电路发出的信号,表示预定的时间间隔已经结束。计时器子体系将中断当前正在实验的代码。定时器中断可以由操作体系处理,用于实现时间片多线程,但是对于 MTIME 和 MTIMECMP 的读写只能由 M-mode 的代码实现,因此内核需要调用 SBI 的服务。
mtime 需要以固定的频率递增,并在发生溢出时回绕。当 mtime 大于或等于 mtimecmp 时,由核内中断控制器 (CLINT, Core-Local Interrupt Controller) 产生 timer 中断。中断的使能由 mie 寄存器中的 MTIE 和 STIE 位控制,mip 中的 MPIE 和 SPIE 则指示了 timer 中断是否处于 pending。在 RV32 中读取 mtimecmp 效果为低 32 位, mtimecmp 的高 32 位需要读取 mtimecmph 得到。
特权级转换
当实验一条 Trap 类指令(如 ecall 时),CPU 发现触发了一个异常并需要进行特别处理,这涉及到 实验环境切换 。
在 RISC-V 架构中,关于 Trap 有一条紧张的规则:在 Trap 前的特权级不会高于 Trap 后的特权级。因此假如触发 Trap 之后切换到 S 特权级(下称 Trap 到 S),说明 Trap 发生之前 CPU 只能运行在 S/U 特权级。
U 切换到 S
当实验一个 trap 时,除了 timer interrupt,全部的过程都是相同的,硬件会自动完成下述过程:
[*]假如该 trap 是一个装备中断并且 sstatus 的 SIE bit 为 0,那么不再实验下述过程
[*]通过置零 SIE 禁用中断
[*]将 pc 拷贝到 sepc
[*]保存当前的特权级到 sstatus 的 SPP 字段
[*]将 scause 设置成 trap 的原因
[*]设置当前特权级为 supervisor
[*]拷贝 stvec(中断服务程序的首地址)到 pc
[*]开始实验中断服务程序
https://i-blog.csdnimg.cn/direct/04367a7c71b749ceb4182c57195e2728.png
CPU 不会自动切换到内核的页表,也不会切换到内核栈,也不会保存除了 pc 之外的寄存器的值,内核需要自行完成。对于Linux而言,内核空间与用户态空间是利用的同一套页表,不需要切换页表。详情可以参考用户态进程的虚拟内存结构。内核空间一般位于进程的高虚拟地址空间。
S 切换到 U
在从 S 切换到 U 时,要手动清除 sstatus 的 SPP 字段,将其置为零;将 sstatus 的 SPIE 字段置为 1,启用用户中断;设置 sepc 为用户进程的 PC 值(你大概疑惑在 U 转换到 S 时不是已经将用户进程的保存在了 sepc 了吗?因为在 S-mode 也会发生中断呀,那么 sepc 就会被用来保存发生中断位置时的 PC 了)。假如启用了页表,就需要想还原用户进程的页表,即将用户进程的页表地址写入 satp,之后恢复上下文,然后实验 sret 指令,硬件会自动完成以下操作:
[*]从 sepc 寄存器中取出要恢复的下一条指令地址,将其复制到程序计数器 pc 中,以恢复现场;
[*]从 sstatus 寄存器中取出用户模式的相关状态,包罗中断使能位、虚拟存储模式等,以恢复用户模式的状态;
[*]将当前特权模式设置为用户模式,即取消特权模式,回到用户模式。
S 切换到 M
S 切换到 M 与从 U 切换到 M 类似,都是从低特权级到高特权级的切换。在 S 运行的代码,也可以通过 ecall 指令陷入到 M 中。
[*]S-mode 的代码实验一个指令触发了异常或陷阱,例如环境调用(ECALL)指令
[*]处理器将当前的 S-mode 上下文的状态保存下来,包罗程序计数器 (PC)、S-mode 特权级别和其他相关寄存器,保存在当前特权级别堆栈中的 S-MODE 陷阱帧(trap frame,其实就是一个页面)中
[*]处理器通过将 mstatus 寄存器中的 MPP 字段设置为 0b11(表示先前的模式是 S 模式)将特权级别设置为 M-mode
[*]处理器将程序计数器设置为在 M-mode 中的陷阱处理程序例程的地址
[*]处理器还在 mstatus 寄存器中设置 M-mode 中断使能位 (MIE) 为 0,以在陷阱处理程序中禁用中断
地址空间结构
启用分页模式下,内核代码的访存地址也会被视为一个虚拟地址并需要颠末 MMU 的地址转换,因此我们也需要为内核对应构造一个地址空间,它除了仍然需要允许内核的各数据段能够被正常访问之后,还需要包罗全部应用的内核栈以及一个 跳板 (Trampoline) 。
使能了分页机制之后,我们必须在 trap 过程中同时完成地址空间的切换。具体来说,当 __alltraps 保存 Trap 上下文的时间,我们必须通过修改 satp 从应用地址空间切换到内核地址空间,因为 trap handler 只有在内核地址空间中才气访问;同理,在 __restore 恢复 Trap 上下文的时间,我们也必须从内核地址空间切换回应用地址空间,因为应用的代码和数据只能在它自己的地址空间中才气访问,应用是看不到内核地址空间的。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]