Boot Image Header Versioning

Starting in Android 9, the boot image header contains a field to indicate the header version. The bootloader must check this header version field and parse the header accordingly. Versioning the boot image header allows future modifications to the header while maintaining backward compatibility.

All devices launching with Android 9 must use boot header version 1.

Boot image header changes

In the legacy boot image header (shown below), the unused field is converted to a header version field for devices launching with Android 9.

struct boot_img_hdr
{
    uint8_t magic[BOOT_MAGIC_SIZE];
    uint32_t kernel_size;  /* size in bytes */
    uint32_t kernel_addr;  /* physical load addr */

    uint32_t ramdisk_size; /* size in bytes */
    uint32_t ramdisk_addr; /* physical load addr */

    uint32_t second_size;  /* size in bytes */
    uint32_t second_addr;  /* physical load addr */

    uint32_t tags_addr;    /* physical addr for kernel tags */
    uint32_t page_size;    /* flash page size we assume */
    uint32_t unused;
    uint32_t os_version;
    uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
    uint8_t cmdline[BOOT_ARGS_SIZE];
    uint32_t id[8]; /* timestamp / checksum / sha1 / etc */
    uint8_t extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
};

Devices launched before Android 9 using the legacy boot image header are considered as using a boot image header version 0. All devices launching with Android 9 must use the following structure for the boot image header with the header version set to 1.

struct boot_img_hdr
{
    uint8_t magic[BOOT_MAGIC_SIZE];
    uint32_t kernel_size;  /* size in bytes */
    uint32_t kernel_addr;  /* physical load addr */

    uint32_t ramdisk_size; /* size in bytes */
    uint32_t ramdisk_addr; /* physical load addr */

    uint32_t second_size;  /* size in bytes */
    uint32_t second_addr;  /* physical load addr */

    uint32_t tags_addr;    /* physical addr for kernel tags */
    uint32_t page_size;    /* flash page size we assume */
    uint32_t header_version;
    uint32_t os_version;
    uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
    uint8_t cmdline[BOOT_ARGS_SIZE];
    uint32_t id[8]; /* timestamp / checksum / sha1 / etc */
    uint8_t extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
    uint32_t recovery_dtbo_size;   /* size of recovery dtbo image */
    uint64_t recovery_dtbo_offset; /* offset in boot image */
    uint32_t header_size;   /* size of boot image header in bytes */
};

The header_size field contains the size of the boot image header. If the boot image header version is set to 1, the id field contains the SHA-1 digest for the recovery_dtbo section of the boot image in addition to the kernel, ramdisk, and second sections. For details on the recovery_dtbo_size and recovery_dtbo_offset fields, refer to Including DTBO in Recovery for Non-A/B Devices.

Implementation

The mkbootimg tool that creates boot images adds the following arguments to support the new boot image header.

Argument Description
header_version Sets the boot image header version.
recovery_dtbo Path to the recovery DTBO image to be included in the recovery image.

The device BoardConfig.mk uses the config BOARD_MKBOOTIMG_ARGS to add header version to the other board-specific arguments of mkbootimg. For example:

  BOARD_MKBOOTIMG_ARGS := --ramdisk_offset $(BOARD_RAMDISK_OFFSET) --tags_offset $(BOARD_KERNEL_TAGS_OFFSET) --header_version $(BOARD_BOOTIMG_HEADER_VERSION)

The Android build system uses the BoardConfig variable BOARD_PREBUILT_DTBOIMAGE to set the argument recovery_dtbo of the mkbootimg tool during the creation of the recovery image.

For details on the Android Open Source Project (AOSP) changes, review the associated changelists for boot image header versioning.

Validation

For all devices launching with Android 9, the Vendor Test Suite (VTS) checks the format of the boot/recovery image to ensure that the boot image header uses version 1.

Including DTB in the boot image

Android 10 updates the boot image header to version 2, which includes a section to store the device tree blob (DTB) image. Android 10 VTS tests verify that all devices launching with Android 10 use boot image header version 2 and include a valid DTB image as part of the boot/recovery images.

About DTB

In Android 9 and earlier releases, the DTB image was either placed in its own partition or appended to the kernel image.gz to create the kernel + DTB image (which is then passed to mkbootimg to create boot.img). In Android 10, the boot image format is further updated with a section for DTB images. This change means that Android doesn't need to support scripts that append the DTB image to image.gz in the kernel and enables the use of VTS tests to verify (and standardize) DTB placement.

