你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32L5 入门课程系列(一) 从Cortex-M33内核认识TrustZone

[复制链接]
gaosmile 发布时间:2020-5-27 17:41

STM32L5 入门课程系列(一)

从Cortex-M33内核认识TrustZone


( l1 I) m$ W9 Q

/ k* V* X! v5 u, v9 X% N


, f3 A; l" z2 [3 _: Q* Y& U

STM32L5生态系统汇总

STM32L5入门课程(二) STM32L5的系统新架构


+ e6 [/ ^' o1 u0 w, P3 A3 \8 {2 ]

欢迎大家关注STM32L5课程。本期我们会介绍STM32L5的内核,Cortex-M33。它是ARM在MCU架构上增加了TrustZone这个安全扩展的一种内核实现。


* h; j% ]# v0 E1 z) P. K* V

从这一期开始,我们进入技术部分的学习。L5快速入门,会由5期的介绍组成,会着重讲解L5的TrustZone安全扩展。除此之外,L5新增了内置SMPS,进一步提高平台的低功耗特性。
% ]7 q% c' E& N( G% d! P


+ ^3 @. O" Q1 c8 H* x% T. z/ T

5 J% v/ o, V" [- p# K5 t! Z

8 i6 L# R3 D' d0 B% Z8 j2 U

Cortex-M33内核


4 r) b3 O0 H6 U# p4 T  {

Cortex-M33,在以往传统的Cortex-M系列内核基础上最大的扩展是新增对TrustZone的支持:从硬件实现角度来说,内核会新增一些外设,并且原来已有的外设也做了相应更新,来支持TZ。从用户角度,即编程模型来说,一方面有新增寄存器、新增指令需要操作,另一方面在中断响应机制、存储区映射上都有新的概念提出,需要开发者理解和运用。

( E; L4 q$ A: p


( A, n- _7 ^7 q* |  H/ _

. J0 h3 _5 j& z* G( v) X& R, W


1 j- G; w% c, Y! a

近些年,ARM在MCU领域的内核架构设计,从V6-M、V7-M到V8-M,实现了丰富的内核软IP。从入门型的CM0、CM0+内核,到主流型CM3、CM4、CM7内核,再到支持TrustZone安全扩展的CM33内核,ST都有具体的产品线。


  I- a5 u2 f) t6 {" c% t8 Q

这两年主推的新产品,G0、G4、WB、WL,还有新一代H7,都是基于V6-M和V7-M架构。STM32L5是ST首款基于CM33内核的MCU产品。


- q6 ]. g4 q1 h# D

通常意义来讲,CM33可以看做是CM4内核基础上新增了TrustZone安全扩展。那么我们接下来看看具体有哪些新增的功能和特性。. w0 q" @8 ]# A, ?- P$ d3 A. d: |0 `


% I9 M, V5 ?: g( F

: Q9 x1 X3 K# z& `


  S% f  J) }9 n( [6 a

CM33内核比CM4内核新增和改进的地方,是图中绿色高亮出来的部分。


/ _( m, g: p) R

对TrustZone的支持,内核会引入“安全状态” 和“非安全状态”的概念,硬件方面,SAU和IDAU是随着新增的硬件单元。TrustZone是Cortex M33内核的一个可选模块,STM32L5实现的CM33内核是包括了TZ的。L5的用户可以通过选项字节,来设定是否使能这个内核的安全扩展。


" b/ c) F5 u8 S8 A# X

堆栈溢出检查,在CM33内核里新增了额外的堆栈指针stack limit,分别指向MSP和PSP堆栈空间最后一个地址。因为Cortex-M内核的堆栈定义,是向下生长的,因此堆栈空间的尽头就是最小的那个地址。当CPU执行某条指令,导致当前堆栈指针越过了stack limit的值,会触发内核异常,exception。


* j7 C8 f- O% L, M9 t. ]

Enhanced debug,配合TrustZone扩展,可以仅开放用户对非安全代码的调试


8 e7 m) S; c! K, d

NVIC,在CM33内核支持更多的用户中断线。我们的STM32L5上目前可以产生109条用户中断。最重要的是,为了配合TrustZone安全扩展,CM33内核里的NVIC可以把内核异常(Core exception)和用户中断(interrupt),target到安全状态或者非安全状态的中断处理函数中。“中断的retarget” 这个概念很新,后面讲到内核中断时,我们会展开来说。


* D% G! c$ Z0 f: L

新一代MPU,基于PMSA v8架构,以前是v7架构。v8架构给用户带来的最大便利之处在于,对MPU管理的区域的范围设置更加灵活。后面专门有一页会来讲。

& Z0 m1 R5 ^8 g, B

