一、为什么需要区分用户级和特权级
用户级和特权级的实现是为了给存储器提供一种保护机制,让用户代码不能访问特定的存储区域。
只有特权级级代码才能有权限访问特定的存储区域。防止用户代码或者应用代码意外或恶意访问操作系统的数据存储区域。一般情况下特权级模式会配合MPU一起使用,通过MPU设定只有特权级才能访问的存储空间
二、模式详细介绍
以Cortex-M4为例,支持 2 个模式和两个权限等级。
特权级别 | 用户级别 | |
异常模式 | 特权级handler模式 | 不存在 |
线程模式 | 特权级线程模式 | 用户级线程模式 |
Handler模式通过异常/中断进入,退出即返回线程模式
handler模式总是特权级的,在复位后,处理器进入线程模式+特权级
当处理器处在线程状态下时,既可以使用特权级,也可以使用用户级;
NVIC的中断控制/状态寄存器都只能在特权级下访问。不过有一个例外——软件触发中断寄存器可以在用户级下访问以产生软件中断(利用这个特性实现用户模式到特权模式转变)。
三、模式切换
CONTROL 寄存器在特权可写,在非特权状态下只读
库写接口:void __set_CONTROL(uint32_t control)
库读接口: uint32_t __get_CONTROL(void)
- Control寄存器:选择栈指针,线程模式的访问等级,是否使用浮点单元。该寄存器可以用来区分特权模式和非特权模式。回复最初的问题了。
位域 | 功能 |
---|---|
nPRIV | 定义线程模式下的特权级别:0:特权模式;1:非特权模式 |
SPSEL | 定义当前使用的哪个栈指针;0:MSP为当前栈指针;1:PSP为当前栈指针。 |
FPCA | 指示当前是否为浮点指针执行,0:非浮点单元运行。1:浮点单元运行。 |
1、特权级变更为用户级的方式
在特权级下的代码可以通过置位 CONTROL[0]来进入用户级。用户级下的代码不能再试图修改 CONTROL[0]来回到特权级。
2、用户级变更为特权级的方式
①不管是任何原因产生了任何异常/中断,处理器都将以handler特权级来运行其服务函数,异常返回后将回到产生异常之前的级别。
②用户级下的代码必须通过一个异常 handler,在异常 handler服务函数中修改 CONTROL[0],才能在返回到线程模式后拿到特权级。
如下图所示,不同级别和操作模式下的转换,寄存器操作可以查询《Cortex-M4的相关寄存器组》。
3、代码示例
void Reg_swtichToPriv(void)
{
__ASM
{
SVC 1;
}
}
void Reg_swtichToUser(void)
{
__ASM
{
mrs r0,control;
orrs r0,r0,#1;
msr control,r0;
//__set_CONTROL(1);
}
}
void SVC_Handler(void)
{
__ASM
{
mrs r0,control;
bics r0,r0,#1;
msr control,r0;
//__set_CONTROL(0);
}
}