01
, E1 G! m. |5 D1 r问题描述" Q4 H7 X7 a( p* [1 [
客户要在STM32U5 SBSFU的基础上开发自己的项目,SBSFU由三个工程组成,Boot,APP,Loader。其中的Boot工程由开源项目MCU boot移植而来,它负责整个系统的安全启动以及升级部分。客户想在此基础上新增一个串口通信,不使用DMA模式的情况下,串口能够正常工作,但切换到DMA模式的时候就不正常了,PC端连接串口发送引脚,显示接收的数据全为0。如下图所示:! C0 q8 H- X+ H8 S* L# `
▲ 图1 SBSFU Boot运行的置log信息及串口收到的数据全为0 02
8 O* c3 j) i$ {! r问题分析及定位3 ^4 Y# _& G$ P
此问题看起来应该是DMA哪里配置出了问题。为了快速找到问题,不如先从STM32CubeMx重新生成一个基于trustzone的USART且带DMA的测试工程,等到测试工程测试通过后,直接移植到boot工程内不就完了?* j$ Q8 x/ R1 F) o" e
于是我直接使用STM32CubeMx新建一个工程,使用LPUART1外设PA2,PA3引脚。并分配其为M33 Secure。配置115200 bps。1+8+1数据格式。
5 W) B0 j$ j6 P" p1 h2 I- P▲ 图2 串口基本配置
; M# X4 C$ ~! z) B7 g" k5 t6 H2 N( k5 a) e
在其DMA配置页面下,点Go to GPDMA1,切换到GPDMA1配置:在上面Mode页面下激活通道0和通道1,并将通道0分配给LPUSART1_TX:
3 `. m" v8 G# b& @9 [2 k4 G▲ 图3 通道0分配给串口发送 ) o8 E. \$ e% U) k/ m0 K
* M5 _' y5 `) r9 F▲ 图4 通道1分配给串口接收 在security页面下配置通道,目标和源均为“secure”:- Q- n A- @" {) _/ Y
▲ 图5 DMA通道的安全属性配置
) U3 F# V5 y5 `1 M- ]; w1 W0 r7 O+ ]- V
然后就是配置内存了。在ld文件中配置使用的内存是SRAM1:% m ?' i3 I# o5 w' C
- /* Memories definition */* P( W- h V! U( ^. w$ n2 }
- MEMORY8 L0 L# R5 @2 |5 f/ ^: C4 s! q, n/ d, {
- {
/ Z5 m7 @: d: H - RAM (xrw) : ORIGIN = 0x30000000, LENGTH = 192K ! M- i0 J0 m- w8 @; X# p
- /* Memory is divided. Actual start is 0x30000000 and actual length is 768K */
$ H6 g7 P# m7 V% W4 @- H/ B - SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 16K
6 Y" _% l/ c" p, M" j' i. F - FLASH (rx) : ORIGIN = 0x0C006000, LENGTH = 500K # O' g$ | L$ n# v0 o
- /* Memory is divided.
( B- t' J2 B. N* w0 n1 x - Actual start is 0x0C000000 and actual length is 2048K */
! I7 Y1 a. u$ J$ T! g0 b* h& p8 Q - FLASH_NSC (rx) : ORIGIN = 0x0C0FE000, LENGTH = 8K. _; I/ X# c+ Q, }' y3 J
- /* Non-Secure Call-able region*/
% k# l4 p. U& q$ N+ o* i+ U1 ~. u - }
复制代码 3 l! ]/ @% k: O, M0 M0 d. F
然后在GTZC中配置MPCBB1:
2 a, b- Z; p% e) a7 m2 \* F; E
1 z! W* H Z$ E+ E, a5 Z
7 v: V1 a3 ?3 `" q▲ 图 6 GTZC下MPCBB1配置
3 L5 f4 j2 ^8 H, u8 J, Q; W6 Y/ R( |1 r) F) E
在MPCBB1中将192KB的SRAM1配置为全部安全。
$ D" ?+ g+ L& n2 D4 J) a然后生成测试工程。9 {* c4 V5 G0 x1 A7 e3 y. J: e/ G
为了让测试代码跟boot工程保护一致,我们也让测试工程从0x0C006000,见之前ld文件中FLASH的定义范围。. J! D) t9 t8 [
还需要在修改向量偏移位置到0x6000处:
% D7 h; k) N7 Z8 J#define VECT_TAB_OFFSET 0x00006000UL
8 O4 D$ g6 ], m% f" n* E) b F9 Z然后在main函数处增加测试代码:2 O" e; Q- `) {' d6 c' t" D6 d# [2 }* S
- void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)! R- Y9 d- _- C: m/ B+ n+ k7 Z
- {; u1 Y7 _: [6 L! K
- txPendingFlag =0;! s2 z6 U N) \# H, P
- }1 Q# h& x+ o L
- int main(void)! n9 d4 H: W3 O0 u6 x9 r9 J
- {! E6 Z! s+ L' {
- //…
( c0 A" t" w& o1 H" g1 { - while (1)0 z; }+ Z K( t& O1 W: g6 B! A
- {
; p3 [. B3 v, Z - if(txPendingFlag==0); u S9 Q+ t& O/ `
- {
( N, y1 `9 L# \8 `. d2 q - txPendingFlag =1;3 C( z$ a' R; g& n* p; C+ w( [
- HAL_UART_Transmit_DMA(&hlpuart1, (uint8_t *)aTxBuffer, sizeof(aTxBuffer));
0 Z3 L: o7 k1 Z9 B. T+ |% [$ \) f# u - } 8 C, t' G2 j4 A
- HAL_Delay(100);
L) j5 s* l+ h7 v, b' Z - }
8 |* S; y- B6 h+ Q I, e1 [; T - //…% K, y1 j- B4 b/ x. c. @4 b# }% b& w
- }
复制代码 让主函数周期性地从串口发送数据。为了让程序正常运行,需要使用STM32CubeProgrammer修改MCU从0x0C00 6000地址启动(主要为了与SBSFU_Boot保持一致):
: m/ b+ X: p7 ]& z▲ 图7 让MCU从0x0C00 6000地址启动 # }9 O+ J6 C2 Q# M8 g
; b4 W2 x2 k. @0 {8 {' Q& G编译并烧录程序,并让程序运行起来后,使用示波器测量PA2引脚的波形,结果一切正常。5 E. K- [+ I, |+ r2 P
于是接下来移植到SBSFU_Boot工程。! z6 K* v" ~* K
由于在测试工程中已经测试OK,按理说,移植到boot工程后,带DMA的串口也应该发送正常。但实际测试却是跟客户所描述一样,发送内容全为0!
7 O$ O" e# M4 L6 a: P* Q4 w问题出在哪里?5 R# S, }7 w0 a0 p- i# |. }
仔细对比两者的差别,发现测试工程是有配置GTZC。MPCBB1的,对应SRAM1,但是Boot工程,它使用的是SRAM2,代码并没有配置MPCBB2,其初始化函数:2 p0 T; F# P& K' a$ J1 |/ j
- static void gtzc_init_cfg(void)
i! W9 M7 Y' h7 p - {- g+ a2 [" ~) x6 d
- __HAL_RCC_GTZC1_CLK_ENABLE(); z# T4 s/ P O+ v
- #ifdef TFM_ERROR_HANDLER_NON_SECURE
+ D0 ]/ ], o$ P! m1 u - /* unsecure only one block in SRAM1 */
, s8 \$ [# d& B+ J. z3 e. N, i2 k - GTZC_MPCBB1_S->SECCFGR[0] = 0xfffffffe;: i1 k, d( M0 `! D1 ?9 P
- #endif, z0 ~+ J0 F* {. v0 G2 ?
- /* TFM_ERROR_HANDLER_NON_SECURE */
# t5 O) |% a, s - }
复制代码
8 N0 z0 {. \8 {0 x) N* C9 j在宏TFM_ERROR_HANDLE_NON_SECURE打开的情况下也只会配置MPCBB1,并没有配置MPCBB2。因此,SRAM的安全及特权属性应该是保留为默认属性,那么其默认属性是怎么定义的呢?- W1 F+ k2 R% p4 Q
在参考手册RM0456 Rev 4第5.4.6节中,我们找到如下内容:$ g7 h0 ]0 F I: |. j2 z
▲ 图 8 SRAM的默认属性定义 # ^1 Q6 y1 \5 E& `- r; _/ X
0 {8 |6 ~/ ^/ o; ]6 L% p这说明,在默认情况下(TZEN),SRAM2的默认属性是Secure+Privieged的。之前在测试代码对应的STM32CubeMx配置中,我们配置了通道,目标及源为安全属性,但并没有配置其特权属性。在CubeMx中其默认为非特权,见之前的图5所示。也就是说,我们需要将通道也配置成特权属性,于是直接修改代码:; J7 p d- ]5 [6 F% w# Q
- //…
. j' B$ ]& D: C0 M# J. e( M - __HAL_LINKDMA(huart, hdmarx, handle_GPDMA1_Channel1);
" L ?7 s8 A* i. n4 c5 U( T - if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel1,
3 b: A4 @, w v N* ] - DMA_CHANNEL_PRIV|DMA_CHANNEL_SEC|DMA_CHANNEL_SRC_SEC|DMA_CHANNEL_DEST_S
: m9 X; I# @; a- [ - EC) != HAL_OK)& q% Q4 e6 q* v% q+ A
- {5 k, X) Q: T# }" i& C4 j3 i! `
- Error_Handler();
! V k+ i) H/ P, I! b - }' ?% s4 k0 ^0 Z' F
- //…
" [0 R( [# v- b; V - __HAL_LINKDMA(huart, hdmatx, handle_GPDMA1_Channel0);
6 ~, x7 q7 M& v* {# u6 ` - if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel0,- [, n$ p7 k! o2 m; k R6 F) k
- DMA_CHANNEL_PRIV|DMA_CHANNEL_SEC|DMA_CHANNEL_SRC_SEC|DMA_CHANNEL_DEST_S
# z2 y; Z, s, P* O* i7 U' G - EC) != HAL_OK)
" x/ h! F7 ^( Z0 y6 p; D/ F/ N - {
9 Q- V4 p- B/ P1 g - Error_Handler();' k+ \, h0 W1 [+ H1 C
- }$ e& K+ w3 v. T# X" x" o/ a; \6 H
- //…
复制代码
! |5 Q9 ~3 ?3 l5 ]' a如上代码所示,在代码中配置DMA通道处(红色标记处),将其改为配置特权属性。然后编译后再次测试,这次则能正常发送数据了:
- ]3 @2 }" i$ } K" C8 c▲ 图 9 串口正常发送数据 , H3 E0 b- A1 c
; D/ }5 y1 s' p1 S; M0 j
038 W9 N* Y2 T7 m) S7 q) c: v
小结
2 T$ E9 K% {% X9 y, e在trustzone使能的情况下,MCU内的资源,若代码中没有显式设置的情况下,要注意其默认状态,内部flash,SRAM,片外存储映射区域以及GPIO引脚均为安全的,且特权,Securable外设基本都是非安全的,在STM32U5安全培训的一页PPT中有做总结,如下图所示:
5 J; C+ m8 E) M! E本文所描述的问题,正是由于MPCBB2(Boot工程对应的RAM)没有配置,其默认为Secure+Prileged属性,但DMA默认属性为NonSecre+NonPrileged。代码中只配置DMA通道为Secure属性,并没有配置其为Prileged,因此导致其访问SRAM2时失败,这也就是为什么发送内容全为0的原因了。这也提醒我们,在开发trustzone相关的项目时,对各个资源的安全属性配置一定要小心。
! A& C4 Z. h8 ]0 q# _! i4 e6 e: u* m" R
0 S6 d( E) P+ { z% R9 L( j6 f |