在RK3588上体验UEFI

本文介绍了UEFI在ARM平台,尤其是RK3588上的应用,探讨了UEFI作为固件技术在解决硬件兼容性和系统启动问题上的作用。文章讲述了UEFI的发展历程,包括其在X86和ARM平台的推广,以及在RK3588上实现UEFI遇到的挑战和解决方法,如魔术码定位错误的调试过程。

在RK3588上体验UEFI

Unified Extensible Firmware Interface

UEFI是一种固件技术,源于英特尔,最初的开发代号叫Tiano。项目的开始时间大约在本世纪初,当时的固件代码都很陈旧,主要用汇编语言和C语言编写,繁杂拖沓,难以维护扩展。与此同时,英特尔当时投巨资的安腾项目使用了众多开创性的创新设计,需要在固件层做很多修改,要在陈旧的BIOS上代码做这些修改难度很大。为此,Tiano项目应需而生,引入当时流行的COM技术精华,普遍使用接口技术,使用GUID来表示接口,使用C++代替C语言。

# 大哉,固件

2004年,Tinao项目初步完成,以EFI为正式名字对外发布。在Intel网站,还可以找到当年的一则新闻稿:

Intel Invites Industry In Design Of Technology To Succeed BIOS,Industry Collaboration Key in Addressing Oldest Software Technology in PC Platforms。

标题大意是,Intel引领产业设计取代BIOS的新技术,针对PC平台上的最古老软件技术开展产业合作。

这个标题把BIOS称为PC平台的最古老软件技术是很合适的。的确,BIOS源于已经成为历史的CP/M操作系统和DRI公司,它的发明者是DRI的创始人Gary Arlen Kildall。我在《软件简史》第六篇《亢龙有悔》中详细描述了Gary的传奇人生和他对PC产业和系统软件所作的巨大贡献。

1b01c6f9b1f019e51e5be4b0a0c2847e.png

8abbc1d347de83a88d4fe6de1045a4d5.jpeg

《软件简史》中关于PC软件

和BIOS技术的追根溯源

Kildall是PC技术的开路者,他在Intel开发8086时,就是Intel的软件顾问。他成名时,比尔·盖茨还在找方向,比尔·盖茨曾多次到Kildall的家里登门求教。从下面这张照片可以看出一些微妙的信息。

189af405cc8f6965a8375376cd4c687f.png

比尔·盖茨(中)与Kildall(右)的合影

固件技术的核心价值是隔离硬件差异,让操作系统可以以统一的接口来管理五花八门、不断变化的硬件。这也正是Gary Arlen Kildall当初发明BIOS技术的初衷。这个技术的价值也被比尔·盖茨所心领神会,凭借这个技术,微软的Windows操作系统在PC时代畅通无阻,只要是有BIOS的盒子,就可以安装Windows。

cf8d2a7c26c63b57d8922d9856e50700.png

b5cc422ace23987b4da483691c83b746.png

# 林纳斯大神怒批ARM

2005年,Intel连同几家伙伴一起成立UEFI论坛(UEFI Forum),从此,Tiano技术的成果开始以UEFI的形式发展。UEFI论坛的最初成员有:

AMD, American Megatrends, Inc., Dell Inc., HP, Intel Corporation, International Business Machines Corporation, Insyde Software Corp., Microsoft Corp., and Phoenix Technologies Ltd. to Collaborate

注意,上面的成员列表中没有ARM,原因当然有很多。从ARM玩家的角度看,当时ARM的主要场景还是嵌入式和垂直领域。对于这些场景,通常是像苹果那样,一家公司做整个软件栈,软件兼容的压力不是很大,所以对固件层的需要不是那么迫切。

d8745095a7f5c24b6298111c88615849.png

a1160fc40dd957fb747c6ce3cf0ff41b.png

但随着ARM系统的硬件越来越强大,越来越多的ARM系统开始使用通用的LINUX内核,这时兼容问题便日益严重了。太多厂商不停往Linux内核的代码树上合并看似相同却又不同的适配代码。这种现象持续了几年,导致Linux内核的arch/arm目录不断膨胀,一大堆以mach开头的目录,一眼看不到边,直到今天,这个目录仍然如此。

终于有一天,当一位名叫Tony Lindgren的人想把自己的代码合并到内核代码树时,遇上了林纳斯大神心情不好,大发雷霆。

FromLinus Torvalds <>

DateThu, 17 Mar 2011 19:50:36 -0700

SubjectRe: [GIT PULL] omap changes for v2.6.39 merge window share 292

On Thu, Mar 17, 2011 at 11:30 AM, Tony Lindgrenwrote:

