基于NAND Flash的CPU安全启动设计与实现*
2022-06-23张剑锋
龚 锐,石 伟,刘 威,张剑锋,王 蕾
(国防科技大学计算机学院,湖南 长沙 410073)
1 引言
嵌入式系统中,一般需要多种不同的非易失存储介质,用于存储代码和数据。这些非易失存储介质包括EEPROM、NOR Flash和NAND Flash等。其中,NOR Flash具有稳定可靠的特点,并且具备直接执行XIP(Execute In Place)的优势,可以通过控制器将其内部地址直接映射成CPU可直接访问的地址,从而使得存储其中的代码可以直接执行。但是,NOR Flash容量较小,所以一般用于存储可执行的启动代码。
相对于NOR Flash,NAND Flash具有更大的存储容量和更快的写入速度,因此一般作为大容量的存储设备使用。但是,NAND Flash的器件特性导致其出厂时就有一定的坏块存在,并且在使用过程中,也可能由于反复擦写出现坏块。因此,使用NAND Flash时,需要首先加载驱动,由驱动对NAND Flash进行坏块管理、读写控制和寿命管理等。所以,NAND Flash并不具备XIP能力,存储其中的代码不能直接执行,必须由驱动加载到内存中才能执行。
因此,在嵌入式系统中,一般同时具有NOR Flash和NAND Flash 2种存储介质。NOR Flash用于存储启动代码,包括bootloader、Uboot等,NAND Flash用于存储OS镜像和文件系统。这种启动方案增加了PCB板面积,不利于系统小型化,也不利于控制系统成本和功耗。
本文首先提出了一种软硬件结合的方法,实现了基于NAND Flash的直接启动。该方法利用NAND Flash器件保证第1块必定为好块的特点,在第1个好块中存储好块寻找程序,并在启动时直接执行。软件将寻找到的好块信息填写在CPU片内NAND Flash控制器中的硬件块映射表中,从而实现NAND Flash中部分地址的直接映射。这种方法使得NAND Flash具备了XIP能力,不需要复杂的驱动就可以实现CPU对NAND Flash的直接访问,并使得存储其中的代码可以直接执行。通过该方法,在系统中可以去掉NOR Flash,实现仅需NAND Flash的启动方案。
基于上述启动方案,本文进一步提出了一种基于NAND Flash的安全启动方案,实现了仅需NAND Flash的安全启动。该方案为简化片内BootROM代码,将一部分代码作为扩展BootROM存储于片外NAND Flash中。片内可信根只执行简单的Hash比对,对存储于片外NAND Flash中第1块内的扩展BootROM代码进行校验。再由第1块代码通过签名验证的方式,对后续的固件代码进行验证。从而建立起逐级的可信链,实现系统的安全启动。
本文的主要贡献包括以下2个方面:
(1)提出了一种软硬件结合的方法,实现了NAND Flash中部分地址的直接映射,从而使得NAND Flash中存储的代码可以直接执行,实现了仅需NAND Flash的系统启动方案;
(2)提出了一种基于上述启动特性的CPU安全启动方案,通过将BootROM扩展存储至NAND Flash中,简化了片内固化的BootROM设计,并实现了多级验签机制。
通过本文提出的方法,可以实现仅需NAND Flash的系统启动方案,并在此系统上实现安全启动,从而有效降低系统的成本和功耗,且提高了系统的安全特性。
2 研究背景与相关工作
2.1 Flash存储器
目前主流的Flash存储器根据其内部架构和实现技术可以分为NOR Flash和NAND Flash[1]。 其中,NOR Flash是由Intel公司于1988年推出的,NAND Flash是由东芝公司于1989年推出的。
NOR Flash具有较快的读速度,并且具备直接执行(XIP)功能,但其容量较低且价格较高,所以一般作为系统的启动代码存储器,也就是说直接映射为内存(Memory)空间使用。NAND Flash读取速度较慢,但其写和擦除操作较NOR Flash更快,且存储容量更大,价格较低。因此,NAND Flash多用于数据存储,也就是说作为外存(Storage)使用。
NAND Flash采用基于块和页的组织结构。一片NAND Flash芯片划分为多个块,擦除以块为单位进行,擦除后整块的数据均为全1。每一个块内又分为若干个页,页是读取和写入的基本单位。以当前典型的4 Gb的NAND Flash芯片为例,一般块大小为256 KB,全片共2 048个块,每块又分为64个4 KB的页。对NAND Flash进行读取访问时,由控制器发出块号和页号等信息,NAND Flash器件返回该页的全部数据。
NAND Flash芯片在出厂时,并不保证所有的块都是好块,即都可用,仅保证第1块一定是好块。在写入时,如果遇到坏块,一般往后顺延一个块写入。一个块是否为好块,标识在NAND Flash的OOB(Out of Band)空间,不同厂家的标识方法略有不同。如东芝的坏块标识是该块的第1页和第2页的OOB空间第1个字节非全1[2],Gigadevice的坏块标识是该块第1页OOB的第1个字节非全1[3],Kioxia的坏块标识是该块所有存储空间均为全0[4]。因此,访问NAND Flash时,一般需要使用厂家提供的驱动进行块页地址映射、坏块识别与跳过等操作。
由于NOR Flash和NAND Flash的固有特性,一般系统中同时具有NOR Flash和NAND Flash 2种存储介质。NOR Flash用于存储启动代码,NAND Flash用于存储文件系统。文献[5]提出了一种全硬件支持的NOR Flash直接地址访问控制器。本文提出一种软硬件结合的方法,支持NAND Flash的直接地址访问,并实现了相关的控制器设计。采用本文提出的方法,可以实现NAND Flash中存储代码的直接执行,并实现了坏块管理的功能。基于本文的控制器,可以取消系统中的NOR Flash,从而缩小PCB板面积,有利于系统小型化,同时控制系统成本和功耗。
2.2 安全启动
信息系统面临多种现实的安全风险。为了应对这些安全风险,一般需要在硬件的支持下,实现硬件资源隔离[6]、安全启动[7]等安全机制。其中,安全启动是比较常见的信任链建立机制,其启动流程如图1所示。通过可信根对片外固件进行验签,确保固件没有被非法篡改过;再由固件对OS进行验签,确保OS的合法性;最后由OS对应用进行验签,保证最终执行的应用的合法性。
Figure 1 Traditional secure boot flow图1 传统的安全启动流程
传统的安全启动流程需要以板级的TCM/TPM(Trusted Cryptography Model/Trusted Platform Model)芯片作为可信根[7]。随着CPU的进一步发展,出现了将可信根内置于CPU内的方案[8,9]。CPU内置的可信根一般由片内ROM及ROM中存储的BootROM代码、efuse和密码加速引擎构成。BootROM代码为CPU启动后执行的代码,efuse中存储公钥的Hash。片外被验签的第1级固件的Hash值运用私钥进行运算,得到签名,并与片外第1级固件一起存储。启动后CPU执行BootROM程序,将片外的第1级固件搬到片内SRAM区域,再启动密码加速引擎,得到固件计算Hash值并用片内efuse存储的公钥进行计算,得到的值与片外存储的签名进行比对,一致则说明固件没有被篡改过,是安全可信的。
传统的安全启动方案需要可信根至少执行Hash和非对称算法2种运算,使得固化于片内的BootROM代码设计复杂,所需的ROM容量大,不利于控制芯片面积和成本。本文提出的扩展BootROM的安全启动方案,利用NAND Flash启动的特性,将部分BootROM扩展存储至片外NAND Flash第1块中,片内BootROM只需要计算Hash就可以验证扩展BootROM,有利于减少片内ROM容量,降低代码复杂度。同时,安全启动也要和片外启动固件的存储相结合。文献[10]提出了一种基于NOR Flash的安全启动控制器设计方案。而本文提出的是基于NAND Flash的安全启动方案,可以实现单NAND Flash系统的安全启动。
3 地址直接映射
为了缩小PCB板面积,实现系统小型化,降低系统的成本与功耗,本文设计实现了一种仅需NAND Flash的系统启动方案。该方案取消了一般系统中存在的NOR Flash芯片,将系统启动代码直接存储在NAND Flash中。此时NAND Flash芯片中前-部分存储系统启动代码,通过特殊设计的地址直接映射方式,实现CPU在启动后对这部分代码的直接访问和执行;NAND Flash后一部分主要的存储空间仍然存储OS镜像和文件系统,通过加载驱动的方式进行访问。
为实现NAND Flash前一部分存储空间的地址直接映射,本文提出了一种软硬件结合的方法。在硬件上,在CPU中设计实现了一种全新的NAND Flash控制器,该控制器由Cache、地址映射逻辑、块映射表和接口逻辑等组成,具体结构如图2所示。
Figure 2 Block diagram of NAND Flash controller图2 NAND Flash控制器结构框图
系统启动后,CPU取指执行时通过软件可见的Memory地址直接访问NAND Flash控制器,获取启动代码。由于NAND Flash接口访问速度较慢,且一般采用整页读取的方式进行操作,本文在NAND Flash控制器内设计实现了一个Cache,该Cache的行大小为一整页的容量。CPU通过Memory地址访问时,首先判断该地址的代码是否在Cache中命中,如果命中,则直接返回;如果不命中,则需要访问片外NAND Flash器件取回相应的启动代码。
Cache不命中时,由地址映射逻辑将Memory地址按照NAND Flash的特性映射为块号和页号。需要注意的是,这里的块号为逻辑块号,是由Memory地址直接映射得到,并没有考虑物理上NAND Flash可能存在的坏块。这些逻辑块号还需要由块映射逻辑映射到物理块号,物理块号才保证是NAND Flash中的好块。
经过块映射逻辑得到的物理块号和页号,由Flash控制模块产生符合NAND Flash接口时序和协议要求的访问序列,访问片外NAND Flash,取回一整页数据,并回填至Cache中,返回相应的代码、数据给CPU核。
上述结构中,最关键的结构为块映射逻辑,本文设计实现了如图3所示的硬件块映射表,通过逻辑块号访问硬件块映射表,获得该逻辑块号对应的物理块号。
Figure 3 Block mapping table图3 块映射表
系统启动时,CPU并不知道片外NAND Flash中的坏块信息,此时块映射表中的信息是无效的。本文利用NAND Flash器件保证第1块为好块的物理特性,将块映射表0号入口固定复位为0,也即将0号逻辑Block固定映射为0号物理Block。通过在NAND Flash器件的0号物理Block空间存放的好块寻找程序,CPU直接访问0号物理Block并执行该程序。该部分代码从1号物理Block开始,寻找N-1块物理好块,并将其物理块号填入硬件块映射表。好块寻找算法伪代码如算法1所示。
算法1好块寻找算法
输入:块映射表BMT项数N;
输出:块映射表BMT。
①for(i=1;i=i+1;i≤N)
②PBN=BMT[i-1]+1;
③while(NANDFlash[PBN] is bad block)
④PBN=PBN+1;
⑤endwhile
⑥BMT[i]=PBN;
⑦endfor
该算法先将块映射表BMT中前一项记录的物理块号加1,作为当前物理块号PBN。通过访问该PBN在NAND Flash器件中对应的块,读取其坏块信息,判断当前物理块是否为好块。如果为好块,则将PBN填入BMT中对应表项,否则将PBN加1,继续寻找好块。
通过执行位于0块空间的算法1,填好块映射表,即可实现CPU所见的存储空间地址到实际的物理块、页号的转换。实际可直接映射的物理空间大小A取决于块映射表大小N和物理块大小B,即A=N×B。假设映射表大小为8项,NAND Flash物理块大小为256 KB,则能够地址直接映射的空间大小为2 MB。也就是说NAND Flash空间中前一部分空间在这种方案下可以实现地址直接映射和访问,存储于其上的启动程序代码可以直接执行。但是,超过这部分地址空间的块,还是需要通过驱动来进行访问。因此,在前一部分可以直接地址映射和执行的NAND Flash空间内,除了需要存储启动代码外,还需要存储NAND Flash驱动,以便访问后续块空间。
本文提出的这种软硬件结合的地址直接映射方法,充分利用了NAND Flash中第1块为好块的特性,在第1块中存储好块寻找程序,通过直接执行该算法,填写硬件块映射表,从而在无驱动支持下,实现软件可见存储地址到物理块、页号的直接映射。基于本文提出的方法,可以取消系统中常见的NOR Flash芯片,实现单NAND Flash启动,从而有效减少系统成本、体积和功耗。
4 基于扩展BootROM的安全启动
传统的安全启动流程要求片内BootROM至少可以执行非对称和Hash 2种类型的密码运算,对片内BootROM的存储容量和代码复杂度要求比较高。特别是非对称密码算法运算复杂,即便是调用片内的硬件密码引擎,也需要有比较复杂的软件驱动,需要占用大量的片内BootROM存储容量。
一般来说,片内BootROM存储在ROM或eFlash中。ROM中的代码在芯片生产时即确定,eFlash可以在芯片生产回片后再烧录。但是,现在先进工艺下均没有eFlash器件。所以,需要先进工艺的高性能CPU只能采用片内ROM存储BootROM代码。片内ROM的容量大小会影响芯片面积,进而影响芯片的成本。而BootROM代码的复杂度又会带来验证的复杂度,必须在芯片流片前将BootROM代码中所有的分支都验证到,保证BootROM代码是无错的。因此,简化BootROM代码,压缩BootROM镜像大小,降低其复杂度,对于降低芯片成本、验证复杂度和流片风险都有很大的作用。
具体到本文提出的单NAND Flash启动方法,由于必须首先执行片外NAND Flash中第1块上的好块寻找程序,才能填写硬件好块映射表,进而对后续地址进行直接地址映射。所以,如果采用传统的安全启动方案,需要BootROM代码对片外第1块的代码先进行验签,验签通过后执行,再由第1块代码验签后续代码(如图4所示)。这不仅使得BootROM程序复杂,镜像容量大,还使得本来可以1次完成的固件验签,至少需要2次验签才能完成,不利于系统快速启动。
Figure 4 Traditional NAND Flash secure boot flow图4 基于传统方法的NAND Flash安全启动流程
为了解决传统的基于验签的安全启动流程运用于NAND Flash启动时导致的ROM容量过大,BootROM代码复杂,需要多次验签等缺点,本文提出了一种基于扩展BootROM的NAND Flash安全启动方法。
本文提出的方法将片外NAND Flash上第1块的代码的Hash值存储在片内efuse中。CPU启动时,执行片内BootROM代码,将片外NAND Flash中第1块的代码拷贝到片内SRAM,再用相同的算法计算Hash值,并与片内efuse中存储的Hash值进行比较,如果比较通过,则认为片外NAND Flash上第1块的代码没有被篡改过,是安全可信的,可以执行。具体流程如图5所示。
Figure 5 NAND Flash secure boot flow based on extended BootROM图5 基于扩展BootROM的NAND Flash安全启动流程
本文提出的方法中,对片外第1级只采用Hash比对,因此当在片内efuse中写入Hash值以后,片外第1级固件就不能再更改了。片外NAND Flash中第1块的代码类似于不可更新的BootROM,本文称之为扩展BootROM。与存放于片内的BootROM相比,扩展BootROM具有一定的灵活性,因为不用在芯片流片前就确定代码镜像,回片后有一次烧录机会。此外,对于一款芯片,片内BootROM的代码完全相同,但是扩展BootROM还可以根据不同的板级设计有所区别。比如,板级集成了不同厂家的NAND Flash芯片时,由于不同厂家的坏块标识略有不同,因此好块寻找算法具体实现也有所区别。在扩展BootROM中实现好块寻找算法,可以根据不同的NAND Flash器件,在回片后在片外Flash的第1块上烧录不同的扩展BootROM代码,并将其Hash值烧录到片内的efuse中。
与传统的基于验签的方法相比,本文提出的方法在片内BootROM中只需执行Hash运算,减少了非对称运算的时间和代码量,有效减少了ROM容量。
5 实验与结果
基于本文提出的软硬件结合的地址映射方法,设计实现了一款SPI接口的NAND Flash控制器。该控制器对内采用APB3接口,连接到片上总线NoC,对外采用标准的SPI接口,连接NAND Flash芯片。该SPI NAND Flash控制器的主要设计参数如表1所示。
Table 1 Design parameters
由于SPI NAND Flash采取整页读出的方式,为了减少SPI接口访问,尽可能多地重复使用一次读出的数据和代码,本文设计的SPI NAND Flash控制器内的Cache必须具备将一整页数据全部缓存的能力。当前主流的NAND Flash的页大小一般为2 KB(总容量不大于2 Gbit器件)或4 KB(总容量不小于4 Gbit器件)。因此,本文将Cache行大小设计为2 KB,Cache行数设计为2。这样对于页大小为2 KB的器件,可以缓存2个页的数据,对于页大小为4 KB的器件,可以缓存1个页的数据。
为了实现地址的直接映射,本文采用了硬件块映射表的结构,该映射表的大小决定了可以直接映射的地址空间大小。本文设计的块映射表大小为4。当前主流的NAND Flash的块大小一般为128 KB(总容量不大于2 Gbit器件)或256 KB(总容量不小于4 Gbit器件)。对于块大小为128 KB的器件,可以直接寻址512 KB的空间,对于块大小为256 KB的器件,可以直接寻址1 MB的空间。一般来说,该直接寻址空间已经足够存储完整的Uboot代码。因此,采用本文设计的SPI NAND Flash控制器可以实现仅需NAND Flash系统的直接启动。
为了验证本文提出的安全启动方案的有效性,设计了一个验证SoC,其框图如图6所示。该SoC采用开源的32位RISC-V架构处理器内核PULPino[11]。该内核通过NoC连接本文设计的SPI NAND Flash控制器,NoC上还挂接了256 KB的SRAM、efuse和密码计算引擎。
Figure 6 SoC diagram图6 SoC框图
本文对比了采用传统验签流程和采用扩展BootROM方式进行验签时对片内ROM的容量需求,具体如图7所示。
Figure 7 Comparison of BootROM code capacity图7 BootROM容量比较
采用传统验签流程时,使用标准的X.509证书格式。X.509标准是国际电信联盟ITU(International Telecommunication Union)制定的公钥证书格式标准,已经广泛应用于众多互联网协议和电子签名服务中。实现基于X.509证书的验签,至少需要进行非对称和Hash 2种运算,本文采用RSA2048和SHA256 2种类型的运算。而采用扩展BootROM的方式,仅需要在片内ROM中实现Hash算法,本文采用了SHA256算法。可以看出,采用传统验签流程,片内ROM容量大小至少需要30 KB以上,而采用扩展BootROM的方式,片内ROM仅需16 KB。因此,本文提出的方法可以大大简化片内BootROM的软件设计,减少片内ROM容量,有利于降低BootROM软件验证风险和缩小芯片面积,降低芯片成本和功耗。
本文还对比了采用传统验签流程和采用扩展BootROM方式进行验签时所需的执行时间,具体如图8所示。图8中采用了归一化的执行时间比较,以X.509验签执行的时钟周期为1,扩展BootROM校验方法执行的时间为X.509验签执行时间的89%。测试执行时间时,假设处理器核、片上网络、Crypto单元、SRAM和ROM等都工作在500 MHz,SPI接口工作在125 MHz。采用X.509验签时,片内BootROM首先执行RSA2048和SHA256 2种类型的运算,对片外NAND Flash上第1块中的128 KB代码进行验签;然后执行第1块的代码,寻找好块,填写好块映射表,再由第1块的代码执行RSA2048和SHA256算法验签后续算法。采用扩展BootROM校验方法时,片内BootROM仅需执行SHA256算法计算片外NAND Flash第1块中的128 KB代码的Hash值;然后再由第1块的代码执行RSA2048和SHA256算法验签后续算法。可以看出,本文提出的安全启动方法从NAND Flash直接启动,相比于传统验签方法可以大大节省启动时间。
Figure 8 Comparison of normalized secure boot execution time图8 归一化安全启动执行时间比较
6 结束语
针对NAND Flash存储器固有的坏块导致不能直接寻址,不能支持存储系统启动代码直接执行的特点,本文提出了一种软硬件结合的方法,实现了NAND Flash中部分地址的直接映射,从而使得NAND Flash中存储的代码可以直接执行,实现了仅需NAND Flash的系统启动方案。本文还提出了一种基于上述启动特性的CPU安全启动方案,通过将BootROM扩展存储至NAND Flash中,简化了片内固化的BootROM设计,并实现了多级验签机制。
通过本文提出的方法,可以实现仅需NAND Flash的系统启动方案,并在此系统上实现了安全启动,从而有效降低了系统的成本和功耗,且提高了系统的安全特性。