供應商啟動分區

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 片段會連接在一起。供應商 RAM 磁碟區表格用於說明供應商 RAM 磁碟區的版面配置,以及每個供應商 RAM 磁碟區片段的中繼資料。

分區結構

供應商啟動分區是以虛擬 A/B 為基礎的 A/B 版本,並受到 Android 驗證開機程序的保護。

版本 3

分割區包含標頭、供應商 RAMDISK 和裝置樹狀結構體 (DTB)。

章節 頁數
供應商啟動標頭 (n 個頁面) n = (2112 + page_size - 1) / page_size
供應商 ramdisk (多頁) 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 = (vendor_ramdisk_size + page_size - 1) / page_size
DTB (p 頁面) p = (dtb_size + page_size - 1) / page_size
供應商 RAM 磁碟資料表 (q 頁面) q = (vendor_ramdisk_table_size + page_size - 1) / page_size
Bootconfig (r 頁面) r = (bootconfig_size + page_size - 1) / page_size

供應商啟動標頭

供應商的啟動分割區標頭內容主要包含從啟動映像檔標頭重新定位的資料。也包含供應商 RAM 磁碟的相關資訊。

版本 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 是供應商定義的硬體 ID 向量。

系統啟動載入程式支援

由於供應商啟動分區包含先前在啟動分區中存在的資訊 (例如閃存記憶體頁面大小、核心、ramdisk 載入位址、DTB 本身),因此 Bootloader 必須同時存取啟動分區和供應商啟動分區,才能取得足夠的資料來完成啟動程序。

引導程式必須在供應商的 RAM 磁碟後,立即將通用 RAM 磁碟載入記憶體 (CPIO、Gzip 和 lz4 格式支援這類連結)。請勿對齊一般 ramdisk 映像檔,或在記憶體與廠商 ramdisk 結尾之間置入任何其他空間。在核心解壓縮後,它會將連結的檔案擷取至 initramfs,產生的檔案結構是供應商 RAMDISK 檔案結構上的一般 RAMDISK。

由於通用 RAM 磁碟和供應商 RAM 磁碟會連結,因此兩者的格式必須相同。GKI 開機映像檔會使用 lz4 壓縮的通用 RAM 磁碟,因此符合 GKI 規範的裝置必須使用 lz4 壓縮的供應商 RAM 磁碟。以下是相關設定。

如要瞭解支援 bootconfig 的系統啟動載入程式需求,請參閱「實作 Bootconfig」一文。

多個供應商 ramdisks (第 4 版)

使用開機映像檔標頭版本 4 時,開機載入程式可選取供應商 RAMDISK 的子集或全部,在開機期間載入為 initramfs。供應商 RAM 磁碟表包含每個 RAM 磁碟的中繼資料,可協助啟動載入程式決定要載入哪些 RAM 磁碟。只要通用 RAM 磁碟最後載入,啟動載入程式就能決定載入所選供應商 RAM 磁碟的順序。

舉例來說,系統啟動載入程式可以在正常啟動期間略過載入 VENDOR_RAMDISK_TYPE_RECOVERY 類型供應商 RAMDISK,以節省資源,因此只有 VENDOR_RAMDISK_TYPE_PLATFORMVENDOR_RAMDISK_TYPE_DLKM 類型的供應商 RAMDISK 會載入記憶體。另一方面,系統會在啟動時將供應商 VENDOR_RAMDISK_TYPE_PLATFORMVENDOR_RAMDISK_TYPE_RECOVERYVENDOR_RAMDISK_TYPE_DLKM 類型的 RAM 磁碟載入復原模式,將其載入記憶體中。

或者,系統啟動載入程式可以忽略供應商 RAMDISK 表格,並載入整個供應商 RAMDISK 區段。這與在 vendor_boot 分割區載入所有供應商 RAMDISK 片段的效果相同。

建構支援

如要為裝置實作供應商啟動支援功能,請按照下列步驟操作:

  • BOARD_BOOT_HEADER_VERSION 設為 3 以上。

  • 如果裝置符合 GKI 規範,或使用 lz4 壓縮的通用 RAM 磁碟,請將 BOARD_RAMDISK_USE_LZ4 設為 true

  • 請考慮將 BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE 設為適合裝置的大小,並考量必須設置在供應商 ramdisk 的核心模組。

  • 更新 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 設為邏輯供應商 RAMDISK 片段名稱清單,以便納入 vendor_boot

  • 如要新增預先建構的供應商 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 引數。這些是供應商 RAM 磁碟片片段的 --board_id[0-15]--ramdisk_type 引數。對於 DLKM 供應商的 RAM 磁碟,如果未指定預設 --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_namerecoveryramdisk_typeVENDOR_RAMDISK_TYPE_RECOVERY。然後,ramdisk 會包含所有復原檔案,也就是在 $(TARGET_RECOVERY_ROOT_OUT) 下安裝的檔案。

mkbootimg 引數

引數 說明
--ramdisk_type 記憶體磁碟的類型可以是 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 片段。第一個是「預設」的 RAM 磁碟,其中包含 DLKM 目錄 baz$(TARGET_VENDOR_RAMDISK_OUT) 中的其他檔案。第二個是 dlkm_foobar ramdisk,內含 DLKM 目錄 foobar--ramdisk_type 則預設為 DLKM