AHB总线,从AHB lite升级到AHB 5,总线上会新增额外的信息,来指示当前CPU访问的安全状态。

5 n0 t2 `9 G  H$ N! ^5 V2 K$ X

第一次接触CM33内核,或者TrustZone的概念,大家可能会对刚才提到的诸多新概念有点懵,什么SAU、IDAU,什么叫做CPU访问的安全状态,什么叫做中断target到安全状态或者非安全状态。如果现在你觉得听得有点一头雾水的感觉,没有关系,后面我们对这些概念,都会掰开揉碎,结合实例来展开阐述。

$ `1 u' k# z6 F4 P  v; B6 G( Z9 p# w

现在我们先来理解什么是TrustZone,什么是安全世界和非安全世界 。


' L' v% y4 V) ?4 r2 ^# P

3 `& K  m3 X7 g0 r7 J* H& O+ o


; l8 C% j" ^. z/ j; G

TrustZone
  g- m1 _1 d; U% `) b" d& s  v


0 ~( h; Q5 K3 T9 Y/ O; D+ J

什么是 TrustZone?它是一个和安全相关的概念。


5 z7 ]0 ~9 L8 z& ]  I

大家还记得2008年北京奥运会的口号吗? “One World One Dream” ,同一个世界,同一个梦想。


- H' y9 _6 G8 r* O6 `& k, h7 ^0 }

TrustZone的概念,用类似的语言来表达,就是:one CPU, two world。这里的two world,两个世界,就是指的安全世界和非安全世界,它们同时存在于同一颗芯片上,但是物理机制又保证了两个世界有可靠的隔离。人类命运共同体,要求我们要有同一个世界同一个梦想的理念;但是从信息安全的角度,两个隔离的世界,是信息安全的基石保证。最近大家的生活,相信也已经深刻体会到了“隔离”机制,对安全的重要意义。


% Q1 p5 z/ W+ k) V: N  ]! |: b


0 X  M0 }+ g; H* g  w

基于具有隔离能力的硬件基础,我们在其上设计应用程序时,就可以把软件分成两部分:

>> 可信的软件和普通软件。可信的软件,通常经过严格的code review,甚至经过实验室的审查和认证,因为它要访问关键资源,比如敏感数据,敏感外设模块。什么是敏感数据,加解密操作用到的密钥、用户的隐私数据,通常是具有Confidentiality安全属性的资产,是我们要极力保护其“Confidentiality” 和,或者“Integrity” 安全属性的资源。敏感模块,通常是连接到外部关键设备的,比如控制门禁的制动器。

>> 普通软件,是用户应用中的另外一个组成部分,刨去了可信软件的,其余的都算在普通软件里面。

; R3 u7 r% H" ?7 V! h8 F

TrustZone的基本原则,就是把应用中的关键操作代码,和其他应用程序隔离开来,避免恶意应用程序的攻击,或者由于应用程序本身的设计漏洞,对敏感操作代码的攻击。

+ F! @! L/ A( h5 R; L5 n2 X; a

如果大家以前有接触过Cortex-A架构上的TZ,那么L5上Cortex-M架构上的TZ和它概念上是差不多的,不同的地方在于M架构上的TZ是通过memory map来划分两个世界各自的地盘,CPU在两个世界执行时,其安全状态的切换是硬件自动完成的,这一点是最大的区别所在。

) A* |$ U* r" `, @

如果大家以前没有接触过TZ的概念,那么现在你可以暂时理解为一个芯片上有两个物理隔离开来的区域,分别存放安全程序、敏感资源,以及普通程序,普通资源。


' E! i5 @. I1 C$ M+ a

Flash就那么一块flash,ram就那么一块ram,以往那些GPIO、RTC、DMA外设,如何区别哪些flash和ram区域,哪些外设是安全的;哪些又是不安全的呢?一颗CPU又是如何在两个世界进行切换的呢?$ v4 a' {' z! x: ^: K2 U


  h# C5 |: K- S8 `. |3 C0 H


4 \) V5 V7 F! _! l  v


. [! g: I8 |" a3 S


) K0 h) U' R0 q1 @! W+ G. F


/ {1 u' W& P. \# o# {$ t! Z* ]% o3 ?

% O  v9 }0 C" u7 _" ?0 A

我们先来回答第一个问题:如何划分“安全世界”和“非安全世界”各自的地盘呢?

答案是按照4G地址空间里的地址范围来区分。可以设定哪些地址空间,在内核看来是安全的世界;哪些地址空间,在内核看来是不安全的世界。执行安全世界的代码时,CPU自己处于安全状态;执行非安全世界的代码时,CPU自己处于非安全状态。


, o  c- J" \  e" E% M# r- j

