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

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

[复制链接]
STMCU小助手 发布时间:2022-3-5 22:20
前言
1 B3 S. F* _0 i. S. w4 cSTM32 系列支持 MPU 内存保护单元,可用来设定内存的属性和访问权限。MPU 的应用笔记提到,将属性寄存器(MPU_RASR)配置成某一个值,在特权(Privileged permissions)和用户模式(Unprivileged permissions)的访问许可是不同的,甚至可将用户模式的权限设置成不可访问。那么,什么是 MPU 的特权模式和用户模式呢? 接下来我们在这篇文章来理解这些名词,并讨论在 STM32 MCU 代码中如何使用内存保护单元 MPU 的特权与用户模式。0 t3 Z5 R6 K. {* |. C! Y
/ l* I; [" I% B
MPU(Memory Protection Unit)/ B/ U0 y/ m- r/ k
MPU 内存保护单元可以用来使嵌入式系统更加健壮与安全。它可以阻止用户应用程序破坏系统关键数据。它可以通过将内存SRAM 区域定义成不可执行,来阻止代码注入型攻击。也可以用来改变内存的性质,例如是否允许缓存(Cache)。用来设置内存属性的 MPU_RASR 寄存器字段描述如下:
3 n. E3 h* x  [/ v. A1 v6 ]3 M4 t! `/ U3 b4 |) i& _
0N43L2IN%{V}6AR]3VTD([G.png
8 E  G/ \8 d5 V( A1 S2 _' P' @, G' c+ _1 X
在 MPU_RASR 中用来设置数据访问许可的 AP 字段详细设置选项如下:1 A- x: X1 Q, ~+ i* [  c( \  Q, ^  \

6 b. G1 p3 A& N 1`F4[{V49P]Q%6(W(N704FU.png
, p0 }& v: M- t
) ]5 e  ]0 w( F9 X: N% e+ X9 @特权模式(Privileged mode)与用户模式(UnPrevileged mode)
+ Y: {+ M# F& i5 b; g! C
特权模式与用户模式是指 Cortex 内核的执行模式。它不是 MPU 的一部分,但与 MPU 单元有关联。当代码运行在特权模式下,代码拥有所有的访问许可;而代码运行在用户模式,则访问权限受限制。限制包括在系统设计阶段就定义的可运行指令限制,可访问内存以及外设限制。也包括由 MPU 单元动态所定义的内存访问规则。
: p8 b$ Q3 f3 T" n/ W, E从特权模式进入用户模式,只需使用 MSR 指令操作 CONTROL 寄存器。但是,不可以直接从用户模式转入特权模式,必须经过一个异常处理操作模式,比如 SVC( Supervisor Calls)。在异常处理通过操作 CONTROL 寄存器,可从用户模式回到特权模式。请注意,特权线程与异常处理线程,都是在特权模式下运行。6 {: r; \2 Y& W$ n# P' [0 w. r
如下图所示:
& v8 x6 l+ w, y
2 S8 k! P% |  k) ` UP0([`8KJ%Q$V0HJJ2P2E.png
+ C2 F" H- V3 W6 }7 L, {3 H/ _3 [3 X& `; H1 D
在代码中结合特权与用户模式使用 MPU. Y9 m) x" k  a) A
1. 开发环境
& g: g( T& r6 P' O# y, t, [* y) Q   开发板: STM32 L476RG NUCLEO
6 n3 g1 ^; Z+ D) Z& X& v. H   开发工具:STM32Cube_FW_L4_V1.7.0
/ X+ O2 x1 S  T  @  E- b# j                   IAR/Keil
, p* C' K# D/ q+ `' }4 s2 }" O   注:也可以选择其他 STM32 系列并选择相应的开发板与固件库。
+ y; w& O% P9 }9 y  i; r+ x7 O2. 开发目标定义/ {5 F2 O, B6 ~5 B9 H
   在软件中定义一块数组,使用 MPU 将该区域配成仅可从特权模式下进行访问,并验证用户模式下访问会导致内存管理异常或者硬错误。
8 E1 Q9 Z* G( z, {- B" J  H+ h2 `; _, t+ `7 A
PJN{JNB)AF0EJ44$U6~E0SO.png ! j0 E; R  v. b8 V; H' [
5 n! U% {- [+ W+ d& y) o
3. 代码资源与整合1 @7 [6 k% W1 f
   STM32 Cube 固件库提供了大量的例程供学习和重用。查找 STM32Cube_FW_L4_V1.6.0 可以发现,固件库已分别实现了特权模式与用户模式的切换(CORTEXM_ModePrivilege),和 MPU 的配置(CORTEXM_MPU)。/ j+ [* V9 C& D2 u

+ |! U+ G, Z" w  x2 `& p VN)ZWQ]RY@)FNA538KROHE9.png . H9 W6 V' q/ Q* O: G  z
9 I% W+ O# C- k: g
   我们需要将两个例程整合在一起来实现我们的开发目标。注意你选择其他 STM32 系列也是没有问题的,也支持 MPU 功能。复制 CORTEXM_MPU\Src\stm32_mpu.c 到 CORTEXM_ModePrivilege\Src 目录下
% g' T/ ^3 g  |
% {2 b% R& I. A5 G5 V QLX]ECL8~OO[Z(AB`O4X7`K.png
' N5 s' y5 ?  G) Z$ Q0 `; K
6 d. `! N: l( c; b, Z5 w0 @   复制 CORTEXM_MPU\Inc\stm32_mpu.h 到 CORTEXM_ModePrivilege\Inc 目录下( F) S, y% c0 k; D; l3 z

# B* Y1 J0 v) `5 C" V# N& Q( x6 d1 U. q XBJO6[[4]_AAD3}3I}96A.png
& ]; d+ p8 P* R: A0 N5 J. s
% K! B( W/ `6 H- E   打开 CORTEXM_ModePrivilege 工程文件,将 stm32_mpu.c 和 stm32_mpu.h 添加到工程文件中。IAR 与 Keil 略有不同,在 IAR 下只需添加 C 文件。" `% F& l8 o8 X. W  D
4. 修改与增加代码3 f# {/ p: g" v( }- {: d- q6 n
   新增加的代码以红色表示
7 B+ [! p$ z3 o5 Z7 ?" Z    在主程序中包含 stm32_mpu.h
# M8 q& _6 U$ m; k
  1.    /* Includes ------------------------------------------------------------------*/2 W0 w9 F% C* K: z& V
  2.    #include "main.h"
    * b3 Z5 J! j0 b) T% p0 e
  3.    #include "stm32_mpu.h" /*added*/" H% S3 j! J3 U) K& d
  4.    /** @addtogroup STM32L4xx_HAL_Examples
    , }5 f5 k- j* g  ~8 E
  5. 0 q: w: B4 E% h
  6. * @{/ y6 h9 v! O7 T4 `, y* `% }
  7.   */; w: Z9 v% N* `" @
  8.   /** @addtogroup CORTEXM_ModePrivilege8 Z, F, E+ s; H9 P+ d4 T
  9. * @{
    7 |3 \6 I9 I6 V9 S8 n' j
  10.   */
复制代码
  @  ]7 n$ [- |
   在主程序里调用 MPU 配置函数
7 ~" q$ E* i* A; a1 i8 N% ^  要对使用的内存进行 MPU 配置,未被 MPU 配置的资源将视之为不可访问。这是 MPU_Config 必须被使用的原因。( R3 M2 B" C2 W2 Y9 ~
  1.   /* Get the Thread mode stack used */
    - v. ]: n: S/ x4 d8 c* t4 E; ?
  2.   if((__get_CONTROL() & 0x02) == SP_MAIN)
    - P: U/ f% S) X# R3 s3 z
  3.   {
    0 u) |) [- D7 q+ j3 F  u
  4.   /* Main stack is used as the current stack */
    2 q5 J! J; M( I; C
  5.   CurrentStack = SP_MAIN;
    , [  `) O, }; \3 x3 [
  6.   }
    & Z) R+ X5 f3 D' Y) D* [) A& s
  7.   else
    5 z5 y. N* A" g) r! b  `# ?# F
  8.   {: e! B/ l0 b' n4 l4 X
  9.   /* Process stack is used as the current stack */, _2 Z2 D7 j9 ]; b: Y# l+ O
  10.   CurrentStack = SP_PROCESS;
    ( E; b3 p# `- C+ D7 ]( Q
  11.   /* Get process stack pointer value */
    7 {* _6 r* ^3 w& f0 T2 o- N
  12.   PSPValue = __get_PSP();
    4 Y$ d% `& L9 U
  13.   }$ ]$ j: i& n) K3 d: c6 I8 Q
  14.   MPU_Config(); /*added*/
    6 p7 y% w! F& b9 w2 L
  15.   MPU_AccessPermConfig(); /*added*/
复制代码
7 O7 z; k7 j+ X, Q# y
   修改 MPU_AccessPermConfig 将数组区域配置成我们设计的权限并增加测试代码。0 o2 a: `7 D; H4 S* t
  MPU_AccessPermConfig 在特权模式下运行,故测试代码不会触发异常。; {1 M* a. T# _$ H8 |
  1.   void MPU_AccessPermConfig(void)0 Q  B3 S# {. |* M& V
  2.   {
    # W* M4 A! c4 e# y# i' [6 Q
  3.   MPU_Region_InitTypeDef MPU_InitStruct;7 j& e; m4 B2 z6 z' S
  4.   /* Configure region for PrivilegedReadOnlyArray as REGION N?, 32byte and R
    7 k4 p* V0 g) o3 p, q$ q- U# `$ E
  5.   only in privileged mode */
    3 K4 i0 k' \" D  h1 O
  6.   /* Disable MPU */
    ( e5 C- I* h, e. N  w
  7.   HAL_MPU_Disable();! M# y) K- I7 ?6 z- N4 F
  8.   MPU_InitStruct.Enable = MPU_REGION_ENABLE;- n6 H3 b8 ]. x7 i
  9.   MPU_InitStruct.BaseAddress = ARRAY_ADDRESS_START;; C! }& y7 A( i8 I# @
  10.   MPU_InitStruct.Size = ARRAY_SIZE;
    2 W  |2 i9 h* |* p3 ]4 X3 ?5 e  b( S1 w
  11.   MPU_InitStruct.AccessPermission = portMPU_REGION_PRIVILEGED_READ_WRITE;
    - Y6 _, P" d+ {
  12.   MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
    ; B6 o& U' ], K# E! `2 Y+ @: `
  13.   MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;' `; C, U# g; x9 J
  14.   MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
    - S# _( B$ j0 t9 C
  15.   MPU_InitStruct.Number = ARRAY_REGION_NUMBER;% [$ y1 N3 k/ t- N1 R" q5 h) w
  16.   MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
    6 O8 D6 `; [" A
  17.   MPU_InitStruct.SubRegionDisable = 0x00;
    & w7 T2 w  P  v2 v0 E; Y
  18.   MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;* M" l4 @" l* S: ]* Q! G
  19. 0 m" \1 t) w# \) R9 y. n) V8 a: x
  20. HAL_MPU_ConfigRegion(&MPU_InitStruct);5 }' D- V2 U# `8 F0 K
  21. 2 v0 Z: {' y0 c5 l& ]1 G1 T$ H
  22. /* Enable MPU (any access not covered by any enabled region will cause a fault) */! }( U$ M) V) c# K" }( W& J
  23. HAL_MPU_Enable(MPU_HFNMI_PRIVDEF_NONE);" E( Q, p% r* N% ^8 J! Q3 m: |% F2 Y4 z
  24. /* Read from PrivilegedReadOnlyArray. This will not generate error */  [* p" ~7 A9 t6 }- {! V8 B
  25. if(PrivilegedReadOnlyArray[0])8 c- t1 ~" F+ K: G5 I
  26. {2 U( T4 V6 j$ W7 F3 `( i
  27. PrivilegedReadOnlyArray[0] = 'e';7 g* m* ?+ k! q2 y) a
  28. }/ s* {9 ?# j6 v" g9 `  w# p
  29. /* Uncomment the following line to write to PrivilegedReadOnlyArray. This will; W5 f8 @' l- U, I4 I/ |
  30. generate error */9 F( s. C% h$ V
  31. PrivilegedReadOnlyArray[0] = 'e';
    " e2 d! L. u1 Q6 E# d' s& i# A& }
  32. }
复制代码

% c' k4 N9 s- u! r, F 在用户模式下尝试读写数组
  P9 O* k- q: D. b0 b; D/ P( X参考代码中直接访问数组,很容易被编译器优化掉而不能发挥测试作用。这里加了个判断后写的操作,使编译器不去优化它。
' G7 k+ I, x* O5 |
  1. /* Switch Thread mode from privileged to unprivileged ------------------------*/
    $ D6 r9 M' R/ Z- D5 v0 o
  2. /* Thread mode has unprivileged access */  k2 Z1 M% R* j, A
  3. __set_CONTROL(THREAD_MODE_UNPRIVILEGED | SP_PROCESS);
    9 t5 e# X3 ]$ |. e2 C

  4. " S0 \! A% h* [
  5. /* Execute ISB instruction to flush pipeline as recommended by Arm */
    & ?2 F0 E9 [, y( O- r
  6. __ISB();
    ! W" H& A5 _: m! A+ Y9 n+ I
  7. /* Unprivileged access mainly affect ability to:8 n% }! B" w1 v1 [+ A
  8. ) p) [& M$ K0 \( |8 N2 N6 s
  9. - Use or not use certain instructions such as MSR fields% T! \- d% E; L4 z
  10. - Access System Control Space (SCS) registers such as NVIC and SysTick */  F$ I% i( i7 G$ t- q. V
  11.   /* Check Thread mode privilege status */# b, a0 Y: r7 K
  12.   if((__get_CONTROL() & 0x01) == THREAD_MODE_PRIVILEGED)
    5 ^/ J+ b# C0 y8 P
  13.   {$ |5 e% Z" B+ S6 a5 S: N' U
  14.   /* Thread mode has privileged access */% p; s; D) X$ f/ L0 P
  15.   ThreadMode = THREAD_MODE_PRIVILEGED;
    : V- x8 T9 e& K( o1 V
  16.   }
    " K; s; r- g, q' q- Q( e6 R
  17.   else
    ) V# v9 R6 g/ Y
  18.   {
    1 {( ^- n% X( M* y( r/ ]4 q! g' |
  19.   /* Thread mode has unprivileged access*/' F! Y8 W% V$ c, Q8 n# ~0 V) s2 c
  20.   ThreadMode = THREAD_MODE_UNPRIVILEGED;
    0 \* f! w: l+ m$ R
  21.   }
    . f+ _  S; w' E4 G
  22.   if(PrivilegedReadOnlyArray[0])
      E+ W  V1 ~" Q  \' ?8 d
  23.   {! D3 F5 H4 I3 q3 B' K/ O1 h; p
  24.   PrivilegedReadOnlyArray[0] = 'e';
    4 v" w& X. ~' e, d7 e9 }
  25.   }
    + s( Q$ R& }8 V
  26.   PrivilegedReadOnlyArray[0] = 'e';
复制代码

1 @/ [+ n0 \! _3 i+ ^  至此,可以编译成功运行来体会特权模式与用户模式的 MPU 的不同配置。执行到主程序中新加的数组访问代码,会产生内存6 q7 D3 [5 s8 V) A( a1 P
  管理异常。
% X, E6 A! [# G" [# T8 L' Y0 d9 x# l/ n& @3 e
5. 关键代码分析" e  p0 K/ R5 b" J) y. ?) ]. S
    从特权模式进入用户模式。直接设置 CONTROL 寄存器。
! j" ?$ U: y% i# x+ h/ R9 O
  1.    /* Switch Thread mode from privileged to unprivileged ------------------------*/
    0 p$ }# V" P# `6 B# j1 r
  2.    /* Thread mode has unprivileged access */
      T# ?) n$ b7 a3 \- K6 ]+ A
  3.    __set_CONTROL(THREAD_MODE_UNPRIVILEGED | SP_PROCESS);
复制代码

9 v. b+ [+ K5 R' p& J1 R3 }2 F    从用户模式回到特权模式,需要触发异常处理- k% F& ]. I% n+ \, P! y* K
  1.    /* Generate a system call exception, and in the ISR switch back Thread mode
    + U1 G6 a: @+ Q' y0 b! ]& v. A
  2.    to privileged */1 q& F! t+ E) m+ T- z7 _0 z' k
  3.    __SVC();
复制代码
) g6 g5 v# s* r1 }
    在异常处理中将线程回到特权模式执行6 B$ v" C+ I& }+ E' m$ d
  1.    /**( s; a  e2 g; G4 A  C+ K
  2. * @brief This function handles SVCall exception.. H  j' P7 m& f$ O9 k
  3. * @param None
    6 Q6 {+ e# u# W+ e) Z
  4. * @retval None! N* ~" J4 b% g2 f7 D
  5.   */+ q, z( l5 w" D: A% a1 H
  6.   void SVC_Handler(void). s( S% m$ N# @) m2 ?/ E: j$ m" H" W" K
  7.   {- Y& N8 J. k" i5 L/ s' {8 Z# S
  8.   /* Switch back Thread mode to privileged */4 F" X' R2 p. T* v0 y
  9.   __set_CONTROL(THREAD_MODE_PRIVILEGED | SP_PROCESS);
    : z3 O! ?, U% b6 E( b) b

  10. $ d4 m. c8 q$ ~
  11. /* Execute ISB instruction to flush pipeline as recommended by Arm */2 d& t- r  H/ u
  12. __ISB();; Y7 l' P- C" b- p# R  t
  13. }
复制代码

  L" @: O: b7 I1 E$ D2 N 若用户模式下代码试图访问无权限数据将产生内存管理异常,从而进入下述函数。3 m! [- b5 y  Y# E$ O
  1. /**
    / `& u3 Z. n6 E+ q! ~. g
  2. * @brief This function handles Memory Manage exception.6 E! o& j. ^! j" F' b+ i3 C
  3. * @param None+ h" ?/ m, b* m; d( Y3 @' Z
  4. * @retval None  S" L; ~. L% U1 k2 [; T
  5.   */
    - ~( Z+ A8 W& b
  6.   void MemManage_Handler(void)$ Y8 k& V; @6 m" o5 M0 O3 r" ^
  7.   {
    ) ^+ V3 e3 m) y. Z$ @9 s( ]
  8.   /* Go to infinite loop when Memory Manage exception occurs */
    * w, B9 A+ ]8 \0 ~- m' _3 t6 T
  9.   while (1)
    " h/ k- t4 x  _* ?) }2 u9 g
  10.   {
    ; O' l: y" D& Y" F2 j$ x
  11.   }
    2 |" v% O' S$ k; [& B# H
  12.   }
复制代码

2 @5 ]% B% q% Y# ^3 @1 ~ 结论
. d: G' N) V9 i本文档介绍了 MPU 的特权与用户模式,并整合与修改已有代码进行了应用。在实际中可将 MPU 与 Cortex 运行模式结合,可使应用更加健壮与安全。
& P4 G; H4 l6 r5 t+ ?
7 d" t, B: v' o$ D! j0 {
# a$ `/ r& E# P7 x4 x' q0 h
收藏 评论0 发布时间:2022-3-5 22:20

举报

0个回答

所属标签

相似分享

官网相关资源

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