中断的话呢,和原来的中断模型一样,被打断执行的程序,当前执行上下文,即通用寄存器,还有PSR之类的内核寄存器,被硬件入栈。因为是异步的中断,不是软件去调用,所以不会像前个胶片讲的,软件,即编译器去产生那些入栈的指令;硬件,由于是异步发生的,所以入栈必定是由硬件来完成。说到入栈,和以前一样,不论是安全世界还是非安全世界,都有各自的MSP和PSP堆栈,当前使用哪个堆栈,取决于CPU在当前世界的处理器模式,是handler还是thread。
除了硬件自动入栈和以前一样,考虑到安全世界被非安全世界中断打断时,跳转过去不能泄露了安全世界的执行上下文环境,还会硬件清零通用寄存器和PSR之类的内核寄存器值。
一句话小结:以中断方式在两个世界间切换,是通过硬件入栈执行上下文,并条件性地清零寄存器现场 来完成的。条件性就是指,从安全切到非安全才需要清零;非安全到安全,不需要。
CPU内核维度
TZ架构以前,CPU内核有特权级别 和 非特权级别的区别;还有handler 和 thread 两种不同的处理器模式。
CPU执行中断时就处于handler模式,执行前台程序时就是thread模式;而handler模式下,一定是特权级别,就一定使用MSP。执行前台程序时,即处于thread模式时,可以是特权级别,也可以是非特权级别,取决于内核寄存器CONTROL的一个位域决定的。特权级别就使用MSP堆栈,非特权级别就使用PSP级别。
四者是正交的关系,如左图。
TZ架构以后,CPU内核除了特权级别、处理器模式,2个维度的信息外,新增了第三个维度,安全状态。
如右图所示。对应的MSP和PSP,也扩展成了安全世界的MSP、PSP和非安全世界的MSP、PSP,分别以_S和_NS为尾缀。
安全世界的handler模式,thread模式;非安全世界的handler模式,thread模式,四个象限下,它们使用不同的堆栈,能访问的资源范围有所不同。这就构成了ARM提出的TFM Level2的硬件基础。TFM是trusted firmware for arm cortex-M的简称,是ARM提出的一个规范,带TZ架构MCU上实现安全可信执行环境的一个规范。这个内容,我们暂时不会在STM32L5入门课程里讲,大家有兴趣可以去翻看相关资料。
TrustZone 架构把在一个芯片上区分出了安全世界和非安全世界,也给 CPU 引入了继“特权级别”、“处理器模式”后的第三个维度,即当前状态是安全的还是非安全的。CPU 现在执行哪个世界的代码,它就是什么安全状态的。
CPU 在当前的安全状态,要发出一个指令或者数据访问,这个访问 transaction 是安全还是不安全的,不仅和当前CPU所处安全状态有关,还和CPU这条访问是指令访问、还是数据访问有关。并且,最重要的是,还和目标地址处于安全世界还是非安全世界有关。
先来看指令访问,相对简单:无论当前 CPU 当前处于什么状态,它取指的目标地址所在的世界,决定了这次取 transaction 的安全属性。前面讲过,程序代码区域有三种,隶属于两种安全世界下,要么是安全世界,要么是非安全世界。从SAU、IDAU来讲,它们不会对取指transaction做任何限制。这就是我们之前多次讲过,相互调用对方世界的程序,从内核角度都是完全自由的,没有原则性限制。
数据访问,从表格可以看到,不仅和目标数据所在地址有关系,还和CPU当前状态有关系了。黄色的case,当CPU本身在非安全状态,想去访问安全世界的数据时,从 SAU 和 IDAU 角度,就会把这个 transaction 给 block 住。这是和指令访问最大不同的地方。另外,即使 CPU 当前处于安全状态,如果要取的数据处于非安全世界,这个transaction也会被SAU、IDAU标上非安全 transaction 的标志。
在 SAU 和 IDAU 审查后,可以通过的t ransaction,根据安全还是不安全,再分别到对应的MPU那里去被进一步审查。【】审查都通过后,携带着 HNONSEC 信号,来到AHB5总线上,再和各个 AHB slave 从设备
通信。接下来发生的事情,就不是内核负责的,是由各个芯片厂家实现时,各自发挥的舞台了。一个重要的问题,芯片厂家需要解决的时,各自的片上外设,如何识别 transaction 携带的 HNONSEC 信号,并采取相应行动?我们的 STM32L5 如何做的,会在下一节课来讲。
NS 的t ransaction,也可以是在 CPU 的 S 状态下发出的
CPU 在NS状态下要访问S的目标地址,在 SAU/IDAU 阶段就会被 block
无论 CPU 处于什么状态,在 SAU 看来都可以访问 NS region
了解了内核关于 trustzone 的运行机制,接着来看一下和我们最直接相关的,开发者的软件开发模型。和V7m架构及以前的系统,最大的不同在于,V8M-TrustZone 架构上的软件开发,需要两个工程项目,一个运行在安全世界里,里面是可信软件;一个运行在非安全世界里,里面就是普通的用户程序。
系统复位后,总是从安全世界开始运行安全工程项目。首先执行的就是安全工程里的复位处理,对整个系统做安全属性的初始化和配置。完成之后,跳转到非安全工程项目,执行非安全工程的复位处理。一旦非安全工程开始运行,它就和以往的普通应用一样,运行用户的应用逻辑。它可以调用安全世界里暴露出的服务,比如执行加解密操作,但是无需也无法触碰到加解密密钥。
非安全世界的代码也可以把自己这边实现的回调函数注册到安全世界里。安全世界可以通过函数指针来调用非安全世界里的代码。举个例子,TLS 协议栈作为可信代码,运行在安全世界里,但是应用中具体的数据接收、发送操作还是在非安全世界里执行。TLS协议栈需要通过之前注册的回调函数的函数指针,来调用每个用户应用中各种不同的数据收发驱动。有的可能是通过串口操作无线通信模块,有的可能通过 MCU 自身的以太网 MAC 直接通信,这些都是用户自己应用的不同实现。
进一步,运行在V8-M TrustZone 架构上的RTOS,和以往在 V7-M 架构及以前的系统上,也有所不同。在像 STM32L5 这样的支持 trustzone 安全扩展的平台上,两个世界,可以分别搭建两个执行环境。安全世界里搭建 TEE,trust execute environment,可信执行环境;非安全世界,通常作为 rich execute environment,REE 和 TEE 相对。RTOS 一般运行在REE里,即非安全世界中。RTOS 除了和往常一样,执行任务间调度、通信、同步;如果任务使用了安全世界里的资源,RTOS还要负责分配安全世界里堆栈的分配,以及切换到安全世界时的上下文管理。
如果大家现在凭空想象还不太能理解,没有关系。现阶段,你需要知道就是RTOS,比如说FreeRTOS,为了支持TZ,除了以往的FreeRTOS代码依旧运行在非安全世界,还额外新增了一些代码,运行在安全世界,用于上面说的安全世界堆栈分配和上下文管理。
STM32L5Cube 固件包里有 FreeRTOS 的例子,大家有兴趣可以去参考。
接下来,我们回过头看一下为了支持TZ的安全扩展,CM33内核在硬件上和寄存器上,和以往我们熟悉的Cortex-M系列内核有什么不同。
通用寄存器,R0 到 R15,在安全世界和非安全世界之间共享。因此从安全世界,切换到非安全世界之前,需要软件或者硬件把这些寄存器,就是所谓的执行环境上下文给清零;
CM33 本身,基于 CM4 内核,新增的是两个堆栈 PSP 和 MSP 的栈底指针,PSPLIM 和 MSPLIM;
如果 CM33 还实现了 TZ 安全扩展,像我们 STM32L5 一样,就会新增出来一套所谓的 banked 寄存器。就是图里绿色框中的部分。
堆栈指针、堆栈栈底指针、CONTROL 寄存器、异常掩码寄存器组,都是 banked 的,即,在安全世界和非安全世界,各有一套。
区域属性设置
CM33内核集成新一代MPU,对区域范围的设置更加灵活。以前的MPU区域,区域起始地址要是其大小的整数倍。比如图里的例子,0x3BC00到0x80400这段范围,它的大小是274K,不是起始地址0x3BC00的约数。因此在以前的PMSAv7规范下的MPU,一个region是无法cover的。需要拆分成4个region:大小分别是1K、16K、256K、1K,才是0x3BC00,0x3C000,0x40000,0x80000这些起始地址的约数。
而新一代MPU,隶属PMSAv8规范,只要求区域的大小是32字节为颗粒度,并没有起始地址和大小的约束关系。示例里的这个范围,在STM32L5的MPU里,一个region就可以cover。
除此之外,内核MPU寄存器组,和区域属性设置相关的部分,有了较大调整,寄存器名称、位域都和以前不兼容。当然,STM32L5的HAL层,会把这些硬件实现细节的不同给屏蔽掉,对上层应用保持尽可能稳定的API。
用户中断,可以被安全世界的代码,在系统刚复位后执行初始化时,配置把它target到安全世界还是非安全世界。什么叫做target到安全世界还是非按世界呢?就是一个中断发生了,比如UART接收中断,是去安全世界里去处理,执行ISR,还是去非安全世界里去执行ISR。前面讲过,TZ架构上的软件开发,有两个工程项目,各自也有各自的中断向量表,VTOR_S和VTOR_NS。系统复位,CPU一定处于安全状态,执行的第一个复位向量,一定是去VTOR_S中取第一个word和第二个word的值,分别赋值给安全世界的MSP堆栈指针MSP_S和PC。在CPU执行过程中,发生了中断,内核根据该中断被target到哪个世界,这是硬件根据内核NVIC寄存器组里一个寄存器之前被用户的配置来判断的,自动去VTOR_S或者VTOR_NS的对应向量地址取该中断的响应函数ISR执行地址。
在前面讲中断导致的安全状态切换时,大家知道,为了防止安全世界里的信息被泄露,当安全世界的代码执行,被非安全中断打断时,在跳转到非安全的中断处理函数执行之前,硬件要在入栈后额外把寄存器内容全部擦除清零,这就势必会造成一点额外的中断响应延迟,从12个时钟周期,延迟到21个时钟周期。
关于系统异常和用户中断的安全属性,我们再多讲一些。
这个表格的前四列比较重要,是和安全属性配置直接相关的部分,也是TZ的安全扩展,给内核中断新扩展的概念。
芯片的Reset,永远是在安全世界里执行它对应的handler。HardFault和SecureFault,和Reset一样,都是一个绿色点,表示它们的handler一定是在安全世界里,用户不用做配置,也没有机会做配置。
像NMI、BusFault、以及最下面一行的用户中断IRQ,是蓝色的椭圆形。这表示这些异常和中断,可以通过寄存器,就是表格中的第四列里的寄存器,把这些中断配置成target到安全世界,还是非安全世界。这些寄存器复位值都是0,因此NMI、BusFault、以及所有用户中断,上电时都是默认target到安全世界,即发生中断,是去VTOR_S中对应的entry取中断向量来执行,并且是在安全世界里执行中断响应程序。
剩下的MemManagement、UsageFaut、SVCall、PendSV、Systick等,都是banked中断,表示它们在安全世界和非安全世界同时有各自一套中断处理函数。什么世界里触发的这些中断,就去什么世界的VTOR中断向量表取对应ISR地址。
系统异常的优先级,从HardFault以下,到systick,都是可以配置的;用户中断的优先级当然也可以配置,都和以往一样。
关于优先级,在TZ安全扩展的实施下,有几点和以前不大相同:
一个就是系统异常里优先级固定的那几个。NMI,优先级固定-2;一直可被Reset抢占;当NMI被target到NS,非安全世界时(BFHFNMINS=1),可被Hardfault抢占,因为后者优先级在BFHFNMINS置位时,提到-3。hardfault永远target在Secure state。只是,BFHFNMINS=1时,优先级提到-3,高过NMI,处理的是所有Secure 的fault;而BFHFNMINS=0时,处理所有上访的来自安全世界或者非安全世界的fault,优先级和以前一样,只有-1。
另外,对于可配置target到安全还是非安全世界,并且优先级可配置的中断和异常,从表格里看到,满足这两个条件的,除了BusFault是系统异常,其他都是用户应用中断IRQ。它们的优先级除了NVIC寄存器里的interrupt priority register可以设置外,还可受到AIRCR,应用中的和复位控制寄存器里PRIS(prioritize secure interrupt)位域的影响。这一位置1时,整个target到NS的中断,其优先级都要被压缩映射到原本0~255的优先级空间里的128~255低优先级空间。
以上我们对TrustZone在内核里的运行机制进行了讲解。CPU的当前状态,对memory寻址时的安全属性策略,由SAU和IDAU共同定义,但,都仅仅是从内核的视角看出去的。
携带着安全属性的transaction从内核出来,来到AHB总线上,传达到芯片上集成的各个外设模块和存储模块时,内核外的模块们如何识别transaction的安全属性,并做相应的反馈,这就需要芯片厂家,把安全设计部署在整个芯片系统上。