处于安全状态的CPU,从内核看来,是可以访问所有资源的,无论这个资源是在安全世界里,还是非安全世界里。而处于非安全状态的CPU,从内核看来,只能访问非安全世界里的资源。注意,以上这几句话,多次出现了“在内核看来”这五个字的限定语。内核通过寄存器SAU,和IDAU模块, 把地址范围划分出来,那么所有看到的或者认为的安全还是不安全的地址,都是从内核看出去的视角。

; I9 P- m$ F. R& U, ?; Z& \

内核看出去的视角,具体哪些区域是安全的世界,哪些是非安全世界,由SAU和IDAU一起定义。SAU是内核里面的一个模块,用户可以通过寄存器来配置。它有点类似MPU,可以通过起始和结束地址设定几个区域的范围,然后执行该区域是安全世界还是非安全世界。IDAU在内核之外,是芯片在实现时,就由芯片厂家设置好的安全属性控制模块,一般不对用户开放设置。它们分别就是左边图里,两个蓝色的方框,Security Attribute Unit,就是SAU;System level control就是IDAU。


% q8 F' p# ?$ `+ S7 F6 M9 S

根据CPU要访问的地址,SAU和IDAU根据目标地址的安全属性,以及当前CPU的安全状态,会决定该访问的transaction,是否批准,如果批准的话,这个transaction的安全属性如何?是安全transaction,还是非安全transaction?


