Since Android 13 (boot image header version 4) the generic ramdisk lives in its own init_boot partition and boot holds only the GKI kernel. The bootmeth loaded boot and vendor_boot only, so such a device could not reach init unless its images were repacked.
Scan init_boot like boot and vendor_boot through a shared helper (the header is the boot image header with a kernel size of 0), load it to init_boot_comp_addr_r, add it to the partitions libavb verifies (as a preloaded buffer, like the others) and hand it to bootm through set_ainit_bootimg_addr(), which then takes the generic ramdisk from it. A header version 4 device without an init_boot partition still boots with the ramdisk from boot; a broken init_boot is an error. Tested on QEMU virt with the Cuttlefish arm64 images: boot, init_boot and vendor_boot are verified in place and Android starts from the unmodified GKI boot image. Signed-off-by: Igor Opaniuk <[email protected]> --- Devices launched with Android 13 or later keep the generic ramdisk in the init_boot partition, so the Android bootmeth could not boot them from an unmodified GKI boot image. This adds init_boot to the images the bootmeth loads and has libavb verify. Split out of a larger Android bootmeth series; it applies to and builds on its own on main. --- boot/bootmeth_android.c | 187 +++++++++++++++++++++++++++------------ cmd/abootimg.c | 5 ++ doc/develop/bootstd/overview.rst | 4 + include/image.h | 10 ++- 4 files changed, 149 insertions(+), 57 deletions(-) diff --git a/boot/bootmeth_android.c b/boot/bootmeth_android.c index 6f7e232d2c3..0fabeecdb7b 100644 --- a/boot/bootmeth_android.c +++ b/boot/bootmeth_android.c @@ -30,6 +30,7 @@ #define BCB_PART_NAME "misc" #define BOOT_PART_NAME "boot" #define VENDOR_BOOT_PART_NAME "vendor_boot" +#define INIT_BOOT_PART_NAME "init_boot" #define SLOT_LEN 2 /** @@ -41,6 +42,16 @@ * @boot_mode: Requested boot mode (normal, recovery, bootloader) * @slot: Nul-terminated partition slot suffix read from BCB ("a\0" or "b\0") * @header_version: Android boot image header version + * @boot_img_size: Size of the boot image (from its header) + * @vendor_boot_img_size: Size of the vendor_boot image, header version >= 3 + * @init_boot_img_size: Size of the init_boot image, header version >= 4 and + * the partition exists (0 otherwise) + * @boot_addr: Where the boot image was loaded (0 if not loaded) + * @boot_part_size: Size of the boot partition in bytes + * @vendor_boot_addr: Where the vendor_boot image was loaded (0 if not loaded) + * @vendor_boot_part_size: Size of the vendor_boot partition in bytes + * @init_boot_addr: Where the init_boot image was loaded (0 if not loaded) + * @init_boot_part_size: Size of the init_boot partition in bytes */ struct android_priv { enum android_boot_mode boot_mode; @@ -48,11 +59,13 @@ struct android_priv { u32 header_version; u32 boot_img_size; u32 vendor_boot_img_size; - /* Where boot and vendor_boot were loaded, and the partition sizes */ + u32 init_boot_img_size; ulong boot_addr; ulong boot_part_size; ulong vendor_boot_addr; ulong vendor_boot_part_size; + ulong init_boot_addr; + ulong init_boot_part_size; }; static int android_check(struct udevice *dev, struct bootflow_iter *iter) @@ -71,7 +84,25 @@ static int android_check(struct udevice *dev, struct bootflow_iter *iter) return 0; } -static int scan_boot_part(struct udevice *blk, struct android_priv *priv) +/** + * scan_image_part() - Read and check the header of an Android image partition + * + * @blk: Block device holding the partition + * @name: Partition name without the slot suffix + * @slot: Nul-terminated slot suffix ("a\0") or NULL without A/B + * @hdr_size: Size of the image header to read + * @is_hdr: Checks the magic of the header + * @get_size: Computes the image size from the header + * @img_sizep: Returns the image size + * @hdr_versionp: If not NULL, returns the boot image header version + * Return: 0 if OK, -ENOENT if the partition does not exist, other negative + * errno on failure + */ +static int scan_image_part(struct udevice *blk, const char *name, + const char *slot, ulong hdr_size, + bool (*is_hdr)(const void *), + bool (*get_size)(const void *, u32 *), + u32 *img_sizep, u32 *hdr_versionp) { struct blk_desc *desc = dev_get_uclass_plat(blk); struct disk_partition partition; @@ -80,16 +111,16 @@ static int scan_boot_part(struct udevice *blk, struct android_priv *priv) char *buf; int ret; - if (priv->slot) - sprintf(partname, BOOT_PART_NAME "_%s", priv->slot); + if (slot) + snprintf(partname, sizeof(partname), "%s_%s", name, slot); else - sprintf(partname, BOOT_PART_NAME); + snprintf(partname, sizeof(partname), "%s", name); ret = part_get_info_by_name(desc, partname, &partition); if (ret < 0) - return log_msg_ret("part info", ret); + return log_msg_ret("part info", -ENOENT); - num_blks = DIV_ROUND_UP(sizeof(struct andr_boot_img_hdr_v0), desc->blksz); + num_blks = DIV_ROUND_UP(hdr_size, desc->blksz); bufsz = num_blks * desc->blksz; buf = malloc(bufsz); if (!buf) @@ -101,66 +132,53 @@ static int scan_boot_part(struct udevice *blk, struct android_priv *priv) return log_msg_ret("part read", -EIO); } - if (!is_android_boot_image_header(buf)) { + if (!is_hdr(buf)) { free(buf); - return log_msg_ret("header", -ENOENT); + return log_msg_ret("header", -EINVAL); } - if (!android_image_get_bootimg_size(buf, &priv->boot_img_size)) { + if (!get_size(buf, img_sizep)) { free(buf); - return log_msg_ret("get bootimg size", -EINVAL); + return log_msg_ret("get image size", -EINVAL); } - priv->header_version = ((struct andr_boot_img_hdr_v0 *)buf)->header_version; + if (hdr_versionp) + *hdr_versionp = ((struct andr_boot_img_hdr_v0 *)buf)->header_version; free(buf); return 0; } -static int scan_vendor_boot_part(struct udevice *blk, struct android_priv *priv) +static int scan_boot_part(struct udevice *blk, struct android_priv *priv) { - struct blk_desc *desc = dev_get_uclass_plat(blk); - struct disk_partition partition; - char partname[PART_NAME_LEN]; - ulong num_blks, bufsz; - char *buf; - int ret; - - if (priv->slot) - sprintf(partname, VENDOR_BOOT_PART_NAME "_%s", priv->slot); - else - sprintf(partname, VENDOR_BOOT_PART_NAME); - - ret = part_get_info_by_name(desc, partname, &partition); - if (ret < 0) - return log_msg_ret("part info", ret); - - num_blks = DIV_ROUND_UP(sizeof(struct andr_vnd_boot_img_hdr), desc->blksz); - bufsz = num_blks * desc->blksz; - buf = malloc(bufsz); - if (!buf) - return log_msg_ret("buf", -ENOMEM); - - ret = blk_read(blk, partition.start, num_blks, buf); - if (ret != num_blks) { - free(buf); - return log_msg_ret("part read", -EIO); - } - - if (!is_android_vendor_boot_image_header(buf)) { - free(buf); - return log_msg_ret("header", -ENOENT); - } - - if (!android_image_get_vendor_bootimg_size(buf, &priv->vendor_boot_img_size)) { - free(buf); - return log_msg_ret("get vendor bootimg size", -EINVAL); - } + return scan_image_part(blk, BOOT_PART_NAME, priv->slot, + sizeof(struct andr_boot_img_hdr_v0), + is_android_boot_image_header, + android_image_get_bootimg_size, + &priv->boot_img_size, &priv->header_version); +} - free(buf); +static int scan_vendor_boot_part(struct udevice *blk, struct android_priv *priv) +{ + return scan_image_part(blk, VENDOR_BOOT_PART_NAME, priv->slot, + sizeof(struct andr_vnd_boot_img_hdr), + is_android_vendor_boot_image_header, + android_image_get_vendor_bootimg_size, + &priv->vendor_boot_img_size, NULL); +} - return 0; +/* + * init_boot carries the generic ramdisk since Android 13 (boot image header + * version 4) and uses the boot image format, with a kernel size of 0. + */ +static int scan_init_boot_part(struct udevice *blk, struct android_priv *priv) +{ + return scan_image_part(blk, INIT_BOOT_PART_NAME, priv->slot, + sizeof(struct andr_boot_img_hdr_v0), + is_android_boot_image_header, + android_image_get_bootimg_size, + &priv->init_boot_img_size, NULL); } static int android_read_slot_from_bcb(struct bootflow *bflow, bool decrement) @@ -304,6 +322,22 @@ static int android_read_bootflow(struct udevice *dev, struct bootflow *bflow) } } + /* + * Header version 4 devices normally have init_boot, but a device may + * still keep the generic ramdisk in boot: a missing partition is not + * an error, a broken one is. + */ + if (priv->header_version >= 4) { + ret = scan_init_boot_part(bflow->blk, priv); + if (ret == -ENOENT) { + log_debug("no init_boot partition\n"); + priv->init_boot_img_size = 0; + } else if (ret < 0) { + log_debug("scan init_boot failed: err=%d\n", ret); + goto free_priv; + } + } + /* * Ignoring return code for the following configurations: * these are not mandatory for booting. @@ -442,7 +476,9 @@ static int run_avb_verification(struct bootflow *bflow) { struct blk_desc *desc = dev_get_uclass_plat(bflow->blk); struct android_priv *priv = bflow->bootmeth_priv; - const char * const requested_partitions[] = {"boot", "vendor_boot", NULL}; + /* boot, vendor_boot, init_boot and the terminator */ + const char *requested_partitions[4]; + int num_requested = 0; struct AvbOps *avb_ops; AvbSlotVerifyResult result; AvbSlotVerifyData *out_data = NULL; @@ -461,10 +497,21 @@ static int run_avb_verification(struct bootflow *bflow) sprintf(slot_suffix, "_%s", priv->slot); /* - * Hand libavb the buffers boot and vendor_boot were loaded into by + * Verify what gets booted: boot always, vendor_boot from header version + * 3 and init_boot when the device has one (header version 4). All of + * them are hash partitions in vbmeta. + */ + requested_partitions[num_requested++] = BOOT_PART_NAME; + if (priv->header_version >= 3) + requested_partitions[num_requested++] = VENDOR_BOOT_PART_NAME; + if (priv->init_boot_addr) + requested_partitions[num_requested++] = INIT_BOOT_PART_NAME; + requested_partitions[num_requested] = NULL; + + /* + * Hand libavb the buffers the images were loaded into by * boot_android_normal(), so the bytes it hashes are the bytes that get - * booted and the partitions are read only once. vendor_boot exists from - * boot image header version 3 on. + * booted and the partitions are read only once. */ snprintf(partname, sizeof(partname), "boot%s", slot_suffix); ret = avb_ops_add_preloaded(avb_ops, partname, @@ -486,6 +533,18 @@ static int run_avb_verification(struct bootflow *bflow) goto out; } } + if (priv->init_boot_addr) { + snprintf(partname, sizeof(partname), "init_boot%s", + slot_suffix); + ret = avb_ops_add_preloaded(avb_ops, partname, + map_sysmem(priv->init_boot_addr, 0), + priv->init_boot_img_size, + priv->init_boot_part_size); + if (ret) { + ret = log_msg_ret("avb preload init_boot", ret); + goto out; + } + } ret = avb_ops->read_is_device_unlocked(avb_ops, &unlocked); if (ret != AVB_IO_RESULT_OK) { @@ -602,6 +661,7 @@ static int boot_android_normal(struct bootflow *bflow) int ret; ulong loadaddr = env_get_hex("loadaddr", 0); ulong vloadaddr = env_get_hex("vendor_boot_comp_addr_r", 0); + ulong iloadaddr = env_get_hex("init_boot_comp_addr_r", 0); /* * Load the images first and verify them in place: libavb hashes these @@ -623,6 +683,19 @@ static int boot_android_normal(struct bootflow *bflow) priv->vendor_boot_addr = vloadaddr; } + if (priv->init_boot_img_size) { + if (!iloadaddr) { + log_err("init_boot_comp_addr_r is not set\n"); + return log_msg_ret("init_boot addr", -EINVAL); + } + ret = read_slotted_partition(desc, "init_boot", priv->slot, + priv->init_boot_img_size, iloadaddr, + &priv->init_boot_part_size); + if (ret < 0) + return log_msg_ret("read init_boot", ret); + priv->init_boot_addr = iloadaddr; + } + ret = run_avb_verification(bflow); if (ret < 0) return log_msg_ret("avb", ret); @@ -634,6 +707,8 @@ static int boot_android_normal(struct bootflow *bflow) if (priv->header_version >= 3) set_avendor_bootimg_addr(vloadaddr); + /* bootm takes the generic ramdisk from init_boot when set */ + set_ainit_bootimg_addr(priv->init_boot_addr ? iloadaddr : -1); set_abootimg_addr(loadaddr); if (priv->slot) diff --git a/cmd/abootimg.c b/cmd/abootimg.c index eae3e643b60..e8e6b8f5ced 100644 --- a/cmd/abootimg.c +++ b/cmd/abootimg.c @@ -33,6 +33,11 @@ ulong get_ainit_bootimg_addr(void) return _ainit_bootimg_addr; } +void set_ainit_bootimg_addr(ulong addr) +{ + _ainit_bootimg_addr = addr; +} + ulong get_avendor_bootimg_addr(void) { return _avendor_bootimg_addr; diff --git a/doc/develop/bootstd/overview.rst b/doc/develop/bootstd/overview.rst index eab44d0cdb2..058a97a6915 100644 --- a/doc/develop/bootstd/overview.rst +++ b/doc/develop/bootstd/overview.rst @@ -296,6 +296,10 @@ script_size_f vendor_boot_comp_addr_r Address to which to load the vendor_boot Android image, e.g. 0xe0000000 +init_boot_comp_addr_r + Address to which to load the init_boot Android image (boot image header + version 4 devices with an init_boot partition), e.g. 0xe4000000 + Some variables are set by script bootmeth: devtype diff --git a/include/image.h b/include/image.h index f059fc5811c..af977069451 100644 --- a/include/image.h +++ b/include/image.h @@ -2210,10 +2210,18 @@ void set_abootimg_addr(ulong addr); /** * get_ainit_bootimg_addr() - Get Android init boot image address * - * Return: Android init boot image address + * Return: Android init boot image address, -1 if there is none */ ulong get_ainit_bootimg_addr(void); +/** + * set_ainit_bootimg_addr() - Set Android init boot image address + * + * @addr: Address of the init_boot image, -1 if the ramdisk is in the boot + * image + */ +void set_ainit_bootimg_addr(ulong addr); + /** * get_avendor_bootimg_addr() - Get Android vendor boot image address * --- base-commit: c267fa81d42b5a5bd1055d3858f74496faa6082f change-id: 20261028-upstream-android_init_boot_b4-29d9a2471096 Best regards, -- Igor Opaniuk <[email protected]>
