供應商引導分區

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。多個供應商 ramdisk 片段在供應商 ramdisk 部分中一個接一個地連接。供應商 ramdisk 表用於描述供應商 ramdisk 部分的佈局和每個供應商 ramdisk 片段的元數據。

分區結構

供應商引導分區使用虛擬 A/B 進行 A/B 處理,並受 Android 驗證引導保護。

版本 3

該分區由標頭、供應商 ramdisk 和設備樹 blob (DTB) 組成。

部分頁數
供應商引導標頭(n 頁) n = (2112 + page_size - 1) / page_size
供應商 ramdisk (o pages) 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 表組成。

部分頁數
供應商引導標頭(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
引導配置(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 ramdisk 包含平台特定位。引導加載程序必須始終將這些加載到內存中。
    • VENDOR_RAMDISK_TYPE_RECOVERY ramdisk 包含恢復資源。引導加載程序必須在引導到恢復時將這些加載到內存中。
    • VENDOR_RAMDISK_TYPE_DLKM ramdisk 包含動態可加載內核模塊。
  • 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類型的供應商 ramdisk 以節省資源,因此只有VENDOR_RAMDISK_TYPE_PLATFORMVENDOR_RAMDISK_TYPE_DLKM類型的供應商 ramdisk 被加載到內存中。另一方面, VENDOR_RAMDISK_TYPE_PLATFORMVENDOR_RAMDISK_TYPE_RECOVERYVENDOR_RAMDISK_TYPE_DLKM類型的供應商 ramdisk 在啟動到恢復模式時被加載到內存中。

或者,引導加載程序可以忽略供應商 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中包含多個供應商 ramdisk:

  • 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_ARGSmkbootimg參數。這些是供應商 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_namerecovery並且ramdisk_typeVENDOR_RAMDISK_TYPE_RECOVERY 。 ramdisk 然後包含所有恢復文件,這些文件是安裝在$(TARGET_RECOVERY_ROOT_OUT)下的文件。

mkbootimg參數

爭論描述
--ramdisk_type ramdisk 的類型,可以是NONEPLATFORMRECOVERYDLKM之一。
--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 片段。第一個是“默認”ramdisk,它包含 DLKM 目錄baz$(TARGET_VENDOR_RAMDISK_OUT)中的其余文件。第二個是dlkm_foobar ramdisk,它包含 DLKM 目錄foobar--ramdisk_type默認為DLKM