01
: S& a8 a! W, [3 v3 [" U问题描述
& t, `: \/ I& F; m客户要在STM32U5 SBSFU的基础上开发自己的项目,SBSFU由三个工程组成,Boot,APP,Loader。其中的Boot工程由开源项目MCU boot移植而来,它负责整个系统的安全启动以及升级部分。客户想在此基础上新增一个串口通信,不使用DMA模式的情况下,串口能够正常工作,但切换到DMA模式的时候就不正常了,PC端连接串口发送引脚,显示接收的数据全为0。如下图所示:' ~6 `3 d* i% m
▲ 图1 SBSFU Boot运行的置log信息及串口收到的数据全为0 02
1 p, M* U- J. y9 p, U问题分析及定位
& s; S5 \. K& O此问题看起来应该是DMA哪里配置出了问题。为了快速找到问题,不如先从STM32CubeMx重新生成一个基于trustzone的USART且带DMA的测试工程,等到测试工程测试通过后,直接移植到boot工程内不就完了?
6 _5 ]+ V3 U4 u; ~6 v9 _3 J$ e0 }于是我直接使用STM32CubeMx新建一个工程,使用LPUART1外设PA2,PA3引脚。并分配其为M33 Secure。配置115200 bps。1+8+1数据格式。
8 B& _5 @2 a0 w* v/ \4 A) u▲ 图2 串口基本配置
$ z# R4 ]! Z( [
% y$ y/ d7 {+ ]! `8 f在其DMA配置页面下,点Go to GPDMA1,切换到GPDMA1配置:在上面Mode页面下激活通道0和通道1,并将通道0分配给LPUSART1_TX:: ~/ t k0 n3 C5 F$ C' K; V
▲ 图3 通道0分配给串口发送
9 i" F. \7 r" l* L( D& z$ {" c; J5 L6 F$ R$ A) O+ u
▲ 图4 通道1分配给串口接收 在security页面下配置通道,目标和源均为“secure”:7 N& S* ?- C6 Q+ d
▲ 图5 DMA通道的安全属性配置
1 A) _' }: z# U- Z! g) r, }- r" F9 a! }" O9 x3 X* S
然后就是配置内存了。在ld文件中配置使用的内存是SRAM1:! D6 W7 d: L) C9 R8 E2 }) c
- /* Memories definition *// N1 t* a" z8 t$ _
- MEMORY
5 b7 k- M% }; \! ~3 q - {
+ ^; R$ p" |( j$ K - RAM (xrw) : ORIGIN = 0x30000000, LENGTH = 192K
+ K# ~. P/ S5 r! R, f - /* Memory is divided. Actual start is 0x30000000 and actual length is 768K */
1 @- F5 k) I |2 N$ k9 P( o - SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 16K
. i. @3 s/ Z m; F) o$ q& `# N - FLASH (rx) : ORIGIN = 0x0C006000, LENGTH = 500K
! {( Z0 B. W$ O6 b4 U - /* Memory is divided.
% A0 p( r8 C8 ?! { S& O( n - Actual start is 0x0C000000 and actual length is 2048K */
* X, o& e8 E, L- B$ l - FLASH_NSC (rx) : ORIGIN = 0x0C0FE000, LENGTH = 8K
1 _5 I B& i3 S3 A8 ] - /* Non-Secure Call-able region*/
; t# ]( A. N h8 Z& N - }
复制代码
4 f3 @0 Y8 ]; X5 `( o& U3 ^2 }7 \8 i然后在GTZC中配置MPCBB1:
! v/ I8 d1 |; o5 Q7 P: k' ^& d/ @) U' g. U
! i: k+ K2 p( J" n
▲ 图 6 GTZC下MPCBB1配置 # G# @0 D5 s7 Y$ O
* D# f' W ]" @6 @9 j
在MPCBB1中将192KB的SRAM1配置为全部安全。
- J2 A+ `/ c/ J( C0 _& b0 l然后生成测试工程。
/ n, r K# w, R M- l为了让测试代码跟boot工程保护一致,我们也让测试工程从0x0C006000,见之前ld文件中FLASH的定义范围。8 [. F6 T, \# J. n b$ s7 J
还需要在修改向量偏移位置到0x6000处:9 T5 S1 u; V5 D' ^
#define VECT_TAB_OFFSET 0x00006000UL; G5 I9 M) _9 \
然后在main函数处增加测试代码:% M& i* V3 y' X# F1 w( O& i
- void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart), p3 q7 q& r8 X1 Z/ o( T
- {
0 f+ ]/ q, w1 \! L$ g& R0 n - txPendingFlag =0;
# H7 W L6 C! C, { - }
% n7 r5 ` [' i" D$ c1 O - int main(void); p' `7 {, H8 @/ @0 x1 L
- {
9 e5 ?) S7 K: g, X- V$ {+ q - //…) X# n5 m T0 F* E4 H! s5 z
- while (1)' V6 N& S- L( d- K
- { 7 c8 p' h5 I. B5 R/ z6 L0 a
- if(txPendingFlag==0)
3 y0 @* J: `7 [6 d - {
5 W' W9 m3 N" W9 R, M* u0 P& `, M - txPendingFlag =1;
+ H3 U' B( G2 ~1 }& } - HAL_UART_Transmit_DMA(&hlpuart1, (uint8_t *)aTxBuffer, sizeof(aTxBuffer));% f& U. l' k- ?' s7 \/ C
- }
1 [6 {" |& y8 [2 F# M' N - HAL_Delay(100);! n! T: m0 U; x
- }7 y- A0 Q3 Z+ E" w
- //…
0 T: E; c4 t2 E - }
复制代码 让主函数周期性地从串口发送数据。为了让程序正常运行,需要使用STM32CubeProgrammer修改MCU从0x0C00 6000地址启动(主要为了与SBSFU_Boot保持一致):
" V% N: W! u( R( b: h1 T6 a/ m C3 O$ a▲ 图7 让MCU从0x0C00 6000地址启动 5 U3 d6 ~6 I" o' T
" y/ p5 R% E6 R6 m6 z0 a
编译并烧录程序,并让程序运行起来后,使用示波器测量PA2引脚的波形,结果一切正常。
. m/ Z: g& Y9 i! J# A- D/ ]于是接下来移植到SBSFU_Boot工程。
8 L8 r8 e2 c* V: i) H! n5 T由于在测试工程中已经测试OK,按理说,移植到boot工程后,带DMA的串口也应该发送正常。但实际测试却是跟客户所描述一样,发送内容全为0!
! ?! y" }) U9 g/ L9 M, d- B+ J问题出在哪里?
7 Y4 v; s! d0 m$ Q( I$ X仔细对比两者的差别,发现测试工程是有配置GTZC。MPCBB1的,对应SRAM1,但是Boot工程,它使用的是SRAM2,代码并没有配置MPCBB2,其初始化函数:
& Z6 B1 Z# J% X |! v2 k- static void gtzc_init_cfg(void)
4 x8 P2 d' e# J6 n5 m - {8 L7 g& M$ ]5 m) a
- __HAL_RCC_GTZC1_CLK_ENABLE();
( m0 ^; O1 \. V, g - #ifdef TFM_ERROR_HANDLER_NON_SECURE0 c3 @* E1 j# z" ~
- /* unsecure only one block in SRAM1 */
6 K% g F/ K B2 H; `* H - GTZC_MPCBB1_S->SECCFGR[0] = 0xfffffffe;8 Y; P j7 v; b' W. C
- #endif; I3 Q, }' h: H
- /* TFM_ERROR_HANDLER_NON_SECURE */! V1 ^$ Q5 y- W, g
- }
复制代码
: h6 ]& |( @0 g7 l. W在宏TFM_ERROR_HANDLE_NON_SECURE打开的情况下也只会配置MPCBB1,并没有配置MPCBB2。因此,SRAM的安全及特权属性应该是保留为默认属性,那么其默认属性是怎么定义的呢?6 s7 A! V9 y- G# f% ]( {
在参考手册RM0456 Rev 4第5.4.6节中,我们找到如下内容:
) [5 w0 G$ S' ` D: @5 O3 X6 n▲ 图 8 SRAM的默认属性定义
/ C" U ]% D9 a. K+ @7 r/ Z4 M/ V8 W
这说明,在默认情况下(TZEN),SRAM2的默认属性是Secure+Privieged的。之前在测试代码对应的STM32CubeMx配置中,我们配置了通道,目标及源为安全属性,但并没有配置其特权属性。在CubeMx中其默认为非特权,见之前的图5所示。也就是说,我们需要将通道也配置成特权属性,于是直接修改代码:, C0 S4 a1 S. d- X. s
- //…* Q2 w0 K8 ~( q) H
- __HAL_LINKDMA(huart, hdmarx, handle_GPDMA1_Channel1);, {- {/ f) y* v6 D+ g0 U% }$ p% J
- if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel1,- f0 `! [# a. ~5 R
- DMA_CHANNEL_PRIV|DMA_CHANNEL_SEC|DMA_CHANNEL_SRC_SEC|DMA_CHANNEL_DEST_S1 ^! ]' G+ G2 C/ |
- EC) != HAL_OK)
6 o) t9 a/ T7 U5 j7 F( H/ l - {
8 h1 Z( G3 e5 z* {( Y0 l - Error_Handler();; @: I5 I; D. V# S: @5 P' C
- }
( H5 N. h& C4 H- z9 Y5 k& C - //…3 \( L; ]. f }( k9 M
- __HAL_LINKDMA(huart, hdmatx, handle_GPDMA1_Channel0);
0 d6 u9 s* a3 o5 n( P - if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel0,- S# N4 m' O* Q
- DMA_CHANNEL_PRIV|DMA_CHANNEL_SEC|DMA_CHANNEL_SRC_SEC|DMA_CHANNEL_DEST_S
% {! [ K3 ?& G - EC) != HAL_OK)
0 h, v% D4 Z% d7 `1 W7 j: K - {+ m5 |# v% K( l8 P o
- Error_Handler();
' r3 j; B/ u% k8 ]1 {) P - }, f; D% L7 D$ H J6 h
- //…
复制代码 # o/ b. U/ b1 _' @+ p: S' O* i
如上代码所示,在代码中配置DMA通道处(红色标记处),将其改为配置特权属性。然后编译后再次测试,这次则能正常发送数据了:
4 h8 D5 V# t2 ]' ^% P▲ 图 9 串口正常发送数据 & F6 L% W. P( X0 D0 |1 Y4 q: O7 a
& v) E% h+ ?, P03
/ _; p& M' V' ~- k! M6 ?' {0 x! w% I$ i小结- D3 j% I/ k! P# q }- G# L
在trustzone使能的情况下,MCU内的资源,若代码中没有显式设置的情况下,要注意其默认状态,内部flash,SRAM,片外存储映射区域以及GPIO引脚均为安全的,且特权,Securable外设基本都是非安全的,在STM32U5安全培训的一页PPT中有做总结,如下图所示:
1 ^. H# Y/ m6 C# ~5 y本文所描述的问题,正是由于MPCBB2(Boot工程对应的RAM)没有配置,其默认为Secure+Prileged属性,但DMA默认属性为NonSecre+NonPrileged。代码中只配置DMA通道为Secure属性,并没有配置其为Prileged,因此导致其访问SRAM2时失败,这也就是为什么发送内容全为0的原因了。这也提醒我们,在开发trustzone相关的项目时,对各个资源的安全属性配置一定要小心。! F4 K% F1 A, [! A8 X
* m) B, @# d& _2 ~
|