01
9 P3 P9 l/ W- F: ~问题描述
* Z- Q& h8 s8 G, ~( B客户要在STM32U5 SBSFU的基础上开发自己的项目,SBSFU由三个工程组成,Boot,APP,Loader。其中的Boot工程由开源项目MCU boot移植而来,它负责整个系统的安全启动以及升级部分。客户想在此基础上新增一个串口通信,不使用DMA模式的情况下,串口能够正常工作,但切换到DMA模式的时候就不正常了,PC端连接串口发送引脚,显示接收的数据全为0。如下图所示:
7 m9 E% Y3 b% q" ?3 \) y8 w$ a▲ 图1 SBSFU Boot运行的置log信息及串口收到的数据全为0 02; _# j+ k) V1 R) `9 T, O, d
问题分析及定位+ S; a Y& R+ q; l9 q, x
此问题看起来应该是DMA哪里配置出了问题。为了快速找到问题,不如先从STM32CubeMx重新生成一个基于trustzone的USART且带DMA的测试工程,等到测试工程测试通过后,直接移植到boot工程内不就完了?
/ Y: Y3 A& R9 y" |2 c F! F于是我直接使用STM32CubeMx新建一个工程,使用LPUART1外设PA2,PA3引脚。并分配其为M33 Secure。配置115200 bps。1+8+1数据格式。6 o" W# T! E) i
▲ 图2 串口基本配置
6 |6 Q) h* A0 ~6 s7 y
' @& Y0 J) Y7 n6 D. ?0 b在其DMA配置页面下,点Go to GPDMA1,切换到GPDMA1配置:在上面Mode页面下激活通道0和通道1,并将通道0分配给LPUSART1_TX:; a, s& x: C; J7 o8 I- E1 H
▲ 图3 通道0分配给串口发送 Y: V Y6 n. w2 a
& J) N9 M- X6 `- C' @- @% r▲ 图4 通道1分配给串口接收 在security页面下配置通道,目标和源均为“secure”:
1 W5 n; H# ^5 A% b9 e, O▲ 图5 DMA通道的安全属性配置 6 i7 Y0 I% @3 d3 |; d r+ { Y
5 K! s( E, ^6 @- p7 b. p! i然后就是配置内存了。在ld文件中配置使用的内存是SRAM1:- \" l- u$ E! w+ k8 i0 @- v l
- /* Memories definition */
. M7 D/ D4 D& C/ S. e2 i4 j - MEMORY
. }$ [- w5 S/ T5 _8 v - {
( f- G u& K. i% c% Q. V - RAM (xrw) : ORIGIN = 0x30000000, LENGTH = 192K
' \ ]3 X8 C2 w u1 P, C9 E - /* Memory is divided. Actual start is 0x30000000 and actual length is 768K */
* I' ?) M% i% O& K' @2 E% e; Z, j - SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 16K3 y, Q/ y. U* @6 w, l; v
- FLASH (rx) : ORIGIN = 0x0C006000, LENGTH = 500K 4 W! b4 [( e* ?+ Q* k$ l
- /* Memory is divided.# @7 {! J& }$ q% f7 t2 H9 ~# r
- Actual start is 0x0C000000 and actual length is 2048K */. c, L& {4 `( H' q- u% r. R4 H! r
- FLASH_NSC (rx) : ORIGIN = 0x0C0FE000, LENGTH = 8K
" E% P3 p: P, F4 R+ z; u4 q - /* Non-Secure Call-able region*/1 V% E" `' r6 [
- }
复制代码
" Z4 c2 T$ W9 L; g% e0 m7 Y- _然后在GTZC中配置MPCBB1:
& O( J4 E1 o/ ~2 {7 \- W) {, ^! U R) {3 x3 g
8 Z6 |9 Z5 U: W▲ 图 6 GTZC下MPCBB1配置 : Y l! y% g$ S/ ~
) l' l A, [* {- T+ b在MPCBB1中将192KB的SRAM1配置为全部安全。( O' [& s9 a1 H
然后生成测试工程。4 Y0 ?% O- J/ N& q) t" {: Z
为了让测试代码跟boot工程保护一致,我们也让测试工程从0x0C006000,见之前ld文件中FLASH的定义范围。# Z, C& j, [3 S) K& a
还需要在修改向量偏移位置到0x6000处:$ I* M w3 t8 i0 y
#define VECT_TAB_OFFSET 0x00006000UL
5 E8 c* R( M2 C M然后在main函数处增加测试代码:
) e6 K( ~5 j- \5 P$ v& P, W+ n$ s- void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)7 _% Q, n6 U- _* x8 w2 d2 p$ a
- {
, R* p: f/ o( O; b' ~ - txPendingFlag =0;
& z. {1 `! F4 C7 K4 q2 B - }% ^" x: G* L. V( G, ?+ `
- int main(void), r4 s9 \+ p/ K: M! V8 Q/ {6 y- z
- {
$ i+ V6 ~' k: _% ?$ E; I' C - //…8 S& p# k9 j& F4 s
- while (1)8 a- w( z" M6 |) f# s8 w. m# `
- {
7 p6 |) V+ v. `% ~1 Q - if(txPendingFlag==0)
9 S s! u* o' z- E( G/ {6 R - {& G+ T5 W; S* U) P
- txPendingFlag =1;- |" T' o; ~- y- L! k* E& H+ ?
- HAL_UART_Transmit_DMA(&hlpuart1, (uint8_t *)aTxBuffer, sizeof(aTxBuffer));5 ~: t, @! H8 ]
- }
) U; l3 ]4 D4 h1 m - HAL_Delay(100);
( U2 r9 v2 U+ x$ g; {# _5 E - }4 ?: b6 I, N4 e6 W/ U
- //…6 g% k8 U2 a" P: t# v8 U2 f
- }
复制代码 让主函数周期性地从串口发送数据。为了让程序正常运行,需要使用STM32CubeProgrammer修改MCU从0x0C00 6000地址启动(主要为了与SBSFU_Boot保持一致):
9 a+ i3 v" V$ ^( x+ a▲ 图7 让MCU从0x0C00 6000地址启动
6 w; W7 a" k5 F/ ^4 y. {# n) A7 F! h$ d0 d. z# |9 H6 H
编译并烧录程序,并让程序运行起来后,使用示波器测量PA2引脚的波形,结果一切正常。) ^ W0 h) e6 u. A' L! f
于是接下来移植到SBSFU_Boot工程。) U& z r6 x9 S8 r7 h+ f. @
由于在测试工程中已经测试OK,按理说,移植到boot工程后,带DMA的串口也应该发送正常。但实际测试却是跟客户所描述一样,发送内容全为0!
% s B- R; U; l2 Z% Q6 I问题出在哪里?7 M+ |% y4 k, P9 o
仔细对比两者的差别,发现测试工程是有配置GTZC。MPCBB1的,对应SRAM1,但是Boot工程,它使用的是SRAM2,代码并没有配置MPCBB2,其初始化函数:
) ?% }& O1 p; N; h/ V" T& W. Q) u- static void gtzc_init_cfg(void)! F0 m; i% {8 i, r5 y6 ?! ~
- {
: I% {) M1 ]- z - __HAL_RCC_GTZC1_CLK_ENABLE();
" T, V4 {/ Q0 W' j - #ifdef TFM_ERROR_HANDLER_NON_SECURE
$ Z7 {+ J* d( R0 h7 \# l3 I) W. ^( |9 C' _ - /* unsecure only one block in SRAM1 */9 |! y: l5 f* ^) i
- GTZC_MPCBB1_S->SECCFGR[0] = 0xfffffffe;" j$ c: b( `5 K% M3 ]' K9 Q
- #endif
) r' {5 ~, F2 {( F5 g5 R4 |: ] - /* TFM_ERROR_HANDLER_NON_SECURE */, Y. D6 H3 O: M6 T6 Q
- }
复制代码
% U8 j- ]' t. o( L在宏TFM_ERROR_HANDLE_NON_SECURE打开的情况下也只会配置MPCBB1,并没有配置MPCBB2。因此,SRAM的安全及特权属性应该是保留为默认属性,那么其默认属性是怎么定义的呢?
* P0 s$ {. {) D+ z在参考手册RM0456 Rev 4第5.4.6节中,我们找到如下内容:
2 Z7 V* }! ?5 W▲ 图 8 SRAM的默认属性定义
* _' P7 j Y& F3 [6 K) u S9 P6 s: t6 V- ~1 J
这说明,在默认情况下(TZEN),SRAM2的默认属性是Secure+Privieged的。之前在测试代码对应的STM32CubeMx配置中,我们配置了通道,目标及源为安全属性,但并没有配置其特权属性。在CubeMx中其默认为非特权,见之前的图5所示。也就是说,我们需要将通道也配置成特权属性,于是直接修改代码:% X' H' c$ y+ Z, f
- //…5 _+ j/ T7 V0 }; s
- __HAL_LINKDMA(huart, hdmarx, handle_GPDMA1_Channel1);3 g/ ]! f; s9 [: T, j- e! D& i
- if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel1,
$ [) C9 y" z2 y$ i - DMA_CHANNEL_PRIV|DMA_CHANNEL_SEC|DMA_CHANNEL_SRC_SEC|DMA_CHANNEL_DEST_S8 \* m. f, j' `% P
- EC) != HAL_OK)
- m" `; Y0 u& ^+ E m4 ~3 h D- e3 d- V - {5 z, \1 y: U3 O- g# T
- Error_Handler();
@+ \. p: P3 U5 Y, e - }
& [8 ]. V' c; {2 A9 n, T i - //…
( S/ c5 o0 w. ?4 o" I9 r# ]' K - __HAL_LINKDMA(huart, hdmatx, handle_GPDMA1_Channel0);
5 U) y$ j8 c' I( H/ y0 M2 j' W. N - if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel0,
& r9 ?3 y! X1 g2 J - DMA_CHANNEL_PRIV|DMA_CHANNEL_SEC|DMA_CHANNEL_SRC_SEC|DMA_CHANNEL_DEST_S
5 f! ^, N3 S0 E! f z. Y( [ - EC) != HAL_OK)
, @) t; `" H G) T3 Z - {
, } k8 A0 H d: n9 H8 I. [ - Error_Handler();
/ ]# _/ m- q- _/ D( _! R) j! n - }& j' c6 m) _8 V1 h, ~, ?
- //…
复制代码
3 K) V) c4 t7 A' ]如上代码所示,在代码中配置DMA通道处(红色标记处),将其改为配置特权属性。然后编译后再次测试,这次则能正常发送数据了:
# I) X- a3 D7 @5 Y* u" W▲ 图 9 串口正常发送数据
* ~3 y+ F* Q4 b1 {3 b$ T* |( S L/ R" R
039 l# ]# w; y! R$ {
小结8 L, t. t; y! l% F/ B1 C; k7 x# R7 K
在trustzone使能的情况下,MCU内的资源,若代码中没有显式设置的情况下,要注意其默认状态,内部flash,SRAM,片外存储映射区域以及GPIO引脚均为安全的,且特权,Securable外设基本都是非安全的,在STM32U5安全培训的一页PPT中有做总结,如下图所示:
4 H% A+ s6 a" t/ t, r) ^. q本文所描述的问题,正是由于MPCBB2(Boot工程对应的RAM)没有配置,其默认为Secure+Prileged属性,但DMA默认属性为NonSecre+NonPrileged。代码中只配置DMA通道为Secure属性,并没有配置其为Prileged,因此导致其访问SRAM2时失败,这也就是为什么发送内容全为0的原因了。这也提醒我们,在开发trustzone相关的项目时,对各个资源的安全属性配置一定要小心。7 U ~2 Q9 z7 x' r& [- f
. V1 ~6 M9 d3 S. T. ?
|