>

> Please pull omap changes for this merge window from:

Gaah. Guys, this whole ARM thing is a f*cking pain in the ass. You need to stop stepping on each others toes. There is no way that your changes to those crazy clock-data files should constantly result in those annoying conflicts, just because different people in different ARM trees do some masturbatory renaming of some random device. Seriously. That usb_musb_init() thing in arch/arm/mach-omap2/usb-musb.c also seems to be totally insane. I wonder what kind of insanity I'm missing just because I don't happen to see the merge conflicts, just because people were lucky enough to happen to not touch the same file within a few lines. 

林纳斯是诗人后裔,说话言简意赅。“整个ARM的东西就是x。你们必须停止相互挖坑,踩别人的脚趾头。

03a1a0e21b46b164dc794d0f1bea00a8.png

# 十年能否磨出一剑

林纳斯大神的怒批很有效果。大约在2013年,ARM加入了UEFI论坛。

3f7c5626bddb27528d62a384692d4515.png

2014年8月12日,UEFI论坛发布ACPI 5.1标准中,在这个标准中,加入了对ARM的支持,包括ARM的中断控制器GIC,以及ARMv8的众多特征。当年新闻稿的标题为:

UEFI FORUM’S NEW ACPI 5.1 SPECIFICATION ADAPTS CONFIGURATION AND POWER INTERFACE TO 64-BIT FOCUSED FEATURES OF THE ARMV8-A ARCHITECTURES

UEFI的新ACPI 5.1规约采纳了支持ARMv8 64位特征的配置和电源接口

有了标准支持,是必要条件,但却不是充分条件。

为了在ARM生态中推动UEFI技术,ARM一直在努力,比如ARM名为SystemReady的认证技术就把UEFI作为重点内容。

cd3ddfc886183298e1dce31c9c76842f.png

ARM的Dong Wei院士领衔推动这个方向。

0bc8a75c34679497e121c8484c13ffea.png

从2013年算起,已经10年了。ARM平台上的UEFI到底是什么状况呢?

昨天,格蠹的小伙伴将来自硬件伙伴的UEFI代码编译通过,刷到了幽兰代码本上,第一次没有刷成功,第二次刷成功了,熟悉的UEFI风格设置界面在幽兰上出现了。

5e63a2d4c161f1d755f9498ad3b86570.jpeg

听到小伙伴说刷好后一开机能进UEFI设置界面,我首先很高兴,因为没花什么时间就把UEFI的代码在幽兰上跑起来了,这反映了ARM的十年努力是有成果的。ARM平台的固件技术积贫积弱,能在固件阶段显示出图形界面,这是值得纪念的一个里程碑。放眼整个ARM生态,ARM系统的总量以百亿记也不过分,但是能有界面的固件凤毛麟角,难得一见,不足百万分之一。

但听说一开机就进了UEFI界面,我也知道情况不妙。凭借我多年来对UEFI的了解,自动进图形界面意味着正常启动失败了。从整个系统的角度讲,固件主要在启动阶段运行,它的主要使命就是为操作系统铺平道路。它把准备工作做好,就应该把执行权交给操作系统。从这个角度来看,它很像是帮助越王勾践灭吴后就泛舟五湖、功成隐退的陶朱公(范蠡)。

可是当下,UEFI没能把控制权成功交给OS。这既是意外,也在情理之中。

2493bbdc5aeaed4bdb46fafad8c03391.png

# 交接为何失败?

幽兰本来是使用u-boot作为固件的,它负责加载LINUX内核,并把控制权平稳过渡。u-boot和Linux都存储在幽兰的EMMC闪存上面。闪存的分区情况如下:

35c3278cd4a3b17922f9a85a21e1f627.png

以下是小伙伴抓的串口输出:

