实战经验 | TrustZone应用中串口通信的DMA传输失败问题

01、问题描述 

客户要在STM32U5 SBSFU的基础上开发自己的项目,SBSFU由三个工程组成,Boot,APP,Loader。其中的Boot工程由开源项目MCU boot移植而来,它负责整个系统的安全启动以及升级部分。客户想在此基础上新增一个串口通信,不使用DMA模式的情况下,串口能够正常工作,但切换到DMA模式的时候就不正常了,PC端连接串口发送引脚,显示接收的数据全为0。如下图所示:

1741830430328747.png

▲ 图1 SBSFU Boot运行的置log信息及串口收到的数据全为0

02、问题分析及定位

此问题看起来应该是DMA哪里配置出了问题。为了快速找到问题,不如先从STM32CubeMx重新生成一个基于trustzone的USART且带DMA的测试工程,等到测试工程测试通过后,直接移植到boot工程内不就完了?

于是我直接使用STM32CubeMx新建一个工程,使用LPUART1外设PA2,PA3引脚。并分配其为M33 Secure。配置115200 bps。1+8+1数据格式。

1741830448137432.png

▲ 图2 串口基本配置

在其DMA配置页面下,点Go to GPDMA1,切换到GPDMA1配置:在上面Mode页面下激活通道0和通道1,并将通道0分配给LPUSART1_TX:

1741830458935526.png

▲ 图3 通道0分配给串口发送

1741830473865088.png

▲ 图4 通道1分配给串口接收

在security页面下配置通道,目标和源均为“secure”:

1741830488872626.png

▲ 图5 DMA通道的安全属性配置

然后就是配置内存了。在ld文件中配置使用的内存是SRAM1:

/* Memories definition */ 
MEMORY 
{
    RAM (xrw)  : ORIGIN = 0x30000000, LENGTH = 192K     
    /* Memory is divided. Actual start is 0x30000000 and actual length is 768K */ 
    SRAM4 (xrw)    : ORIGIN = 0x38000000, LENGTH = 16K
    FLASH (rx)   : ORIGIN = 0x0C006000, LENGTH = 500K    
    /* Memory is divided. 
    Actual start is 0x0C000000 and actual length is 2048K */ 
    FLASH_NSC (rx) : ORIGIN = 0x0C0FE000, LENGTH = 8K
    /* Non-Secure Call-able region*/
}

然后在GTZC中配置MPCBB1:

1741830515989626.png

▲ 图 6 GTZC下MPCBB1配置 

在MPCBB1中将192KB的SRAM1配置为全部安全。

然后生成测试工程。

为了让测试代码跟boot工程保护一致,我们也让测试工程从0x0C006000,见之前ld文件中FLASH的定义范围。 

还需要在修改向量偏移位置到0x6000处:

#define VECT_TAB_OFFSET  0x00006000UL

 然后在main函数处增加测试代码:

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) 
{ 
    txPendingFlag =0; 
} 
int main(void)
{ 
    //… while (1) 
    {  
        if(txPendingFlag==0)   
        {    
            txPendingFlag =1;  
            HAL_UART_Transmit_DMA(&hlpuart1, (uint8_t *)aTxBuffer, sizeof(aTxBuffer));    
        }     
        HAL_Delay(100); 
    } //… 
}

让主函数周期性地从串口发送数据。为了让程序正常运行,需要使用STM32CubeProgrammer修改MCU从0x0C00 6000地址启动(主要为了与SBSFU_Boot保持一致):

1741830555836719.png

▲ 图7 让MCU从0x0C00 6000地址启动 

   编译并烧录程序,并让程序运行起来后,使用示波器测量PA2引脚的波形,结果一切正常。

于是接下来移植到SBSFU_Boot工程。

由于在测试工程中已经测试OK,按理说,移植到boot工程后,带DMA的串口也应该发送正常。但实际测试却是跟客户所描述一样,发送内容全为0!

问题出在哪里?

仔细对比两者的差别,发现测试工程是有配置GTZC。MPCBB1的,对应SRAM1,但是Boot工程,它使用的是SRAM2,代码并没有配置MPCBB2,其初始化函数:

static void  gtzc_init_cfg(void)
{
  __HAL_RCC_GTZC1_CLK_ENABLE(); 
  #ifdef TFM_ERROR_HANDLER_NON_SECURE 
  /*  unsecure only one block in SRAM1 */ 
  GTZC_MPCBB1_S->SECCFGR[0] = 0xfffffffe; 
  #endif 
  /*  TFM_ERROR_HANDLER_NON_SECURE */ 
}

在宏TFM_ERROR_HANDLE_NON_SECURE打开的情况下也只会配置MPCBB1,并没有配置MPCBB2。因此,SRAM的安全及特权属性应该是保留为默认属性,那么其默认属性是怎么定义的呢?

在参考手册RM0456 Rev 4第5.4.6节中,我们找到如下内容:

8.png

▲ 图 8 SRAM的默认属性定义

这说明,在默认情况下(TZEN),SRAM2的默认属性是Secure+Privieged的。之前在测试代码对应的STM32CubeMx配置中,我们配置了通道,目标及源为安全属性,但并没有配置其特权属性。在CubeMx中其默认为非特权,见之前的图5所示。也就是说,我们需要将通道也配置成特权属性,于是直接修改代码:

//…
__HAL_LINKDMA(huart, hdmarx, handle_GPDMA1_Channel1); 
if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel1, DMA_CHANNEL_PRIV|DMA_CHANNEL_SEC|DMA_CHANNEL_SRC_SEC|DMA_CHANNEL_DEST_S EC) != HAL_OK) 
{ 
    Error_Handler(); 
} 
//… 
__HAL_LINKDMA(huart, hdmatx, handle_GPDMA1_Channel0); 
if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel0, DMA_CHANNEL_PRIV|DMA_CHANNEL_SEC|DMA_CHANNEL_SRC_SEC|DMA_CHANNEL_DEST_S EC) != HAL_OK) 
{ 
    Error_Handler(); 
} 
//…

如上代码所示,在代码中配置DMA通道处(红色标记处),将其改为配置特权属性。然后编译后再次测试,这次则能正常发送数据了:

1741830605755366.png

▲ 图 9 串口正常发送数据

03、小结

在trustzone使能的情况下,MCU内的资源,若代码中没有显式设置的情况下,要注意其默认状态,内部flash,SRAM,片外存储映射区域以及GPIO引脚均为安全的,且特权,Securable外设基本都是非安全的,在STM32U5安全培训的一页PPT中有做总结,如下图所示:

1741830631333688.png

本文所描述的问题,正是由于MPCBB2(Boot工程对应的RAM)没有配置,其默认为Secure+Prileged属性,但DMA默认属性为NonSecre+NonPrileged。代码中只配置DMA通道为Secure属性,并没有配置其为Prileged,因此导致其访问SRAM2时失败,这也就是为什么发送内容全为0的原因了。这也提醒我们,在开发trustzone相关的项目时,对各个资源的安全属性配置一定要小心。

来源:STM32

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。