Vendor boot partitions

Android 11 introduced the concept of the Generic Kernel Image (GKI). To enable booting an arbitrary device with the GKI, Android 11 devices can use boot image header version 3. In version 3, all vendor-specific information is factored out of the boot partition and relocated into a new vendor_boot partition. An ARM64 device launching with Android 11 on the 5.4 Linux kernel must support the vendor_boot partition and the updated boot partition format to pass testing with the GKI.

Android 12 devices can use boot image header version 4, which supports including multiple vendor ramdisks in the vendor_boot partition. Multiple vendor ramdisk fragments are concatenated one after another in the vendor ramdisk section. A vendor ramdisk table is used to describe the layout of the vendor ramdisk section and the metadata of each vendor ramdisk fragment.

Partition structure

The vendor boot partition is A/B-ed with virtual A/B and protected by Android Verified Boot.

Version 3

The partition consists of a header, the vendor ramdisk, and the device tree blob (DTB).

Section Number of pages
Vendor boot header (n pages) n = (2112 + page_size - 1) / page_size
Vendor ramdisk (o pages) o = (vendor_ramdisk_size + page_size - 1) / page_size
DTB (p pages) p = (dtb_size + page_size - 1) / page_size

Version 4

The partition consists of a header, the vendor ramdisk section (consisting of all vendor ramdisk fragments, concatenated), the device tree blob (DTB), and the vendor ramdisk table.

Section Number of pages
Vendor boot header (n pages) n = (2128 + page_size - 1) / page_size
Vendor ramdisk fragments (o pages) o = (vendor_ramdisk_size + page_size - 1) / page_size
DTB (p pages) p = (dtb_size + page_size - 1) / page_size
Vendor ramdisk table (q pages) q = (vendor_ramdisk_table_size + page_size - 1) / page_size
Bootconfig (r pages) r = (bootconfig_size + page_size - 1) / page_size

Vendor boot header

The contents of the vendor boot partition header consist primarily of data that has been relocated there from the boot image header. It also contains information about the vendor ramdisk.

Version 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 */

};

Version 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 is the total size of all the vendor ramdisk fragments.
  • ramdisk_type denotes the type of the ramdisk, possible values are:
    • VENDOR_RAMDISK_TYPE_NONE indicates the value is unspecified.
    • VENDOR_RAMDISK_TYPE_PLATFORM ramdisks contain platform specific bits. The bootloader must always load these into memory.
    • VENDOR_RAMDISK_TYPE_RECOVERY ramdisks contain recovery resources. The bootloader must load these into memory when booting into recovery.
    • VENDOR_RAMDISK_TYPE_DLKM ramdisks contain dynamic loadable kernel modules.
  • ramdisk_name is an unique name of the ramdisk.
  • board_id is a vector of vendor defined hardware identifiers.

Bootloader support

Because the vendor boot partition contains information (such as flash page size, kernel, ramdisk load addresses, the DTB itself) that previously existed in the boot partition, the bootloader must access both the boot and vendor boot partitions to have enough data to complete booting.

The bootloader must load the generic ramdisk into memory immediately following the vendor ramdisk (the CPIO, Gzip, and lz4 formats support this type of concatenation). Don't page align the generic ramdisk image or introduce any other space between it and the end of the vendor ramdisk in memory. After the kernel decompresses, it extracts the concatenated file into an initramfs, which results in a file structure that's a generic ramdisk overlaid on the vendor ramdisk file structure.

Because the generic ramdisk and vendor ramdisk get concatenated, they must be in the same format. The GKI boot image uses an lz4-compressed generic ramdisk, so a device that is GKI-compliant must use an lz4-compressed vendor ramdisk. The configuration for this is shown below.

The bootloader requirements for supporting bootconfig are explained in Implement Bootconfig.

Multiple vendor ramdisks (version 4)

