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

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

[复制链接]
STMCU小助手 发布时间:2022-3-5 22:20
前言
1 W' Y* b# v9 b! C$ @STM32 系列支持 MPU 内存保护单元,可用来设定内存的属性和访问权限。MPU 的应用笔记提到,将属性寄存器(MPU_RASR)配置成某一个值,在特权(Privileged permissions)和用户模式(Unprivileged permissions)的访问许可是不同的,甚至可将用户模式的权限设置成不可访问。那么,什么是 MPU 的特权模式和用户模式呢? 接下来我们在这篇文章来理解这些名词,并讨论在 STM32 MCU 代码中如何使用内存保护单元 MPU 的特权与用户模式。
( ~9 V% j/ A( q8 {3 ~
5 k) B7 i: B# D2 x$ T. zMPU(Memory Protection Unit)" M* f" |1 r5 d
MPU 内存保护单元可以用来使嵌入式系统更加健壮与安全。它可以阻止用户应用程序破坏系统关键数据。它可以通过将内存SRAM 区域定义成不可执行,来阻止代码注入型攻击。也可以用来改变内存的性质,例如是否允许缓存(Cache)。用来设置内存属性的 MPU_RASR 寄存器字段描述如下:3 R1 g% z& S5 M6 k5 \; @( T
* {; F' t, [7 y- G
0N43L2IN%{V}6AR]3VTD([G.png
2 A  t- F2 r, _0 W$ ~2 X7 q
, k' b8 f* A2 `% `1 [9 M2 ~: W在 MPU_RASR 中用来设置数据访问许可的 AP 字段详细设置选项如下:! {; ], M$ C2 N. q# w9 f

2 D+ T8 s; R8 T  n) N 1`F4[{V49P]Q%6(W(N704FU.png
5 V( V, o, r9 u/ S1 e8 P
$ O7 F9 ^1 R, b  `+ j5 b$ s特权模式(Privileged mode)与用户模式(UnPrevileged mode)

% N) a5 ^0 {3 A; x) w7 v特权模式与用户模式是指 Cortex 内核的执行模式。它不是 MPU 的一部分,但与 MPU 单元有关联。当代码运行在特权模式下,代码拥有所有的访问许可;而代码运行在用户模式,则访问权限受限制。限制包括在系统设计阶段就定义的可运行指令限制,可访问内存以及外设限制。也包括由 MPU 单元动态所定义的内存访问规则。. B5 F- i3 X) ~# D
从特权模式进入用户模式,只需使用 MSR 指令操作 CONTROL 寄存器。但是,不可以直接从用户模式转入特权模式,必须经过一个异常处理操作模式,比如 SVC( Supervisor Calls)。在异常处理通过操作 CONTROL 寄存器,可从用户模式回到特权模式。请注意,特权线程与异常处理线程,都是在特权模式下运行。7 Q1 ]5 ~! Y3 i+ G, M! m1 Z! M
如下图所示:
$ w8 u7 q, ]3 A/ k7 Y2 y3 `8 T( t7 \8 B  ?/ T/ y: L
UP0([`8KJ%Q$V0HJJ2P2E.png 7 H5 n9 t& z1 H

