LWN:U-Boot的最新状态!
关注了就能看到更多这么棒的文章哦~
A status update for U-Boot
By Jake Edge
July 26, 2023
EOSS
ChatGPT assisted translation
https://lwn.net/Articles/938769/
U-Boot(universal boot loader)在嵌入式 Linux 领域有广泛使用。在 2023 年嵌入式开源峰会(EOSS)上,Simon Glass 做了一个关于该项目状况的演讲(幻灯片 https://static.sched.com/hosted_files/eoss2023/b3/Recent%20Advances%20in%20U-Boot.pdf ,YouTube 视频 https://www.youtube.com/watch?v=YlJBsVZJkDI ),重点介绍了过去几年中添加的新功能。他还想介绍一下在 firmware 这个世界中的复杂性,他认为这种复杂性在增加,以及 U-Boot 如何有助于管理这种复杂性。这次演讲涉及了在这个项目中日益庞大的各种想法和变化。
Background
U-Boot 背后的理念是要有通用性;"在任何设备上启动任何操作系统",Glass 说。这个项目已经存在了大约 20 年,目前正在积极开发中,每年有 6000 次提交。U-Boot 按照固定的时间表发布,每年四次。
它有大约 300 万行 C 代码,还有一些额外的 Python 工具。U-Boot 与 Linux 内核有很多相似之处:它有相同的代码风格,使用 Kconfig 进行构建时配置,并且有一些兼容层,可以将 Linux 子系统和驱动程序移植到 U-Boot "不会有太多麻烦"。U-Boot 拥有一个持续集成(CI)测试系统,"涵盖了 boot loader 程序的大部分功能"。
U-Boot 受欢迎主要是源于其拥有大量的功能,但"它也容易被黑客攻击";很容易进来向代码库中添加一个新的命令或功能。它是单线程的应用程序,这使得开发起来更容易,因为不需要加锁;"它并不会试图成为一个操作系统"。U-Boot 的开发者通常对新的想法和功能持开放态度,这也有助于项目的发展。
Complexity
"在这个世界里,一切都变得更加复杂",他说。在我们设备中使用的 SoC(systems-on-chip)现在有了更多的功能,它们需要固件 firmware 来完成工作。固件需要打包成一个镜像文件,但这个过程也变得更加复杂。现在固件需要满足安全和签名要求,以确保系统使用了正确的固件。十年前,这种问题要少得多。
[Simon Glass]
启动流程也变得多样化;系统现在需要"在多个项目中来回跳转,使用不同的二进制文件等等,以启动操作系统"。此外,不同设备型号的差异也越来越大,彼此各有不同,需要为每个模型都提供不同的固件镜像文件。处理这么多的多样性并不容易。
U-Boot 的驱动模型可以把 SoC 的复杂性处理得"相当好",Glass 说。有一个 device tree,其中设备属于不同的类别;各个设备之间有关联,并使用设备树将它们联系在一起。
其中一个最复杂的事情是处理时钟;与过去不同,现在的板卡数据手册甚至都没有提供时钟图。但是 U-Boot 利用自己的基础设施使得这些工作变得容易得多。"你可以说'请给我 MMC 设备'",然后 U-Boot 会设置所有所需的时钟和 pin 脚复用,打开任何所需的供电通路,并把这个 device 返回回来。"用手动操作的话会非常复杂",他说。
以前如果要支持多个略有不同的设备型号的话需要为每个型号使用不同的引导加载程序,但 U-Boot 可以在运行时更改其配置,这样就只需要一个固件镜像就够了。它使用了不同的 device tree 来表示各种型号。针对某个型号的 device tree 可以稍后添加到引导镜像中,甚至可以作为产线生产制造过程的一部分。
有很多不同的方式来打包固件,每个人或项目可能都有自己的方式。初看的话似乎并不难,所以人们写了一些脚本,但最终整个构建系统都发展成了专门用于打包固件了。Binman 是一个试图解决这个问题的工具;它随附在 U-Boot 中,但也适用于其他项目,比如 Zephyr。它有一个配置文件,描述了需要放入固件镜像中的所有各个组成部分,并支持各种固件格式,因此可以创建适合安装到闪存内的正确文件格式。
Binman 解决了许多问题,它可以很容易更改镜像文件的内容,并提供镜像中存在内容的文档。无需使用各种不同的工具对二进制镜像进行解码,只需查阅 binman 配置即可。它还具有获取和构建需要对于 firmware 中一些 bit 上进行处理的工具的机制(例如签名,供应商特有的格式化任务)。
Standard boot
"标准引导"功能的目的是使 U-Boot 的引导更加标准化。传统的 U-Boot bootm 命令可用于引导各种以 flat device tree(FIT)格式生成的镜像,它会处理签名验证和图像解压缩等操作,但缺少一种方法来确定应使用哪些镜像以及确定这些镜像在设备上的位置。目前使用脚本来完成这一任务。在过去几年里,随 U-Boot 提供的 distro_bootcmd 脚本被用于提供处理引导所需的正确部分和组件。
标准引导,就添加了一个更高级的接口,使 U-Boot 能够完全看到设备上可用的引导设备和镜像;然后可以向用户提供一个菜单来选择选项。bootdev 是在存储设备(例如 MMC)之上的一个层,它可以列举其包含的可引导镜像。然后,bootmeth 可以列举描述系统特定引导流程的可用 bootflow 文件。"distro" bootmeth 将在由 bootdev 发现的文件系统中查找名为 extlinux/extlinux.conf 的文件。这些文件描述了特定发行版(如 Android、Fedora、Ubuntu、Chrome OS 等)的引导流程。这是一个简单的模型,但可能有点难以理解,他说,而 U-Boot 还在逐步转换到使用这种模型。
U-Boot 长期以来支持 UEFI,并可以直接通过 UEFI 引导发行版,如 Fedora 和 Ubuntu。它还支持 UEFI Secure Boot,并可以处理使用可信平台模块(TPM, Trusted Platform Module)对引导过程进行测量和验证。如果需要,U-Boot 还可以直接作为 EFI 应用程序来运行。
现在,UEFI 有一个替代方案。也就是 Verified Boot for Embedded (VBE),它的出现主要是为了回答一个问题:“如果我们不打算使用 UEFI,那会是什么样子?” 他不想详细介绍 VBE,因此参考他去年的 Open Source Firmware Conference (OSFC)的演讲以获取更多信息。VBE 基于现有的标准,如 FIT 格式。与 UEFI Secure Boot 中从引导镜像到 U-Boot 的一堆回调不同,VBE 提供了引导镜像需要的一切内容,这是一种更简单的方法,他认为。
在过去的几年里,U-Boot 项目已经开始使用 Sphinx 进行文档编写。之前有过一份 U-Boot 手册,但是在制作过程中“就逐渐被抛弃了”。新文档的好处在于它存储在源代码树中,因此添加新功能或命令的 patch 可以同时包含文档(也希望还包含相关 test)。大部分现有文档已经转换为 reStructuredText 格式,但仍有一些命令和功能缺乏文档。希望在不久的将来弥补这个差距。
U-Boot 的测试和持续集成系统的功能在过去几年里也“显著扩展”。Glass 指出,在 EOSS 有一个关于使用 emulated device 进行测试的 Zephyr 讲座;U-Boot 也使用这些模拟器,因此可以在 Linux 工作站上模拟 SPI 闪存测试 U-Boot。这样就可以编写各种无需硬件即可运行的测试;模拟器可以更改“硬件”以检查需要验证的内容。现在,如果有人提交了没有测试过的 patch,可以要求他们提供一个 test,而且不是一个巨大的负担。
Devicetree and beyond
U-Boot 在 2011 年左右就引入了 devicetree,与 Linux 差不多同时,这带来了一个问题,它们之间的 binding (属性、节点等)是有差异的。这些差异正在逐步解决。另一个问题是,U-Boot 一直没有能够将其 schema 需求推送到内核 upstream 上,不过这也在发生变化。最终希望 U-Boot 可以从 Linux 获取其 device tree,并且只要从 device tree 中明确了所需的硬件驱动程序,U-Boot 就可以直接使用它。
虽然 U-Boot 使用 Kconfig 的时间至少和 Glass 在上面工作的时间(将近十年)一样长,但它仍然有许多散布在头文件中的#define 配置语句,不过这些在今年年初都得到了整理。现在有一个文本文件描述了配置,这意味着完全可以不需要任何跟特定 board 关联起来的 config.h 文件;完全可以在配置文本文件中定义清楚一个 board 的需求。
该项目还添加了链接时优化(LTO, link-time optimization)。"它会让你的 board 的构建时间变长四倍,但 size 会减小 5%。"这通常来说是有好处的,所以大多数 Arm 板默认开启了 LTO。此外,events 功能提供了一种监视设备创建等相关事件的方法;这是 weak function 的一个替代方案,他不不很喜欢 weak function,因为很难确定是在哪里被真正调用的。一个 event 会被发布出来,其他地方可以告知说对特定事件感兴趣,然后在 event 发生时进行处理;所有这些监听事件的地方都可以使用工具轻松列出来,所以有着更好的可见性。
目前正在进行的工作之一是允许固件的不同部分把配置数据和其他信息传递给固件的下一阶段。目前可能会有多个阶段(包括验证程序加载器(VPL, verifying program loader)、二级程序加载器(SPL, secondary program loader)、可信执行环境或可信固件(trusted execution environment or trusted firmware)以及 U-Boot),这些都是互相独立的项目,每个项目都有自己的配置机制。固件传递规范(Firmware Handoff specification)是用来“简单标记数据结构的”,可在这些组件之间进行传递。
近年来,U-Boot 的网络支持发生了很多变化,包括添加了 TCP/IP,这意味着可以使用 Wget 命令,非常方便。TFTP 支持已经存在一段时间了,但功能较为有限。现在还支持 IPv6 和新的 PHY API。目前正在进行讨论来决定是否应该切换到轻量级 TCP/IP 协议栈 lwIP 还是继续使用自己的实现。
U-Boot 现在支持 21 种不同的 RISC-V 开发板。在 x86 上引导发行版的支持也几乎完全成型了,只需一些尚未合入的 patch。在 x86 上使用 U-Boot 作为 coreboot 的 payload 得到了增强。coreboot 可以用于系统的初始引导,设置硬件,包括 ACPI 表,然后跳转到 U-Boot 引导操作系统。这个过程现在“更加完善”。
为了调查引导时的性能瓶颈在哪里,可以在 U-Boot 中使用 tracing 功能。tracer 会记录函数的进入和退出情况,并将其存储在内存缓冲区中,以便后续导出在另一个系统中进行分析。tracing 功能在 U-Boot 中已经存在了一段时间了,但最近进行了更新,以使用 trace-cmd 接口。
U-Boot 是单线程的,这使得它易于处理,但有时也可能有些麻烦。例如,如果进行 USB 扫描,你必须等待扫描完成,等待端口超时等,如果有很多端口,就可能需要几秒钟。希望有一种方式可以启动扫描过程,并同时执行其他任务,这正是 cyclic 子系统的用武之地。
当 U-Boot 处于空闲状态时,它会进入 cyclic scheduler 调度器。多年来,cyclic 子系统一直用于对看门狗定时器进行 reset,但现在它也可供系统中的其他场景使用了。他有许多地方都计划使用这个功能(包括用于 USB 扫描),但也期待看到其他人的创意。
还有一个实验性的新功能,就是使用 expo menu 添加一个类似图形用户界面的功能。这些菜单是一组被称为 scene 的屏幕内容,用户可以使用键盘导航来设置或更改各种参数。它们看起来以及操作起来都类似于简化的 x86 BIOS 菜单。
之后,演讲者进行了一系列快速演示,以展示他在演讲中提到的一些不同功能。他首先展示了在 QEMU 中的标准引导过程,然后运行了 binman 以显示所使用的启动镜像的内容。他还演示了如何使用 binman 去获取缺失的工具(例如 fiptool),以便用于构建固件镜像。此外,他还推送了一个分支到 GitLab,并简要浏览了 CI 系统,根据 tracing 的输出创建了火焰图,并展示了 expo GUI。
之后,他回答了一些观众提出的问题;问题之一是关于标准引导中的优先级问题。是否可以指定某些 bootdevs(或存储类型,例如可移动或不可移动)优先于其他设备?演讲者表示每种 bootdev 类型确实有一个可以指定的优先级,但是没有基于媒体是否可移动的区分,所以这可能需要今后增加。启动顺序也可以根据设备出现在环境变量中的顺序来指定。
[I would like to thank LWN's travel sponsor, the Linux Foundation, for assisting with my travel to Prague.]
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~