01/ X j/ B( \ k5 E8 B
问题描述
5 y2 P: P6 y8 H. @# ]' R客户要在STM32U5 SBSFU的基础上开发自己的项目,SBSFU由三个工程组成,Boot,APP,Loader。其中的Boot工程由开源项目MCU boot移植而来,它负责整个系统的安全启动以及升级部分。客户想在此基础上新增一个串口通信,不使用DMA模式的情况下,串口能够正常工作,但切换到DMA模式的时候就不正常了,PC端连接串口发送引脚,显示接收的数据全为0。如下图所示:
* P/ k- [& L3 r3 d: T5 Q* ~7 v/ G▲ 图1 SBSFU Boot运行的置log信息及串口收到的数据全为0 02
) V' [* c: m- k! R+ ]问题分析及定位
: z3 e2 s) d+ ~6 O( j; s此问题看起来应该是DMA哪里配置出了问题。为了快速找到问题,不如先从STM32CubeMx重新生成一个基于trustzone的USART且带DMA的测试工程,等到测试工程测试通过后,直接移植到boot工程内不就完了?7 \) F' \) c/ d) j7 M3 @* j3 \
于是我直接使用STM32CubeMx新建一个工程,使用LPUART1外设PA2,PA3引脚。并分配其为M33 Secure。配置115200 bps。1+8+1数据格式。
' S6 g, i& V; h/ d3 R( X6 {▲ 图2 串口基本配置
& I* l" R) q) q2 k8 L
. \7 m2 m% t; ~& g& M1 t在其DMA配置页面下,点Go to GPDMA1,切换到GPDMA1配置:在上面Mode页面下激活通道0和通道1,并将通道0分配给LPUSART1_TX:
' S" u% k" ^. B6 I" Y: m3 Z▲ 图3 通道0分配给串口发送
0 ?4 l0 }2 e; F' U, ^0 y: E( M0 Y2 [7 S! w4 F& X9 ?7 [, G- _
▲ 图4 通道1分配给串口接收 在security页面下配置通道,目标和源均为“secure”:
5 @) e/ ]) I' z+ s: z) P▲ 图5 DMA通道的安全属性配置 * @" q2 \' Y! d& a, ~4 d" p: g
! _ }3 D! {6 y U/ L然后就是配置内存了。在ld文件中配置使用的内存是SRAM1:0 A; c$ P1 I3 n0 \4 t6 \% ^
- /* Memories definition */
+ w) m% s2 l; ~* h7 `/ z - MEMORY: B1 U( q1 {4 @
- {
9 K( a% U: U) P" g9 j# ]; E! ]3 p; u - RAM (xrw) : ORIGIN = 0x30000000, LENGTH = 192K + i' y$ g; q" l5 J
- /* Memory is divided. Actual start is 0x30000000 and actual length is 768K */
1 E% X, y2 V# C - SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 16K
+ m1 e1 |6 {* R' n8 I0 F, R0 D - FLASH (rx) : ORIGIN = 0x0C006000, LENGTH = 500K $ w' l4 Q" L0 o
- /* Memory is divided.) W' i3 |# r6 f0 E, f5 Z
- Actual start is 0x0C000000 and actual length is 2048K */
+ `1 Q; H$ l& _ - FLASH_NSC (rx) : ORIGIN = 0x0C0FE000, LENGTH = 8K
2 Z' G R# p* B4 K/ b. Q) o. k - /* Non-Secure Call-able region*/
0 Z6 h( I+ Y, p+ F& \: s! O - }
复制代码
( i! H% F+ A9 I7 l. a( Z( ~然后在GTZC中配置MPCBB1:9 U( R% T( O* A+ B7 J# ?
, s W: a8 W8 R! x: m5 X. S0 j# J. x T: P7 m L
▲ 图 6 GTZC下MPCBB1配置
v5 h7 ^/ j5 \& k: x- R( T/ P- J2 c, Q! {" j8 q
在MPCBB1中将192KB的SRAM1配置为全部安全。" ?1 q9 B/ m0 e( k3 d9 c# J l
然后生成测试工程。- ~6 I& g6 v. W9 t/ r p. v
为了让测试代码跟boot工程保护一致,我们也让测试工程从0x0C006000,见之前ld文件中FLASH的定义范围。, I: v8 K* S& O$ V! E" C" c! u# X: M
还需要在修改向量偏移位置到0x6000处:
5 H# s: l' O4 p+ b: }' C/ ^#define VECT_TAB_OFFSET 0x00006000UL
K" R6 C' L6 G0 v* N然后在main函数处增加测试代码:1 O! I% d7 y2 R* U0 ~+ R
- void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
, P$ ?( d7 H2 T - {
6 T) W- X% E( }' I - txPendingFlag =0;
+ w/ {5 H! F! M& ?/ J - }
0 e4 c- v/ Y* R6 s/ l, O - int main(void)! W/ y4 I9 v, c% V0 S# Z3 i
- {
6 X- ?+ x' [. K P! z - //…
4 n) r [9 u* t( w6 }3 k2 n2 ] - while (1)
" C2 L: W! i- c# d0 C g& N( [ - { , e! E) E5 m9 M8 p
- if(txPendingFlag==0)
8 ]" b, ^8 `/ k; ~) K - {% s& Q; `1 d0 L
- txPendingFlag =1;6 |$ o( v) l5 R% |, Z
- HAL_UART_Transmit_DMA(&hlpuart1, (uint8_t *)aTxBuffer, sizeof(aTxBuffer));
/ W' k9 J/ r L y( |; T - } 2 I$ n* `2 z/ q! V' S
- HAL_Delay(100);4 ~( E7 o! @( V* Z* D
- }
' v% ^* N5 W% O ~5 I - //…
. O6 e+ S5 D- O7 g5 U - }
复制代码 让主函数周期性地从串口发送数据。为了让程序正常运行,需要使用STM32CubeProgrammer修改MCU从0x0C00 6000地址启动(主要为了与SBSFU_Boot保持一致):
; K: y( V! U" o: i▲ 图7 让MCU从0x0C00 6000地址启动
6 e0 y! y; g' N @: U4 j* k4 z/ C+ C( r, c4 B% C6 k
编译并烧录程序,并让程序运行起来后,使用示波器测量PA2引脚的波形,结果一切正常。" ~% }4 p3 T2 R+ m- V
于是接下来移植到SBSFU_Boot工程。
/ x3 B, y5 t# ]6 }由于在测试工程中已经测试OK,按理说,移植到boot工程后,带DMA的串口也应该发送正常。但实际测试却是跟客户所描述一样,发送内容全为0!6 o8 u: A$ k, A+ s3 J
问题出在哪里?
/ @8 q: T: N1 a仔细对比两者的差别,发现测试工程是有配置GTZC。MPCBB1的,对应SRAM1,但是Boot工程,它使用的是SRAM2,代码并没有配置MPCBB2,其初始化函数:! R5 _; [7 l' T; K& z0 R& A6 b) M+ u
- static void gtzc_init_cfg(void)3 ]3 |8 f& ]# V, D+ A
- {
: x- r+ g" b9 r- K# }, T5 Z6 V - __HAL_RCC_GTZC1_CLK_ENABLE();7 |: C9 c# \# L, Q9 ?
- #ifdef TFM_ERROR_HANDLER_NON_SECURE
, k1 x/ S4 d0 b9 _/ R& [1 \& A% C9 H - /* unsecure only one block in SRAM1 */
4 v7 v- W; Z+ A1 y' Y% D9 V \ - GTZC_MPCBB1_S->SECCFGR[0] = 0xfffffffe;
1 y- g7 k5 d9 G) V& ~1 G - #endif
; [6 C: y8 r3 j& ]3 l/ q6 k - /* TFM_ERROR_HANDLER_NON_SECURE */# q( T; \* X3 i3 N. f. m
- }
复制代码 / Q |4 O, Y7 y; m; a' P5 a
在宏TFM_ERROR_HANDLE_NON_SECURE打开的情况下也只会配置MPCBB1,并没有配置MPCBB2。因此,SRAM的安全及特权属性应该是保留为默认属性,那么其默认属性是怎么定义的呢?' u* E B/ [3 T& h
在参考手册RM0456 Rev 4第5.4.6节中,我们找到如下内容:6 N# m- [- |2 P: n- t( h
▲ 图 8 SRAM的默认属性定义 ( C- g5 c1 {+ R+ ^( B
' ~$ S' l+ I$ P' K. n4 I这说明,在默认情况下(TZEN),SRAM2的默认属性是Secure+Privieged的。之前在测试代码对应的STM32CubeMx配置中,我们配置了通道,目标及源为安全属性,但并没有配置其特权属性。在CubeMx中其默认为非特权,见之前的图5所示。也就是说,我们需要将通道也配置成特权属性,于是直接修改代码:6 Y# C9 R( N/ t6 s5 C4 ^: x
- //…
" ~1 ]0 z% {: o7 v3 h/ y - __HAL_LINKDMA(huart, hdmarx, handle_GPDMA1_Channel1);
$ c. z3 ^* o* v$ o s5 S8 z6 z0 u - if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel1,
8 p9 |1 y! R( `8 u' ^& [3 ] - DMA_CHANNEL_PRIV|DMA_CHANNEL_SEC|DMA_CHANNEL_SRC_SEC|DMA_CHANNEL_DEST_S
: d! c1 u2 q5 U0 \; W. E4 b - EC) != HAL_OK)
5 y1 Y7 l1 X$ U! u* B) S1 Z; F( n; S - {$ ~& v8 ^, ^# X3 b K. q
- Error_Handler();; l$ ~' y8 |: F6 ]
- }
/ u! _0 K* N9 i8 m2 ~/ ?( Y. `! F - //…
. U1 X: H% ~ X9 w. k" S - __HAL_LINKDMA(huart, hdmatx, handle_GPDMA1_Channel0);8 u) [) d1 o+ l$ x/ J$ W$ Q
- if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel0,, S9 m" [$ ?* S& T5 O6 N& |3 A
- DMA_CHANNEL_PRIV|DMA_CHANNEL_SEC|DMA_CHANNEL_SRC_SEC|DMA_CHANNEL_DEST_S
& _: F& H9 D/ V! i4 ^9 Z$ ]2 ~ - EC) != HAL_OK)
1 H. |# o5 t. H) |- i4 a - {3 ~! Y% H& c: A! C) q: y* ^
- Error_Handler();7 D7 |# }. X6 [
- }
} e! S1 q5 m" w8 k - //…
复制代码 , C* V3 I7 ^ ?
如上代码所示,在代码中配置DMA通道处(红色标记处),将其改为配置特权属性。然后编译后再次测试,这次则能正常发送数据了:
5 G- I. D: x; Y▲ 图 9 串口正常发送数据 " O. S" y* n8 y
9 @- p2 X8 ]1 m. y
03
' u$ z: {2 d5 _- C5 C小结1 Q9 } L% k( f& p# u( e1 B7 D, \
在trustzone使能的情况下,MCU内的资源,若代码中没有显式设置的情况下,要注意其默认状态,内部flash,SRAM,片外存储映射区域以及GPIO引脚均为安全的,且特权,Securable外设基本都是非安全的,在STM32U5安全培训的一页PPT中有做总结,如下图所示:
( R0 P6 V, K8 E0 N0 |本文所描述的问题,正是由于MPCBB2(Boot工程对应的RAM)没有配置,其默认为Secure+Prileged属性,但DMA默认属性为NonSecre+NonPrileged。代码中只配置DMA通道为Secure属性,并没有配置其为Prileged,因此导致其访问SRAM2时失败,这也就是为什么发送内容全为0的原因了。这也提醒我们,在开发trustzone相关的项目时,对各个资源的安全属性配置一定要小心。
' d- i' h8 X; x) h0 {7 a
0 R) q2 }! h3 q1 A" x5 `( j+ Y, r, X |