你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】理解与应用 MPU 的特权与用户模式

[复制链接]
STMCU小助手 发布时间:2022-3-5 22:20
前言
STM32 系列支持 MPU 内存保护单元,可用来设定内存的属性和访问权限。MPU 的应用笔记提到,将属性寄存器(MPU_RASR)配置成某一个值,在特权(Privileged permissions)和用户模式(Unprivileged permissions)的访问许可是不同的,甚至可将用户模式的权限设置成不可访问。那么,什么是 MPU 的特权模式和用户模式呢? 接下来我们在这篇文章来理解这些名词,并讨论在 STM32 MCU 代码中如何使用内存保护单元 MPU 的特权与用户模式。

MPU(Memory Protection Unit)
MPU 内存保护单元可以用来使嵌入式系统更加健壮与安全。它可以阻止用户应用程序破坏系统关键数据。它可以通过将内存SRAM 区域定义成不可执行,来阻止代码注入型攻击。也可以用来改变内存的性质,例如是否允许缓存(Cache)。用来设置内存属性的 MPU_RASR 寄存器字段描述如下:

0N43L2IN%{V}6AR]3VTD([G.png

在 MPU_RASR 中用来设置数据访问许可的 AP 字段详细设置选项如下:

1`F4[{V49P]Q%6(W(N704FU.png

特权模式(Privileged mode)与用户模式(UnPrevileged mode)

特权模式与用户模式是指 Cortex 内核的执行模式。它不是 MPU 的一部分,但与 MPU 单元有关联。当代码运行在特权模式下,代码拥有所有的访问许可;而代码运行在用户模式,则访问权限受限制。限制包括在系统设计阶段就定义的可运行指令限制,可访问内存以及外设限制。也包括由 MPU 单元动态所定义的内存访问规则。
从特权模式进入用户模式,只需使用 MSR 指令操作 CONTROL 寄存器。但是,不可以直接从用户模式转入特权模式,必须经过一个异常处理操作模式,比如 SVC( Supervisor Calls)。在异常处理通过操作 CONTROL 寄存器,可从用户模式回到特权模式。请注意,特权线程与异常处理线程,都是在特权模式下运行。
如下图所示:

UP0([`8KJ%Q$V0HJJ2P2E.png

在代码中结合特权与用户模式使用 MPU
1. 开发环境
   开发板: STM32 L476RG NUCLEO
   开发工具:STM32Cube_FW_L4_V1.7.0
                   IAR/Keil
   注:也可以选择其他 STM32 系列并选择相应的开发板与固件库。
2. 开发目标定义
   在软件中定义一块数组,使用 MPU 将该区域配成仅可从特权模式下进行访问,并验证用户模式下访问会导致内存管理异常或者硬错误。

PJN{JNB)AF0EJ44$U6~E0SO.png

3. 代码资源与整合
   STM32 Cube 固件库提供了大量的例程供学习和重用。查找 STM32Cube_FW_L4_V1.6.0 可以发现,固件库已分别实现了特权模式与用户模式的切换(CORTEXM_ModePrivilege),和 MPU 的配置(CORTEXM_MPU)。

VN)ZWQ]RY@)FNA538KROHE9.png

   我们需要将两个例程整合在一起来实现我们的开发目标。注意你选择其他 STM32 系列也是没有问题的,也支持 MPU 功能。复制 CORTEXM_MPU\Src\stm32_mpu.c 到 CORTEXM_ModePrivilege\Src 目录下

