最近看了一篇Blog讲的是关于PC安全的,其中很多的地方还是有一定相似之处。其中这个UEFI引起了我兴趣,以前安装系统的时候听说过这个名词。这里于是便来认识一下什么是UEFI。
前言
大多数人接触UEFI都是在PC的应用场景上,有在PC上安装过多操作系统的经历的同学,通常会进入UEFI界面设置操作系统引导顺序、CPU虚拟化等设置。UEFI诞生之初也确实是作为BIOS的替代者,主要应用在PC电脑上。
随着手机/平板等移动设备的发展,高通从MSM8998开始使用UEFI替代LK(Little Kernel)作为手机的Bootloader,作为一个嵌入式开发者势必比较好奇,UEFI相比嵌入式常见Bootloader(u-boot、LK等)区别在哪?
UEFI(Unified Extensible Firmware Interface,统一可扩展固件接口)定义了操作系统和平台固件之间的接口,它是UEFI Forum发布的一种标准。它只是一种标准,没有提供实现。下面让我们通过Tianocore社区提供的EDK2源码一窥UEFI的具体实现,相比其他的Bootloader有什么优势特点。
一、UEFI入门
1. 启动过程
UEFI系统从上电到关机可分为7个阶段:SEC-> PEI-> DXE-> BDS-> TSL -> RT-> AL
开源嵌入式的Bootloader大多分两个阶段(Stage1、Stage2)。(想想Uboot还真是)
- Stage1:系统资源环境较少情况下部分硬件初始化,为Stage2准备执行环境。
- Stage2:系统硬件进一步初始化,定制化功能的实现和操作系统的引导。
进行类比,SEC/PEI可以看作是Stage1,DXE/BDS/TSL可以看作是Stage2。
二、UEFI的特点
LK、u-boot等开源的Bootloader都是结构简单的轻量级系统,开发门槛低。
UEFI系统虽然较为复杂,但其具有模块扩展性、多任务实现等类操作系统的特性,更易实现复杂功能,且具有较好的开发灵活性,因而高通使用UEFI替换LK作为手机的Bootloader也就不奇怪了。
1. 扩展性
UEFI-统一可扩展固件接口,可扩展性是UEFI的突出优势。
这主要体现在UEFI可以动态地加载其它编译好的efi镜像,而u-boot在编译完后并不能动态地加载其它定制化的驱动或者命令。那UEFI是如何实现这一特性地呢?
DXE是UEFI的核心阶段,这一阶段初始化了Boot Services(后面简称BS),BS为后面的引导过程和应用程序的执行提供了几乎所有基础服务接口。
2. 设备驱动模型
UEFI驱动实现了一种类Linux系统的驱动模型,实现了Host Bus、Driver和Device的架构解耦。(DXE_DRIVER可以看做是一种驱动类型的功能服务,不一定需要硬件支持)
以SCSI驱动为例,在UEFI中的驱动架构大致如下图:
UEFI驱动一般分为两部分,一部分是架构部分的Driver Binding Protocol,一部分是和硬件IO相关的Protocol。
3. 多任务机制
目前接触的嵌入式Bootloader都是单线程系统,但UEFI实现了Linux系统中类似eloop的单线程多任务机制,UE,eloop机制是通过Linux系统的select/epoll系统调用实现了多任务、定时器等机制,那UEFI中是如何实现这一机制的呢?
UEFI中的应用和Event的Notifier可以看作是任务,DXE中初始化的BS提供了事件相关接口实现了异步操作。
小结
u-boot、LK和UEFI等Bootloader各有优劣,无所谓哪一种更好,只有合不合适的情况。
目前在嵌入式领域应用最广的BL还是u-boot,结构简单,驱动移植和裁剪的门槛低,可以满足绝大部分嵌入式系统的需求,而UEFI在手机、平板等较复杂的嵌入式硬件更具优势,满足手机设备的多样化需求。
三、UEFI在ARM生态中现状如何?
在大多数人心目中,ARM世界的BootLoader还被uboot+DeviceTree统治。是啊,uboot简单快捷,移植容易,为什么不用呢?
但uboot有个显著的缺点:缺乏标准和互操作性。这让它更合适专用系统,而不是通用系统。
在ARM多用于嵌入式设备,如摄像头和手机等等的时候,这个并不是问题。
嵌入式设备往往是垂直定制开发的,从硬件、固件、BSP和操作系统,都深度定制,没有问题是定制修改程序所不能解决的。但当ARM进入到x86的传统领域后,如PC和服务器市场,情况发生了变化。
1、ARM生态的通用性需求不断增长
与大多数人基本的概念不同,在某种意义上来说,X86体系比ARM体系更加开放。X86是很多小伙伴一起玩,以生态圈的概念提供产品,并对自己那部分负责;而ARM体系虽然也依赖生态圈,但最终有个大Boss统合整个生态链,提供最后产品并对该产品负总责。
X86生态圈玩家众多,有OS 厂商(OSV)定期发布操作系统,如Windows,Ubuntu;芯片厂商提供CPU,如Intel, AMD;主板厂商(OEM)提供电脑主板;独立硬件供应商(IHV)生产扩展板卡如显卡等等PCIE扩展卡,再如内存厂家推出一代一代不同的内存条等等。
DIY玩家可以自由选择搭配合适/兼容的产品搭配出自己心仪的机器,休闲上网用户花2000多元就可以搭配出一套可用的电脑,而游戏玩家则可能花费上万元才能满足游戏配置需求。还有些品牌机厂商如Dell和联想等,他们提供整套最终产品给用户。但他们实际上是在所有小伙伴的零件基础上拼凑出个产品,技术不强,话语权弱,并不能统一整个产业链。用户津津乐道的反而是用的什么CPU,安装的什么操作系统,用的那种显卡等等。Windows死机、蓝屏和缓慢等等时候,用户往往会抱怨微软和Intel,而不是品牌厂商。
在X86生态中,强势的是整个生态链的两端:微软和Intel,分别卡住最上游和最下游。在争取了链条利润的最大头后,也要直接面对数千数万种千奇百怪的硬件产品,于是才发明了UEFI和ACPI标准,规范了各个层面的调用接口。更重要的是,为了规范中间链条各个厂商的行为,分别推出了一软一硬两个认证:WHQL和Intel最新的EVO。保证了市面上上百种产品的质量可控。
传统ARM体系由最后品牌厂商统合整个产品,它负责打通整个产业链,并对其中所有部分负责,话语权极强,同时对技术也相对较强。用户面对的具体品牌的产品,而不是碎片化的各个部分。强势的如Apple,硬件软件一起抓,完全组成闭环的链条。稍差也如华为等安卓手机,要负责安卓系统在自己手机移植部分(BSP),客户出了问题并不会找谷歌,而会去找华为。对规范和标准的需求并不强烈。
随着采用ARM内核的芯片性能不断提高,ARM内核芯片不断进入x86传统领域。移动和桌面有Apple的Axx,服务器端有鲲鹏920(也可以用于桌面)、倚天710,、安培和Graviton系列,它们的性能完全可以和Intel/AMD的芯片打擂台。当ARM的小伙伴们进入这些领域后,他们发现一样也要面临碎片化的生态系统的问题。于是接受UEFI和ACPI变成了必然的选择。这也从EDK2社区最活跃的用户在近几年已经不是x86的开发者,而是ARM相关开发者(最近一年多换成了RISC-V)可以看出。于此同时EDK2开源平台仓库EDK2_Platforms下面也涌入了大量ARM平台,好不热闹。
现状是,所有成熟的ARM服务器产品都采用UEFI+ACPI方案;很多ARM移动和桌面产品已经采用UEFI+ACPI方案;大量ARM产品在赶来的路上;形式喜人。同时,作为ARM世界的灵魂,ARM公司也推出了大量规范和标准,来规范ARM产品的行为,如针对服务器的服务器的SBSA和SBBR规范,最新的基本系统架构(BSA)、基本启动要求(BBR),以及针对安全的基本启动安全要求(BBSR)等等。
多个层次和方向的规范标准相当繁杂,而且标准有些是必须达到的,有些则是推荐,这样给最终用户造成了不少困扰,也造成了市场的混乱。如何定义“好的”ARM产品标准,这个标准必须是可衡量的、可标识的,并且要简单易懂,最好还能朗朗上口?微软以前的答案是WHQL认证测试和Windowsxx标签,ARM的是什么呢?
ARM公司在ARM进入服务器领域不久就发现了这种需求,推出ARM ServerReady计划。随着ARM内核芯片的市场不断外延,ARM在2020年10月开发者峰会上,正式宣布升级版:ARM SystemReady [1]和白皮书[2] 。
ARM SystemReady: ARM公司在ARM进入服务器领域不久就发现了这种需求,推出ARM ServerReady计划。随着ARM内核芯片的市场不断外延,ARM在2020年10月开发者峰会上,正式宣布升级版:ARM SystemReady [1]和白皮书[2] 。
ARM SystemReady的口号是“Just Work”(即开即用,不是仅仅工作),强调它的可用性和已验证性。它主要包括四种认证:
-
SystemReady SR:即以前的ServerReady,适用于server和workstation。它除了要求SBBR之外,SBSA和SBBR被替换为通用的BSA规范和SBSA规范。
-
SystemReady ES:适用于基础架构和物联网边缘设备等嵌入式服务器。
-
SystemReady IR:用于基础架构物联网和物联网边缘设备。
-
SystemReady LS:适用于当下火热的LinuxBoot来启动server。
标准可以说涵盖了从云端到物联网边缘计算中的所有部分。因为覆盖范围大,所以各个部分的标准要求各有不同:
如果通过了基本启动安全要求(BBSR)的要求,还可以在Logo旁配享ARM特制小盾牌:
关于BSA、SBSA、SBBR和BBSR等标准大家可以在ARM官网或者System Ready Spec[3]页面 找到相关链接,并仔细阅读。如果大家留言足够踊跃,也许今后本专栏会专文介绍其中比较重要的SBBR和SBSA规范。
这里要特别一点,那就是UEFI和ACPI、DeviceTree(DT)的关系。我们可以看到,SystemReady IR固件spec是个奇怪的组合UEFI+DeviceTree。我们普通看到的系统都是UEFI+ACPI,或是uboot+DeviceTree,但是实际上,DT和UEFI并不矛盾,更有甚者,有些定制化奇怪的系统上甚至出现UEFI+ACPI+DT的怪异组合。这里采用DT主要是考虑物联网IOT系统使用DT更加简单。关于为什么在ARM中,ACPI要替代DT见参考资料5 。
四、UEFI之 Secure boot
1、概要
Secure Boot的目的,是防止恶意软件侵入。它的做法就是采用密钥。UEFI规定,主板出厂的时候,可以内置一些可靠的公钥。
然后,任何想要在这块主板上加载的操作系统或者硬件驱动程序,都必须通过这些公钥的认证。也就是说,这些软件必须用对应的私钥签署过,否则主板拒绝加载。由于恶意软件不可能通过认证,因此就没有办法感染Boot。
在安全启动中,证书颁发机构 (Ca) 包括 OEM (或其委托) 和 Microsoft。Ca 生成构成信任根的密钥对,然后使用私钥对合法操作(如允许的早期启动 EFI 模块和固件服务请求)进行签名。对应的公钥将嵌入到启用了安全启动的 Pc 上的 UEFI 固件中,并用于验证这些操作。
2、说明
-
缩写CA: Certificate authority;PUK: Public key;RSA:One public-key cryptosystems;SHA: Secure hash algorithm
-
公钥加密,公钥加密使用一对数学相关的加密密钥,称为公钥和私钥。如果你知道其中一个密钥,则无法轻松地计算其他密钥。如果使用一个密钥对信息进行加密,则只有对应的密钥才能对该信息进行解密。对于安全启动,私钥用于对代码进行数字签名,公钥用于验证该代码的签名以证明其真实性。如果私钥被泄露,则具有相应公钥的系统不再安全。这可能会导致启动工具包攻击,并会损害负责确保私钥安全性的实体的信誉。
-
密钥对:公钥分发,私钥自留。常见的公钥格式:cer/der,常见的私钥格式:pfx
-
RSA 2048 加密:RSA-2048 是一种非对称加密算法。以原始形式存储 RSA-2048 模块所需的空间为2048位。
-
自签名证书由与证书的公钥匹配的私钥签名的证书称为自签名证书。根证书颁发机构 (CA) 证书属于此类别。
-
证书数字证书的主要用途是验证签名数据(如二进制等)的来源。证书的常见用途是使用传输层安全性 (TLS) 或安全套接字层 (SSL) 的 internet 消息安全。通过使用证书验证已签名的数据,接收方可以知道数据的来源,以及是否在传输过程中对其进行了更改。通常,数字证书包含一个可分辨名称 (DN) 、一个公钥和一个签名。DN 标识一个实体(例如,公司),其中包含与证书的公钥匹配的私钥。使用私钥对证书进行签名,并在证书中放置签名会将私钥与公钥进行联系。证书可以包含某些其他类型的数据。例如,一个 X.509 证书包括该证书的格式、该证书的序列号、用于对该证书进行签名的算法、颁发该证书的 CA 的名称、请求该证书的实体的名称和公钥以及 CA 的签名。
-
PK(Platform Key):平台密钥(允许操作KEK的私钥)平台密钥在平台所有者和平台固件之间建立了信任关系,可以在Setup UI中设置,此步骤会在安装模式下将平台移到用户模式。Microsoft 建议平台密钥的类型为 EFI _ CERT _ X509 _ GUID ,其中包含公钥算法 RSA、公钥长度为2048位和签名算法 sha256RSA。如果需要考虑存储空间,则平台所有者可以使用类型 EFI _ CERT _ RSA2048 _ GUID;规定一个硬件平台只能被一个拥有者所拥有,即 PK 只能存在一个 ,与拥有者相关的公钥被存储在 FLASH 里面的 PK 变量里面,同时,拥有者的私钥可以来对 PK, KEK, db, dbx 进行签名和管理。
-
KEK(Key Exchange Key):密钥交换密钥 (允许操作db的公钥集合)是用于在硬件平台和操作系统之间建立信任关系 ,KEK 的公钥可以在主板的 FLASH 存在多个不同项,即 KEK 可以存在多个 ,每一项对应一种可以被启动的操作系统,同样,KEK 的私钥可以来对 db, dbx 进行签名和管理。清除 KEK可以 “清除” (删除 KEK) 。请注意,如果平台上未安装 PK,则不需要对 “clear” 请求进行签名。如果已对其进行签名,则清除 KEK 需要使用 PK 签名的包,若要清除 db 或 .dbx,需要由 KEK 中存在的任何实体签名的包。
-
db(Database):白名单数据库 (允许bootloaders的公钥集合)是用于对 被许可的 EFI 文件予以加载的数据签名库 ,和 KEK 一样, db 的公钥可以存在很多项 。(在 UEFI 平台里面,操作系统加载文件就是一个 EFI 文件)
-
dbx(Database Excluded):黑名单数据库 (不允许bootloaders的公钥集合)是一个 黑名单数据签名库 ,只要谁的 EFI 签名在这,谁就被屏蔽掉, dbx 的公钥也可以存在很多项 。
-
清除平台密钥平台所有者通过调用 SetVariable (大小为0的变量,并重置平台)来清除平台密钥(PKpub) 。如果平台处于设置模式,则无需对空变量进行身份验证。如果平台处于用户模式,则必须用当前 PKpriv 对空变量进行签名; 强烈建议不要将生产 PKpriv 用于对包进行签名以重置平台,因为这样可以通过编程方式禁用安全启动。这主要是一种预生产测试方案。还可以使用安全的平台特定方法(Delete PK)清除平密钥。在这种情况下,全局变量安装模式也必须更新为1(Setup mode)。
3、原理
UEFI 只管验efi文件,grub也是efi文件,grub验kernel,kenel验module,Bios Secure Boot的原理:
把公钥包在code里面,当使用gBS->LoadImage()去加载模块(UEFI Image)的时候会用BIOS里面的公钥去验证Image有没有正确签名,验证通过则Image成功被加载签名实例:signtoolx64.exe sign -f 私钥.pfx -fd sha256 shell.efi
**例如:**当我们在BIOS里面把Secure Boot[Enable]之后,会发现我们的U盘shell进不去,这是因为shell环境(bootx64.efi)没有经过签名,如果要使Secure Boot[Enable]之后,也能进U盘shell,需要对bootx64.efi进行签名,同时把签名用的私钥的对应公钥包到BIOS里面。
参考文献
UEFI
1. EDK2源码:https://github.com/tianocore/edk2
2. 《UEFI原理与编程》-- 戴正华
3. https://mp.weixin.qq.com/s/tgW9-FDo2hgxm8Uwne8ySw
SystemReady
[0]https://mp.weixin.qq.com/s/yUlsCdIcg3W2g3BIDCNvAg
[1]SystemReady官网: https://developer.arm.com/architectures/system-architectures/arm-systemready
[2]白皮书: https://armkeil.blob.core.windows.net/developer/Files/pdf/white-paper/arm-systemready-whitepaper.pdf
[3]Specs: https://developer.arm.com/architectures/system-architectures/arm-systemready/specifications
[4]ACS官网: https://github.com/ARM-software/arm-systemready
[5]ACPI on ARM: https://www.kernel.org/doc/html/latest//arm64/arm-acpi.html#why-acpi-on-arm
[6]partners: https://www.arm.com/zh-tw/why-arm/architecture/systems/systemready-certification-program/partners
[7]SystemReady的最新进展: https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/progress-in-arm-system-ready-program
UEFI之 Secure boot
https://mp.weixin.qq.com/s/gJST89ei_JHwyIGl3XzKfQ