TrustZone

一、引言

在之前的 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 所示。

1.png

2.1. TrustZone 环境中 NS 代码不使用 RTOS 的情况

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

2.png

2.2. TrustZone 环境中在 NS 代码使用 RTOS 的情况

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

3.png


这时候安全侧依旧是没有 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 所示。

4.png


这是一个相对简单的情况,因为 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 所示。

5.png


我们知道通常情况下在 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 所示。

6.png


这种情况中,Thread2 切换到 S 侧时,需要使用 Thread2 对应的安全侧 stack,当切换回 Thread1 时,需要使用 Thread1 对应的安全侧 stack,因此 RTOS 内核的线程调度器需要有能力为每个线程记录并恢复 PSP_S,而运行在 NS 非安全侧的 RTOS 内核线程调度器无法直接访问 PSP_S,因此,RTOS 内核也需要有 S 安全侧的一部分代码,来协助完成线程PSP_S 的记录和恢复操作,这样第三种情况会变成类似图 7 的样子,RTOS 线程调度器在S 安全侧也有一部分配合线程切换的代码,来管理线程的安全侧 PSP_S 的记录与恢复。

7.png


总结对上述三种情况的分析,我们可以看到,完整支持 CM33 内 TrusZone 的 RTOS 内核需要能够处理多个线程调用 S API 的情况,这可能包括对 EXC_RETURN 的处理,以及管理那些可能调用 S API 的线程在 S 安全侧需要使用的 stack 空间的分配以及以及对 PSP_S的操作和管理。那么实际中 TrustZone 环境下 NS 侧使用 RTOS 的样子可能如图 8 所示,RTOS 提供的上层功能如 thread,mutex,semaphore,message queue 等依旧是在非安全侧,由非安全侧应用程序使用,但是 RTOS 内核的调度器还有一部分代码运行于安全侧,用于配合辅助不同情况下的线程调度。

8.png

三、TrustZone 环境中 NS 非安全侧使用 RTOS 的注意事项

首先需要注意 RTOS 的版本,比较旧的版本可能还没包含对 ARM CM33 内核以及TrustZone 的支持。如果应用的项目是从某个过去的项目中迁移到 STM32MCU 的新产品,尤其使用了 TrustZone 的时候,建议检查原始工程中的 RTOS 版本,确认是否已经包含对CM33 内核以及 TrustZone 的支持。

其次,要注意在安全和非安全工程中添加正确的 RTOS 代码。以 FreeRTOS 为例,它的portable 代码有多个目录,在 TrustZone 环境中应当使用 ARM_CM33 目录下的文件。

9.png


同时这个目录下又有两个子目录,分别对应 secure 和 non-secure,在构建应用工程的时候除了要添加 NS 非安全工程中的 FreeRTOS 代码外,还需要在安全工程中添加对应的secure 目录下的 FreeRTOS 代码。例如在 IAR 中工程包含的 FreeRTOS 源代码文件中将会类似图 10 所示。

10.png


最后还要注意的一点是关于安全侧的 stack 分配。如果 NS 非安全侧线程需要调用安全侧API,那么通常需要为该线程分配安全侧的 stack,该 stack 的大小由 S API 函数所需要的stack size 决定。依旧以 FreeRTOS 为例,FreeRTOS 提供了新的 API,用于线程分配其在安全侧的堆栈,需要调用 S API 的线程应当在其线程主函数的开始旧调用函数portALLOCATE_SECURE_CONTEXT(),分配安全侧的 stack,如图 11 所示。

11.png

四、小结

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

本文是 STM32MCU TrustZone 开发技巧的系列文章的最后一篇,希望对开发者有所帮助,也欢迎大家多提宝贵意见。

来源:STM32

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

围观 23

01、简介

客户使用 STM32U5 进行开发,并使能了 TrustZone 架构,程序需要从 bootloader 跳转到app。在之前版本都是正常跳转的,某一天 IAR 从 9.20 升级到 9.30 后,程序跳转失败,并且会导致 hardfault,想知道为什么会失败。

1.png

图1.IAR9.20 和 IAR9.30 生成的汇编代码对比

02、问题分析

通过断点和单步调试,我们发现出现问题的指令如下所示:

2.png

图2.程序下一步将 Hardfault

而没有发生 hardfault 的版本汇编代码,如下图:

3.png

图3.程序不会发生 Hardfault

通过单步调试,我们知道了 VLSTM SP 这条指令导致了 hardfault。接着我们再确认下 SP 指针,错误版本的 SP 的内容为:0x300020b4,正确版本的 SP 内容为:0x30000258。首先,我们对比了生成的 map 文件中 stack 的地址信息,发现其中 Stack 的地址和这里 SP 指令是相符的。

然后继续查找了 VLSTM 这条指令相关的描述,关于 VLSTM 在 PM0264 中有以下描述:

