01
1 D4 a2 S; B! y! H+ A8 w8 }问题描述
4 N# Z( q3 |; _3 k* L/ \1 j客户要在STM32U5 SBSFU的基础上开发自己的项目,SBSFU由三个工程组成,Boot,APP,Loader。其中的Boot工程由开源项目MCU boot移植而来,它负责整个系统的安全启动以及升级部分。客户想在此基础上新增一个串口通信,不使用DMA模式的情况下,串口能够正常工作,但切换到DMA模式的时候就不正常了,PC端连接串口发送引脚,显示接收的数据全为0。如下图所示:9 E c: i5 P! X. d
▲ 图1 SBSFU Boot运行的置log信息及串口收到的数据全为0 02! @0 e4 k# j+ \2 o) m3 a4 P
问题分析及定位! ~! v/ _% f7 E- s j/ X9 G
此问题看起来应该是DMA哪里配置出了问题。为了快速找到问题,不如先从STM32CubeMx重新生成一个基于trustzone的USART且带DMA的测试工程,等到测试工程测试通过后,直接移植到boot工程内不就完了?+ {# x1 r+ w% J( m0 ^
于是我直接使用STM32CubeMx新建一个工程,使用LPUART1外设PA2,PA3引脚。并分配其为M33 Secure。配置115200 bps。1+8+1数据格式。
- h9 H8 {/ r3 L2 A0 X3 b▲ 图2 串口基本配置
" ^, s8 R% ?$ Y) y: A
) o4 k; t8 s& t' A! i在其DMA配置页面下,点Go to GPDMA1,切换到GPDMA1配置:在上面Mode页面下激活通道0和通道1,并将通道0分配给LPUSART1_TX:1 _# z' E6 P) ]
▲ 图3 通道0分配给串口发送
! u( G# v5 K% a& I$ h( a/ u8 h2 M
6 p8 l% W- y7 G- M5 _. Z▲ 图4 通道1分配给串口接收 在security页面下配置通道,目标和源均为“secure”:
2 D9 D3 H; W, f& M$ W▲ 图5 DMA通道的安全属性配置 ( J/ q5 t- n7 J( K1 `1 F0 q8 S
- b3 _/ {, W& q' O" x* _: s; f$ p
然后就是配置内存了。在ld文件中配置使用的内存是SRAM1:
: e( d. m. b. \4 {8 V- /* Memories definition */( Z& e. W3 b+ f- h; q, o! l( ~ O
- MEMORY
' C Z4 O* J+ l - {
. s: A8 u- V. [! J7 @ - RAM (xrw) : ORIGIN = 0x30000000, LENGTH = 192K & |/ X& w5 i, k$ Y
- /* Memory is divided. Actual start is 0x30000000 and actual length is 768K */
# C2 O5 g" P, n8 S6 \ - SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 16K# Z# W" U e' ~: g$ l+ S
- FLASH (rx) : ORIGIN = 0x0C006000, LENGTH = 500K
2 ~0 q X7 y3 P3 i$ k- S' x - /* Memory is divided.
6 Y& w1 C% I( J. B: S e, g - Actual start is 0x0C000000 and actual length is 2048K */; ^2 l9 b$ P6 B: b ]$ Y
- FLASH_NSC (rx) : ORIGIN = 0x0C0FE000, LENGTH = 8K( A; V- e- M7 \6 ]& y% Q) s
- /* Non-Secure Call-able region*/6 H0 k# L# l* f. N/ I# L4 u
- }
复制代码 6 F3 U# v6 m9 a
然后在GTZC中配置MPCBB1:5 N9 M* B5 H9 b. ~
9 v$ l+ Y6 |, x6 J9 b% z( |- ?6 d& ~/ ]
▲ 图 6 GTZC下MPCBB1配置 ' I9 i* H" s3 Q+ e T; N0 q/ @9 s
* I' d }2 b$ S! F' L& W在MPCBB1中将192KB的SRAM1配置为全部安全。
9 r1 P" X2 O2 x" ^然后生成测试工程。
% q [, }0 W9 Y4 j! C6 ^+ E& |+ x) f为了让测试代码跟boot工程保护一致,我们也让测试工程从0x0C006000,见之前ld文件中FLASH的定义范围。( r0 a! [' C+ m( z! S, O: H! i
还需要在修改向量偏移位置到0x6000处:
9 @' n7 J/ b" L6 Z#define VECT_TAB_OFFSET 0x00006000UL; q) Q" x2 ?7 ]) E, H
然后在main函数处增加测试代码:/ ^! {" s% s- Z& j+ c5 c9 M
- void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)- r+ B9 S. K4 ~/ @
- {: f, O% Q; \3 k/ W1 b
- txPendingFlag =0;# ~1 C# _% W/ e9 t- J
- }
% V; f, V: U, y( U8 L7 J - int main(void) b. I$ @ x0 d2 N7 h! c
- {
/ X6 F! Q9 C! K) |! x u - //…6 h; l% x; |5 P) ]5 v+ Z2 i
- while (1)
0 N) s& q7 S0 |' s, Q b - { ! v% j0 d# j3 B
- if(txPendingFlag==0)5 a D6 F; Q+ O( F z N" u' J4 L" f' l
- {* B, T5 R) |4 ?1 A5 C
- txPendingFlag =1;4 R2 {+ I* ^" o1 e- J' s! ?6 h
- HAL_UART_Transmit_DMA(&hlpuart1, (uint8_t *)aTxBuffer, sizeof(aTxBuffer));
9 o+ |% p# J4 ?' g& E - }
: P C0 H" |4 s4 t% A/ U - HAL_Delay(100);
( z& [2 M2 ^' T; q - }
5 x2 Y! K. H! @4 M0 N - //…0 p% J4 m, W8 G3 D1 l. l7 T, z
- }
复制代码 让主函数周期性地从串口发送数据。为了让程序正常运行,需要使用STM32CubeProgrammer修改MCU从0x0C00 6000地址启动(主要为了与SBSFU_Boot保持一致):+ O. \5 \' P d& b' f& a) e7 _! z
▲ 图7 让MCU从0x0C00 6000地址启动
+ I) }/ ~- b/ {0 P e9 c2 H$ d% F6 r. O9 w# V- ^/ R @9 G, t
编译并烧录程序,并让程序运行起来后,使用示波器测量PA2引脚的波形,结果一切正常。
3 |* v3 w b' J5 `! C于是接下来移植到SBSFU_Boot工程。' k- D* l+ Q4 r; R: ]3 N- I6 l
由于在测试工程中已经测试OK,按理说,移植到boot工程后,带DMA的串口也应该发送正常。但实际测试却是跟客户所描述一样,发送内容全为0!+ S; B6 O h# l. [+ d! |, \4 s4 J
问题出在哪里?3 B" u# ~5 W0 [( _3 S* m
仔细对比两者的差别,发现测试工程是有配置GTZC。MPCBB1的,对应SRAM1,但是Boot工程,它使用的是SRAM2,代码并没有配置MPCBB2,其初始化函数:/ K; i% g0 @) M2 B! \! [9 j
- static void gtzc_init_cfg(void). W, I( m( u* c' ?0 E
- {& S% Y5 H* K+ s
- __HAL_RCC_GTZC1_CLK_ENABLE();
& V6 E2 u9 R" d: @ - #ifdef TFM_ERROR_HANDLER_NON_SECURE0 l9 k" ]# u; P+ n/ P1 d
- /* unsecure only one block in SRAM1 */, x2 w3 I0 J9 Y: C
- GTZC_MPCBB1_S->SECCFGR[0] = 0xfffffffe;2 {8 i' y# M1 f, z
- #endif. U$ ]' k& v @* R8 f
- /* TFM_ERROR_HANDLER_NON_SECURE */
4 \7 s7 Z. M* V$ w - }
复制代码
: H/ g6 j9 S" I& ~4 |在宏TFM_ERROR_HANDLE_NON_SECURE打开的情况下也只会配置MPCBB1,并没有配置MPCBB2。因此,SRAM的安全及特权属性应该是保留为默认属性,那么其默认属性是怎么定义的呢?' l; I7 r( b% A S! H8 z& g
在参考手册RM0456 Rev 4第5.4.6节中,我们找到如下内容:. P5 E8 v1 U( r% M* I8 Q( U
▲ 图 8 SRAM的默认属性定义 0 P0 J: G% V' M3 q5 O) x
; s& P8 D8 G. v- r9 w9 h
这说明,在默认情况下(TZEN),SRAM2的默认属性是Secure+Privieged的。之前在测试代码对应的STM32CubeMx配置中,我们配置了通道,目标及源为安全属性,但并没有配置其特权属性。在CubeMx中其默认为非特权,见之前的图5所示。也就是说,我们需要将通道也配置成特权属性,于是直接修改代码:0 j& \# f( n; r6 x
- //…- o' m; o! z5 i' I
- __HAL_LINKDMA(huart, hdmarx, handle_GPDMA1_Channel1);7 v5 k- C& h) U8 m
- if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel1,
7 ^ R2 O! q3 n8 @) d7 g6 g0 w - DMA_CHANNEL_PRIV|DMA_CHANNEL_SEC|DMA_CHANNEL_SRC_SEC|DMA_CHANNEL_DEST_S! V9 u o! V8 y. @( @0 B% P" R; K7 ]
- EC) != HAL_OK)
) ]+ V; L8 d/ a - {
+ L( l. x, j: i. M9 S - Error_Handler();; E, d2 s S. t7 K. u8 B( v) P6 ~, T3 c
- }; C4 s+ n9 ?( A( v) F; J
- //…
1 C/ r0 @. P8 S5 O# l0 s) k8 f - __HAL_LINKDMA(huart, hdmatx, handle_GPDMA1_Channel0);
3 j) I! I5 T7 S" j% l' \ - if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel0,
`& S5 I1 p o - DMA_CHANNEL_PRIV|DMA_CHANNEL_SEC|DMA_CHANNEL_SRC_SEC|DMA_CHANNEL_DEST_S
# ?) L% d) g N; J x3 P, v+ L - EC) != HAL_OK)
# l6 }2 A7 n! e+ b9 e. z- C2 B - {- p6 u; m1 s9 f
- Error_Handler();4 Z3 _* h d2 }4 K- `
- }
7 h& q/ d! T$ L* v9 B4 b9 \7 v* S - //…
复制代码
6 E3 W. l! s0 ^ p3 B% B如上代码所示,在代码中配置DMA通道处(红色标记处),将其改为配置特权属性。然后编译后再次测试,这次则能正常发送数据了:$ f* |/ P% v7 k1 i2 A
▲ 图 9 串口正常发送数据
& y/ \% h& Q$ W8 R
' g6 i/ U. q5 A( q4 B! n03
9 b! v* o7 N3 N# h' [( y# i小结/ d7 h$ P5 q5 p/ O9 _4 w
在trustzone使能的情况下,MCU内的资源,若代码中没有显式设置的情况下,要注意其默认状态,内部flash,SRAM,片外存储映射区域以及GPIO引脚均为安全的,且特权,Securable外设基本都是非安全的,在STM32U5安全培训的一页PPT中有做总结,如下图所示:3 v: e! P$ s1 B$ f* M! }' M' O
本文所描述的问题,正是由于MPCBB2(Boot工程对应的RAM)没有配置,其默认为Secure+Prileged属性,但DMA默认属性为NonSecre+NonPrileged。代码中只配置DMA通道为Secure属性,并没有配置其为Prileged,因此导致其访问SRAM2时失败,这也就是为什么发送内容全为0的原因了。这也提醒我们,在开发trustzone相关的项目时,对各个资源的安全属性配置一定要小心。
+ X3 v6 [' t* {8 i$ O" @3 W" Q
7 V! [! H, R% Q |