01
: `! j" v' u/ ^2 y8 j# M' D- w问题描述0 \5 H8 P5 o9 b3 n5 d+ d1 B9 ]; l7 K
客户要在STM32U5 SBSFU的基础上开发自己的项目,SBSFU由三个工程组成,Boot,APP,Loader。其中的Boot工程由开源项目MCU boot移植而来,它负责整个系统的安全启动以及升级部分。客户想在此基础上新增一个串口通信,不使用DMA模式的情况下,串口能够正常工作,但切换到DMA模式的时候就不正常了,PC端连接串口发送引脚,显示接收的数据全为0。如下图所示:" C2 ?: m1 E; p2 d. V
▲ 图1 SBSFU Boot运行的置log信息及串口收到的数据全为0 02
, g3 |8 x' h5 P& f- j问题分析及定位
$ C# s: z! M; d7 Q* I1 j& u9 c此问题看起来应该是DMA哪里配置出了问题。为了快速找到问题,不如先从STM32CubeMx重新生成一个基于trustzone的USART且带DMA的测试工程,等到测试工程测试通过后,直接移植到boot工程内不就完了?
4 E1 ]: q+ b% H+ p于是我直接使用STM32CubeMx新建一个工程,使用LPUART1外设PA2,PA3引脚。并分配其为M33 Secure。配置115200 bps。1+8+1数据格式。' |9 M8 s* G" Y& |
▲ 图2 串口基本配置
6 ^+ J9 G" n. h" ^+ u8 m1 R4 J6 j, y1 E! F$ \3 ?
在其DMA配置页面下,点Go to GPDMA1,切换到GPDMA1配置:在上面Mode页面下激活通道0和通道1,并将通道0分配给LPUSART1_TX:" T+ R- j5 O. z5 {+ }1 d( g+ z
▲ 图3 通道0分配给串口发送 0 ]; d0 k1 j q \4 `, x; L
8 z$ M) _6 s# l8 [8 I
▲ 图4 通道1分配给串口接收 在security页面下配置通道,目标和源均为“secure”:
) Q: G; o1 f u5 `2 s▲ 图5 DMA通道的安全属性配置 ) L6 s, P7 ]* N. k+ U+ a) z
! j* S9 Q; @! e+ ^; o0 @1 X3 G
然后就是配置内存了。在ld文件中配置使用的内存是SRAM1:
( H" L) `# q( w G1 r8 F- /* Memories definition */* g: Z" f) [) f+ W& w2 r% a
- MEMORY
( E- L6 H8 k& r; n5 g - {- s3 ^' ~% w# X) e1 H6 n
- RAM (xrw) : ORIGIN = 0x30000000, LENGTH = 192K 1 F c3 k, M3 `2 F
- /* Memory is divided. Actual start is 0x30000000 and actual length is 768K */
# `2 h! O' B4 u2 { - SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 16K0 M! ~, q0 l& t8 U5 p
- FLASH (rx) : ORIGIN = 0x0C006000, LENGTH = 500K . j& x u, a6 G" ^2 Q+ k
- /* Memory is divided.
5 _: M' n0 Z: c2 B6 y5 t - Actual start is 0x0C000000 and actual length is 2048K */
: b& R9 X, H4 G- L - FLASH_NSC (rx) : ORIGIN = 0x0C0FE000, LENGTH = 8K
- D) p6 A S5 i - /* Non-Secure Call-able region*/
$ J/ D) I% J: o - }
复制代码 ) i' Z9 L7 b* b4 C# f
然后在GTZC中配置MPCBB1:/ w6 l6 I$ E( n" f, h% l
3 O, b8 v, s4 l& R" M6 U4 e; c* R j+ U$ S+ N! z7 Z% X
▲ 图 6 GTZC下MPCBB1配置
) e3 m; k% A( E
0 C# S; z9 K m7 L1 q! [* `在MPCBB1中将192KB的SRAM1配置为全部安全。
7 W! [% n% Y- s8 y7 m9 K) U/ {! J然后生成测试工程。0 N1 }2 }, ?6 r: ]5 w! n* y
为了让测试代码跟boot工程保护一致,我们也让测试工程从0x0C006000,见之前ld文件中FLASH的定义范围。4 ^4 S# h' m& t. V4 `
还需要在修改向量偏移位置到0x6000处:
j7 N) }! L/ ^; B ~% i( ^3 J! v#define VECT_TAB_OFFSET 0x00006000UL& [0 A# _2 n: y
然后在main函数处增加测试代码:
. {$ k7 @- ]! O- void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
8 P) e& d6 K1 p% I7 [# F& N: p+ F( a - {( e/ j* L0 S" \2 V
- txPendingFlag =0;2 N) h& f1 O" `% y
- }2 j$ [0 Y9 W5 f: \; `% ?" x! Y
- int main(void)
! y0 P( F6 V v& P - {
; b- v! P3 F( |' C8 [ - //…2 r1 c4 B- G' G O
- while (1)3 x! ~3 {+ [2 w1 ~
- {
: z4 d/ C/ x8 a0 \ - if(txPendingFlag==0)
# s5 Y: D6 Q+ B4 G% Q - {
6 v1 L( y" ^4 Z+ Y - txPendingFlag =1;
' M4 ]& z1 E D3 q" h% S* f4 J - HAL_UART_Transmit_DMA(&hlpuart1, (uint8_t *)aTxBuffer, sizeof(aTxBuffer));
& |0 _" ^6 F- ?2 s3 ]- T& S; F - }
$ V/ R; e/ h1 K& v* W& r - HAL_Delay(100);
% I( a; u0 g" x9 R - }
: T% P5 |# [, t' a - //…6 E5 A) }8 c& Y/ @! h$ ^) ?
- }
复制代码 让主函数周期性地从串口发送数据。为了让程序正常运行,需要使用STM32CubeProgrammer修改MCU从0x0C00 6000地址启动(主要为了与SBSFU_Boot保持一致):" k# ~$ T% h2 x9 N: ]0 M
▲ 图7 让MCU从0x0C00 6000地址启动
! @% E5 \1 Q7 a- x' P5 D5 Z% l- R/ D9 V5 _) Y1 t
编译并烧录程序,并让程序运行起来后,使用示波器测量PA2引脚的波形,结果一切正常。
& d) E& [, n7 s+ ^2 M于是接下来移植到SBSFU_Boot工程。* X% f! \$ Q( z. y9 N4 I9 X+ r% h
由于在测试工程中已经测试OK,按理说,移植到boot工程后,带DMA的串口也应该发送正常。但实际测试却是跟客户所描述一样,发送内容全为0!" k9 u1 l1 y0 J, z2 [. O
问题出在哪里?6 b; M' M% X s' \, z. G5 q
仔细对比两者的差别,发现测试工程是有配置GTZC。MPCBB1的,对应SRAM1,但是Boot工程,它使用的是SRAM2,代码并没有配置MPCBB2,其初始化函数:. Q& ~3 _. ?4 W3 v
- static void gtzc_init_cfg(void)
* w u, |4 Y8 D( r% V; C* k - {1 k, ~8 I; R5 `5 f
- __HAL_RCC_GTZC1_CLK_ENABLE();
7 N5 B) A% h, I- v& N. d0 W6 O, K - #ifdef TFM_ERROR_HANDLER_NON_SECURE
: w/ ]2 ^) h2 d - /* unsecure only one block in SRAM1 */. x. z4 }7 G, {: X8 z2 T! q! C8 Q
- GTZC_MPCBB1_S->SECCFGR[0] = 0xfffffffe;
0 o& e3 |5 \; [8 ^ - #endif+ D( u, i& E; p" M2 F
- /* TFM_ERROR_HANDLER_NON_SECURE */0 r8 }( }" a0 F! H9 v; `, ^
- }
复制代码
8 l& p8 k7 A+ c, R" B在宏TFM_ERROR_HANDLE_NON_SECURE打开的情况下也只会配置MPCBB1,并没有配置MPCBB2。因此,SRAM的安全及特权属性应该是保留为默认属性,那么其默认属性是怎么定义的呢?2 d; E- N6 j9 V8 ^0 b
在参考手册RM0456 Rev 4第5.4.6节中,我们找到如下内容:
- Q" ^ v! P v p: b▲ 图 8 SRAM的默认属性定义 ' w# B' w% x C$ @0 ~$ I% l
# P8 }5 \/ z! x$ L这说明,在默认情况下(TZEN),SRAM2的默认属性是Secure+Privieged的。之前在测试代码对应的STM32CubeMx配置中,我们配置了通道,目标及源为安全属性,但并没有配置其特权属性。在CubeMx中其默认为非特权,见之前的图5所示。也就是说,我们需要将通道也配置成特权属性,于是直接修改代码:% u% a( u( v" H3 c4 T4 y
- //…
: K% p2 j( }% e$ b - __HAL_LINKDMA(huart, hdmarx, handle_GPDMA1_Channel1);: i' ^& C6 F* @( h
- if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel1,
# q8 d: R1 @" M2 n. B8 ] - DMA_CHANNEL_PRIV|DMA_CHANNEL_SEC|DMA_CHANNEL_SRC_SEC|DMA_CHANNEL_DEST_S
( O( M# N, a4 }/ r - EC) != HAL_OK)" U: O- v+ _& v0 z
- {
$ T3 J* u/ A. l# m" q - Error_Handler();" g, N1 Y$ ^/ p- J, r1 H
- }
& w2 N/ c6 i! `' G' E* m \0 K - //…
4 s7 a1 X/ Y. c5 R0 n, a, k( \ - __HAL_LINKDMA(huart, hdmatx, handle_GPDMA1_Channel0);0 V/ `. j# O- }( p) m
- if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel0,
$ X" ? `% P& M$ k, S+ \1 S: S - DMA_CHANNEL_PRIV|DMA_CHANNEL_SEC|DMA_CHANNEL_SRC_SEC|DMA_CHANNEL_DEST_S; [% `# z' L" s' T2 ~6 Y( ]5 o$ B
- EC) != HAL_OK)
, U0 d% O9 E) B6 y - {
% d7 z- R5 J2 {* V - Error_Handler();: @6 w; ~1 H& p
- }
e$ u5 e3 y% E- ~& | - //…
复制代码
% u; |1 H6 C, Y& a: q如上代码所示,在代码中配置DMA通道处(红色标记处),将其改为配置特权属性。然后编译后再次测试,这次则能正常发送数据了:4 z6 N2 [/ t9 | o! u) t
▲ 图 9 串口正常发送数据 ( y- q. m4 p/ ]' f+ p0 t
9 a8 K6 S5 h4 e+ G035 m4 \7 A$ B* p
小结
& a' p" i8 k0 p5 w c/ q) U在trustzone使能的情况下,MCU内的资源,若代码中没有显式设置的情况下,要注意其默认状态,内部flash,SRAM,片外存储映射区域以及GPIO引脚均为安全的,且特权,Securable外设基本都是非安全的,在STM32U5安全培训的一页PPT中有做总结,如下图所示:
7 ?# t1 Z4 Z1 N4 N本文所描述的问题,正是由于MPCBB2(Boot工程对应的RAM)没有配置,其默认为Secure+Prileged属性,但DMA默认属性为NonSecre+NonPrileged。代码中只配置DMA通道为Secure属性,并没有配置其为Prileged,因此导致其访问SRAM2时失败,这也就是为什么发送内容全为0的原因了。这也提醒我们,在开发trustzone相关的项目时,对各个资源的安全属性配置一定要小心。6 G4 y- y) Q% c% R7 S
2 Y0 r" m" B1 F- L6 B
|