4.png

图4.关于 VLSTM 指令

从上图可以看到,VLSTM SP 这条指令会把安全的浮点运算寄存器的值保存到 SP 地址中,并清除安全浮点寄存器的内容,如果 CPU 的状态是非安全的,那么这条指令相当于空指令,也不会导致 hard fault,所有从这里也还是分析不出为什么会导致 hard fault。

重新回到这条指令,现在问题可能比较大的就是 SP 的地址了。有问题的版本的 SP 内容为:0x300020b4,会不会是对齐导致的呢? 

基于这个猜测,我们直接在 IAR 界面强制修改了 SP 的地址为 0x300020b8,并继续单步执行,然后程序可以正常执行了。所以目前所知的结论就是 VLSTM SP 这条指令,要求 SP 必须 8 字节对齐,可能 IAR 在编译的时候并没有注意到这一点。

然后,把这些信息反馈到 IAR 以后,IAR 的工程师回复如下: 

根据目前的信息,问题应该是在 VLSTM 要求 8 字节对齐上。在 9.30.1 中,由于 PUSH.W {R4, R5, R7-R11}指令执行后,相当于占用了 28 个字节的栈空间,导致了 SP 和 9.20.1 相比,不是 8 字节对齐。

03、总结

在调试 TrustZone 工程的时候,由于使用了新的架构及新的汇编指令,需要对这些指令有一定基本的了解。在调查问题的时候,可以进行单步调试来定位发生问题的指令,然后再继续深入了解下为什么会导致 hardfault。

来源:STM32单片机

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

围观 15

TrustZone

恩智浦基于Cortex M33内核的MCU,LPC55S6x/LPC55S1x, RT600/RT500等产品提供了对TrustZone的支持,并在SOC上提供了安全AHB控制器等功能,旨在帮助客户完成良好的安全隔离,并建立可信执行环境。

在产品设计之初,我们就要有一个基本的“隔离”的概念。

需要考虑的问题大致有以下两个方面:

  • 产品中的哪些功能和模块应该放在安全区?(这部分代码往往是核心且精简,且经过安全审查的,安全区的内容不允许非安全区的代码触碰);
  • 哪些功能应该放在非安全区?(这样放在非安全区中的代码,即使出现安全漏洞(例如栈溢出漏洞等等)后被攻击,MCU的安全区中的资源和外设也无法被攻击者利用)。

令人头疼的HardFault

对于MCU工程师,TrustZone是一个比较新的技术,在开发调试过程中,由于TrustZone配置问题,时常遇到各种意料之外的HardFault,我们也经常调侃,这个TrustZone也太安全了,连我们自己都进不去。

其实这种问题往往是由于在开发阶段,我们实际的行为和我们对TrustZone的划分不一致。

平常我们认为正常的行为与操作,对于TrustZone来说,已经越界违规了,这就会使TruztZone触发HardFault,并阻止违规行为。

在这种情况下,我们就需要找到导致HardFault的原因,并调整对于TrustZone的配置。

注:TrustZone相关的故障会触发SecureFault异常。在芯片上电后的默认情况下,SecureFault异常并没有被使能,因此,此异常会被“升级”为HardFault。在通常情况下,建议使用默认的设置,因为HardFault拥有更高的中断优先级,可以使故障在第一时间被响应。

获取违规操作的蛛丝马迹

Cortex M33内核本身在SAU中提供了两个寄存器:SFSR(Secure FaultStatus Register,安全故障状态寄存器)和SFAR(Secure Fault Address Register,安全故障地址寄存器)。

“调试TrustZone时,如何处理HardFault?"

SFSR寄存器用于指示出现错误的类型,例如非安全区试图访问安全区,从安全区到非安全区的非法跳转等。

SFAR寄存器用于指示出现错误的内存地址。

看起来这两个寄存器就足以帮助我们查到问题的根源了。但是,有时候,我们从这两个寄存器拿到了错误的原因和地址,仔细检查后发现无论是安全区还是非安全区的程序,都没有显式的访问这个地址。

这是由于MCU系统愈发复杂,总线上除了M33内核之外,还有许许多多的其他的外设,例如DMA,USB等等。肇事者不一定是M33内核,还有可能是其他的外设,例如DMA。

下面是一种常见的事故:被划分为非安全的DMA在工作中访问了安全区的地址或外设,违反了TrustZone的配置,造成了HardFault。

恩智浦的MCU在SOC层面提供了一个安全AHB控制器,能够帮我们侦查肇事现场,找出肇事者的蛛丝马迹。

其实原理很简单,安全AHB控制器提供了三个寄存器,SEC_VIO_INFO_VALID,SEC_VIO_MISC_INFO和SEC_VIO_ADDR。

