01! y4 \& A* S; o$ I
问题描述1 V- e: Q: \/ F. J% E
客户要在STM32U5 SBSFU的基础上开发自己的项目,SBSFU由三个工程组成,Boot,APP,Loader。其中的Boot工程由开源项目MCU boot移植而来,它负责整个系统的安全启动以及升级部分。客户想在此基础上新增一个串口通信,不使用DMA模式的情况下,串口能够正常工作,但切换到DMA模式的时候就不正常了,PC端连接串口发送引脚,显示接收的数据全为0。如下图所示:
: A$ T1 v' k8 B$ v0 V; e▲ 图1 SBSFU Boot运行的置log信息及串口收到的数据全为0 029 O5 p$ q" o3 r9 o) n, ?" O
问题分析及定位8 P9 n0 g8 e; ]. Y8 M/ _! Z) X
此问题看起来应该是DMA哪里配置出了问题。为了快速找到问题,不如先从STM32CubeMx重新生成一个基于trustzone的USART且带DMA的测试工程,等到测试工程测试通过后,直接移植到boot工程内不就完了?
% o# D9 I2 f% j7 w5 q- i% }于是我直接使用STM32CubeMx新建一个工程,使用LPUART1外设PA2,PA3引脚。并分配其为M33 Secure。配置115200 bps。1+8+1数据格式。
7 G( i& k. f/ v, w1 s▲ 图2 串口基本配置 7 ~% B2 z9 A! [+ d! h
( X8 e- P. c5 d% }1 e: A+ C
在其DMA配置页面下,点Go to GPDMA1,切换到GPDMA1配置:在上面Mode页面下激活通道0和通道1,并将通道0分配给LPUSART1_TX:" _/ [! q9 e# `, C
▲ 图3 通道0分配给串口发送
* {/ d1 P$ E# ~0 n5 {
* C0 d$ c" H& \+ V▲ 图4 通道1分配给串口接收 在security页面下配置通道,目标和源均为“secure”:
# Y. i: \+ C# ~6 X▲ 图5 DMA通道的安全属性配置 6 o2 D, K7 e6 E" J4 k
" u& \0 V/ `& C. w8 Y然后就是配置内存了。在ld文件中配置使用的内存是SRAM1:5 h% {, m1 c* I, v# j( m4 u s( `
- /* Memories definition */) Z6 E5 {: y1 p+ V
- MEMORY
$ {" m* ^4 w1 f, t5 ?# r6 J% e - {
) w( k- g" x/ t! g, L- [3 N7 S - RAM (xrw) : ORIGIN = 0x30000000, LENGTH = 192K 1 Z( y( ?" L2 H, F
- /* Memory is divided. Actual start is 0x30000000 and actual length is 768K */
4 c+ d& }- F1 j4 q* a5 s1 \ - SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 16K
3 k; h$ X+ s- a s - FLASH (rx) : ORIGIN = 0x0C006000, LENGTH = 500K
7 h' p9 b7 A% {; n8 F4 V: k" S - /* Memory is divided.
2 {$ p3 p& X4 K2 O( L) L9 } - Actual start is 0x0C000000 and actual length is 2048K */' B N" c3 ]& a' k1 G$ [
- FLASH_NSC (rx) : ORIGIN = 0x0C0FE000, LENGTH = 8K3 j9 K/ Y7 |) o1 _# R
- /* Non-Secure Call-able region*/
" g s$ X/ f3 ^4 x1 D! e4 Y - }
复制代码
: g D) W$ K. x$ B6 A" a/ }然后在GTZC中配置MPCBB1:
]/ f8 v6 |5 s" b
6 D/ a0 o" O& F) _1 u
0 d1 ]9 @* G; w/ v- @0 E: }▲ 图 6 GTZC下MPCBB1配置 : p, o/ d/ `- h+ P" q7 J
$ M& q8 i2 X8 Q9 ?在MPCBB1中将192KB的SRAM1配置为全部安全。
+ l2 c# K3 S9 y9 i9 p然后生成测试工程。4 _2 a/ `; r+ L: a$ @
为了让测试代码跟boot工程保护一致,我们也让测试工程从0x0C006000,见之前ld文件中FLASH的定义范围。
3 J l6 O% i7 |, `) i, t6 W/ M还需要在修改向量偏移位置到0x6000处:& f1 v8 T6 ^5 h
#define VECT_TAB_OFFSET 0x00006000UL" o7 j2 B: r( k( y
然后在main函数处增加测试代码:
% L1 z) N6 o4 J7 _( |( Y3 O- void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)1 `" t6 [& h3 \& @" w" {
- {5 ]$ b) s$ K" i' K0 z3 Z2 Q+ _
- txPendingFlag =0;
( p7 T$ M' o: O9 v5 x# h( [ - }1 ~' g& J8 s% ?% p
- int main(void)
% p$ A$ r( f- D0 g" s - {( [- E* W$ f. t& `" D& T* G
- //…) D0 C& w- U }7 F
- while (1)
4 x' C4 v; {6 c* |1 j7 H - { 9 o3 q; Z; U( o$ i
- if(txPendingFlag==0)7 e& V$ `/ {9 k: _5 q* \
- { M1 E" x% a3 J' K8 d
- txPendingFlag =1;
7 a, \/ q, o; ~, a, ?- C, E - HAL_UART_Transmit_DMA(&hlpuart1, (uint8_t *)aTxBuffer, sizeof(aTxBuffer));$ `% y$ K9 r, O# o( [: u0 R. ^
- } ! [: h* B3 W# H# z' @, c! \
- HAL_Delay(100);9 m$ U) @- a( k; j x
- }
/ ]0 `% ]% [; u6 O+ w - //…( g8 ~* w; d G v, K! i, N
- }
复制代码 让主函数周期性地从串口发送数据。为了让程序正常运行,需要使用STM32CubeProgrammer修改MCU从0x0C00 6000地址启动(主要为了与SBSFU_Boot保持一致):
& B% _& `% @# F- ~▲ 图7 让MCU从0x0C00 6000地址启动
) n" s) r( C- f1 Q/ C. S" m% _, d' ?- I9 B
编译并烧录程序,并让程序运行起来后,使用示波器测量PA2引脚的波形,结果一切正常。2 b* ?* @/ }1 Y$ g. g1 j3 y
于是接下来移植到SBSFU_Boot工程。5 V1 T7 e: C1 w* |
由于在测试工程中已经测试OK,按理说,移植到boot工程后,带DMA的串口也应该发送正常。但实际测试却是跟客户所描述一样,发送内容全为0!
7 W* a5 @1 F1 @. q9 C) T问题出在哪里?
`( j. P6 ?& m3 T* c仔细对比两者的差别,发现测试工程是有配置GTZC。MPCBB1的,对应SRAM1,但是Boot工程,它使用的是SRAM2,代码并没有配置MPCBB2,其初始化函数:
# \) t* f6 p E- static void gtzc_init_cfg(void)
6 y3 |; I/ R1 N - {
" }% V9 w2 N3 i0 u# R+ { - __HAL_RCC_GTZC1_CLK_ENABLE();. e( ]* n j1 G9 |" g2 T
- #ifdef TFM_ERROR_HANDLER_NON_SECURE
; H# H- ?/ l x, t5 J - /* unsecure only one block in SRAM1 */2 u# o1 X3 z* [$ b, y6 Q3 U8 I: j
- GTZC_MPCBB1_S->SECCFGR[0] = 0xfffffffe;
$ c# i/ J, n' Q, } - #endif
Z0 g: N( @( m9 X0 v) H6 u9 X - /* TFM_ERROR_HANDLER_NON_SECURE */ ]& D" D9 m" O0 o
- }
复制代码 & J+ B7 x+ P* J9 r- w9 Q# A
在宏TFM_ERROR_HANDLE_NON_SECURE打开的情况下也只会配置MPCBB1,并没有配置MPCBB2。因此,SRAM的安全及特权属性应该是保留为默认属性,那么其默认属性是怎么定义的呢?9 o6 V. A; t# y7 `/ H" N t
在参考手册RM0456 Rev 4第5.4.6节中,我们找到如下内容:
' Q1 t; p$ h% \0 P+ x▲ 图 8 SRAM的默认属性定义
( Z3 w: h) L {, M* i1 Z! J# r/ ~ h3 [& |/ l/ Y; v
这说明,在默认情况下(TZEN),SRAM2的默认属性是Secure+Privieged的。之前在测试代码对应的STM32CubeMx配置中,我们配置了通道,目标及源为安全属性,但并没有配置其特权属性。在CubeMx中其默认为非特权,见之前的图5所示。也就是说,我们需要将通道也配置成特权属性,于是直接修改代码:
7 b+ D; u4 b+ B* J- //… \+ |) r2 H U9 |4 Y" m' o
- __HAL_LINKDMA(huart, hdmarx, handle_GPDMA1_Channel1);
N8 ?. q' x- N) @, j - if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel1,
3 X0 u2 \, }& u2 t" H - DMA_CHANNEL_PRIV|DMA_CHANNEL_SEC|DMA_CHANNEL_SRC_SEC|DMA_CHANNEL_DEST_S
1 `( T3 V2 Y) q4 t9 t - EC) != HAL_OK)! y' p6 ]8 u D- O' E# n
- {! M3 N) J6 p. E
- Error_Handler();
* C( O9 ], ]! i - }
% q) r4 _- f. ~- I' v# ] - //…8 h( d: s7 T4 E' ~7 h
- __HAL_LINKDMA(huart, hdmatx, handle_GPDMA1_Channel0);
$ |' O5 x: K* x - if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel0,
( S- j5 a( T: D' R1 r7 H - DMA_CHANNEL_PRIV|DMA_CHANNEL_SEC|DMA_CHANNEL_SRC_SEC|DMA_CHANNEL_DEST_S
) t+ A- A$ D0 I. g6 Y n+ R' @ - EC) != HAL_OK)
$ u& y t' X5 J; D. D1 [ - {* ~9 w+ P" N- s6 d: ?, l' `$ n5 n7 P
- Error_Handler();9 D# G9 I, ?1 r# t2 q; s, K
- }# A: Q' \" E% f; }0 b$ }" d0 L
- //…
复制代码 ) v N5 D5 J$ `9 d3 f& y# T7 u! s0 V
如上代码所示,在代码中配置DMA通道处(红色标记处),将其改为配置特权属性。然后编译后再次测试,这次则能正常发送数据了:; {$ k) ]: s2 i) B7 g
▲ 图 9 串口正常发送数据
/ j3 q7 h; } d0 f2 Q# }3 `8 ^
0 V/ R1 x6 p4 t/ A" L: p. C03
* D* |2 J/ [. }& L小结/ i: I$ d- s9 S# O+ j% H
在trustzone使能的情况下,MCU内的资源,若代码中没有显式设置的情况下,要注意其默认状态,内部flash,SRAM,片外存储映射区域以及GPIO引脚均为安全的,且特权,Securable外设基本都是非安全的,在STM32U5安全培训的一页PPT中有做总结,如下图所示:- Q b0 ?* u! Q3 v; Z# C: h
本文所描述的问题,正是由于MPCBB2(Boot工程对应的RAM)没有配置,其默认为Secure+Prileged属性,但DMA默认属性为NonSecre+NonPrileged。代码中只配置DMA通道为Secure属性,并没有配置其为Prileged,因此导致其访问SRAM2时失败,这也就是为什么发送内容全为0的原因了。这也提醒我们,在开发trustzone相关的项目时,对各个资源的安全属性配置一定要小心。
: v; d/ @+ Z3 D8 G' f% R% g- s- h3 l4 s
|