DDR V1.09 a930779e06 typ 22/11/21-17:50:56
LPDDR4X, 2112MHz
channel[0] BW=16 Col=10 Bk=8 CS0 Row=17 CS=1 Die BW=16 Size=2048MB
channel[1] BW=16 Col=10 Bk=8 CS0 Row=17 CS=1 Die BW=16 Size=2048MB
channel[2] BW=16 Col=10 Bk=8 CS0 Row=17 CS=1 Die BW=16 Size=2048MB
channel[3] BW=16 Col=10 Bk=8 CS0 Row=17 CS=1 Die BW=16 Size=2048MB
Manufacturer ID:0xff
CH0 RX Vref:27.7%, TX Vref:20.8%,0.0%
CH1 RX Vref:26.7%, TX Vref:20.8%,0.0%
CH2 RX Vref:26.7%, TX Vref:23.8%,0.0%
CH3 RX Vref:28.7%, TX Vref:21.8%,0.0%
change to F1: 528MHz
change to F2: 1068MHz
change to F3: 1560MHz
change to F0: 2112MHz
out
U-Boot SPL board init
U-Boot SPL 2017.09-gc060f28d70-220414 # (Apr 18 2022 - 18:13:34)
Failed to set cpub01
Failed to set cpub23
Trying to boot from MMC2
MMC: no card present
mmc_init: -123, time 0
spl: mmc init failed with error: -123
Trying to boot from MMC1
Trying fit image at 0x4000 sector
## Verified-boot: 0
## Checking atf-1 0x00040000 ... sha256(909ea14106...) + OK
## Checking uboot 0x00200000 ... sha256(36148e13ec...) + OK
## Checking fdt 0x002f0000 ... sha256(c07f4a4d71...) + OK
## Checking atf-2 0x000f0000 ... sha256(6a970ae6b4...) + OK
## Checking atf-3 0xff100000 ... sha256(3ea8cf0d7e...) + OK
## Checking optee 0x08400000 ... sha256(fde0860845...) + OK
Jumping to U-Boot(0x00200000) via ARM Trusted Firmware(0x00040000)
Total: 542.534 ms


INFO:    Preloader serial: 2
NOTICE:  BL31: v2.3():v2.3-468-ge529a2760:derrick.huang
NOTICE:  BL31: Built : 09:59:49, Nov 21 2022
INFO:    spec: 0x1
INFO:    ext 32k is valid
INFO:    ddr: stride-en 4CH
INFO:    GICv3 without legacy support detected.
INFO:    ARM GICv3 driver initialized in EL3
INFO:    valid_cpu_msk=0xff bcore0_rst = 0x0, bcore1_rst = 0x0
INFO:    system boots from cpu-hwid-0
INFO:    idle_st=0x21fff, pd_st=0x11fff9, repair_st=0xfff70001
INFO:    dfs DDR fsp_params[0].freq_mhz= 2112MHz
INFO:    dfs DDR fsp_params[1].freq_mhz= 528MHz
INFO:    dfs DDR fsp_params[2].freq_mhz= 1068MHz
INFO:    dfs DDR fsp_params[3].freq_mh

上面的串口打印信息来自两个软件,一个是ATF,即ARM的可信固件,运行在ARMv8的最高特权级别,里面执行高安全级别的敏感逻辑,包括缓解CPU的缺陷和设计不足。另一个是u-boot,u-boot在ARM生态上使用多年,根深蒂固。实际上,我们拿到的固件包叫uboot_uefi,它其实是uefi和uboot的混杂版本,使用时仍需要使用u-boot,只不过是u-boot很快就把执行权交给uefi。

对于上面的打印信息,还没有看到uefi。我实际参与调试整个问题后,终于看到了uefi的输出信息:

add-symbol-file /extd/guolin/uefi_sdk/uefi_emmc/Build/RK3588/DEBUG_GCC5/AARCH64/EmbeddedPkg/Application/AndroidBoot/AndroidBootApp/DEBUG/AndroidBootApp.dll 0x39828000
Loading driver at 0x00039827000 EntryPoint=0x▒Failed to get AndroidBootImg Size: Invalid Parameter
Error: Image at 00039827000 start failed: Invalid Parameter
remove-symbol-file /extd/guolin/uefi_sdk/uefi_emmc/Build/RK3588/DEBUG_GCC5/AARCH64/EmbeddedPkg/Application/AndroidBoot/AndroidBootApp/DEBUG/AndroidBootApp.dll 0x39828000
Image Return Status = Invalid Parameter
[Bds]Booting Android Fastboot
add-symbol-file /extd/guolin/uefi_sdk/uefi_emmc/Build/RK3588/DEBUG_GCC5/AARCH64/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp/DEBUG/AndroidFastbootApp.dll 0x39829000
Loading driver at 0x00039828000 EntryPoint=0x0▒Fastboot: Couldn't open Fastboot Transport Protocol: Not Found
Error: Image at 00039828000 start failed: Not Found
remove-symbol-file /extd/guolin/uefi_sdk/uefi_emmc/Build/RK3588/DEBUG_GCC5/AARCH64/EmbeddedPkg/Application/AndroidFastboot/AndroidFastbootApp/DEBUG/AndroidFastbootApp.dll 0x39829000
Image Return Status = Not Found
Process PlatformRecovery0000 (Default PlatformRecovery) ...
[Bds] Unable to boot!
[Bds]Booting UiApp
add-symbol-file /extd/guolin/uefi_sdk/uefi_emmc/Build/RK3588/DEBUG_GCC5/AARCH64/MdeModulePkg/Application/UiApp/UiApp/DEBUG/UiApp.dll 0x30509000
Loading driver at 0x00030508000 EntryPoint=0x0003050FE30 UiApp▒[HiiDatabase]: Memory allocation is required after ReadyToBoot, which may change memory map and cause S4 resume issue.