In addition, for non-A/B devices, it's safer to have the DTB as part of the recovery image rather than in a separate partition to prevent issues caused by interrupted OTAs. During an OTA, if a problem occurs after the DTB partition is updated (but prior to completing the full update), the device tries to boot into recovery mode to complete the OTA. However, because the DTB partition has already been updated, a mismatch could occur with the recovery image (which has not yet been updated). Having the DTB image as part of the boot image format prevents such issues by making the recovery image self sufficient (that is, it doesn't depend on another partition).

Boot image header version 2

Boot image header version 2 is defined as shown below with fields representing the size of the DTB image and its physical load address.

struct boot_img_hdr
{
    uint8_t magic[BOOT_MAGIC_SIZE];
    uint32_t kernel_size;  /* size in bytes */
    uint32_t kernel_addr;  /* physical load addr */

    uint32_t ramdisk_size; /* size in bytes */
    uint32_t ramdisk_addr; /* physical load addr */

    uint32_t second_size;  /* size in bytes */
    uint32_t second_addr;  /* physical load addr */

    uint32_t tags_addr;    /* physical addr for kernel tags */
    uint32_t page_size;    /* flash page size we assume */
    uint32_t header_version;
    uint32_t os_version;
    uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
    uint8_t cmdline[BOOT_ARGS_SIZE];
    uint32_t id[8]; /* timestamp / checksum / sha1 / etc */
    uint8_t extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
    uint32_t recovery_dtbo_size;   /* size of recovery dtbo image */
    uint64_t recovery_dtbo_offset; /* offset in boot image */
    uint32_t header_size;   /* size of boot image header in bytes */
    uint32_t dtb_size;   /* size of dtb image */
    uint64_t dtb_addr; /* physical load address */
};

To allow the inclusion of the DTB image, update the boot image format as follows.

Boot image format Number of pages
Boot header (1 page) 1
Kernel (l pages) l = (kernel_size + page_size - 1) / page_size
Ramdisk (m pages) m = (ramdisk_size + page_size - 1) / page_size
Second stage bootloader (n pages) n = (second_size + page_size - 1) / page_size
Recovery DTBO (o pages) o = (recovery_dtbo_size + page_size - 1) / page_size
DTB (p pages) p = (dtb_size + page_size - 1) / page_size

The DTB image must use one of the following formats:

  • DT blobs concatenated one after the other. Bootloader uses the totalsize field in each FDT header to read and parse the corresponding blob.
  • DTB/DTBO partitions structure. Bootloader has an efficient way to select the correct DT blob by examining the dt_table_entry struct (contains id, rev, and custom fields) that can hold hardware identifying information for the entry.

Android 10 modifies mkbootimg.py to support the following arguments.

Argument Description
dtb Path to the DTB image to be included in the boot/recovery images.
dtb_offset When added to the base argument, provides the physical load address for the final device tree. For example, if the base argument is 0x10000000 and the dtb_offset argument is 0x01000000, the dtb_addr_field in the boot image header is populated as 0x11000000.

The board config variable BOARD_PREBUILT_DTBIMAGE_DIR must be used to specify the path to the DTB image. If more than one file with extension *.dtb is present in the directory BOARD_PREBUILT_DTBIMAGE_DIR, the Android build system concatenates the files to create the final DTB image used in the boot image creation.

The board config variable BOARD_INCLUDE_DTB_IN_BOOTIMG must be set to true.

BOARD_INCLUDE_DTB_IN_BOOTIMG := true

This causes the argument dtb to be passed to mkbootimg.py with the DTB image from the directory specified by BOARD_PREBUILT_DTBIMAGE_DIR.

The dtb_offset argument can be appended to the board config variable BOARD_MKBOOTIMG_ARGS along with the other offsets and header version.

BOARD_MKBOOTIMG_ARGS := --ramdisk_offset $(BOARD_RAMDISK_OFFSET) --dtb_offset $(BOARD_DTB_OFFSET) --tags_offset $(BOARD_KERNEL_TAGS_OFFSET) --header_version $(BOARD_BOOTIMG_HEADER_VERSION)

Bootloader must support the updated boot image and must add a new kernel command line parameter androidboot.dtb_idx to indicate the index of the selected DT to facilitate VTS verification. You can only specify one (1) index. For example, the parameter androidboot.dtb_idx=N reports N as the zero-based index of the device tree selected by the bootloader from the set of DT blobs present in the boot image.