quinnwencn / blog

Apache License 2.0
0 stars 0 forks source link

01 TrustZone简要原理介绍 #3

Open quinnwencn opened 7 months ago

quinnwencn commented 7 months ago

1.TrustZone是什么

TrustZone是ARM为cortex-A核芯片设计的一种安全架构,这个安全架构首先在ARMv6K引入,随后在ARMv7和ARMv8逐渐普及并越来越被重视。TrustZone为芯片提供了硬件级别隔离的两种工作环境:安全世界和非安全世界。 image

非安全世界,或者说正常世界运行的是一个我们常见的Linux或者其他类型的操作系统,为用户的正常应用提供基础OS能力(也有可能运行的是一个hypervisor,然后基于hypervisor再创建不同的操作系统,如下图)。 image 由于正常世界运行了很多APP,因此对外暴露的风险也就越高,为了维护正常世界的安全性,就需要付出非常大的代价。相比之下,安全世界只运行了非常小的软件,所提供的的可信服务也非常有限,比如密钥管理、证书存储等,尤其是对外提供的接口和访问非常有限,这就确保了安全世界的供给面非常小,因此也更容易维护和提高安全性。

2. TrustZone在处理器中的管理

在介绍TrustZone的切换前,先介绍下ARMv8的异常模型。ARMv8权限的分配划分为四个异常等级(Exception Level,EL),分别是EL0、EL1、EL2和EL3。 image 异常等级只有在以下几种情况下才会变更:

在ARMv8中,TrustZone把运行世界划分为了安全和非安全两个状态,结合ARMv8的异常等级来看的话,可以用下图来描述: image 在EL0、EL1和EL2处理器可能是安全状态,也可能是非安全状态,状态的确定由SCR_EL3这个bit决定,我们可以用NS表示非安全状态(Non-Secure),而安全状态则由S表示,比如:

关于ARMv8模型的详情可以参考:ARMv8异常模型 从图中可以看出来,EL3总是在安全状态。

2.1 安全状态切换

从上面的图也可以看出来,不管是从安全状态切换到非安全世界还是从非安全世界切换到安全世界,都要经过EL3。ARMv8架构下的安全状态和非安全状态切换涉及了ARM的处理器模式。ARMv6一共有7中处理器模式,在原有的基础上,ARMv7增加了Hyp和Monitor模式。结合ARMv7的特权等级(Privilege Level),这9个模式可以以下表呈现: image 其中,在ARMv7中安全状态的特权等级有PL0和PL1,而非安全状态下三个特权等级都存在:

用一张图概括: image

在ARMv8下,异常等级和处理器模式和ARMv7有所不同,主要在于特权等级和异常等级的区别,细节可以以下图说明: image

回到安全世界和非安全世界的切换话题上,前面说道,SCR_EL3.NS是用来标识安全世界和非安全世界的,SCR_EL3.NS其实是一个bit,这个bit只能由EL3来变更,因此,只要保证从非安全世界的EL1或者EL2进入EL3的方式安全,也就能保证安全世界是安全的,而不是非法进入的或者是非法篡改的。为简化介绍,我们这里不考虑hypervisor的机制,只看非安全世界只有一个Rich OS的情况。 image

从非安全世界进入安全世界的步骤可以拆分为三个步骤:

a. APP通过产生异常来进入更高等级的异常等级,产生的异常一般是FIQ或者是调用SMC产生异常,从而从EL1进入EL3

b. 进入EL3后,由运行在EL3的Firmware/Secure Monitor变更SCR_EL3.NS,从1到0

c. 从异常返回时,由于SCR_EL3.NS为0,因此返回的是安全世界的EL1

2.1 寄存器管理

在从非安全世界切换到非安全世界时,还涉及了寄存器的管理。对于通用寄存器以及大多数系统寄存器,不区分安全世界,只有一份,因此必须由软件去管理这些寄存器的备份和恢复,这不是由硬件去保证的。图中的EL3中的Secure Monitor就是负责在安全世界和非安全世界切换时来备份和恢复这些寄存器,他的动作主要有:

**变更SCR_EL3.NS为0

**保存非安全世界的寄存器状态

**恢复安全世界的寄存器状态

**变更SCR_EL3.NS为1

