在 Android 12 中实现 bootconfig

在 Android 12 中,bootconfig 功能取代了 Android 11 及更低版本使用的 androidboot.* 内核 cmdline 选项。bootconfig 功能是一种将配置详细信息从 build 和引导加载程序传递给 Android 12 的机制。

此功能可用于将 Android 用户空间的配置参数与内核的配置参数分离开。将冗长的 androidboot.* 内核参数移至 bootconfig 文件中会在内核 cmdline 中腾出空间,并使其便于日后扩展之用。

内核和 Android 用户空间都必须支持 bootconfig

  • 首个提供此支持的版本:Android 12
  • 首个提供此支持的内核版本:12-5.4.xx 内核

请为搭载 12-5.10.xx 内核版本的新设备实现 bootconfig 功能。如果是升级设备,就不需要实现此功能。

示例和源代码

在查看本部分的示例和源代码时,请注意 bootconfig 代码的格式与 Android 11 及更低版本中使用的内核 cmdline 格式只有细微不同。不过,以下差异对您使用这些代码非常重要:

  • 必须以换行符转义序列 \n 分隔参数,而非以空格分隔。

引导加载程序示例

如需查看引导加载程序示例,请参阅 Cuttlefish U-boot 参考引导加载程序实现。下面列出了该参考实现中的两项提交内容。第一项将启动头文件版本支持升级到最新版本。在该示例中,第一项提交内容将版本支持更新(或升级)到下一个版本 (v4)。第二项提交内容执行了两项操作;它添加了 bootconfig 处理,并展示了如何在运行时添加参数:

build 示例

如需查看 build 示例,了解为使用供应商启动头文件 v4 构建 vendor_boot.img 而进行的 mkbootimg 变更,请参阅 mkbootimg changes for bootconfig。请参阅 Cuttlefish 变更,以执行以下操作:

实现

合作伙伴必须为其引导加载程序增添支持,并将其构建时 androidboot.* 参数从内核 cmdline 移至 bootconfig 文件中。实现此变更的最佳方式是逐步实现;如需了解如何遵循逐步实现过程,请参阅逐步实现和验证部分。

如果您所做的变更需要在 /proc/cmdline 文件中搜索 androidboot.* 参数,请改为让其搜索 /proc/bootconfig 文件。ro.boot.* 属性是使用新的 bootconfig 值设置的,因此无需更改使用这些属性的代码。

build 变更

首先,将您的启动头文件版本升级到版本 4:

- BOARD_BOOT_HEADER_VERSION := 3

+ BOARD_BOOT_HEADER_VERSION := 4

添加 bootconfig 内核 cmdline 参数。这样,内核就会查找 bootconfig 部分:

BOARD_KERNEL_CMDLINE += bootconfig

bootconfig 参数是通过 BOARD_BOOTCONFIG 变量中的参数创建的,就像内核 cmdline 是通过 BOARD\_KERNEL\_CMDLINE 创建的一样。

任何 androidboot.* 参数都可以原样移动,类似于以下内容:

- BOARD_KERNEL_CMDLINE += androidboot..selinux=enforcing

+ BOARD_BOOTCONFIG += androidboot..selinux=enforcing

引导加载程序的变更

引导加载程序会先设置 initramfs,然后再跳转到内核。内核启动配置会搜索 bootconfig 部分,并预计此部分位于 initramfs, 末尾且包含预期的尾部。

引导加载程序会从供应商启动映像头文件中获取 vendor_boot.img 布局信息。

bootconfig 内存分配布局示意图

图 1. Android 12 bootconfig 内存分配

引导加载程序会在内存中创建 bootconfig 部分。bootconfig 部分包含以下各项的内存分配:

  • 参数
  • 4 个字节大小的 parameters size
  • 4 个字节大小的 parameters checksum
  • 12 个字节的 bootconfig 神奇字符串 (#BOOTCONFIG\n)

这些参数有两个来源:构建时已知的参数和构建时未知的参数。您必须添加未知参数。

构建时已知的参数会打包到 vendor_boot 映像中的 bootconfig 部分末尾。这部分的大小以字节形式存储在供应商启动头文件的 vendor_bootconfig_size 字段中。

构建时未知的参数只能在运行时从引导加载程序中得知。在应用 bootconfig 尾部之前,必须先将这些参数添加到 bootconfig 参数部分的末尾。

如果需要在应用 bootconfig 尾部之后添加任何参数,请覆盖尾部然后重新应用尾部。

逐步实现和验证

请按照本部分介绍的过程逐步实现 bootconfig 功能。在添加 bootconfig 参数时,不要改动内核 cmdline 参数。

以下是逐步实现和验证过程的步骤:

  1. 更改引导加载程序和 build,然后执行以下操作:
    1. 使用 BOARD_BOOTCONFIG 变量添加新的 bootconfig 参数。
    2. 保持内核 cmdline 参数不变,以便设备可以继续正常启动。这会让调试和验证都变得更容易。
  2. 通过检查 /proc/bootconfig 的内容验证您的工作。验证能否在设备启动后看到新添加的参数。
  3. 使用 BOARD_BOOTCONFIG 变量和引导加载程序,将 androidboot.* 参数从内核 cmdline 移到 bootconfig。
  4. 验证每个参数是否都存在于 /proc/bootconfig 中且不在 /proc/cmdline 中。如果能够证实这一点,就表示您的实现成功了。

OTA 升级和降级注意事项

管理不同 Android 版本间或不同内核版本间的 OTA 升级和降级时,应特别注意。

Android 12 是首个支持 bootconfig 的版本。如果降级为低于此版本的任何版本,必须使用内核 cmdline 参数而非 bootconfig。

内核版本 12-5.4 及更高版本支持 bootconfig。如果降级为任何更低的版本(包括版本 11-5.4),必须使用内核 cmdline 参数。

从 Android 11 及更低版本升级到 Android 12 及更高版本可以继续使用内核 cmdline 参数。升级内核版本时同样如此。

问题排查

执行验证步骤时,如果未在 /proc/bootconfig 中看到预期参数,请检查 logcat 中的内核日志。如果内核支持 bootconfig,就一定会存在 bootconfig 的日志条目。

日志输出示例

$ adb logcat | grep bootconfig
02-24 17:00:07.610     0     0 I Load bootconfig: 128 bytes 9 nodes

如果您看到返回错误日志,就表示加载 bootconfig 时出现了问题。如需了解不同的错误类型,请查看 init/main.c