With boot image header version 4, the bootloader can select either a subset or all of the vendor ramdisks to load as the initramfs during boot time. The vendor ramdisk table contains the metadata of each ramdisk, and can aid the bootloader in deciding which ramdisks to load. The bootloader can decide the order to load the selected vendor ramdisks, as long as the generic ramdisk is loaded last.

For example, the bootloader can omit loading vendor ramdisks of type VENDOR_RAMDISK_TYPE_RECOVERY during normal boot to conserve resources, so only vendor ramdisks of type VENDOR_RAMDISK_TYPE_PLATFORM and VENDOR_RAMDISK_TYPE_DLKM are loaded into memory. On the other hand, vendor ramdisks of type VENDOR_RAMDISK_TYPE_PLATFORM, VENDOR_RAMDISK_TYPE_RECOVERY and VENDOR_RAMDISK_TYPE_DLKM are loaded into memory when booting into recovery mode.

Alternatively, the bootloader can ignore the vendor ramdisk table and load the entire vendor ramdisk section. This has the same effect as does loading all of the vendor ramdisk fragments in the vendor_boot partition.

Build support

To implement vendor boot support for a device:

  • Set BOARD_BOOT_HEADER_VERSION to 3 or greater.

  • Set BOARD_RAMDISK_USE_LZ4 to true if your device is GKI-compliant, or if it otherwise uses an lz4-compressed generic ramdisk.

  • Set BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE to an appropriate size for your device, considering the kernel modules that must go on the vendor ramdisk.

  • Update AB_OTA_PARTITIONS to include vendor_boot and any vendor-specific lists of OTA partitions on the device.

  • Copy your device fstab into /first_stage_ramdisk in the vendor_boot partition, not the boot partition. For example, $(LOCAL_PATH)/fstab.hardware:$(TARGET_COPY_OUT_VENDOR_RAMDISK)/first_stage_ramdisk/fstab.$(PRODUCT_PLATFORM).

To include multiple vendor ramdisks in vendor_boot:

  • Set BOARD_BOOT_HEADER_VERSION to 4.
  • Set BOARD_VENDOR_RAMDISK_FRAGMENTS to a list of logical vendor ramdisk fragment names to be included in vendor_boot.

  • To add a prebuilt vendor ramdisk, set BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).PREBUILT to the prebuilt path.

  • To add a DLKM vendor ramdisk, set BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).KERNEL_MODULE_DIRS to the list of kernel module directories to be included.

  • Set BOARD_VENDOR_RAMDISK_FRAGMENT.$(vendor_ramdisk).MKBOOTIMG_ARGS to mkbootimg arguments. These are the --board_id[0-15] and --ramdisk_type arguments for the vendor ramdisk fragment. For DLKM vendor ramdisk, the default --ramdisk_type would be DLKM if it's not otherwise specified.

To build recovery resources as a standalone recovery ramdisk in vendor_boot:

  • Set BOARD_BOOT_HEADER_VERSION to 4.
  • Set BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT to true.
  • Set BOARD_INCLUDE_RECOVERY_RAMDISK_IN_VENDOR_BOOT to true.
  • This adds a vendor ramdisk fragment whose ramdisk_name is recovery and ramdisk_type is VENDOR_RAMDISK_TYPE_RECOVERY. The ramdisk then contains all recovery files, which are files installed under $(TARGET_RECOVERY_ROOT_OUT).

mkbootimg arguments

Argument Description
--ramdisk_type The type of the ramdisk, can be one of NONE, PLATFORM, RECOVERY or DLKM.
--board_id[0-15] Specify the board_id vector, defaults to 0.

Following is an example configuration:

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

The resulting vendor_boot would contain two vendor ramdisk fragments. The first one is the "default" ramdisk, which contains the DLKM directory baz and the rest of the files in $(TARGET_VENDOR_RAMDISK_OUT). The second one is the dlkm_foobar ramdisk, which contains the DLKM directories foo and bar, and the --ramdisk_type defaults to DLKM.