写在前面
本随笔是非常菜的菜鸡写的。如有问题请及时提出。
可以联系:[email protected]
GitHhub:https://github.com/WindDevil (目前啥也没有
官方文档
仍然是一上来就丢出来的官方文档.
只摘抄了我觉得有意思的部分:
实现特权级机制的根本原因是应用程序运行的安全性不可充分信任。
由于二者通过编译器形成一个单一执行程序来执行,导致即使是应用程序本身的问题,也会让操作系统受到连累,从而可能导致整个计算机系统都不可用了。
包括之前做MCU
开发的时候,实际上都是这样的,也很难理解为什么可以做到应用是应用,操作系统是操作系统,之前使用RT-Thread
做MCU
开发的过程中,如果出现某一个线程出现问题, 貌似 还是会出现整体的问题.包括能不能分别编译,能不能实现安装与卸载都是我对操作系统(Linux,windows)的刻板印象.
解决问题的方法:
应用程序不能访问任意的地址空间(这个在第四章会进一步讲解,本章不会涉及)
应用程序不能执行某些可能破坏计算机系统的指令(本章的重点)
有一部分指令不能在应用程序中执行,就可以保证操作系统出现问题吗?怎么体现呢?体现之后,那怎么实现应用程序的功能呢?
具体实现指令分级的方法:
处理器设置两个不同安全等级的执行环境:用户态特权级的执行环境和内核态特权级的执行环境。且明确指出可能破坏计算机系统的内核态特权级指令子集,规定内核态特权级指令子集中的指令只能在内核态特权级的执行环境中执行。处理器在执行指令前会进行特权级安全检查,如果在用户态执行环境中执行这些内核态特权级指令,会产生异常。
为了让应用程序获得操作系统的函数服务,采用传统的函数调用方式(即通常的 call
和 ret
指令或指令组合)将会直接绕过硬件的特权级保护检查。为了解决这个问题, RISC-V 提供了新的机器指令:执行环境调用指令(Execution Environment Call,简称 ecall
)和一类执行环境返回(Execution Environment Return,简称 eret
)指令。
操作系统要做什么:
硬件具有了这样的机制后,还需要操作系统的配合才能最终完成对操作系统自身的保护。首先,操作系统需要提供相应的功能代码,能在执行 sret
前准备和恢复用户态执行应用程序的上下文。其次,在应用程序调用 ecall
指令后,能够检查应用程序的系统调用参数,确保参数不会破坏操作系统。
这张图就很明了了,把原来的执行环境栈从特权级的架构去看待,那么现在只需要在SBI
和ABI
两部分做编程实现上述功能就可以了:
这里也是比较重要的一点:
执行环境的另一种功能是对上层软件的执行进行监控管理。监控管理可以理解为,当上层软件执行的时候出现了一些异常或特殊情况,导致需要用到执行环境中提供的功能,因此需要暂停上层软件的执行,转而运行执行环境的代码。
把一部分代码放在操作系统里,这样就实现了ABI
,每次要执行为好到操作系统的操作的时候都需要操作系统亲自执行,这样如果出现错误,那么一些错误就会被阻止.
因为M
模式的内容是在SBI
里边的,所以这一部分的内容专注于S
模式和U
模式的特权级切换.
可以看到,为了执行一部分需要在内核中使用的功能的时候就需要进行特权级切换,这里官方文档和常规控制流进行对比,把入校流程称为 异常控制流
- 上层软件执行过程中出现了一些异常或 特殊情况 , 需要用到执行环境中提供的功能
- 这里可以看到虽然都叫做 异常 但是实际上有一部分情况是特殊情况需要使用执行环境中的功能,不能非黑即白地把 异常 理解为 坏的
- 用户态应用直接触发从用户态到内核态的异常的原因总体上可以分为两种
- 其一是用户态软件为获得内核态操作系统的服务功能而执行特殊指令
- 指令本身属于高特权级的指令,如
sret
指令(表示从 S 模式返回到 U 模式) - 指令访问了 S模式特权级下才能访问的寄存器 或内存,如表示S模式系统状态的 控制状态寄存器
sstatus
等
- 指令本身属于高特权级的指令,如
- 其二是在执行某条指令期间产生了错误(如执行了用户态不允许执行的指令或者其他错误)并被 CPU 检测到
- 其一是用户态软件为获得内核态操作系统的服务功能而执行特殊指令
- 暂停上层软件的功能,转而运行执行环境的代码 (伴随 特权级切换 )
- 回到上层软件暂停的位置继续执行
配合下图食用更加: