打印
[应用相关]

STM32 TrustZone 开发调试技巧 | TrustZone 环境中使用 RTOS

[复制链接]
78|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 STM新闻官 于 2023-12-20 14:19 编辑

一、引言
在之前的 STM32MCU TrustZone 开发调试技巧的系列文章中,我们已经介绍了 ARM CM33 内核 Trust Zone 特性,STM32MCU 的系统级 TrustZone 架构设计,TrustZone 环境下使用外设的注意事项,以及 HardFault 的处理和调试等内容。我们知道在某些较为复杂的应用中,往往还会用到 RTOS,在这个系列的最后一篇,我们将主要讨论 STM32MCU 应用程序开发中,在 TrustZone 环境下使用 RTOS 时的情况以及相关软件开发的一些注意事项,供开发者参考。

二、TrustZone 环境+RTOS 的软件架构通常情况下,在 TrustZone 环境中,Secure 安全侧的代码主要负责处理一些关键数据的操作和关键外设的管理和控制,而更多的业务逻辑相关的应用程序则会运行在 TrustZone 的Non-Secure 非安全侧。软件的开发模式也会由原来的单一应用程序工程变为安全加非安全两个工程的联合开发。系统复位后 CPU 总是从安全工程的 reset handler 开始运行,安全代码完成初始化以及系统的安全配置之后,调用非安全代码的 reset handler 切换到非安全状态,继而开始运行非安全侧代码,非安全代码执行过程中可能会调用安全侧代码提供的一些API 函数,这个过程类似图 1 所示。


2.1. TrustZone 环境中 NS 代码不使用 RTOS 的情况
在逻辑相对简单的应用中,非安全侧代码不使用 RTOS,应用程序代码完成初始化之后会进入一个主循环,顺序执行各种操作,其间可能被中断打断。此时安全和非安全工程之间的关系也比较简单,基本上涉及的是直接的函数调用,可能是从非安全侧调用安全侧的API,或者从安全代码调用非安全侧注册的回调函数,类似图 2 所示。


2.2. TrustZone 环境中在 NS 代码使用 RTOS 的情况
在处理逻辑更复杂的应用中,应用程序可能需要用到 RTOS 来同时执行多个任务,此时RTOS 的功能例如多线程管理、mutex、semaphore、消息队列等也会在非安全侧使用,非安全侧既有 RTOS 的内核,也有上层执行业务逻辑的线程,应用线程中可能调用安全侧提供的 API 函数,类似图 3 所示。

这时候安全侧依旧是没有 RTOS 的环境,和裸跑时候一样不运行任何的调度器(scheduler),安全侧本身没有多线程的概念,也没有一直运行的任务,安全侧代码只是被动地提供一些 API 函数,供非安全侧软件来调用。这个过程看起来似乎与不使用 RTOS 的时候一样,但是一旦非安全侧有了 RTOS 和多线程的参与,而且线程又需要调用安全侧的 API,实际情况相比非安全侧裸跑就变得复杂了。接下来,我们来看几种可能的情况:
  • Case1:应用线程 1 中调用 S 安全侧 API,API 返回前没有出现线程调度
  • Case2:应用线程 1 中调用 S 安全侧 API,但是 API 返回前出现了线程调度,切换执行线程 2 任务,线程 2 没有调用 S 安全侧 API
  • Case3:应用线程 1 需要调用 S 安全侧 API,但是 API 返回前出现了线程调度,切换执行线程 2 任务,线程 2 也调用 S 安全侧 API

2.2.1. Case1:Thread1 调用 S 安全侧 API,返回前没有出现线程调度
第一种情况比较简单,在这个场景中 Thread1 会调用 S API,在其运行期间 CPU 会切换到 S 安全侧执行,S API 函数执行结束后返回 NS 非安全侧的 Thread1 继续执行,整个过程中没有出现线程调度,如图 4 所示。