. u* h  T3 [! f% z- X9 W" F' u. p4 b' Y; E

根据不同的安全属性,由不同的MPU来进一步审查该transaction,是否符合受该MPU管理的目标地址所在区域的访问控制。比如,经过SAU和IDAU的判断,这是一个安全的取指令的transaction,那么Secure MPU就会看目标地址所在的区域是否可执行,如果是XN,execute never,该transaction就被Secure MPU给block住,就不会走到下一步,到AHB总线上了。如果这个安全的取指令transaction,在Secure MPU检查后放行,那么这个secure的transaction就可以来到AHB总线上了。这也是为什么我们会在左边的图里,看到两个MPU模块的原因。这也正是CM33内核为支持TrustZone安全扩展而新增的内核模块。


* ~- z6 A; H0 G2 @4 n( D' ~

需要注意的是,对4G地址空间的安全属性设置,只能在安全世界执行。而CM33内核,如果支持并使能了TrustZone这个安全扩展,上电启动的时候,内核是在安全状态,即上电时的启动地址,是默认处于安全世界的。

1 H  A) J. z& p" m8 ?

7 R# `/ c3 t  d9 l+ ~, K

IDAU,全名:implementation defined attribute unit。用户拿到一颗芯片时,IDAU的配置已经由芯片厂家设置好了。


" Y/ Z& D1 e7 E0 |8 \# G

它设置了哪些区域是Secure的;哪些是NSC,NSC是Non-securable callable的简称;哪些区域是Non-secure的。NSC区域也属于安全世界,执行NSC区域的代码时,CPU是处于安全状态的。IDAU的设置属于上电即生效的。由于是芯片厂家的设置,它的粒度比较粗,是以64MB为最小单位。每个厂家的设置可能稍有不同。


% E' Q& x$ k+ P) v- [: k* \2 k2 }

SAU,全名:security attribute unit。它是CM33内核的一个模块,有寄存器供用户在上电后编程设置。这提供给用户一个机会,来按照自己的意愿对4G空间上的自己选择的8个区域做精确的安全属性调整。大家查看内核SAU寄存器可以发现:SAU所设置的区域的起始地址,只要是32字节对齐即可,区域大小最小粒度可以精确到32字节。


6 w! [! A7 c' R& D( D) Z

当同一个区域范围,被SAU和IDAU定义了不同的安全属性,则取安全性要求更高的那个属性。显而易见,Secure高于NSC,NSC又高于Non-secure。


. r: c- \( ~' Q: T) C) C

如图所示:0x0c00 0000到0x1000 0000这个范围内的区域,IDAU定义的是NSC属性,而SAU把其中的高地址部分定义成Secure属性,那么二者共同作用后,这段区域就选取安全性更高的Secure这个属性。

3 b: S- ?" l! w9 j, ?) a

这里再次强调一下,就算最后IDAU和SAU共同定义出了一个区域的安全属性,那也仅仅是从CPU内核看出去的,这个目标地址是安全还是非安全地址。实际上这个地址,如果是落在flash或者ram的某个区域,或者是落在某个外设寄存器身上,这个地址本身是安全还是不安全的,要看实际物理实现它的flash、ram或者外设的设置。这个是每个芯片厂家各自发挥的舞台了,我们的STM32L5是怎样的,会在下一集来讲。

& ^7 [) Z7 p% A8 Q0 t: _" ?5 A


* E+ l# o* S/ A/ R4 u8 ?' m. j

5 ?$ [# M8 G* V; T: g+ L$ U


1 ~* t. v* f: {6 m

TrustZone 技术,在一个单独的芯片上,按照地址,实现了两个世界,安全世界和非安全世界。但是一个地址,它的安全属性,却有三种。前一页已经提到过了,分别是Secure,Non-Secure callble,和 Non-secure。前面两个都是绿色标识的,按照我这节胶片里的 color legend,应该知道,都是属于安全世界的。Secure 属性的区域里面放置经过code review的可信软件,执行涉及敏感数据或者敏感外设的关键操作。Non-secure属性的区域里,放置普通应用软件,这很好理解。那么为什么还有一个同属于安全世界,但是安全属性定义是NSC的区域呢?


1 V' N0 x% `5 t8 L

原因就是,TZ隔离出来安全和非安全两个世界,两个世界的代码可以互相调用。安全世界可以随意调用非安全世界的代码,谁要人家安全等级高呢。而CPU在非安全世界里的执行的时候,要调用安全世界的代码,就不是那么随意了。调用是可以调用,但是只能调用安全世界给你展现出来的API。比如,安全世界里有10个函数,但是只有2个是export出来可以给非安全世界调用的,如图里的Sec_ABC_API和Sec_XYZ_API。另外,非安全世界,即左边红色框里的程序,是不能直接调用右边深绿色区域,即Secure区域的这两个API,必须要先执行一条指令:SG。SG是secure gate的简称。顾名思义,它是作为安全世界的大门。要从非安全世界进入安全世界,必须走这个大门进,因此CPU从非安全世界进入安全世界,执行的第一条指令,一定要是SG。而SG指令作为操作码,无非也就是32位或者16位的01序列,为了防止其他二进制数据,比如具有与SG指令的操作码相同值的查找表,被用作安全状态的入口函数,因此定义出这样一个NSC区域,里面就是放SG指令和随后的跳转指令。


" L  o% ]0 }8 F" N) y3 N* I


4 D$ _; n+ x' u; X( G+ ^


  @2 y' m, h7 p# i

说到 IDAU,它除了是芯片厂家给4G地址空间,粗粒度地预先设置了区域的安全属性的作用之外,它还给带来一个好处,就是大大减轻了SAU区域个数的负担。SAU会影响CPU的速度,因此SAU区域的个数,不是越多越好。大多数芯片厂家在实现芯片时,把SAU的个数定义在8,也是一个权衡的结果。如果没有IDAU,我们来想象一下。按照以往ARM内核的存储空间定义,0x2000 0000 到0x4000 0000这块区域是SRAM。SRAM通常是BB属性,即block base的存储器件,可以按照每个page,独立定义其安全属性。如果一个应用,需要芯片上的SRAM,比如256K,每个page 2K,间隔地具有安全、非安全、这样的属性配置。如果紧靠SAU来设置,就需要N=128个region,N等于SRAM的page数目。这是远远大于8这个数字的。


/ ~- _8 X$ {4 z# l) p9 s5 a

有了IDAU,芯片厂家可以预先对几个大区域进行设置,如图所示。以往的Code区域,512M空间,平分两半。低地址部分,定义成NS,高地址部分定义为S。对SRAM和外设,也是如此。N个page的这块SRAM,它物理上就只有这么一块SRAM,它的默认地址是0x2000 0000开始。但是它有一个别名区,是从0x3000 0000开始。从0x2000 0000和从0x3000 0000去访问,都是访问的同一个物理地址,即这一块物理SRAM的首地址。但是访问transaction本身的安全属性,从CPU看来是不同的。


( l+ A4 z" K9 |0 f( R

在这样的情况下,再来看,刚才同样的用户应用需求,需要芯片上SRAM,每个page,间隔具有安全和非安全属性。SAU只需要设置2个region,分别cover到从0x2000 0000开始到0x2003 FFFF这个区域,和从0x3000 0000开始到0x3003 FFFF的区域即可。

. P# z8 T6 m9 ^+ @  i8 a

只需要从低地址访问page1/3/5/7,从高地址访问2/4/6,从CPU看来,就能对每个page间隔,有不同的访问安全属性。

如果大家现在听得还是有点糊涂,没有关系,后面我们会结合STM32L上真实的物理SRAM,和物理Flash,在hands-on环节,具体来体会。

- d) v0 B7 O! }


' \9 N& Y8 Y5 C* N+ Q

& r! K' q7 d) m% b: U+ e' }

状态切换 $ a, B' Z/ |9 N, p4 q$ C4 t

现在我们来回答第二个问题:CPU在安全世界,和非安全世界里执行程序,是如何实现两个世界间的切换的呢?

& k: D5 m) q* V9 r

首先,安全世界和非安全世界,彼此可以调用对方的代码,无论是处于该世界的thread模式,还是handler模式。两个世界的代码,都可以被对方世界的中断打断运行,只要优先级足够。一句话就是,代码的相互跳转,是没有原则性限制的。但是毕竟这是两个世界之间的切换,涉及到CPU安全状态的切换。


- Y  Q' o' c2 u# I0 L2 o: u" Y

我们先来看函数相互调用。这需要CM33内核新增的指令来支持。SG,刚才说了,是从非安全状态,切换到安全状态后,CPU必须执行的第一条指令,是非安全世界进入安全世界的大门。BXNS和BLXNS,都是跳转指令,目标地址是非安全世界。

我们看下面的图:

; p5 \# K, H( u  H' y! p# t

安全世界,调用非安全世界代码时:因为是函数调用,需要返回,而且是跳转到非安全世界,因此会使用BLXNS指令。在跳转之前,为了不泄露安全世界的信息,需要软件来把安全世界里执行时,通用寄存器全部清零,当然清零前这些寄存器的值都要先入栈,保存在安全世界MSP或PSP堆栈里。使用BLX跳转时,要携带LR寄存器信息,由于LR是CPU从非安全函数返回后,接着执行的下一条安全指令的地址,这也属于敏感信息,不能通过LR传递到非安全世界。因此执行BLXNS指令时,硬件会自动被LR给入栈到安全世界的MSP或者PSP堆栈中,然后被FUC_RETURN赋值给LR,传递到非安全世界。那么这样,非安全世界,既看不到刚才安全世界执行时通用寄存器的值,也看不到从自己这里返回后,安全世界下面要执行的指令的地址。
3 ~+ V0 _/ ?$ w5 _5 L


* Q# V9 o' k/ t9 U

非安全世界里的代码执行完成后,返回安全世界,使用的跳转指令是普通的BX,目标地址是FNC_RETURN。硬件识别到这个动作后,会自动从安全世界的堆栈,把真正的LR赋值给到PC值,然后软件再从安全堆栈里恢复之前用到的通用寄存器值,继续往下执行。

1 h3 \4 X6 x5 D* v: R

从非安全世界,调用安全世界的代码,无需软件和硬件做太多事情,因为非安全世界,没有什么秘密的上下文,需要对安全世界隐瞒的。直接按照TZ架里要求的跳转到NSC,non-secure callabler区域,先执行一条SG指令,再执行其后的跳转指令,到S区域里函数实现的地方去执行就可以了。从非安全世界带来的LR,在安全世界仍然可见;并且在返回时,赋值给PC来做跳转回非安全世界。


- U0 n* l3 E* _5 D9 Y6 k: P

一句话小结一下:以函数调用的方式在两个世界间切换,是通过新指令、新标志、结合软硬件配合完成的。

0 K" R. ^  X6 |. ~/ r

% [1 w! S  f' f9 U% B: D


7 ^0 R( t1 B: F$ A: k

0 ?: g/ P4 D; Y8 q; H

, X; k8 h. `/ ]6 @

中断的话呢,和原来的中断模型一样,被打断执行的程序,当前执行上下文,即通用寄存器,还有PSR之类的内核寄存器,被硬件入栈。因为是异步的中断,不是软件去调用,所以不会像前个胶片讲的,软件,即编译器去产生那些入栈的指令;硬件,由于是异步发生的,所以入栈必定是由硬件来完成。说到入栈,和以前一样,不论是安全世界还是非安全世界,都有各自的MSP和PSP堆栈,当前使用哪个堆栈,取决于CPU在当前世界的处理器模式,是handler还是thread。


# S( H8 n2 J7 `% ~1 B/ c

除了硬件自动入栈和以前一样,考虑到安全世界被非安全世界中断打断时,跳转过去不能泄露了安全世界的执行上下文环境,还会硬件清零通用寄存器和PSR之类的内核寄存器值。


) y/ Q# p1 a) k9 g: ^' ^: k0 w% N3 E

一句话小结:以中断方式在两个世界间切换,是通过硬件入栈执行上下文,并条件性地清零寄存器现场 来完成的。条件性就是指,从安全切到非安全才需要清零;非安全到安全,不需要。


' R5 u& |- W& g/ E9 Q7 t' M


) n4 _9 V3 B5 x4 W


; \* F; e5 i3 \4 k) O

CPU内核维度


& _3 s* b; b2 x1 x/ z

3 z+ A6 e( V1 f  l: I

TZ架构以前,CPU内核有特权级别 和 非特权级别的区别;还有handler 和 thread 两种不同的处理器模式。

; V; T) H% E) k

