RFC-0167:早期用户空间引导中的软件包 | |
---|---|
状态 | 已接受 |
领域 |
|
说明 | 引入了 BootFS 和启动解析软件包。 |
问题 | |
Gerrit 更改 | |
作者 | |
审核人 | |
提交日期(年-月-日) | 2022-05-10 |
审核日期(年-月-日) | 2022-06-13 |
摘要
此 RFC 会提议在 bootfs 中引入软件包。这样一来, 具有软件包隔离和命名空间的优势,这些优势让后期用户模式一直都受益于早期用户模式 启动,并消除第三方驱动程序开发障碍。
设计初衷
在 组件框架拥有坚实的打包架构,但迄今为止我们还没有 致力于将现在可供后期用户空间使用的工具引入到早期启动阶段。 因此,用户空间引导发现自己面临着几个问题, 引入包装来解决这个问题例如进程沙盒、可验证 内容和变量库版本
这些前期启动问题表现为意想不到的复杂性和低效 与 bootfs 映像的交互时, 这在很大程度上是通过在前期启动过程中采用打包技术来消除的。打包 为改善系统运行状况、 不仅仅是解决现有问题。针对以下各项实现内容标识符标准化: 可执行文件和库,这使得我们可以重复使用 之前不相交的存储空间(如 bootfs 和 blobfs)中的等效数据。
组件框架有一个“package”这个概念就是 fuchsia-pkg;而组件框架和打包系统 严格来说,它们是相互独立的,它们紧密相关,足以使 世界的组件化和打包是合作的目标。
近期工作(例如,组件管理器作为首次用户启动后执行) 都会导致越来越多的“组件一直向下”架构。 甚至包括文件系统和设备驱动程序等提前启动可执行文件, Fuchsia 组件,或者正向这一模型迁移。时机已到 将系统的这种组件与 "packages-all-the-way-down"系统组装,并将所有值 促使打包产品进行用户空间引导。
更具体地说,bootfs 中缺少软件包名称 是树外驱动程序开发的障碍,因为它无法 生成有效的 fuchsia 映像,其中在 bootfs 中编码的可执行文件 共享库依赖项中存在版本偏差。此外,目前的 bootfs 映像出现泄露 ABI。驱动程序可以在启动时根据是否存在或 而不需要其他驱动程序,例如选择使用较新的库, 可以做到这一点,而无需明确定义对另一个驱动程序的依赖。 同样,由于驱动程序最终都在 bootfs 内的同一文件夹中, 驱动程序的名称本身会创建一个 ABI。这些类型的 ABI 的可用时间越长,弃用起来就越难。 正是此类行为(Windows 驱动程序和视频游戏)的先例 DRM 驱动程序会公开这种类型的意外 ABI)。
利益相关方
Facilitator:hjfreyer@google.com
审核人:geb@google.com、mcgrathr@google.com、surajmalhotra@google.com、 aaronwood@google.com、galbanum@google.com、wittrock@google.com、 jfsulliv@google.com
已咨询:
社交化:通过设计文档与利益相关方一起探索了主题, 之后在 RFC 之前向 tq-eng 开放了一般讨论。
设计
此更改涉及到 bootfs 和 /boot 目录、 BootResolver、BootUrl 和产品组件。
bootfs 变更
图片更改
fuchsia bootfs 软件包将由 bootfs 中的 Metal.far 文件表示,
bootfs 目录条目将命名为 blob/<merkle root of meta.far>
。每个
blob 会在
bootfs、blob/<merkle root of the dependency>
。
- </ph>
- 我们已经在运行时计算 Merkles 以验证内容,并且速度很快 所以在创建 zbi 等流程中 压缩图片,这无关紧要。
- 此提案要求的新图片构建流程已经是 在实际的图像构建步骤之前确定内容标识, 填充 meta.far 文件这意味着,基于内容的重复信息删除 大多发生在生成清单的过程中 推进图像构建步骤。它应该非常简单 只需确保我们已在这一阶段执行了所有可能的重复信息删除即可。
在讨论向 /blob 下的 bootfs 添加新条目时,我们只是 添加引用相同命令的新 bootfs 目录条目 底层文件(概念上是硬链接)。
bootfs 中一个名为“pkg_map”的新文件将保留从 复制到对 软件包。
bootfs 大小将根据新添加的 meta.fars 和新的 pkg_map 文件增大。 其他的一切都只是 已存在。保守地说,经过压缩的 bootfs 将增加约 70KiB 介绍 x64 架构。
/boot 更改
/boot 中会引入一个名为 /blob 的新子目录。以下文件中的所有文件: 名称以 /blob 作为前缀的 bootfs 映像将放置在其中。时间 bootfs 中的所有组件向软件包的迁移已完成, 级别目录将仅包含内核 vmos、Shell 脚本和 /boot 成为“命名空间”所需的组件管理器。
组件管理器命名空间最初是 /boot 的子目录 “布局”中的与所有依赖项预期的一样最终,我们的目标是 将组件管理器合并为一个 meta.far 编码,这种编码依赖于 分辨率的 SWD 堆栈。
BootResolver 变更
软件包名称空间 bootfs 组件的核心工作与以下项高度重叠: package-resolver 完成的工作。必须映射直观易懂的软件包名称 添加到经过内容 ID 处理的 meta.far 中,必须对 meta.far 进行解码,且其内容文件 才能构造命名空间。因此,该设计旨在重复利用 现有的软件包解析器逻辑。
重复使用 package-resolver 逻辑的最简单入口点是 package-directory::serve ,了解所有最新动态。此入口点接受 BlobFS 和 content-id 识别 meta.far,打开 meta.far,解析其 meta/contents 文件,以及 构造和提供文件中编码的命名空间。我们可以重复利用 但前提是我们能提供由 bootfs 提供支持的目录, 将 blob 作为 BlobFS 客户端发送到 API。
let (proxy, server) =
fidl::endpoints::create_proxy().map_err(ResolverError::CreateEndpoints)?;
let () = package_directory::serve(
package_directory::ExecutionScope::new(),
<some blobfs::Client-like view on top of the bootfs blobs>,
<meta.far hash>,
fio::OPEN_RIGHT_READABLE | fio::OPEN_RIGHT_EXECUTABLE,
server,
)
.await
.map_err(ResolverError::ServePackageDirectory)?;
blobfs::Client 只是封装了 fio::DirectoryProxy,这就是 bootfs 的 通过组件管理器公开。我们可以使用 Blobfs 客户端,并将其传递给 package_directory::Serve 调用。有点小 但需要进行更改以确保某些 blobfs::Client API 失败 妥善地安装在 bootfs(即需要可变性的 bootfs)上时 open_blob_for_write、delete_blob),但在加载过程中不需要可变性。 package_directory::serve 执行。
BootUrl 更改
BootUrl 目前不使用其网址的主机或路径部分,因为 无需编码代码库和软件包这意味着,您无需引入 新的网址架构和解析器,我们可以改为引入新组件加载 。存在 或缺少软件包路径,都会指示新的 “BootResolver 更改”中所述的解决路径。
示例:
fuchsia-boot:///#my_component.cm
:bootresolver 会将此网址解读为
命名空间已在
/boot 目录即可。
fuchsia-boot:///my_package#my_component.cm
:bootresolver 将
将此网址视为打包组件,而其中“my_package”添加到
存在 my_package 的 Metal.far 的 Merkle 根,并且应该用于
构造一个特定于“my_package”命名空间的命名空间。
产品/图片装配变更
Bootfs 映像构建通过两阶段的工作执行来完成 由构建系统和映像汇编系统组成
首先,我们将引入一个名为 bootfs_packages
的新 gn 变量。
此列表将在 product.gni 中声明,并分配给
名为 bootfs_package_labels
的调用方变量,传递给任何对象
分配 build/input:bootfs
的 assemble_system
调用
bootfs_labels
变量。
当我们将 bootfs 组件从其无软件包编码迁移到
bootfs 软件包时,我们将将其从包含
将其添加到 bootfs_labels
依赖项集中,并将其添加到 bootfs_packages
。
接下来,在生成映像组装配置时,我们将使用现有的
list_package_manifests
模板,用于从软件包中收集软件包清单
(在调用方的 bootfs_package_labels
变量中定义)。
接下来,映像汇编从构建遍历中获取清单,并将其用于
调用 zbi
等工具,将 build dir 中的文件打包到映像中。
软件包清单格式包含“blob”列表这些对象在图像上
创建的内容,用于将 blob/<merkle_root>
命名文件添加到
bootfs 映像
在迭代这些 blob 对象时,我们会检查 是包 meta.far 的标识符,将添加 将软件包名称复制到 meta.far 的 Merkle 根目录中。bootfs 映像结束时 该地图将以 json 格式写入“pkg_map”描述的文件 (请参阅上面的 bootfs 更改部分)。
我们选择在图像汇编级别实现这一转换, 原因:
ProductAssembly 只是合并了其各种 软件包。
ImageAssemblyConfig 验证仍然很简单。
在“packages”的产品组合中验证继续 对“软件包”执行操作
实现
功能实现
映像汇编更改、bootfs 更改、BootResolver 更改 和 BootUrl 更改都可以同时完成。
为了确保我们不会看到 在实现过程中使用 bootfs_package_labels, 我们先从“Image Assembly”更改开始我们将 在 bootfs 软件包清单之前 实现此功能 聚合,我们将在构建时检查该集是否为空。
BootUrl 的语义将通过 3 个 CL 发生变化。首先,迁移之前 首个 bootfs 组件添加命名空间时,我们会将 BootUrl 更改为 则视为无效(确保 我们保留软件包路径的可用性作为 解决策略)。第二个是迁移到了第一个 bootfs 组件, fuchsia_package 中,我们将允许在 fuchsia-boot 网址中含有软件包路径。第三, 当最后一个 bootfs 组件迁移到 fuchsia_package 后,我们将禁止 不包含软件包的 fuchsia-boot 网址。
Migration
这些功能完全实现后,我们将迁移 bootfs 组件 增量。给定组件的迁移如下:
- 我们将找到组件的未打包依赖项的位置 (.cml 文件、二进制文件等)会添加到 bootfs_labels 依赖项中。
- 我们会将该未打包依赖项集合转换为 fuchsia_package gn 目标。
- 我们将从现有的 bootfs_labels 组中移除该软件包,并添加 添加到 bootfs_package_labels 组中。
- 我们会将 bootstrap.cml 文件中组件的网址更新为 添加软件包名称
性能
系统大小
保守地说,bootfs 映像将增加约 70KiB(压缩后) 介绍 x64 架构。这是因为在将所有组件迁移到 软件包之后,bootfs 大小将根据新添加的 meta.fars 和 新的 pkg_map 文件。其他的一切都只是 打包前的 bootfs 文件的目录条目。
运行时影响
如今,整个 bootfs 被急于在 component_manager 解析到某个目录 启动。迁移后,我们最终会将等效的解析工作 在 /boot 内设置组件的命名空间,直到该组件启动。
除了以前只是解析 bootfs 头文件;我们必须解析 meta.far
ZBI 已签字,因此我们目前不对 zbi 内的 bootfs 文件,只验证 zbi 本身。如果我们 不在引导加载程序中对 blob 进行运行时验证,我们将严格按照 与现在一样的安全地位
引导加载程序可能今天会发现一个潜在的错误状态, 程序集可能会错误地将源文件放在 错误。bootfs 中存在 blob 意味着 通过对启动磁盘进行运行时验证来避免这种情况 Blob。我们一开始不打算这样做。
构建时的影响
zbi 构建时的构建时性能保持不变;迁移之后 而无需遍历包含的每个依赖项的目的地条目清单 在 bootfs 中,我们改为遍历软件包清单来对 blob 进行编码。在 我们要遍历相同数量的工件。
向后兼容性
此方案涉及的许多更改都在 bootfs 中是独立的。 在 而不是整个 bootfs。因此,不存在系统造成的风险 提供了不完整的 bootfs 打包实现;提供的已打包的 bootfs 组件。
向后兼容性问题的一个潜在根源是,如果图片汇编程序 不知如何发现自己包含了一个带有新组件的旧版 bootfs 映像 在同一个 zbi 内的经理账号。如果发生这种情况,在迁移完成后 和非封装组件在 bootfs 中被禁止,这可能会导致 错误。不过,考虑到产品组件构建 bootfs 的方式, 这种状态今天根本不可能出现。
同样,对 BootUrl 的语义进行更改功能 实现不会偏离功能 因为它也包含在 bootfs 中。
安全注意事项
软件包名称/进程沙盒可严格提高运行时安全性 减少进程有权访问的工件
bootfs 映像是经过签名的只读映像 与格式设置无关, 图片。如果有,请在命令行中提供可执行文件的 Merkle 根身份(如果有的话) 如果我们做出决定, bootfs 映像应可增量更新
隐私注意事项
无。
测试
在组件解析器和产品组装方面进行测试的做法不错 记录,并将扩展以涵盖新功能。
我们将在语义更改的 3 个阶段分别添加 BootUrl 解析器测试 以表示正在强制执行网址的预期行为。
文档
有关用户空间引导的 API 文档,以及有关 bootfs 映像汇编时,需要更新。
缺点、替代方案和未知问题
重复使用 bootfs_labels 与引入新标签。
今天,依赖项列表中意外包含软件包 是空操作。进行这项更改后, 软件包的存在在语义上具有重要意义。因此, 我们需要“清理”无意中包含来自 bootfs_labels 命名空间。
此处的唯一复杂性涉及 fuchsia_driver_packages。 fuchsia_driver_package 是唯一的构建模板;如何组装产品 与 fuchsia_driver_package 的交互方式取决于驱动程序是否能够 被放入 bootfs 或 blobfs 中。放入 bootfs_labels 时,产品 会遍历驱动程序软件包,以便将驱动程序视为 依赖项,当放入 blobfs 中时,驱动程序通过 meta.far。这样做是为了让单个驱动程序目标可以在 bootfs 之间切换 和 blobfs,具体取决于产品。过去它能正常运行 但产品组合并没有为 其依赖项图表。不过,对于 bootfs 软件包名称 解读是否存在 fuchsia_package (及其关联的 package_manifest)作为声明,以放置 将软件包命名空间编码为 bootfs。
如果我们今天发布 bootfs 命名空间功能,大约有 20 个驱动程序会结束 同时作为未打包依赖项放置在 bootfs 中,并由 关联的 meta.far 和关联的 Blob;驱动程序的 meta.far 编码 在对 Drive_manager 的运行程序做出更改之前,未使用这些命令。而非膨胀 我们希望清除所有 fuchsia_packages 然后逐步将目标迁移到适当的打包环境。周三 选择清除 fuchsia_driver_packages 的 package_manifests 引入新的 gn 元数据屏障,防止 list_package_manifests 走到紫红色的车手。这样,您无需拆分每个驱动程序 将 fuchsia_driver_package 嵌入到 bootfs 目标和 blobfs 目标中, 因为它能显著降低驱动程序产品组装的复杂性 逻辑。此外,build 中可以提供服务的单个软件包定义 这可避免由于拆分每个目的而产生用户流失 然后移除所有“仅旧 bootfs”伪软件包。
遗憾的是,这具有不同的复杂性,例如引入 真正的 fuchsia_package 目标,对用户隐藏其名称。这就引出了 无法兼容其他现有和正在进行的工作,例如黄金测试 按名称验证设备上存在的所有软件包清单是否符合预期, 或产品组装工作,要求生成包装索引的标签 仅直接依赖于软件包目标本身。
因此,干扰性更小的方法是直接添加新标签, 将前期启动中的组件迁移到软件包时,请将其从旧版 复制到新标签中最终,我们需要合并这些列表 如果尚未将驱动程序迁移到适当的组件, 需要提高 fuchsia-driver-package 模板的复杂性。
在产品或映像组装操作中实现
- 是否应在
产品组合还是图片组合?换句话说,我们是否应该遵循
解析清单,直到我们准备调用
使用 zbi 工具构建 bootfs?
- ProductAssembly(ffx 组装产品)
- 优点:
<ph type="x-smartling-placeholder">
- </ph>
- ImageAssembly 非常专注于生成图片文件 (zbi、blobfs 等)
- ImageAssemblyConfig 包含包含所有 bootfs 文件的较为简单的列表。
- 缺点:
<ph type="x-smartling-placeholder">
- </ph>
- 需要进行更复杂的验证 ImageAssemblyConfig,用于生成旧版汇编输入 bundle 与 ProductAssembly 创建的 ImageAssemblyConfig 匹配 (或者需要舍弃/弱化验证)。
- 在产品组装结束时进行验证需要知道 查找 bootfs 中组件的“packages”(例如 value-file 已针对结构化配置完成存在性验证)。
- ImageAssembly (ffx assembly create-system)
- 优点:
<ph type="x-smartling-placeholder">
- </ph>
- ProductAssembly 只是合并了其各种 套装
- ImageAssemblyConfig 验证仍然很简单
- 在产品组合中“packages”的验证继续 对“软件包”执行操作
- 缺点:
<ph type="x-smartling-placeholder">
- </ph>
- ImageAssemblyConfig 不再包含 bootfs 的完整内容 文件。
- Image Assembly 需要执行软件包 ->到条目映射 然后再创建 zbi。
在 bootfs 中对 Metal.fars 进行编码,还是在构建 bootfs 映像时设置命名空间?
一种替代方案是简单地对“命名空间”启动映像 构建映像,使 /boot 下的每个组件都有自己的 组件管理器用作该组件的命名空间根目录的子目录。
在启动 bootfs 中对 meta.fars 进行编码时,我们通过 在 boot-resolver 中引入对 SWD 软件包解析库的依赖项, 并负责解读 meta.far 格式 面对 meta.far 格式的变化,继续采取这种做法。
在构建映像期间执行命名空间设置时, 如何解读产品装配的复杂程度 package-manifest/meta.far 格式,以及编写代码来翻译 meta.far 转换为 bootfs 映像中的软件包命名空间。这意味着需要新的代码, 第二个团队就成为解读 Meta.fars 和 负责确保产品与其保持同步。SWD 团队 表示担心新团队会接管 meta.far 的依赖项 而且可以通过汇合单个软件包来完全避免这种情况 编码和重复使用由 SWD 维护的工具, 格式。
两种策略的资源影响几乎相同( KiB)。
最后,软件包不仅仅是它们编码的命名空间。元数据中的 运行时功能(例如平台)需要 meta.fars(如软件包版本) 版本控制如果未使用 meta.fars,则需要引入一些新方法 对这种“额外信息”在基于 bootfs 的命名空间中,并教 组件管理器等额外编码服务, 元数据。