上面的多种信息都代表了UEFI的风格,或者说代表了来自Tiano的基因(原始设计)。

首先,其中的add-symbol-file语句是供调试使用的,打印整个信息代表了UEFI具有与调试器配套的调试支持。

其次,上面的文件路径指示的文件后缀都是dll,因为Tiano的最初开发环境是Windows,最初的编译工具也是微软的Visual C++。所以UEFI的模块是windows平台上流行的DLL,对应的调试符号是微软使用的PDB格式。

上面的信息中包含了三个DLL的名字,分别是:

AndroidBootApp.dll

AndroidFastbootApp.dll

UiApp.dll

从App的名字来看,这几个模块都属于扩展性质的App模块,不属于Tiano的核心框架(比如DXECORE)。

上面三个DLL其实代表了UEFI在完成基本工作后,准备移交执行权时所做的三种努力:

先尝试以Android Boot方式引导内核。

再尝试以Android FastBoot方式引导内核。

都不成功,只好运行UiApp,呈现图形界面。

b4647c87adb9ce54147837e793faff73.png

# 安卓魔术码,你在何处?

根据错误信息查找uefi的源代码,可以看到是下面的函数报错:

008f6474772eb9c65733115f14f89628.png

这个函数是获取内核大小。所谓的Android启动方式,就是在闪存的固定位置放一段描述信息,这段信息需要符合如下格式:

#pragma pack(1)
/* https://android.googlesource.com/platform/system/core/+/master/mkbootimg/bootimg.h */
typedef struct {
  UINT8   BootMagic[ANDROID_BOOT_MAGIC_LENGTH];
  UINT32  KernelSize;
  UINT32  KernelAddress;
  UINT32  RamdiskSize;
  UINT32  RamdiskAddress;
  UINT32  SecondStageBootloaderSize;
  UINT32  SecondStageBootloaderAddress;
  UINT32  KernelTaggsAddress;
  UINT32  PageSize;
  UINT32  Reserved[2];
  CHAR8   ProductName[16];
  CHAR8   KernelArgs[ANDROID_BOOTIMG_KERNEL_ARGS_SIZE];
  UINT32  Id[32];
} ANDROID_BOOTIMG_HEADER;
#pragma pack ()

上面信息的开头是个魔术码:ANDROID!

出错的代码就是没有匹配到这个魔术码,所以因为参数错误。

其实格蠹的GDK就是使用的这种启动方式来引导LINUX内核的。使用od命令查看GDK8的boot分区,就可以看到ANDROID!魔术码。

3a9b2d981635049895b6b6786afff035.png

上图中的4e41就分别是N和A的ASCII代码。

6affa2674f0ac08a69fcc2b37009c43e.png

# GRUB打岔

在调试这个问题的过程中,团队中出现了两种不同的意见,一种意见是制作带有GRUB的启动盘,然后让UEFI把控制权移交给GRUB,GRUB再加载LINUX内核。另一种意见是不用GRUB,直接加载LINUX内核。

我是支持第二种的意见的,已经有了UEFI,而且里面已经包含了一个还没有完全退休的u-boot,没有必要再把GRUB拉进来。太多人同台唱戏,会让系统很复杂,而且影响启动时间。

但是持第一种观点的人很快取得了进展,使用UEFI中做好的多启动目标支持,在幽兰上成功跑起了GRUB。

179e93cfad6f6580a24557e81b0e32af.png

但是我仍坚持第二种方式,第一种方式即使成功了,第二种方式也有价值,它的启动速度会更快。于是我继续坚持调试第二种方法。

失败的根源是找不到魔术码,那只要按这个线索排查就可以了,一种简单的方式是向源代码里加打印信息。这种调试方法如果不成功,那么还可以动用幽兰上具有的更强大调试设施——CoreSight。

372c907a3be0cf3fb236a53cfffd99b6.png

30459d4d717e08308250d1cb79629aa8.png

# 穷追不舍查魔码

读不到魔术码,那可能是分区位置指定错了。查看源代码,出问题的函数是通过PCD配置技术来读分区的。

a50d77f308f4958cb7d7b600efabb7ec.png

查找对应的PCD设置,果然发现有两种设置,一种是设置为\\EFI\\BOOT\\GRUBAA64.EFI。另一种是设置为UUID的分区表形式,我们应该选用第二种。而实际使用的是第一种。