这是一个相对简单的情况,因为 Thread1 调用 S API 的整个过程没有被打断,因此看起来 RTOS 内核不需要做特殊处理,线程可以正常执行。但是这里需要注意的一个问题是线程的 stack,因为当 Thread1 调用 S API 切换至 S 安全侧执行的时候,CPU 使用的堆栈将不再是 NS 非安全侧的线程堆栈(PSP_NS 所指向的 RAM 地址),而是会切换使用安全侧的堆栈(PSP_S 或者 MSP_S 所指向的位于 RAM 安全区的地址)。如果 RTOS 内核要管理线程stack,那么对于需要调用 S API 的那些线程,还需要管理他们在安全侧要用到的 stack。在这种 case 中,只要安全侧 API 函数有局部变量需要用到堆栈,RTOS 内核就需要为Thread1 分配 S 安全侧的内存,并且管理其对应的 PSP_S,以便 Thread1 需要调用 S API的时候,将能够使用 PSP_S 所指向的 S 安全侧的线程堆栈。


2.2.2. Case2:Thread1 调用 S 安全侧 API,返回前出现线程调度
第二种情况比第一种情况略微复杂,在这个场景中 Thread1 依旧会调用 S API,在其运行期间由于时间片切换等原因,CPU 会切换到 S 安全侧执行,但是 S API 函数执行尚未结束就出现了线程调度,NS 非安全侧 RTOS 内核的调度器将挂起 Thread1,并切换到Thread2 执行,Thread2 执行一段时间后再次出现线程调度,回到 Thread1 继续执行,如图5 所示。

我们知道通常情况下在 ARM CM33 内核进入调度器中断时,当前线程的上下文会压栈到PSP,调度器可能还会压栈其他一些寄存器的内容到 PSP 指向的 stack 中,然后通常会把当前线程 Thread1 的 PSP 指针记录在对应线程的数据结构中,接下来找到下一个需要启用的线程 Thread2,恢复 Thread2 的 PSP,这样从调度中断返回的时候,EXC_RETURN 加载到 PC,系统会回到线程模式,从 Thread2 的 PSP 指向的 stack 恢复线程上下文,继续从Thread2 执行。但是我们现在讨论的情况是内核使能了安全扩展,也就是 CPU 在 TrustZone 环境下运行,这时候进入线程调度中断时,EXC_RETURN 中除了有常规的数据,还同时记录了额外的信息,其中包括上下文压栈的使用的 stack 是 S 还是 NS 的指示。在上述的 Case2 场景中,Thread1 被打断时 CPU 正运行在 S 安全状态,EXC_RETURN 将标记压栈使用 S 安全侧 stack,当调度器恢复了 Thread2 的 PSP,系统从线程调度中断返回时,按照EXC_RETURN 的标记,CPU 将回到 S 安全态执行,而不是执行 Thread2 的上下文,这样会造成问题。所以对于这样的情况,RTOS 的内核调度需要对 EXC_RETURN 做处理,需要记录线程的 EXC_RETURN 值,在调起某个线程的时候,也要恢复对应的 EXC_RETURN,以便从中断退出时能够进入正确的线程运行状态(S 安全或者 NS 非安全)。

2.2.3. Case3:Thread1 调用 S API,返回前出现线程调度,Thread2 也调用 S
API第三种情况更加复杂,在 Case2 的基础上,如果 Thread2 也需要调用 S API,那么Thread2 也会切换到 S 侧执行,如图 6 所示。
这种情况中,Thread2 切换到 S 侧时,需要使用 Thread2 对应的安全侧 stack,当切换回 Thread1 时,需要使用 Thread1 对应的安全侧 stack,因此 RTOS 内核的线程调度器需要有能力为每个线程记录并恢复 PSP_S,而运行在 NS 非安全侧的 RTOS 内核线程调度器无法直接访问 PSP_S,因此,RTOS 内核也需要有 S 安全侧的一部分代码,来协助完成线程PSP_S 的记录和恢复操作,这样第三种情况会变成类似图 7 的样子,RTOS 线程调度器在S 安全侧也有一部分配合线程切换的代码,来管理线程的安全侧 PSP_S 的记录与恢复。
总结对上述三种情况的分析,我们可以看到,完整支持 CM33 内 TrusZone 的 RTOS 内核需要能够处理多个线程调用 S API 的情况,这可能包括对 EXC_RETURN 的处理,以及管理那些可能调用 S API 的线程在 S 安全侧需要使用的 stack 空间的分配以及以及对 PSP_S的操作和管理。那么实际中 TrustZone 环境下 NS 侧使用 RTOS 的样子可能如图 8 所示,RTOS 提供的上层功能如 thread,mutex,semaphore,message queue 等依旧是在非安全侧,由非安全侧应用程序使用,但是 RTOS 内核的调度器还有一部分代码运行于安全侧,用于配合辅助不同情况下的线程调度。