QLX]ECL8~OO[Z(AB`O4X7`K.png

   复制 CORTEXM_MPU\Inc\stm32_mpu.h 到 CORTEXM_ModePrivilege\Inc 目录下

XBJO6[[4]_AAD3}3I}96A.png

   打开 CORTEXM_ModePrivilege 工程文件,将 stm32_mpu.c 和 stm32_mpu.h 添加到工程文件中。IAR 与 Keil 略有不同,在 IAR 下只需添加 C 文件。
4. 修改与增加代码
   新增加的代码以红色表示
    在主程序中包含 stm32_mpu.h
  1.    /* Includes ------------------------------------------------------------------*/
  2.    #include "main.h"
  3.    #include "stm32_mpu.h" /*added*/
  4.    /** @addtogroup STM32L4xx_HAL_Examples

  5. * @{
  6.   */
  7.   /** @addtogroup CORTEXM_ModePrivilege
  8. * @{
  9.   */
复制代码

   在主程序里调用 MPU 配置函数
  要对使用的内存进行 MPU 配置,未被 MPU 配置的资源将视之为不可访问。这是 MPU_Config 必须被使用的原因。
  1.   /* Get the Thread mode stack used */
  2.   if((__get_CONTROL() & 0x02) == SP_MAIN)
  3.   {
  4.   /* Main stack is used as the current stack */
  5.   CurrentStack = SP_MAIN;
  6.   }
  7.   else
  8.   {
  9.   /* Process stack is used as the current stack */
  10.   CurrentStack = SP_PROCESS;
  11.   /* Get process stack pointer value */
  12.   PSPValue = __get_PSP();
  13.   }
  14.   MPU_Config(); /*added*/
  15.   MPU_AccessPermConfig(); /*added*/
复制代码

   修改 MPU_AccessPermConfig 将数组区域配置成我们设计的权限并增加测试代码。
  MPU_AccessPermConfig 在特权模式下运行,故测试代码不会触发异常。
  1.   void MPU_AccessPermConfig(void)
  2.   {
  3.   MPU_Region_InitTypeDef MPU_InitStruct;
  4.   /* Configure region for PrivilegedReadOnlyArray as REGION N?, 32byte and R
  5.   only in privileged mode */
  6.   /* Disable MPU */
  7.   HAL_MPU_Disable();
  8.   MPU_InitStruct.Enable = MPU_REGION_ENABLE;
  9.   MPU_InitStruct.BaseAddress = ARRAY_ADDRESS_START;
  10.   MPU_InitStruct.Size = ARRAY_SIZE;
  11.   MPU_InitStruct.AccessPermission = portMPU_REGION_PRIVILEGED_READ_WRITE;
  12.   MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
  13.   MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
  14.   MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
  15.   MPU_InitStruct.Number = ARRAY_REGION_NUMBER;
  16.   MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
  17.   MPU_InitStruct.SubRegionDisable = 0x00;
  18.   MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;

  19. HAL_MPU_ConfigRegion(&MPU_InitStruct);

  20. /* Enable MPU (any access not covered by any enabled region will cause a fault) */
  21. HAL_MPU_Enable(MPU_HFNMI_PRIVDEF_NONE);
  22. /* Read from PrivilegedReadOnlyArray. This will not generate error */
  23. if(PrivilegedReadOnlyArray[0])
  24. {
  25. PrivilegedReadOnlyArray[0] = 'e';
  26. }
  27. /* Uncomment the following line to write to PrivilegedReadOnlyArray. This will
  28. generate error */
  29. PrivilegedReadOnlyArray[0] = 'e';
  30. }
复制代码

 在用户模式下尝试读写数组
参考代码中直接访问数组,很容易被编译器优化掉而不能发挥测试作用。这里加了个判断后写的操作,使编译器不去优化它。
  1. /* Switch Thread mode from privileged to unprivileged ------------------------*/
  2. /* Thread mode has unprivileged access */
  3. __set_CONTROL(THREAD_MODE_UNPRIVILEGED | SP_PROCESS);

  4. /* Execute ISB instruction to flush pipeline as recommended by Arm */
  5. __ISB();
  6. /* Unprivileged access mainly affect ability to:

  7. - Use or not use certain instructions such as MSR fields
  8. - Access System Control Space (SCS) registers such as NVIC and SysTick */
  9.   /* Check Thread mode privilege status */
  10.   if((__get_CONTROL() & 0x01) == THREAD_MODE_PRIVILEGED)
  11.   {
  12.   /* Thread mode has privileged access */
  13.   ThreadMode = THREAD_MODE_PRIVILEGED;
  14.   }
  15.   else
  16.   {
  17.   /* Thread mode has unprivileged access*/
  18.   ThreadMode = THREAD_MODE_UNPRIVILEGED;
  19.   }
  20.   if(PrivilegedReadOnlyArray[0])
  21.   {
  22.   PrivilegedReadOnlyArray[0] = 'e';
  23.   }
  24.   PrivilegedReadOnlyArray[0] = 'e';
复制代码

  至此,可以编译成功运行来体会特权模式与用户模式的 MPU 的不同配置。执行到主程序中新加的数组访问代码,会产生内存
  管理异常。

5. 关键代码分析
    从特权模式进入用户模式。直接设置 CONTROL 寄存器。
  1.    /* Switch Thread mode from privileged to unprivileged ------------------------*/
  2.    /* Thread mode has unprivileged access */
  3.    __set_CONTROL(THREAD_MODE_UNPRIVILEGED | SP_PROCESS);
复制代码

    从用户模式回到特权模式,需要触发异常处理
  1.    /* Generate a system call exception, and in the ISR switch back Thread mode
  2.    to privileged */
  3.    __SVC();
复制代码

    在异常处理中将线程回到特权模式执行
  1.    /**
  2. * @brief This function handles SVCall exception.
  3. * @param None
  4. * @retval None
  5.   */
  6.   void SVC_Handler(void)
  7.   {
  8.   /* Switch back Thread mode to privileged */
  9.   __set_CONTROL(THREAD_MODE_PRIVILEGED | SP_PROCESS);

  10. /* Execute ISB instruction to flush pipeline as recommended by Arm */
  11. __ISB();
  12. }
复制代码

 若用户模式下代码试图访问无权限数据将产生内存管理异常,从而进入下述函数。
  1. /**
  2. * @brief This function handles Memory Manage exception.
  3. * @param None
  4. * @retval None
  5.   */
  6.   void MemManage_Handler(void)
  7.   {
  8.   /* Go to infinite loop when Memory Manage exception occurs */
  9.   while (1)
  10.   {
  11.   }
  12.   }
复制代码

结论
本文档介绍了 MPU 的特权与用户模式,并整合与修改已有代码进行了应用。在实际中可将 MPU 与 Cortex 运行模式结合,可使应用更加健壮与安全。


收藏 评论0 发布时间:2022-3-5 22:20

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版