**保存安全世界的寄存器状态

**恢复非安全世界的寄存器状态

有一小部分的寄存器由安全状态存储,也就是说系统中将存储两份这些寄存器的值,CPU自动使用属于当前状态的寄存器值。比如说ICC_BPR1_EL1,一个用于控制中断抢占的GIC寄存器。当系统寄存器被存储时,可以通过用(S)和(NS)来标识使用哪个寄存器,例如:ICC_BPR1_EL1(S) 和 ICC_BPR1_EL1(NS) 。

2.2 内存管理

对于安全世界和非安全世界,系统提供了两种不同的内存映射原理: image 在获取虚拟内存地址时,根据地址的不同,分别从不同的内存映射机制进行地址映射:

也就是说安全世界和非安全世界有着两套独立的虚拟地址空间,相同的地址并不能跨世界访问,而只能访问当前状态下的虚拟地址。虚拟地址隔离还不够,物理地址也要隔离。在TrustZone的架构中,物理地址同样提供了两套物理地址空间:安全世界的和非安全的。当在非安全世界时,虚地址总是会映射到非安全世界的物理地址空间,这也就保证了处于非安全世界的软件只能访问非安全世界的资源,绝不可能访问安全世界的资源。而当处于安全世界时,软件既可以访问安全世界的物理地址空间,也可以访问非安全世界的物理地址空间,这时候的NS bit的作用就是判断虚拟地址是映射到非安全世界的物理地址空间还是安全世界的物理地址空间,用两张图来表示: image image

2.3 Translaion Look aside Buffer

TLB缓存也为TrustZone做了相对应的设计。在TLB表中,增加了NS bit的标识,用来表示当前的页表对应的虚拟地址映射是从非安全世界映射到非安全世界的物理地址,还是从安全世界的虚拟地址映射到(安全/非安全世界)物理地址。我们以下表为例: image 可以看到,每条缓存表的内容都会记录对应状态下的虚拟地址映射的物理地址。当TLB缓存不命中时,会触发一次TLB不命中,CPU也只会在当前的状态下进行重新映射。表中也提供了一个信息,安全世界的虚拟地址0xC00000也会映射到非安全世界的物理地址0x500000,但是,非安全世界的虚拟地址是绝对不会映射到安全世界的物理地址空间的,这点很重要。 表中只有EL1和EL2的TLB条目,而没有EL3的。这是因为EL3是一个特殊的案例,它始终处于安全世界,SCR_EL3位的状态对于EL3并没有影响,这个位实际影响的是EL3下的软件影响的是哪个TLB。这里补一个我暂时没理解的知识点:TLBI ALLE1操作会使EL0/1的地址翻译机制失效。因此,对于在EL3的TLBI ALLE1操作:

2.4 SMC异常

SMC(Secure Monitor Call)的提出会为了支持非安全世界和安全世界之间的通信,一次SMC执行会出发一次SMC异常,CPU会陷入EL3异常等级,然后根据对应的SMC指令做相应的处理。一般来说,SMC是由REE向EL3的固件或者TEE侧的服务发起的请求,EL3的固件有一个SMC分发器,分发器会根据SMC指令分发到EL3的固件或者TEE的服务,再由对应的目标服务完成REE的一次请求,用一张图来表示就是: image ARM制定了SMC调用规范和电源状态协调接口平台设计规范,这些规范规定了SMC是如何请求服务的。作为TEE的简要介绍,这里不展开学习和介绍这两份规范。 SMC异常处于EL1时,可以陷入EL2,这主要用于一些有虚拟化的系统上,虚拟化系统模拟了硬件,并负责和固件/Secure Monitor打交道。

2.5 安全虚拟化