CPU执行中断时就处于handler模式,执行前台程序时就是thread模式;而handler模式下,一定是特权级别,就一定使用MSP。执行前台程序时,即处于thread模式时,可以是特权级别,也可以是非特权级别,取决于内核寄存器CONTROL的一个位域决定的。特权级别就使用MSP堆栈,非特权级别就使用PSP级别。


7 p; i# u" D! A4 i# z, s$ f* Q

四者是正交的关系,如左图。

7 G  G' v! R& H: I$ x+ W' ~3 x

TZ架构以后,CPU内核除了特权级别、处理器模式,2个维度的信息外,新增了第三个维度,安全状态。

9 I+ K0 y1 H! T- F5 C; s' z6 R. X

如右图所示。对应的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入门课程里讲,大家有兴趣可以去翻看相关资料。


( R3 ~3 P, q/ O# k


" [* t% \, J7 P

* ~" W: h& m' b; ?


7 g# A7 G- x( }# u; x6 Q8 K7 x' p

TrustZone 架构把在一个芯片上区分出了安全世界和非安全世界,也给 CPU 引入了继“特权级别”、“处理器模式”后的第三个维度,即当前状态是安全的还是非安全的。CPU 现在执行哪个世界的代码,它就是什么安全状态的。

- [  V$ o# s4 a7 y

CPU 在当前的安全状态,要发出一个指令或者数据访问,这个访问 transaction 是安全还是不安全的,不仅和当前CPU所处安全状态有关,还和CPU这条访问是指令访问、还是数据访问有关。并且,最重要的是,还和目标地址处于安全世界还是非安全世界有关。

; |- Z5 Q2 d% w

先来看指令访问,相对简单:无论当前 CPU 当前处于什么状态,它取指的目标地址所在的世界,决定了这次取 transaction 的安全属性。前面讲过,程序代码区域有三种,隶属于两种安全世界下,要么是安全世界,要么是非安全世界。从SAU、IDAU来讲,它们不会对取指transaction做任何限制。这就是我们之前多次讲过,相互调用对方世界的程序,从内核角度都是完全自由的,没有原则性限制。

1 n6 e1 @: @6 g; H

数据访问,从表格可以看到,不仅和目标数据所在地址有关系,还和CPU当前状态有关系了。黄色的case,当CPU本身在非安全状态,想去访问安全世界的数据时,从 SAU 和 IDAU 角度,就会把这个 transaction 给 block 住。这是和指令访问最大不同的地方。另外,即使 CPU 当前处于安全状态,如果要取的数据处于非安全世界,这个transaction也会被SAU、IDAU标上非安全 transaction 的标志。


& i( I% [; T) a9 u$ F5 j

在 SAU 和 IDAU 审查后,可以通过的t ransaction,根据安全还是不安全,再分别到对应的MPU那里去被进一步审查。【】审查都通过后,携带着 HNONSEC 信号,来到AHB5总线上,再和各个 AHB slave 从设备通信。接下来发生的事情,就不是内核负责的,是由各个芯片厂家实现时,各自发挥的舞台了。一个重要的问题,芯片厂家需要解决的时,各自的片上外设,如何识别 transaction 携带的 HNONSEC 信号,并采取相应行动?我们的 STM32L5 如何做的,会在下一节课来讲。

/ Y& P  P$ m9 `: w. A

NS 的t ransaction,也可以是在 CPU 的 S 状态下发出的

CPU 在NS状态下要访问S的目标地址,在 SAU/IDAU 阶段就会被 block

无论 CPU 处于什么状态,在 SAU 看来都可以访问 NS region

$ N0 _, u. C5 i( Q0 v

5 A7 j, G) Y* D


0 y0 S6 }1 e4 h" w


& _) i& h, H# N. i2 Z

了解了内核关于 trustzone 的运行机制,接着来看一下和我们最直接相关的,开发者的软件开发模型。和V7m架构及以前的系统,最大的不同在于,V8M-TrustZone 架构上的软件开发,需要两个工程项目,一个运行在安全世界里,里面是可信软件;一个运行在非安全世界里,里面就是普通的用户程序。


5 h) }+ K, b% x0 M( R

系统复位后,总是从安全世界开始运行安全工程项目。首先执行的就是安全工程里的复位处理,对整个系统做安全属性的初始化和配置。完成之后,跳转到非安全工程项目,执行非安全工程的复位处理。一旦非安全工程开始运行,它就和以往的普通应用一样,运行用户的应用逻辑。它可以调用安全世界里暴露出的服务,比如执行加解密操作,但是无需也无法触碰到加解密密钥。

0 ]* y1 m; H9 E) {& p" E" J

非安全世界的代码也可以把自己这边实现的回调函数注册到安全世界里。安全世界可以通过函数指针来调用非安全世界里的代码。举个例子,TLS 协议栈作为可信代码,运行在安全世界里,但是应用中具体的数据接收、发送操作还是在非安全世界里执行。TLS协议栈需要通过之前注册的回调函数的函数指针,来调用每个用户应用中各种不同的数据收发驱动。有的可能是通过串口操作无线通信模块,有的可能通过 MCU 自身的以太网 MAC 直接通信,这些都是用户自己应用的不同实现。

9 \+ e  a5 ~8 V& H% i

2 |# H* {8 [6 A


' q7 c% W- d- N( a$ X


( c2 Z6 \0 I. c1 h  d+ p: O

进一步,运行在V8-M TrustZone 架构上的RTOS,和以往在 V7-M 架构及以前的系统上,也有所不同。在像 STM32L5 这样的支持 trustzone 安全扩展的平台上,两个世界,可以分别搭建两个执行环境。安全世界里搭建 TEE,trust execute environment,可信执行环境;非安全世界,通常作为 rich execute environment,REE 和 TEE 相对。RTOS 一般运行在REE里,即非安全世界中。RTOS 除了和往常一样,执行任务间调度、通信、同步;如果任务使用了安全世界里的资源,RTOS还要负责分配安全世界里堆栈的分配,以及切换到安全世界时的上下文管理。


; Y( D8 V# z; k" H

如果大家现在凭空想象还不太能理解,没有关系。现阶段,你需要知道就是RTOS,比如说FreeRTOS,为了支持TZ,除了以往的FreeRTOS代码依旧运行在非安全世界,还额外新增了一些代码,运行在安全世界,用于上面说的安全世界堆栈分配和上下文管理。


4 y' k: }3 ~( u1 s& M+ O4 V0 e

STM32L5Cube 固件包里有 FreeRTOS 的例子,大家有兴趣可以去参考。

' h1 b. ^4 ^$ ]7 p

( f0 V# B. U* @5 d8 [$ @


' f5 e3 ?& t4 B

" b# E) T; K; D, Y' B

* T/ {9 n1 l. _% U- A8 h  h' m


: x0 f" Z3 [* h7 r6 `7 l* E( x# V

接下来,我们回过头看一下为了支持TZ的安全扩展,CM33内核在硬件上和寄存器上,和以往我们熟悉的Cortex-M系列内核有什么不同。

7 Y1 E9 H7 n; h/ k+ ~/ f3 j

通用寄存器,R0 到 R15,在安全世界和非安全世界之间共享。因此从安全世界,切换到非安全世界之前,需要软件或者硬件把这些寄存器,就是所谓的执行环境上下文给清零;

CM33 本身,基于 CM4 内核,新增的是两个堆栈 PSP 和 MSP 的栈底指针,PSPLIM 和 MSPLIM;

如果 CM33 还实现了 TZ 安全扩展,像我们 STM32L5 一样,就会新增出来一套所谓的 banked 寄存器。就是图里绿色框中的部分。

堆栈指针、堆栈栈底指针、CONTROL 寄存器、异常掩码寄存器组,都是 banked 的,即,在安全世界和非安全世界,各有一套。

+ C( O: N1 K3 E+ c6 t


+ @; q8 \) ?, n, p


, M8 a; |: G* [" t+ J

区域属性设置


7 c% e( n7 j  N- e- {- D& V


4 D# W: f9 U: ^+ ?. J

CM33内核集成新一代MPU,对区域范围的设置更加灵活。以前的MPU区域,区域起始地址要是其大小的整数倍。比如图里的例子,0x3BC00到0x80400这段范围,它的大小是274K,不是起始地址0x3BC00的约数。因此在以前的PMSAv7规范下的MPU,一个region是无法cover的。需要拆分成4个region:大小分别是1K、16K、256K、1K,才是0x3BC00,0x3C000,0x40000,0x80000这些起始地址的约数。

% \% k2 |6 f. L! M

而新一代MPU,隶属PMSAv8规范,只要求区域的大小是32字节为颗粒度,并没有起始地址和大小的约束关系。示例里的这个范围,在STM32L5的MPU里,一个region就可以cover。


/ O) p: A0 v9 h2 s5 ^1 a7 T2 @% B

除此之外,内核MPU寄存器组,和区域属性设置相关的部分,有了较大调整,寄存器名称、位域都和以前不兼容。当然,STM32L5的HAL层,会把这些硬件实现细节的不同给屏蔽掉,对上层应用保持尽可能稳定的API。


# j  t! w: o/ w

( W* t- d  B$ x

# b1 _6 |, }# q/ F  \


3 Y. Y" |; s, x( Y


3 ]* ?- k" Z7 n8 \; \

用户中断,可以被安全世界的代码,在系统刚复位后执行初始化时,配置把它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执行地址。


+ S4 P0 P$ r& X* K/ V

在前面讲中断导致的安全状态切换时,大家知道,为了防止安全世界里的信息被泄露,当安全世界的代码执行,被非安全中断打断时,在跳转到非安全的中断处理函数执行之前,硬件要在入栈后额外把寄存器内容全部擦除清零,这就势必会造成一点额外的中断响应延迟,从12个时钟周期,延迟到21个时钟周期。

+ O9 }7 {/ U" z/ H. ^& w5 w  p2 N1 S

: f* ~8 K+ D( ]& _7 q6 C1 u1 c+ g5 Q1 s


. ~6 v, i7 ?6 g" H5 n  I0 k8 b& O* `

- |0 U" @( J0 W' v

关于系统异常和用户中断的安全属性,我们再多讲一些。

9 U/ l' i! ?0 L3 M- G# E5 c

这个表格的前四列比较重要,是和安全属性配置直接相关的部分,也是TZ的安全扩展,给内核中断新扩展的概念。

; e9 X7 z7 C1 }2 _7 k4 \

芯片的Reset,永远是在安全世界里执行它对应的handler。HardFault和SecureFault,和Reset一样,都是一个绿色点,表示它们的handler一定是在安全世界里,用户不用做配置,也没有机会做配置。


. H- x5 A+ m* k% T' c3 c

像NMI、BusFault、以及最下面一行的用户中断IRQ,是蓝色的椭圆形。这表示这些异常和中断,可以通过寄存器,就是表格中的第四列里的寄存器,把这些中断配置成target到安全世界,还是非安全世界。这些寄存器复位值都是0,因此NMI、BusFault、以及所有用户中断,上电时都是默认target到安全世界,即发生中断,是去VTOR_S中对应的entry取中断向量来执行,并且是在安全世界里执行中断响应程序。


  X. z% ?8 H4 @$ s8 G

剩下的MemManagement、UsageFaut、SVCall、PendSV、Systick等,都是banked中断,表示它们在安全世界和非安全世界同时有各自一套中断处理函数。什么世界里触发的这些中断,就去什么世界的VTOR中断向量表取对应ISR地址。


8 I1 y2 K) b& A7 B, N# i

2 p# L$ X8 z0 y: e. ~+ U2 b


! ?0 b% d- z# {& T

; F8 F  ?; C0 }) g6 |

系统异常的优先级,从HardFault以下,到systick,都是可以配置的;用户中断的优先级当然也可以配置,都和以往一样。

关于优先级,在TZ安全扩展的实施下,有几点和以前不大相同:


, b% b$ x! R6 r

一个就是系统异常里优先级固定的那几个。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。

" f$ F. d8 Q  V; w" B6 |

另外,对于可配置target到安全还是非安全世界,并且优先级可配置的中断和异常,从表格里看到,满足这两个条件的,除了BusFault是系统异常,其他都是用户应用中断IRQ。它们的优先级除了NVIC寄存器里的interrupt priority register可以设置外,还可受到AIRCR,应用中的和复位控制寄存器里PRIS(prioritize secure interrupt)位域的影响。这一位置1时,整个target到NS的中断,其优先级都要被压缩映射到原本0~255的优先级空间里的128~255低优先级空间。

' A; K8 Z5 w" c1 g5 N* q

* @1 f! a8 N( X) N


( m, _; ?. A# d5 o. F+ M/ }0 p

/ k/ u( ~8 d& `7 x6 H

以上我们对TrustZone在内核里的运行机制进行了讲解。CPU的当前状态,对memory寻址时的安全属性策略,由SAU和IDAU共同定义,但,都仅仅是从内核的视角看出去的。


; N( K! ^, P8 W

携带着安全属性的transaction从内核出来,来到AHB总线上,传达到芯片上集成的各个外设模块和存储模块时,内核外的模块们如何识别transaction的安全属性,并做相应的反馈,这就需要芯片厂家,把安全设计部署在整个芯片系统上。STM32L5是怎么做的?我们下期来看。


; z; Y$ F; _8 I1 j! U# v
收藏 2 评论2 发布时间:2020-5-27 17:41

举报

2个回答
李康1202 回答时间:2020-5-28 08:45:16
谢谢分享
慎微 回答时间:2020-5-28 09:56:29
thanks fou your sharings
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版