7de84af4080e3130842699cc0657e3ba.png

UEFI具有非常好的可配置性,调整INF文件就可以改变PCD设置。

8b46e35399587b5df834270f10dab259.png

调整后,通过串口打印的信息可以确认,UEFI已经使用正确的分区了。

b7f94d4567dae2e6ae9f4aa89b8d557f.png

但是无效参数错误还是有,也就是UEFI还没有找到合适魔术码。为此,我让小伙伴增加打印,打印读到数据,发现魔术码位置都是0。

98f57fe7ef203a599b4609c9fa91c7b5.png

因为打印时套用了UEFI的%r格式符,所以上图中的四个Success其实是四个0。

这时,目标已经很明确,UEFI读的分区位置不对,所以读到的都是0。有了确凿的数据作为支撑后,小伙伴仔细核对起PCD参数,发现参数中的分区起始位置是错误的。

PCD串很长,前面一直把注意力集中在核对UUID上,没有注意UUID后面的起始位置和分区大小部分。

L"VenHw(100C2CFA-B586-4198-9B4C-1683D195B1DA)/HD(3,GPT,7A3F0000-0000-446A-8000-702F00006273,0x8000,0x20000)"

把分区信息纠正后,UEFI成功把执行权交给了内核,UEFI的界面不自动出现了,取而代之的是幽兰图标,一朵兰花在黑色屏幕中闪现,鲜绿的叶子,金黄色的花瓣,红黄两色的花蕊,它傲然绽放,又略带娇羞,因为它幽然独立,与众不同。

1703345f77daa90b2cf3380c68e9eec7.png

1fab07f8d1cebae37d93ac5bfc6b2d99.png

在上文提到的Intel新闻稿中,特意描述了Tiano项目有超过200人参与开发,他们在Intel的上海软件中心,以及位于Oregon和华盛顿的实验室。 

The project represents more than 200 person years of development by Intel's China Software Center in Shanghai, and Intel software labs in Oregon and Washington.

https://www.intel.com/pressroom/archive/releases/2004/20040601corp_a.htm

的确,Tiano的很多初始代码诞生在上海。某种意义上说,Tiano项目是Intel最早在上海进行的著名项目。2003年我加入Intel时,还有很多同事在做Tiano项目。

Tiano项目是成功的,它使用C++语言重新构建了固件层的框架和核心逻辑,在固件领域搭建了一座软件大厦,这座大厦使用新世纪的技术,新世纪的思想,具有新世纪的特征。先进的接口思想让它充分模块化,非常容易维护和扩展。

回看历史,当年投巨资的安腾项目并不成功,慢慢被历史所淡忘。为安腾铺路的Tiano项目却很成功,具有持久的生命力,随着时代的发展而不断扩大领地。不仅成为X86平台的特色技术,服务于今天的X86桌面和X86服务器领域,而且已经延展到ARM平台,作为X86平台的技术精华输送给日新月异的ARM平台。

55504e5baedf509ed4e95f89061f745c.png

在RK3588上体验UEFI

3a77a2eb9e39a1cf784707a2eb71bb47.png

购买幽兰代码本即可成为兰舍会员,与众多技术高手一起成长。

购买可前往淘宝格友小店:

https://m.tb.cn/h.Uuv7fit?tk=N1iIdn8t4CI

6ea43a20755b9bf78c5b2305d39b943f.png

7ba6d1c2487be80f4b800fc3631923a6.png

盛格塾是格蠹科技旗下的知识分享平台,是以“格物致知”为教育理念的现代私塾。

本着为先圣继绝学的思想,盛格塾努力将传统文化中的精华与现代科技密切结合,以传统文化和人文情怀阐释现代科技,用现代科技传播传统文化。

访问方式

手机端:微信小程序搜索“盛格塾”

电脑端:下载Nano Code社区版客户端

https://nanocode.cn/#/download

7a10913115482ed6ae343e0289434e25.png

格友公众号

d6a681fe0d39eaecee33f896c5d3f8ab.png

盛格塾小程序

dc7a10fec5a422e50155e10c4d978cb0.png

喜欢此内容的人还喜欢

比内存被踩还难调试的问题

6f13cad57c29fee015433577527252a4.png

在调试器下理解RK3588和LINUX5.10

935581cc0b32c673da35008392f6d373.jpeg

编译器,你在说啥?

d7f22f8067b4a1d4f3408e82346da636.png

2023杭州研习班回顾

ed67191d48ecc53acb151cdb94b7346d.jpeg

左右滑动查看下一篇 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值