虚拟化在ARMv7引入,当时并没有安全虚拟化的概念,虚拟化只在非安全世界存在,到了ARMv8.3以前, 虚拟化一直只存在于非安全世界,如下图所示: image 但是,随着TEE的普及,一些系统希望引入多TEE的架构,因此在安全世界引入虚拟化的需求也就应运而生了。ARMv8.4引入了安全世界的虚拟化,也就是在安全世界也支持EL2了。此外,ARMv8.4还将EL3的一些固件功能也移到了安全世界的EL2中进行处理,遵循了最小化原则。 不同于非安全世界的虚拟化,安全世界的虚拟化系统是一个安全分区管理器(SPM, Secure Partition Manager),它允许创建隔离的环境,用于支持不同的TEE系统,不同的TEE之间是隔离的,也就是说一个TEE OS是不能看到同一个安全世界中的其他TEE OS,TEE服务也同理。 除了支持不同的TEE OS外,SPM还支持创建平台固件,这部分平台固件也就是从原来的TrustZone架构中的EL3移过来的。 对于支持安全世界EL2的架构,S.EL2是可以通过SCR_EL3.EEL2这个位来控制使能与否,置1则使能。 image

3. TrustZone的系统架构

前面提到的都是处理器上的TrustZone介绍,实际上TrustZone不仅仅是处理器的特性,而是芯片上的完整架构,举个例子: image 图中包含了不同的安全敏感设施,其中一些可配置的并不一定是必备的,只有在一些场景下才需要配置成安全相关的。比如说,GPU对于一般的系统方案而言,并不需要关注安全性,但如果用于人脸识别加速,那么就需要GPU配置成安全相关,并且在安全世界进行人脸模式识别等操作时,GPU必须工作在安全状态。

3.1 外设和内存

前面说到过物理内存寻址分为了安全地址空间和非安全地址空间。处理器访问内存是通过总线去访问的,因此总线也是区分为安全总线和非安全总线的。安全总线指的是总线访问安全物理地址空间,非安全总线指的是总线访问非安全物理地址空间。其他内存和外设也是同样有安全和非安全的概念,因此,可以把内存和这些外设划分为TrustZone敏感和TrustZone不敏感两种类型。 TrustZone敏感的外设指的是这种外设支持TrustZone特性,可以由处理器控制进入安全世界和非安全世界,比如说前面提到的通用中断控制器(GIC, General Interrupt Controller),GIC可以由处于安全世界和非安全世界的软件访问,但是非安全世界的软件只能看到非安全世界的中断,而安全世界的软件可以看到所有中断。 TrustZone不敏感的外设表示普通系统中的大多数外设,它们不知道总线安全,也不知道安全世界和非安全世界,比如说定时器,或者是片上内存,它们要么处于安全世界,要么处于非安全世界,而决定不会同时处于两个世界。 因此,外设和内存也因此可以划分为几个不同的类型:

上图中就把外设做了分类,但是这并不是所有系统都一样,只是一个示例。对于不是片载内存,ARM也提供了TZASC(TrustZone Address Space Controller)用于将内存分区于安全区域和非安全区域,做法如下: image TZASC的作用类似于MPU(Memory Protection Unit),它可以将内存划分为多片区域,每片区域单独根据策略划分为安全区域或者非安全区域。为了使得这个过程也是安全的,TZASC的寄存器只允许在安全状态管理和访问,也就是安全的设备。

3.2总线请求

前面提到,设备可以笼统的划分为TrustZone和非TrustZone两种类型,对于ARM-A芯片而言,CPU是可以做到区分这两种设备的,但是对于一些其他系统,比如说GPU、DMA和其他一些没有CPU的芯片,他们如何去访问安全和非安全的内存呢?通常来说,可以将内存静态划分为安全和非安全,固定去访问这些内存,但是这样是非常低效的,也非常浪费内存。ARM的做法是引入了SMMU(System Memory Management Unit),SMMU就类似于MMU,也有一个NS位用于管理安全世界和非安全世界的物理地址空间。

3.2中断

通用中断控制器(GIC, Generic Interrupt Controller)支持TrustZone,前面也已经提到过。每个中断源都被划分为以下三个组之一,并以INTID标识一个中断:

Ref

  1. https://developer.arm.com/documentation/ddi0406/cb/System-Level-Architecture/The-System-Level-Programmers--Model/System-level-concepts-and-terminology/Mode--state--and-privilege-level?lang=en#CIHDDABH
  2. https://developer.arm.com/documentation/ddi0406/cb/System-Level-Architecture/The-System-Level-Programmers--Model/ARM-processor-modes-and-ARM-core-registers/ARM-processor-modes?lang=en
  3. https://developer.arm.com/documentation/102418/latest/