" |: d; C7 N: p4 \在代码中结合特权与用户模式使用 MPU: u5 \9 d6 c" }; S( R2 N/ [! |
1. 开发环境5 \% D( \- p  G) e: C5 h/ ^
   开发板: STM32 L476RG NUCLEO
, {7 }" R' [% i& ^1 C4 M& `   开发工具:STM32Cube_FW_L4_V1.7.0
8 c% v* q. N5 T& f                   IAR/Keil
; f! D9 j4 f& C6 |  N6 \4 m9 V   注:也可以选择其他 STM32 系列并选择相应的开发板与固件库。3 d7 u! D) @8 D1 N. p& H
2. 开发目标定义6 q' ?' O( A4 |+ U
   在软件中定义一块数组,使用 MPU 将该区域配成仅可从特权模式下进行访问,并验证用户模式下访问会导致内存管理异常或者硬错误。
' K' M, j3 \8 q7 |7 p" a4 b; s1 \% O  S: p+ z2 l. l9 F0 J
PJN{JNB)AF0EJ44$U6~E0SO.png
% W0 ~2 `7 c' W- \& ~0 J, j( n
& `# k  y5 j/ c9 V( U  _4 b' I3. 代码资源与整合4 x8 i: z- K, {* S! w9 V+ N
   STM32 Cube 固件库提供了大量的例程供学习和重用。查找 STM32Cube_FW_L4_V1.6.0 可以发现,固件库已分别实现了特权模式与用户模式的切换(CORTEXM_ModePrivilege),和 MPU 的配置(CORTEXM_MPU)。+ S0 L2 L/ d8 v) X4 d

% t1 D7 n0 G9 f! k- D VN)ZWQ]RY@)FNA538KROHE9.png % o4 f& R" f4 c
1 b1 l  o: X" F1 s; J0 \8 T: L
   我们需要将两个例程整合在一起来实现我们的开发目标。注意你选择其他 STM32 系列也是没有问题的,也支持 MPU 功能。复制 CORTEXM_MPU\Src\stm32_mpu.c 到 CORTEXM_ModePrivilege\Src 目录下