三、TrustZone 环境中 NS 非安全侧使用 RTOS 的注意事项
首先需要注意 RTOS 的版本,比较旧的版本可能还没包含对 ARM CM33 内核以及TrustZone 的支持。如果应用的项目是从某个过去的项目中迁移到 STM32MCU 的新产品,尤其使用了 TrustZone 的时候,建议检查原始工程中的 RTOS 版本,确认是否已经包含对CM33 内核以及 TrustZone 的支持。其次,要注意在安全和非安全工程中添加正确的 RTOS 代码。以 FreeRTOS 为例,它的portable 代码有多个目录,在 TrustZone 环境中应当使用 ARM_CM33 目录下的文件。
同时这个目录下又有两个子目录,分别对应 secure 和 non-secure,在构建应用工程的时候除了要添加 NS 非安全工程中的 FreeRTOS 代码外,还需要在安全工程中添加对应的secure 目录下的 FreeRTOS 代码。例如在 IAR 中工程包含的 FreeRTOS 源代码文件中将会类似图 10 所示。
最后还要注意的一点是关于安全侧的 stack 分配。如果 NS 非安全侧线程需要调用安全侧API,那么通常需要为该线程分配安全侧的 stack,该 stack 的大小由 S API 函数所需要的stack size 决定。依旧以 FreeRTOS 为例,FreeRTOS 提供了新的 API,用于线程分配其在安全侧的堆栈,需要调用 S API 的线程应当在其线程主函数的开始旧调用函数portALLOCATE_SECURE_CONTEXT(),分配安全侧的 stack,如图 11 所示。


四、小结
本文重点讨论了基于 ARM CM33 内核的 STM32MCU 支持 TrustZone 的环境下使用RTOS 的一些情况,并总结了 TrustZone 环境下使用 RTOS 的一些注意事项。某些 RTOS如果没有对 TrustZone 的非常完整的支持,使用起来可能会有些限制,这时候应用程序可能需要注意。例如通过添加 mutex 避免多个线程同时调用 S API 等。当然这里仅仅讨论了 NS侧使用 RTOS 的情况,也就是 S 安全侧只是提供函数由非安全侧调用,本身安全侧没有调度器没有线程概念,如果安全侧也包含了调度器(比如 TF-M)那么配合非安全侧使用RTOS 可能会是另一种情况。本文是 STM32MCU TrustZone 开发技巧的系列文章的最后一篇,希望对开发者有所帮助,也欢迎大家多提宝贵意见。
▼▼▼

《STM32 TrustZone 开发调试技巧 | TrustZone 环境中使用 RTOS》文档点击此处,下载手册


更多TrustZone技术内容,欢迎大家上STM32单片机B站观看STM32 MCU TrustZone培训课程》。(PC端点击蓝字观看)



使用特权

评论回复
沙发
STM新闻官|  楼主 | 2023-12-20 14:32 | 只看该作者

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:意法半导体(中国)投资有限公司
简介:您的嵌入式应用将得益于意法半导体领先的产品架构、技术、多源产地和全方位支持。意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器。

498

主题

625

帖子

16

粉丝