一、背景
本文基于STM32H563 TrustZone应用工程,工程分为Secure工程和Non-Secure工程。所用系统资源的安全属性划分如下表所示:
功能需求:在TIM1更新中断中,翻转 PF4,实现LED闪烁,并通过 UART3输出相应提示字符。在TIM3更新中断中,翻PG4,实现LED闪烁,并通过 UART3输出输出相应提示字符。
将 PD1、PG0配置为外部输入中断,并在各自中断中通过UART3输出字符串。这里将要实现的就是,在不改变外设自身安全属性的前提下,如何实现全部中断在Non-Secure侧响应。
下表罗列了目前所用到的外设安全属性、各中断响应入口、实际中断服务代码所在区域以及各中断服务程序要实现的简单功能。【注:PF4 PG4分别属于Secure和NonSecure属性】
二、中断响应与中断服务程序
在TrustZone工程中,关于中断可以区分为两个层面:
2.1 中断响应
指的是中断被触发后进入哪一侧的中断向量表寻求响应,具体就是获取中断程序入口地址。例如,现在将这些TIM1_UP_IRQn、TIM3_IRQn、EXTI0_IRQn、EXTI1_IRQn 的中断响应都定位【或说路由】到Non-Secure,那么中断被触发后,CPU就进入Non-Secure 侧的中断向量表里相应IRQ Handler入口地址。
2.2 中断服务程序ISR的执行
真正完成中断服务逻辑的代码在哪一侧执行,这要看与中断程序关联的资源属于哪一侧。以本示例来看:若中断ISR关联的是Non-Secure资源,直接在Non-Secure侧执行处理即可。若中断ISR关联的是Secure资源,先通过Non-Secure的中断入口程序做转发,真正的中断处理通过NSC调用Secure侧的功能代码完成。
三、中断服务程序的处理
让所有中断都在Non-Secure响应, 并不意味着把所有中断服务代码(ISR)都写在Non-Secure工程里。下面结合本示例的具体情况来看看。
3.1 对于Non-Secure外设的中断
例如TIM3、PG0,其ISR都放在NonSecure侧处理是没有问题的。
void TIM3_IRQHandler(void){HAL_TIM_IRQHandler(&htim3);}void EXTI0_IRQHandler(void){HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);}
因为这些中断处理函数访问的都是Non-Secure资源。
3.2 对于Secure外设的中断
例如TIM1、PD1/EXTI1,如果在Non-Secure中做中断服务处理,比如:
void TIM1_UP_IRQHandler(void){HAL_TIM_IRQHandler(&htim1);}void EXTI1_IRQHandler(void){HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);}
这时就会出问题。原因是:HAL_TIM_IRQHandler(&htim1)内部还会访问TIM1的寄存器;HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1)也会访问EXTI _1寄存器等。这些都涉及Secure资源。如果在Non-Secure侧直接执行,就是从NS侧访问S侧资源,这是不允许的。
四、参考实现
4.1 Non-Secure侧负责响应全部中断
先在Secure侧运行下面代码,将所有中断响应定位到NonSecure侧。【其它写法也行】
NVIC_SetTargetState(TIM1_UP_IRQn); //target TIM1 to non-secure zoneNVIC_SetTargetState(EXTI0_IRQn); //target EXTI0 (PG0) to non-secure zoneNVIC_SetTargetState(TIM3_IRQn); //target TIM3 to non-secure zoneNVIC_SetTargetState(EXTI1_IRQn); //target EXTI1 (PD1) to non-secure zone
将以下中断的响应入口统一放在Non-Secure侧:TIM1_UP_IRQHandler();TIM3_IRQHandler();EXTI0_IRQHandler();EXTI1_IRQHandler();另外,在S侧需要针对NS侧的UART3写几行代码:huart3.Instance = USART3;huart3.gState = HAL_UART_STATE_READY;huart3.RxState = HAL_UART_STATE_READY;huart3.Lock = HAL_UNLOCKED;目的是为了初始化 UART3 的HAL库软件状态机,让 Secure 侧能正常调用UART3相关API函数。当然,如果你直接操作寄存器应该是不用这几行的。
4.2 只涉及Non-Secure资源的中断处理
在Non-Secure侧直接处理相关中断:TIM3_IRQHandler()调用HAL_TIM_IRQHandler(&htim3)EXTI0_IRQHandler()调用HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0)
4.3 涉及Secure资源的中断处理
在Non-Secure的IRQ入口里,并不直接操作Secure外设,而是调用NSC函数,转发给Secure侧,在Secure侧执行真正的中断处理。即:TIM1_UP_IRQHandler()调用NSC函数SECURE_TIM1_UP_IrqService();EXTI1_IRQHandler()调用NSC函数SECURE_EXTI1_IrqService();这些NSC函数内部可以再调用:HAL_TIM_IRQHandler(&htim1);HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);然后可进一步触发Secure侧callback函数。
五、主要参考代码
5.1 Non-Secure侧:统一中断响应入口
在Non-Secure的stm32h5xx_it.c中,编写所有中断IRQ handler及必要代码。
void TIM3_IRQHandler(void){HAL_TIM_IRQHandler(&htim3);}void EXTI0_IRQHandler(void){HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);}
void TIM1_UP_IRQHandler(void){SECURE_TIM1_UP_IrqService();}void EXTI1_IRQHandler(void){SECURE_EXTI1_IrqService();}
5.2 Secure侧:通过NSC提供中断服务函数Secure侧不再于it.c文件中保留或编写硬件IRQ入口函数,只须在Secure侧的任一源文件中编写相应NSC服务函数,例如:
extern TIM_HandleTypeDef htim1;CMSE_NS_ENTRY void SECURE_TIM1_UP_IrqService(void){HAL_TIM_IRQHandler(&htim1);}CMSE_NS_ENTRY void SECURE_EXTI1_IrqService(void){HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);}
注:这些CMSE_NS_ENTRY函数不是中断向量入口,只是可被Non-Secure 侧调用的Secure服务函数,最终还可以触发Secure侧HAL callback 处理。
5.3 Secure侧的callback函数
TIM1更新回调函数和EXTI1中断回调函数:
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, TimeOut);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, TimeOut);uart_sync_unlock();}}
5.4 Non-Secure侧callback函数
TIM3更新回调函数和EXTI0中断回调函数:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){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, TimeOut);uart_sync_unlock();}}void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin){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, TimeOut);uart_sync_unlock();}}
六、小结
本示例虽然实现了中断响应统一在Non-Secure侧,即所有中断请求进入Non-Secure的向量表寻求响应,但并未把所有中断的完整处理都强行塞到Non-Secure侧。
对于只需访问Nonsecure侧的中断处理,直接在NonSecure侧完成;对于Nonsecure侧需要访问Secure资源的中断服务,通过NSC转到Secure执行。
最终形成了一套既满足功能需求、又符合TrustZone安全边界的实现方案。下面截图是最终的实现输出结果,至于闪灯就无视吧,搬不过来效果。
结尾前,顺便申明下,这里所说的全部中断特指可以定位到NS的中断,并不包括那些根本不可能定位到NS侧的中断。OK,今天的分享就聊到这里,供君参考,也算是抛砖引玉,下次再聊~!
130