1.背景
当前工程基于STM32H563 TrustZone,分为Secure工程和Non-Secure共两个工程。
资源分配:Secure资源:TIM1、PD1、PF4。Non-Secure资源:TIM3、UART3、GPIO[PG0、PG4]。
功能目标:如何在不改变预定的各资源安全属性的前提下,将上述全部中断统一改在Secure侧响应。
调试工具及IDE环境:STM32CubeMxSTM32CubeIDE for vscode 插件 VSCODE 串口助手
2.实现原理
将全部中断切到Secure响应,不只是把IRQHandler()写到Secure工程。通常还要同时满足以下三个条件。
2.1 中断响应定位切到Secure侧
需要将相关IRQ的target state设为Secure,可在secure侧使用如下函数完成:NVIC_ClearTargetState(EXTI0_IRQn);// target EXTI0(PG0) to SecureNVIC_ClearTargetState(TIM3_IRQn);// target TIM3 to SecureNVIC_ClearTargetState(TIM1_UP_IRQn);// target TIM1 to Secure【它本就在secure侧】NVIC_ClearTargetState(EXTI1_IRQn);// target EXTI1(PD1) to Secure【它本就在secure侧】这样做就是将这些中断的硬件异常入口目标侧配置为Secure。
2.2 相关IRQ必须在Secure侧使能
原本某些IRQ的NVIC配置是在Non-Secure工程里完成的,那么切换到Secure响应后,Secure侧还必须重新使能这些中断响应。
本例程中,TIM3_IRQn和EXTI0_IRQn原本属于Non-Secure路径,因此在secure侧还须补充:HAL_NVIC_EnableIRQ(EXTI0_IRQn);HAL_NVIC_EnableIRQ(TIM3_IRQn);
2.3 若Secure侧基于HAL库操作Non-Secure外设,须建立最小HAL软件上下文。
对于TIM3,虽然TIM3位于Non-Secure,但中断已改到Secure处理。若在Secure侧继续调用HAL_TIM_IRQHandler(&htim3),则至少保证:htim3.Instance = TIM3;否则基于HAL库构建的中断入口调用HAL_TIM_IRQHandler()就会访问:htim->Instance->DIERhtim->Instance->SR此时,会因为Instance无效而触发异常。对于UART3,虽然UART3位于Non-Secure,但Secure侧的中断回调仍需要通过它输出字符串。
Secure侧如果要调用HAL_UART_Transmit(&huart3, ...),除了要绑定实例外:huart3.Instance = USART3;还要补齐最小HAL状态机上下文:huart3.gState= HAL_UART_STATE_READY;huart3.RxState = HAL_UART_STATE_READY;huart3.Lock= HAL_UNLOCKED;也就是说,Secure侧访问Non-Secure UART3,虽无须重新初始化硬件,但要在Secure侧建立一个最小可用的HAL句柄上下文。目的是为了符合HAL库相关状态机管理,否则无法正常调用HAL库API。
3.最终的关键配置
本次调试确认有效的关键参考代码如下。
3.1 在secure执行中断响应目标切换
NVIC_ClearTargetState(EXTI0_IRQn);// target EXTI0(PG0) to SecureNVIC_ClearTargetState(TIM3_IRQn);// target TIM3 to Secure
3.2 在Secure侧使能原本由NS管理的IRQ
HAL_NVIC_EnableIRQ(EXTI0_IRQn);HAL_NVIC_EnableIRQ(TIM3_IRQn);
3.3 在Secure侧补写最小TIM3 HAL句柄绑定
htim3.Instance = TIM3;
3.4 在Secure侧补写最小UART3 HAL句柄上下文【如不用HAL库另当别论】
huart3.Instance = USART3;huart3.gState= HAL_UART_STATE_READY;huart3.RxState = HAL_UART_STATE_READY;huart3.Lock= HAL_UNLOCKED;
4.代码建议
4.1 仅需在Secure侧的it.c文件里保留或增加必要的IRQHandler
/* EXTI Line0 interrupt */void EXTI0_IRQHandler(void){HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);}/* EXTI1 (PD1) handled in Secure domain */void EXTI1_IRQHandler(void){/* USER CODE BEGIN EXTI1_IRQn 0 *//* USER CODE END EXTI1_IRQn 0 */HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);/* USER CODE BEGIN EXTI1_IRQn 1 *//* USER CODE END EXTI1_IRQn 1 */}void TIM1_UP_IRQHandler(void){/* USER CODE BEGIN TIM1_UP_IRQn 0 *//* USER CODE END TIM1_UP_IRQn 0 */HAL_TIM_IRQHandler(&htim1);/* USER CODE BEGIN TIM1_UP_IRQn 1 *//* USER CODE END TIM1_UP_IRQn 1 */}/* TIM3 global interrupt */void TIM3_IRQHandler(void){HAL_TIM_IRQHandler(&htim3);}
4.2 Secure侧统一处理全部中断回调代码
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){if (htim->Instance == TIM1){/* toggle PF4 output */HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_4);uart_sync_lock();HAL_UART_Transmit(&huart3, (uint8_t *)"TIM1 event from Securernrn", sizeof("TIM1 event from Securernrn") - 1, 100);uart_sync_unlock();}else if (htim->Instance == TIM3){/* toggle PG4 output */HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_4);uart_sync_lock();HAL_UART_Transmit(&huart3, (uint8_t *)"TIM3 event from NSrnrn", sizeof("TIM3 event from NSrnrn") - 1, 100);uart_sync_unlock();}}void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin){if (GPIO_Pin == GPIO_PIN_1){uart_sync_lock();HAL_UART_Transmit(&huart3, (uint8_t *)"EXTI from PD1 Securernrn", sizeof("EXTI from PD1 Securernrn") - 1, 100);uart_sync_unlock();}else if (GPIO_Pin == GPIO_PIN_0){uart_sync_lock();HAL_UART_Transmit(&huart3, (uint8_t *)"EXTI from PG0_NSrnrn", sizeof("EXTI from PG0_NSrnrn") - 1, 100);uart_sync_unlock();}}
5、小结
经测试证明,在STM32H563 TrustZone工程中,将全部中断统一放在Secure侧响应是可以做到的【当然,原理上也是支持的】。对于从Non-Secure路径迁移到Secure响应的中断,需要注意中断响应的target、IRQ NVIC enable、以及最小HAL软件上下文三方面的调整及处理。
当然,如果反过来,将全部中断统一放在Non Secure侧响应也是可以的,或者说,全部中断兵分两侧也可以【注意,是分两侧放而不是同时放两侧】,最终视具体应用需求而定。分享上面实现过程,仅供参考。下次再聊~!
90