STM32L5在片上flash还预置了一部分代码,即10K的RSS。这是以往STM32产品所没有的。它和系统内置的bootloader一起,可以为用户提供安全的启动服务;RSS本身还用于SFI等和安全相关的服务中。RSS的功能,仅在TZ使能的情况下可用。 STM32L5产品的主打特性之一就是:security、安全。Flash又是所有应用都会涉及的模块,它作为系统安全的基石,提供了丰富的安全相关特性,从这张胶片可见一斑。图里的安全特性,有两种图标,灰色的特性,和以往STM32系列一致,保持兼容;绿色的是TZ使能后才具有特性。我们从上往下看:
>> WRP,写保护,和以往一样,基于起始地址和结束地址来划分出被写保护的区域。以往有的产品是基于flash上的page为单位指定写保护的区域,更加灵活。
>> RDP,读保护,新增了RDP 0.5,在该状态下我们只能对非安全区域进行调试。以往的RDP降级,会造成“mass erase”,即“全片擦除”。L5中新增了RDP0.5,RDP降级中,降到RDP0.5的,叫做“partial mass erase”,顾名思义,会擦除片上Flash非安全的区域,因为在RDP0.5,是可以调试NS区域的,因此降到RDP0.5,为了保护代码的安全,非安全区域会把硬件擦除。另外,Trustzone的撤销,必须伴随着RDP降级带来的“全片擦除”来实现。
>> Secure region,HDP区域,都是512K片上用户flash里,仅能被安全的AHB访问。Secure region可以通过选项字节SECWM,或者Flash接口控制器里的寄存器SECBB来设置。HDP区域的范围是在选项字节里设置,但是使能“隐藏保护” 这个功能,还是需要通过寄存器去设置。
>> 前面讲片上Flash的存储阵列时,提到的10K RSS就是这里的“secure system memory”。它在物理flash上固定就是安全区域,在memory map的角度,它被映射在0x0Fxx xxxx开头的地址,因此从内核看出来对RSS代码的访问是安全的AHB transaction。RSS提供的主要服务之一就是SFI,另外还提供了API供用户代码来使能对“HDP安全隐藏区域”的保护。
// By default, All Flash memory is secure. When the TrustZone is active, additional security features are available: • Secure watermark-based user options bytes defining secure, hide protection areas • Secure or non-secure block-based areas can be configured on-the-fly after reset • An additional readout protection: RDP level 0.5 • Erase or program operation can be performed in secure or non-secure mode with associated configuration bit.
Flash上这么多新增的安全特性都是在TZ使能状态下才有的,而STM32L5芯片在出厂时,TZ是不使能的。用户需要选项字节的设置来使能它。
当需要做一些无需TZ特性的,其他功能模块的验证时,比如尝试STM32CubeL5固件包里PKA例子。这些例子都是基于没有使能TZ的L5 DK板子的。那么就需要撤销TZ在选项字节里的设置。
TZ的撤销,必须通过RDP降级造成的全片擦除来同时完成。如果像使能TZ那样,仅仅去操作TZEN选项字节,是无法做到TZ撤销的。这个和以往在L4、F4上面的PCROP撤销有点类似。但是L5有两个需要大家特别注意的地方:一个是:TZ使能,且RDP不为0的状态下,只有内核处于非安全状态,调试端口才能连接。还有就是:RDP降级只能通过bootloader和调试端口来完成,就是说用户代码无法完成RDP降级。
所以呢,如果我们现在使能了TZ,即使没有设置读保护,RDP=0,为了撤销TZ,还是需要把读保护级别先升高到RDP0.5,或者RDP1。升级之前,一定要确保MCU复位后,运行的程序可以跑到NS状态。无论是从用户Flash启动跑自己的代码,还是从系统Flash启动跑secue bootloader,总之要能跑到稳定的NS代码区。那个时候CPU是稳定处于non secure状态的,调试器才可以连接上去,进一步修改选项字节,来把RDP降到0,同时TZEN从1改到0。 在此,我给大家演示一下撤销TZEN的实际操作。
我们继续回来看片上用户Flash。上一季课程,包括前面的概览介绍,我们多次提到STM32L5的flash控制器是TZ aware的外设。一方面,从寄存器角度,它可以把512K存储阵列空间,分成安全的一些区域,和非安全的一些区域;一方面,Flash接口控制器对过来的AHB transaction进行考察,根据该transaction的安全属性,和该transaction要访问的flash区域的实际安全属性,来判断该次访问是否合法。判断的细节,在本集课程的最后一张胶片的表格里有详细展示。
这里我们要引入的概念是,Flash寄存器有些是NS的,有些是S的。NS的寄存器,安全世界代码和非安全世界代码都可以访问;而S的寄存器,通常只能由安全世界代码访问。
胶片里左边是非安全的寄存器;右边是具有安全属性的寄存器。上面一个表格,就是普通的寄存器;下面的表格是对选项字节做设置的寄存器。
在TZ使能的情况下,通过SECWM或者SECBB寄存器划分出了512K片上flash的安全区域和非安全区域后,对安全和非安全扇区的操作,就分开由两套寄存器来掌管了,包括【】【】键值寄存器、控制寄存器、状态寄存器。
而【】【】启动地址寄存器,如果TZ没有使能,则是NSBOOTADD0和1两个起作用;TZ使能的情况下,SECBOOTADD0寄存器起作用。 512K 片上flash空间,有两种方式来划分安全和非安全区域的方式。选项字节,使用water mark的方式,划出安全区域起始和结尾地址。不在指定范围内的区域,就是非安全区域啦。寄存器SECBB,使用block base或者说page base的方式,来提供动态并且更加精细的区域调整机会。寄存器的设置,虽然上电只能使用缺省值,需要代码运行到设置的时候才能生效,但是它的有点在于灵活,可以动态改变一个区域的安全属性,前提是这一块区域在SECWM选项字节中设置的是NS。原因很简单,一个区域的最终配置结果,取决于SECWM和SECBB的共同作用,并且Secure这个属性最高,只要有一方把一个区域设置成了S,另外一方是无法通过NS的设置,来扭转最终的结果;只有一方设置成NS,另外一方才可以通过也是NS来保存该区域的NS属性,或者通过S来改变成安全区域。
通过以上两种方式,可以设置,当然也可以修改一个区域的安全属性。那么随之带来的一点,当一个区域从安全区域,切换成非安全区域,它里面的内容,硬件是不负责给擦除的。需要用户根据实际需要,采取对应措施。
如果是通过选项字节静态切换,只需要用户代码先做目标区域的擦除,设置新的选型字节值,启动OB_launch让选项字节生效,随着带来系统复位。
如果是通过寄存器动态切换,用户代码擦除了目标区域后,在通过寄存器改变该区域安全属性之前,要清空指令cache,再紧接一个ISB指令。选择字节的改变会自动触发系统复位,也会同时情况cache,所以无需人为刻意去操作一下。
我们现在来跑一下STM32CubeL5软件包里的一个例子,体会一下Flash的安全扇区和非安全扇区的操作,以及动态调整一个扇区安全属性的方法和注意事项。
系统上电后的复位状态,flash的区域划分,由选项字节决定。这个例子和上一季大家熟悉的gpio_toggle例子,是一样的设置:前一半,256K,安全,后一半256K非安全。如左图所示。
复位后先运行s.bin,即安全项目的代码。由于现在CPU运行的是安全代码,处于安全状态,所以可以操作SECBB这个寄存器,来把bank1的后面118个本来处于非安全区域的扇区改成安全的扇区。然后就可以对这些安全扇区做擦除,和编程,写入特殊值,然后读出来。
安全代码执行完毕,跳转到非安全代码执行,即中图的ns.bin。它也试图去读取bank1刚才被改成安全扇区并写入特殊值的地址,我们会看到读出的是0,显然没有有效读出来。道理很简单,此时CPU运行非安全代码,处于非安全状态,必然无法访问安全地址的flash。那怎么办呢?是不是也可以通过SECBB寄存器,再动态地把写了特殊值的这段区域再改回非安全的属性呢?可是可以,但是我们知道SECBB这个寄存器是具有Secure属性的,只有安全代码才可以操作它。所以在执行ns.bin的时候,我们无法直接访问到它,需要通过安全世界暴露出来的API访问。
512K的用户片上Flash,除了配合TZ而划分出安全区域和非安全区域,ST进一步加强了片上Flash特别敏感扇区的安全性,就是这里要介绍的隐藏保护区域。这个特性,在L5的参考手册文档里叫做“secure hide protection area”,简称HDP,Secure表示HDP的区域,一定要是在TZ架构下划分出的Secure区域里。本来在TZ的硬件隔离机制下,已经分隔出了安全区域和非安全区域。HDP区域的出现,是在安全区域内部,进一步作出隔离,划出关键区域。
HDP区域的设置和使能,是通过选项字节设置。注意HDPEN使能了,只是说设置好了这样一个HDP区域,还没有表示该区域的保护开关打开。而HDP区域的保护开关,是有寄存器HDP_ACCDIS (access disable)控制的。一旦HDPxACCDIS=1,对该区域的任何访问都被禁止,直到系统复位。系统复位后,只要没有跑到软件设置ACCDIS之前,HDP区域都是和普通的安全扇区一样使用。对HDP区域使用的通常过程是这样的:
先通过选项字节设置好HDP区域的范围,
【】【】系统启动,执行HDP区域里的代码
【】【】调用RSS lib中的退出HDP区域函数,来置位ACCDIS,从而将该区域隐藏起来,直到系统下次复位
【】【】RSS lib里退出HDP区域这个函数,执行完毕后会跳转到HDP之外的安全区域继续执行用户代码,跳转到目标地址,是在第二步用户代码调用RSS lib函数时就参数传递过去了
【】【】HDP区域保护生效后,执行用户安全代码,也完全访问不到HDP区域了,无论是读、写、还是执行代码。
因此,HDP区域,通常是从系统复位的首地址开始,即上电就运行的一段非常关键的代码,比如安全启动,检查当前系统的安全措施,设置相应安全功能,校验下一步要运行代码的合法性。系统的一些机密信息,比如密钥,通常也会放在HDP区域里。在执行完初始的安全启动后,这段区域里的代码,数据都随着HDP保护的生效,隐藏起来,消失在大家的视野里。
这也就是为什么,第二步,当执行完所有敏感操作,要开启HDP保护,需要调用RSS lib里的API来完成。因为启动关闭HDP的代码,是HDP区域里执行的而最后一句话,它自己不可能把自己所在的地方关闭。当然,逻辑上,你是可以站在比如page8的首地址执行代码,把前面的page,0~7的区域用HDP保护起来。你执行的代码所在去区域不是将要被保护的区域里面,那没有问题。但是实际应用中,不会这样做,这会带来潜在的安全风险。大家可以自己想一想为什么。 10K的安全系统存储区,有别于512k可供用户使用的用户flash存储区;是和bootloader一样,芯片出厂前就在这里预置了类似ROM service的功能代码,只能被执行,不能被修改;但是这里固化的service,它和普通的BL又不一样在于,它主要着眼于提供安全相关的服务。前面提到的RSS(信任根服务)还有打开HDP区域保护开关的API,都是存放在这块区域。
RSS区域首地址存放的代码,做的事情,是给系统BL分配Non Secure资源,然后跳转到普通系统Bootloader去执行;其他安全功能,或者说RSS打算暴露出来给你用的功能,就以API的形式,在存放系统bootloader的Non Secure区域,以RSSLIB_PFunc结构体变量,通过函数指针员,供NS和S世界的代码调用。
这个结构体的地址在0x0BF9 7F40,大家有兴趣可以看一下这里的结构体,每个函数指针的值。前面8个是RSS 安全区域代码,暴露给NS世界调用的,红色字体表示;因此你去看任意一个函数指针指向的地址,会看到第一条就是熟悉的SG指令。RSS安全区域代码,给Secure代码调用的一个API,就是CloseExhitHDP,正是前一页胶片说的,调用RSS lib里的函数来把某段HDP区域保护开关打开。 从用户应用代码角度,我们只要通过RSSLIB_PFUNC这个结构体全局变量,调用对应函数指针即可实现。
安全系统储存区,RSS,这些都是STM32L5新引入的功能,我们现在暂且先了解这么多。后面在下一季,STM32L5的高级课程,讲到SBSFU、TFM和SFI的时候,我们再详细展开。
512K 片上用户Flash的WRP写保护,和以往的系列没有什么区别,并且和是否STM32L5使能了TZ也没有关系。因此这里不再累述。
|