. {  j6 @% I) O  e4 ~: L, I: x! D- A0 L
QLX]ECL8~OO[Z(AB`O4X7`K.png ; W" p; A- e7 d  z8 K" j) ^' [
9 k1 c' J3 U; {$ ?
   复制 CORTEXM_MPU\Inc\stm32_mpu.h 到 CORTEXM_ModePrivilege\Inc 目录下( N; O# G0 b  H$ y/ |7 l" J& F# M
2 w' K) A' g" C) j6 ?- `: _) H; Q2 M* k% D
XBJO6[[4]_AAD3}3I}96A.png
, P6 ?0 t& t' h" m' p
# }( [1 j8 a1 k   打开 CORTEXM_ModePrivilege 工程文件,将 stm32_mpu.c 和 stm32_mpu.h 添加到工程文件中。IAR 与 Keil 略有不同,在 IAR 下只需添加 C 文件。, ?6 y% `4 G3 B7 {6 p" g# y& C
4. 修改与增加代码
2 \# c. W% R6 G2 z" f   新增加的代码以红色表示
4 t) H- @0 k6 x  E9 g3 [$ n    在主程序中包含 stm32_mpu.h% t# T4 E  F2 u$ J9 s7 q% F" w. K
  1.    /* Includes ------------------------------------------------------------------*/# H1 {# \' [4 }
  2.    #include "main.h"
    . M4 z" k9 U! Y1 q7 h( T
  3.    #include "stm32_mpu.h" /*added*/0 ^4 A/ _) F, h
  4.    /** @addtogroup STM32L4xx_HAL_Examples
    & o/ Y: M% ]( H3 S$ f3 ~
  5. ; W/ W; H: L2 {# ]. W
  6. * @{
    7 q) g' B* b3 R& F
  7.   */, w" q  S7 e5 G  _
  8.   /** @addtogroup CORTEXM_ModePrivilege0 j" a$ H5 Y& \) @4 o
  9. * @{4 v" p* \. M; I( }
  10.   */
复制代码
" L9 d3 l4 [- y) [  R' N
   在主程序里调用 MPU 配置函数' V6 V" \( C! A, W, k
  要对使用的内存进行 MPU 配置,未被 MPU 配置的资源将视之为不可访问。这是 MPU_Config 必须被使用的原因。7 U1 e5 K* `/ V9 q5 O
  1.   /* Get the Thread mode stack used */
    " [7 A% X' K& e0 ]6 K# J" r
  2.   if((__get_CONTROL() & 0x02) == SP_MAIN)
    / _2 x( q+ X  y! k# l  a3 g
  3.   {
    ) }: {4 O/ C/ G; T+ J, i& m3 e
  4.   /* Main stack is used as the current stack */
    " {* q: I3 j) k6 S  x
  5.   CurrentStack = SP_MAIN;" k7 a; @& k9 u) D( i% U
  6.   }
    1 \8 n4 ]" |: L  W5 K/ n$ Y$ b
  7.   else
    * V( m$ m7 U1 L4 l, Y& p
  8.   {9 \7 C9 |2 t) ?" X' [
  9.   /* Process stack is used as the current stack */3 W; s  {0 a: k- k1 T
  10.   CurrentStack = SP_PROCESS;5 I: g6 W, B5 p' o; ?
  11.   /* Get process stack pointer value */
    1 K6 J; A& X2 F+ L0 h( ~
  12.   PSPValue = __get_PSP();( F5 u6 o6 D- f* b8 _
  13.   }6 i6 ]6 [3 ^- \! p# W% Q# x
  14.   MPU_Config(); /*added*/! @+ i8 E% c6 b, J* P
  15.   MPU_AccessPermConfig(); /*added*/
复制代码

3 H4 [; s0 f# d  y0 }! R   修改 MPU_AccessPermConfig 将数组区域配置成我们设计的权限并增加测试代码。- y  U/ D' I0 O) U
  MPU_AccessPermConfig 在特权模式下运行,故测试代码不会触发异常。
1 e5 v( P: h) V+ w  P7 Z
  1.   void MPU_AccessPermConfig(void)
    1 \6 b  t- X0 p. }* X6 \
  2.   {9 x2 `: [. ^3 G, _% ?& E
  3.   MPU_Region_InitTypeDef MPU_InitStruct;6 y$ W6 f* ?$ f4 [6 ]' t
  4.   /* Configure region for PrivilegedReadOnlyArray as REGION N?, 32byte and R) ]5 |, q2 S0 {, a3 x* q+ O
  5.   only in privileged mode */$ u- {* [. |  c0 z, x" y* n  [
  6.   /* Disable MPU */
    + t5 Y+ A! B9 N  o, H* F$ e( ^
  7.   HAL_MPU_Disable();2 G& [/ e  F) R, L! R
  8.   MPU_InitStruct.Enable = MPU_REGION_ENABLE;
    * b& u7 ]+ N! i' |
  9.   MPU_InitStruct.BaseAddress = ARRAY_ADDRESS_START;
    7 V* S( S3 }4 w" X& e
  10.   MPU_InitStruct.Size = ARRAY_SIZE;
    4 O+ K; C0 m. S- \0 v" {
  11.   MPU_InitStruct.AccessPermission = portMPU_REGION_PRIVILEGED_READ_WRITE;( z' I6 w. Z" F  D1 J" R/ d
  12.   MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;! R) V4 Y& t( e
  13.   MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
    0 `5 A- O- {4 Q' c4 d
  14.   MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
    + [4 ?- u+ N& {8 @. P
  15.   MPU_InitStruct.Number = ARRAY_REGION_NUMBER;
    9 N9 ^/ G  C/ I4 e" r# t3 P
  16.   MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;$ t# D' M4 m; A7 d8 C# Q" e
  17.   MPU_InitStruct.SubRegionDisable = 0x00;/ ~8 T& r1 i6 p
  18.   MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;% \1 m0 i' ~6 w) K& g

  19. 7 t6 |8 j9 M0 |# D0 S9 |+ W
  20. HAL_MPU_ConfigRegion(&MPU_InitStruct);+ m; f; C& W$ M- Y9 z2 Z/ a4 K

  21. + ]4 \  r! t$ o* w% ^/ ?$ K
  22. /* Enable MPU (any access not covered by any enabled region will cause a fault) */  I( c3 ~9 h( @4 w  I  c/ Q
  23. HAL_MPU_Enable(MPU_HFNMI_PRIVDEF_NONE);
      \; z. Q1 j" |, Y; z
  24. /* Read from PrivilegedReadOnlyArray. This will not generate error */0 W+ a& I7 M3 S5 V- E# W
  25. if(PrivilegedReadOnlyArray[0])
    9 |  ^8 n3 u2 p
  26. {
    . [  [7 M2 r8 k: d3 j9 h* j. Z# k
  27. PrivilegedReadOnlyArray[0] = 'e';
    7 W9 ~' B* ?+ c/ O( @3 ]
  28. }$ W! i; u: @. _8 D
  29. /* Uncomment the following line to write to PrivilegedReadOnlyArray. This will
    % p& f3 m2 ?4 j# m$ R, h
  30. generate error *// w6 v7 C! H+ `- l  H3 B
  31. PrivilegedReadOnlyArray[0] = 'e';) p/ U: n5 @: E- l( ]4 R
  32. }
复制代码

/ [3 P) \3 d& ]8 z& {. e' M 在用户模式下尝试读写数组
% V) \- K1 {( C6 [+ @# U参考代码中直接访问数组,很容易被编译器优化掉而不能发挥测试作用。这里加了个判断后写的操作,使编译器不去优化它。
8 c; l9 p* M4 i. `3 K7 \
  1. /* Switch Thread mode from privileged to unprivileged ------------------------*/
    - _/ c' f" K7 j2 N: i
  2. /* Thread mode has unprivileged access */
    ( U- v  ?7 w' U- ]; C3 ~
  3. __set_CONTROL(THREAD_MODE_UNPRIVILEGED | SP_PROCESS);( K3 t% L- ]+ ^* F9 ^
  4. / Y% k; o0 I+ A9 c# u
  5. /* Execute ISB instruction to flush pipeline as recommended by Arm */
    , D4 d) L3 s8 p+ y0 O
  6. __ISB();
    " g6 \( [. j4 L! s
  7. /* Unprivileged access mainly affect ability to:. t% a; T* }# _( u/ U3 O
  8. 6 P6 }( Q  g. Y1 G+ }& R, S
  9. - Use or not use certain instructions such as MSR fields
    " o) v- q& K- |3 r9 L( R! D/ e
  10. - Access System Control Space (SCS) registers such as NVIC and SysTick */
    ) G' j4 |' `9 d( K# j
  11.   /* Check Thread mode privilege status */& i, J# r, w' Y8 {
  12.   if((__get_CONTROL() & 0x01) == THREAD_MODE_PRIVILEGED)" A8 Y( [- ]& Z$ e
  13.   {
    - j" F# M5 ?- B# `$ A/ L" o
  14.   /* Thread mode has privileged access */. R$ U0 i' o; w( Y
  15.   ThreadMode = THREAD_MODE_PRIVILEGED;
    % v. h, S8 J* k1 R) _
  16.   }
    5 @0 y0 c* j! a2 l
  17.   else3 l% w4 ^& Z* X" |1 @6 S- w8 Z
  18.   {7 G* H- S+ P" D' S
  19.   /* Thread mode has unprivileged access*/
    6 [/ ~) a( [4 H  c' k+ x6 H( J
  20.   ThreadMode = THREAD_MODE_UNPRIVILEGED;
    9 n' X. j: }. w
  21.   }
    4 P7 p3 ?6 V% T3 W/ J
  22.   if(PrivilegedReadOnlyArray[0])
    9 I' w" P6 D6 ^% F1 p4 B' i$ w
  23.   {
    ; g# O3 e5 l+ \: W
  24.   PrivilegedReadOnlyArray[0] = 'e';
    , P$ ~) ]) l9 C. F$ {
  25.   }; I. A" T+ O! l- x  g8 ?9 X: `
  26.   PrivilegedReadOnlyArray[0] = 'e';
复制代码

  x7 T/ N! ~# H- j, a% X  至此,可以编译成功运行来体会特权模式与用户模式的 MPU 的不同配置。执行到主程序中新加的数组访问代码,会产生内存" ~$ O  t+ x* c$ K5 Y2 D
  管理异常。- ]  S  l6 v& U: l5 S, e
/ f; R, ^% ]' |' n! J
5. 关键代码分析
4 d& r+ `' z. P) z; I& T$ H    从特权模式进入用户模式。直接设置 CONTROL 寄存器。& G9 D$ C/ m0 e
  1.    /* Switch Thread mode from privileged to unprivileged ------------------------*/
    ' V; d0 L$ t8 h# R
  2.    /* Thread mode has unprivileged access */
    & w8 L' Z9 N0 [4 o7 y
  3.    __set_CONTROL(THREAD_MODE_UNPRIVILEGED | SP_PROCESS);
复制代码
  q, B0 i& ~0 f- x
    从用户模式回到特权模式,需要触发异常处理
' Y2 c  ?4 Q6 p7 R
  1.    /* Generate a system call exception, and in the ISR switch back Thread mode
    ; c$ B. O) I/ k. ~" m& L! |# n
  2.    to privileged */. H8 \, f2 N4 O' n
  3.    __SVC();
复制代码
. M# y3 k- {2 ^
    在异常处理中将线程回到特权模式执行
) a& |5 {5 R' B
  1.    /**
    / C2 W) `0 K; m) k; B2 I
  2. * @brief This function handles SVCall exception." m/ N. O7 ~- S/ }+ \0 W9 d- l7 h( {
  3. * @param None- G& W% l! f8 E" j, {6 S  Z
  4. * @retval None( [0 ~2 }( E1 L! \
  5.   */% H. U9 ]" u. @3 e5 F
  6.   void SVC_Handler(void)
    & _# F& p* w1 V
  7.   {
    0 }) J7 P4 |  U1 j! C
  8.   /* Switch back Thread mode to privileged */4 y5 F, D9 X0 z
  9.   __set_CONTROL(THREAD_MODE_PRIVILEGED | SP_PROCESS);% z$ o0 b$ g7 d8 Q2 o! l; D
  10. ) S: C! M& c* \' z8 [  Q
  11. /* Execute ISB instruction to flush pipeline as recommended by Arm */4 s! I7 C- L, G; E$ n) `' U1 D
  12. __ISB();
    & U- t8 R& p6 E
  13. }
复制代码

' d1 k. j! f+ A5 Y  [, T 若用户模式下代码试图访问无权限数据将产生内存管理异常,从而进入下述函数。
2 F0 u8 k, Q$ I6 q  s" s% W# ]' O
  1. /**
    : r/ r# v; n( J% b
  2. * @brief This function handles Memory Manage exception.6 Y7 z& n3 |: r; t& \
  3. * @param None* O! ]& z2 H4 V+ F5 y* f
  4. * @retval None7 s5 m9 G4 Z% o# l5 w
  5.   */; o4 X5 ^& ]2 h% c+ B1 r5 R
  6.   void MemManage_Handler(void)
    3 q) o5 \+ a0 {, ^* Z
  7.   {$ j- c6 l2 g7 u! L! ~; ^
  8.   /* Go to infinite loop when Memory Manage exception occurs */
    2 ^8 b& G) I0 m: N- C- t; D9 o
  9.   while (1)
    ! }1 T$ @2 t1 N
  10.   {
    9 r# h: t" R, j6 w3 ^
  11.   }
    7 r# c# v0 I' d7 s
  12.   }
复制代码
( b8 p( Q0 q8 s
结论
+ z8 {0 T; F5 s) P8 Q! }本文档介绍了 MPU 的特权与用户模式,并整合与修改已有代码进行了应用。在实际中可将 MPU 与 Cortex 运行模式结合,可使应用更加健壮与安全。
+ r/ y4 H, |' [9 r8 O0 B' \6 Y, @0 f* U$ B

7 F& h2 G# f) `- p6 V5 e
赞 收藏 评论0 发布时间:2022-3-5 22:20

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版