SEC_VIO_INFO_VALID用来指示肇事现场,这个寄存器中存储了肇事现场的AHB 的端口号(port number),端口号与外设的对应关系参见用户手册的”Memory map overview”章节。

每一个AHB端口都相应有一个SEC_VIO_MISC_INFO寄存器,用来指示肇事的信息,例如违规操作是读操作引起的还是写操作引起的等等,最重要的是会指出肇事者的身份:

“调试TrustZone时,如何处理HardFault?"

每个AHB端口还有一个SEC_VIO_ADDR,用来指示肇事现场的地址。

有了上述信息,我们就捉到了真正的肇事者。在开发阶段,我们就可以利用这些信息去调整我们的TrustZone的配置。

更进一步

当然,上述功能不只能在开发阶段帮我们排查HardFault。我们也可以利用这个机制在产品出厂之后为我们提供防御措施。

产品出厂后,当我们检测到由TrustZone产生的HardFault的原因之后,可以将其记录,以便于后续分析。如果设备有联网能力,可以将其传输至服务器。利用这些信息我们可以发现产品的哪些模块受到了攻击,方便我们后续针对性地进行OTA升级。云端和设备本地也可在此时检测镜像以及存储介质的完整性,以检查程序和存储介质是否被恶意篡改。

小结

综上所述,利用SAU和安全AHB控制器调查HardFault的方法并不复杂,NXP的MCUXpresso SDK也提供了一个完整的demo。

以LPC55S69为例,demo的路径如下:SDK\boards\lpcxpresso55s69\trustzone_examples\secure_faults。

这个demo中,人为制造了几种TrustZone触发HardFault的案例,在产生HardFault后,使用上文描述的方法,处理相关寄存器并打印事故信息。

通常来说,在开发阶段,我们可以参考demo中HardFault的处理代码,按需移植到自己的工程中。

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

围观 73

TrustZone是ARM对ARM6的扩展,其实只是增加了一条指令,一个配置状态位,以及一个新的有别于核心态和用户态的安全态。ARM并没有把TrustZone设计成能够解决所有的安全问题,它的目标是希望TrustZone能把一些安全性要求高的代码放在安全区域里执行,这也就是TrustZone名字的由来。

ARM把TrustZone固化在硬件里的道理是,系统的安全性不能全靠软件来保证,而且改写现有的不安全的软件,使之更安全也不大可行。较为可行的方案就是引入一块安全的硬件逻辑,并且只让一小块软>件控制此安全逻辑。这样既保护了既有投资,也把系统的安全风险降到最小。

因为操作系统和普通应用都可以运行在安全态,所以安全态不同于传统的运行态环状的特权划分,因此ARM把安全态描述成一个平行区域,称之为安全区,又称为安全监控模式。

核心态程序要进入到安全区运行,必须执行安全监控中断( secure monitor interrupt, SMI)指令,而应用程序则必须通过API 函数来调用SMI 指令,此时,操作系统要负责检查应用程序是否安全,若通过检查则执行SMI指令进入到安全区。这样,整个系统的安全性就全由操作系统负责了。换句话说,TrustZone需要软件的支持,才能达到安全目标。

执行SMI 指令的具体动作是,SMI在CP15的安全状态寄存器中设置S位.。S位扩展还体现在AMBA总线上,这样外设也可以实现对TrustZone的支持。

安全监控程序(固件)是独立于操作系统的、自足的、不可重入的一小段代码。只要CP15安全状态寄存的S位处于置1状态,安全监控程序就要起作用(监测所有处理器操作)。首先监控程序要负责保存>上下文状态,即寄存器内容(一般保存到紧致内存TCM中),并且把当前处理器配置信息保存到CP15的单独一组分编(banked)寄存器中。TrustZone的开销是增加了大约350位状态信息,完成上下文切换需要200时钟周期。

由于TrustZone为安全区配备了单独的缓存、TLB和紧致内存TCM,上下文切换并不会导致这些性能构件内容的倒换,也即不会导致系统性能的降低。

ARM建议把安全监控程序及其运行时需要的内存置于一个TCM块中,以使延迟更低且更可预测。

安全扩展对于中断处理也有影响。因为中断既可以来自外设,也可来自软件,那就必须假定中断有可能造成安全漏洞,这样,在安全代码执行时,就不能简单地让任何中断切入进来。ARM建议的方案是,把中断也分为安全的和非安全的,安全代码只能被安全中断打断。当然,也可以把所有中断都提升为安全的,但那样会引入不必要的延迟。

TrustZone是体系结构的扩展,系统软件可以利用这一扩展提供安全支持,TrustZone本身并不能实现安全保障功能,但这一解决方案硬件实现不复杂,也不增加许多功耗,仍是具有很好性价比的安全嵌入式解决方案。

转自: http://blog.csdn.net/fanguannan0706/article/details/46720713

围观 365
订阅 RSS - TrustZone