Android 11 引入了通用内核映像 (GKI) 的概念。为了使用 GKI 轻松启动任意设备,Android 11 设备可以使用启动映像标头版本 3。在版本 3 中,所有特定于供应商的信息都从boot
分区中分离出来,并重新定位到新的vendor_boot
分区中。在 5.4 Linux 内核上搭载 Android 11 的 ARM64 设备必须支持vendor_boot
分区和更新的boot
分区格式才能通过 GKI 测试。
Android 12 设备可以使用启动映像标头版本 4,它支持在vendor_boot
分区中包含多个供应商 ramdisk。多个vendor ramdisk 片段在vendor ramdisk 部分中一个接一个地连接在一起。 vendor ramdisk 表用于描述 vendor ramdisk 部分的布局和每个 vendor ramdisk 片段的元数据。
分区结构
供应商启动分区使用虚拟 A/B 进行 A/B 测试,并受 Android 验证启动保护。
版本 3
该分区由标头、供应商 ramdisk 和设备树 blob (DTB) 组成。
部分 | 页数 |
---|---|
Vendor boot header(n 页) | n = (2112 + page_size - 1) / page_size |
供应商 ramdisk(o 页) | o = (vendor_ramdisk_size + page_size - 1) / page_size |
DTB(p 页) | p = (dtb_size + page_size - 1) / page_size |
版本 4
该分区由标题、供应商 ramdisk 部分(由所有供应商 ramdisk 片段组成,串联)、设备树 blob (DTB) 和供应商 ramdisk 表组成。
部分 | 页数 |
---|---|
Vendor boot header(n 页) | n = (2128 + page_size - 1) / page_size |
供应商 ramdisk 碎片(o 页) | o = (vendor_ramdisk_size + page_size - 1) / page_size |
DTB(p 页) | p = (dtb_size + page_size - 1) / page_size |
供应商 ramdisk 表(q 页) | q = (vendor_ramdisk_table_size + page_size - 1) / page_size |
Bootconfig(r 页) | r = (bootconfig_size + page_size - 1) / page_size |
供应商引导标头
供应商引导分区标头的内容主要由从引导映像标头重新定位到那里的数据组成。它还包含有关供应商 ramdisk 的信息。
版本 3
struct vendor_boot_img_hdr_v3
{
#define VENDOR_BOOT_MAGIC_SIZE 8
uint8_t magic[VENDOR_BOOT_MAGIC_SIZE];
uint32_t header_version;
uint32_t page_size; /* flash page size we assume */
uint32_t kernel_addr; /* physical load addr */
uint32_t ramdisk_addr; /* physical load addr */
uint32_t vendor_ramdisk_size; /* size in bytes */
#define VENDOR_BOOT_ARGS_SIZE 2048
uint8_t cmdline[VENDOR_BOOT_ARGS_SIZE];
uint32_t tags_addr; /* physical addr for kernel tags */
#define VENDOR_BOOT_NAME_SIZE 16
uint8_t name[VENDOR_BOOT_NAME_SIZE]; /* asciiz product name */
uint32_t header_size; /* size of vendor boot image header in
* bytes */
uint32_t dtb_size; /* size of dtb image */
uint64_t dtb_addr; /* physical load address */
};
版本 4
struct vendor_boot_img_hdr_v4
{
#define VENDOR_BOOT_MAGIC_SIZE 8
uint8_t magic[VENDOR_BOOT_MAGIC_SIZE];
uint32_t header_version;
uint32_t page_size; /* flash page size we assume */
uint32_t kernel_addr; /* physical load addr */
uint32_t ramdisk_addr; /* physical load addr */
uint32_t vendor_ramdisk_size; /* size in bytes */
#define VENDOR_BOOT_ARGS_SIZE 2048
uint8_t cmdline[VENDOR_BOOT_ARGS_SIZE];
uint32_t tags_addr; /* physical addr for kernel tags */
#define VENDOR_BOOT_NAME_SIZE 16
uint8_t name[VENDOR_BOOT_NAME_SIZE]; /* asciiz product name */
uint32_t header_size; /* size of vendor boot image header in
* bytes */
uint32_t dtb_size; /* size of dtb image */
uint64_t dtb_addr; /* physical load address */
uint32_t vendor_ramdisk_table_size; /* size in bytes for the vendor ramdisk table */
uint32_t vendor_ramdisk_table_entry_num; /* number of entries in the vendor ramdisk table */
uint32_t vendor_ramdisk_table_entry_size; /* size in bytes for a vendor ramdisk table entry */
uint32_t bootconfig_size; /* size in bytes for the bootconfig section */
};
#define VENDOR_RAMDISK_TYPE_NONE 0
#define VENDOR_RAMDISK_TYPE_PLATFORM 1
#define VENDOR_RAMDISK_TYPE_RECOVERY 2
#define VENDOR_RAMDISK_TYPE_DLKM 3
struct vendor_ramdisk_table_entry_v4
{
uint32_t ramdisk_size; /* size in bytes for the ramdisk image */
uint32_t ramdisk_offset; /* offset to the ramdisk image in vendor ramdisk section */
uint32_t ramdisk_type; /* type of the ramdisk */
#define VENDOR_RAMDISK_NAME_SIZE 32
uint8_t ramdisk_name[VENDOR_RAMDISK_NAME_SIZE]; /* asciiz ramdisk name */
#define VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE 16
// Hardware identifiers describing the board, soc or platform which this
// ramdisk is intended to be loaded on.
uint32_t board_id[VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE];
};
-
vendor_ramdisk_size
是所有供应商 ramdisk 碎片的总大小。 -
ramdisk_type
表示 ramdisk 的类型,可能的值是:-
VENDOR_RAMDISK_TYPE_NONE
表示该值未指定。 -
VENDOR_RAMDISK_TYPE_PLATFORM
虚拟磁盘包含特定于平台的位。引导加载程序必须始终将这些加载到内存中。 -
VENDOR_RAMDISK_TYPE_RECOVERY
虚拟磁盘包含恢复资源。引导加载程序必须在引导进入恢复时将这些加载到内存中。 -
VENDOR_RAMDISK_TYPE_DLKM
虚拟磁盘包含动态可加载内核模块。
-
-
ramdisk_name
是 ramdisk 的唯一名称。 -
board_id
是供应商定义的硬件标识符的向量。
引导加载程序支持
因为供应商引导分区包含以前存在于引导分区中的信息(例如闪存页面大小、内核、ramdisk 加载地址、DTB 本身),引导加载程序必须同时访问引导分区和供应商引导分区以获得足够的数据来完成引导.
引导加载程序必须在供应商 ramdisk 之后立即将通用 ramdisk 加载到内存中(CPIO、Gzip 和 lz4 格式支持这种类型的连接)。不要对通用 ramdisk 映像进行页面对齐或在它与内存中供应商 ramdisk 的末尾之间引入任何其他空间。内核解压缩后,它将连接的文件提取到initramfs
中,这会产生一个文件结构,该文件结构是覆盖在供应商 ramdisk 文件结构上的通用 ramdisk。
因为通用 ramdisk 和供应商 ramdisk 被连接在一起,所以它们必须采用相同的格式。 GKI 引导映像使用 lz4 压缩的通用 ramdisk,因此符合 GKI 的设备必须使用 lz4 压缩的供应商 ramdisk。其配置如下所示。
支持 bootconfig 的引导加载程序要求在实施 Bootconfig页面上进行了说明。
多个供应商 ramdisk(版本 4)
使用引导映像标头版本 4,引导加载程序可以选择一个子集或所有供应商 ramdisk 在引导期间作为initramfs
加载。供应商 ramdisk 表包含每个 ramdisk 的元数据,可以帮助引导加载程序决定加载哪些 ramdisk。引导加载程序可以决定加载所选供应商 ramdisk 的顺序,只要最后加载通用 ramdisk。
例如,引导加载程序可以在正常启动期间省略加载类型为VENDOR_RAMDISK_TYPE_RECOVERY
的供应商虚拟磁盘以节省资源,因此只有类型为VENDOR_RAMDISK_TYPE_PLATFORM
和VENDOR_RAMDISK_TYPE_DLKM
的供应商虚拟磁盘被加载到内存中。另一方面,类型为VENDOR_RAMDISK_TYPE_PLATFORM
、 VENDOR_RAMDISK_TYPE_RECOVERY
和VENDOR_RAMDISK_TYPE_DLKM
的供应商虚拟磁盘在启动进入恢复模式时被加载到内存中。
或者,引导加载程序可以忽略供应商 ramdisk 表并加载整个供应商 ramdisk 部分。这与在vendor_boot
分区中加载所有供应商 ramdisk 碎片具有相同的效果。
建立支持
要为设备实现供应商引导支持:
将
BOARD_BOOT_HEADER_VERSION
设置为3
或更大。如果您的设备符合 GKI,或者如果它使用 lz4 压缩的通用 ramdisk,则将
BOARD_RAMDISK_USE_LZ4
设置为true
。考虑到必须放在供应商 ramdisk 上的内核模块,将
BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE
设置为适合您设备的大小。更新
AB_OTA_PARTITIONS
以包括vendor_boot
和设备上任何供应商特定的 OTA 分区列表。将设备
fstab
复制到vendor_boot
分区中的/first_stage_ramdisk
,而不是boot
分区。例如,$(LOCAL_PATH)/fstab.hardware:$(TARGET_COPY_OUT_VENDOR_RAMDISK)/first_stage_ramdisk/fstab.$(PRODUCT_PLATFORM)
。
在vendor_boot
中包含多个供应商 ramdisks:
- 将
BOARD_BOOT_HEADER_VERSION
设置为4
。 将
BOARD_VENDOR_RAMDISK_FRAGMENTS
设置为要包含在vendor_boot
中的逻辑供应商 ramdisk 片段名称列表。要添加预建供应商 ramdisk,请将
BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).PREBUILT
设置为预建文件路径。要添加 DLKM 供应商 ramdisk,请将
BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).KERNEL_MODULE_DIRS
设置为要包含的内核模块目录列表。将
BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).MKBOOTIMG_ARGS
为mkbootimg
参数。这些是供应商 ramdisk 片段的--board_id[0-15]
和--ramdisk_type
参数。对于 DLKM 供应商 ramdisk,如果没有另外指定,默认的--ramdisk_type
将是DLKM
。
要在vendor_boot
恢复资源构建为独立的recovery
ramdisk:
- 将
BOARD_BOOT_HEADER_VERSION
设置为4
。 - 将
BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT
设置为true
。 - 将
BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT
设置为true
。 - 这会添加一个供应商 ramdisk 片段,其
ramdisk_name
是recovery
并且ramdisk_type
是VENDOR_RAMDISK_TYPE_RECOVERY
。然后 ramdisk 包含所有恢复文件,这些文件安装在$(TARGET_RECOVERY_ROOT_OUT)
下。
mkbootimg
参数
争论 | 描述 |
---|---|
--ramdisk_type | ramdisk 的类型,可以是NONE 、 PLATFORM 、 RECOVERY 或DLKM 之一。 |
--board_id[0-15] | 指定board_id 向量,默认为0 。 |
以下是示例配置:
BOARD_KERNEL_MODULE_DIRS := foo bar baz
BOARD_BOOT_HEADER_VERSION := 4
BOARD_VENDOR_RAMDISK_FRAGMENTS := dlkm_foobar
BOARD_VENDOR_RAMDISK_FRAGMENT.dlkm_foobar.KERNEL_MODULE_DIRS := foo bar
BOARD_VENDOR_RAMDISK_FRAGMENT.dlkm_foobar.MKBOOTIMG_ARGS := --board_id0 0xF00BA5 --board_id1 0xC0FFEE
生成的vendor_boot
将包含两个供应商 ramdisk 片段。第一个是“默认”虚拟磁盘,它包含 DLKM 目录baz
和$(TARGET_VENDOR_RAMDISK_OUT)
中的其余文件。第二个是dlkm_foobar
ramdisk,它包含 DLKM 目录foo
和bar
,-- --ramdisk_type
默认为DLKM
。