[PATCH] boot: android: Add support for the bootflow of Android 13+

Valentin Liu <[email protected]>
Newsgroups gmane.comp.boot-loaders.u-boot
Message-ID <20260813153626.1113424-1-valentinliu__36812.9048658133$1786636653$gmane$org@icloud.com>
The devices launching Android 13+ were using a new partition
named init_boot to store generic ramdisk.

In the new bootflow, kernel still be stored in boot image,
however, the First Stage files in ramdisk were moved to
init_boot image. We should load it to memory and verify it
so that the kernel can execute init program to continue booting.

Link: https://source.android.com/docs/core/architecture/partitions/generic-boot
Signed-off-by: Valentin Liu <[email protected]>
---
 boot/bootmeth_android.c          | 65 ++++++++++++++++++++++++++++++-
 boot/image-android.c             | 67 ++++++++++++++++++++++++++++++++
 cmd/abootimg.c                   |  6 +++
 doc/develop/bootstd/overview.rst |  3 ++
 include/android_image.h          |  1 +
 include/image.h                  | 59 ++++++++++++++++++++++++++++
 6 files changed, 200 insertions(+), 1 deletion(-)

diff --git a/boot/bootmeth_android.c b/boot/bootmeth_android.c
index ec255b072af..c11ef849c56 100644
--- a/boot/bootmeth_android.c
+++ b/boot/bootmeth_android.c
@@ -4,6 +4,7 @@
  *
  * Copyright (C) 2024 BayLibre, SAS
  * Written by Mattijs Korpershoek <[email protected]>
+ * Copyright (c) 2026 Valentin Liu <[email protected]>
  */
 #define LOG_CATEGORY UCLASS_BOOTSTD
 
@@ -29,6 +30,7 @@
 #define BCB_FIELD_COMMAND_SZ 32
 #define BCB_PART_NAME "misc"
 #define BOOT_PART_NAME "boot"
+#define INIT_BOOT_PART_NAME "init_boot"
 #define VENDOR_BOOT_PART_NAME "vendor_boot"
 #define SLOT_LEN 2
 
@@ -47,6 +49,7 @@ struct android_priv {
 	char *slot;
 	u32 header_version;
 	u32 boot_img_size;
+	u32 init_boot_img_size;
 	u32 vendor_boot_img_size;
 };
 
@@ -113,6 +116,51 @@ static int scan_boot_part(struct udevice *blk, struct android_priv *priv)
 	return 0;
 }
 
+static int scan_init_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, INIT_BOOT_PART_NAME "_%s", priv->slot);
+	else
+		sprintf(partname, INIT_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_boot_img_hdr_v3), 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_init_boot_image_header(buf)) {
+		free(buf);
+		return log_msg_ret("header", -ENOENT);
+	}
+
+	if (!android_image_get_init_bootimg_size(buf, &priv->init_boot_img_size)) {
+		free(buf);
+		return log_msg_ret("get init bootimg size", -EINVAL);
+	}
+
+	free(buf);
+
+	return 0;
+}
+
 static int scan_vendor_boot_part(struct udevice *blk, struct android_priv *priv)
 {
 	struct blk_desc *desc = dev_get_uclass_plat(blk);
@@ -292,6 +340,13 @@ static int android_read_bootflow(struct udevice *dev, struct bootflow *bflow)
 	}
 
 	if (priv->header_version >= 3) {
+		if (priv->header_version >= 4) {
+			ret = scan_init_boot_part(bflow->blk, priv);
+			if (ret < 0) {
+				log_debug("scan init_boot failed: err=%d\n", ret);
+				goto free_priv;
+			}
+		}
 		ret = scan_vendor_boot_part(bflow->blk, priv);
 		if (ret < 0) {
 			log_debug("scan vendor_boot failed: err=%d\n", ret);
@@ -425,7 +480,7 @@ 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};
+	const char * const requested_partitions[] = {"boot", "init_boot", "vendor_boot", NULL};
 	struct AvbOps *avb_ops;
 	AvbSlotVerifyResult result;
 	AvbSlotVerifyData *out_data = NULL;
@@ -556,6 +611,7 @@ static int boot_android_normal(struct bootflow *bflow)
 	struct android_priv *priv = bflow->bootmeth_priv;
 	int ret;
 	ulong loadaddr = env_get_hex("loadaddr", 0);
+	ulong iloadaddr = env_get_hex("init_boot_comp_addr_r", 0);
 	ulong vloadaddr = env_get_hex("vendor_boot_comp_addr_r", 0);
 
 	ret = run_avb_verification(bflow);
@@ -573,6 +629,13 @@ static int boot_android_normal(struct bootflow *bflow)
 		return log_msg_ret("read boot", ret);
 
 	if (priv->header_version >= 3) {
+		if (priv->header_version >= 4) {
+			ret = read_slotted_partition(desc, "init_boot", priv->slot,
+						     priv->init_boot_img_size, iloadaddr);
+			if (ret < 0)
+				return log_msg_ret("read init_boot", ret);
+			set_ainit_bootimg_addr(iloadaddr);
+		}
 		ret = read_slotted_partition(desc, "vendor_boot", priv->slot,
 					     priv->vendor_boot_img_size, vloadaddr);
 		if (ret < 0)
diff --git a/boot/image-android.c b/boot/image-android.c
index 7740cae8cb6..05eb7e9b6be 100644
--- a/boot/image-android.c
+++ b/boot/image-android.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright (c) 2011 Sebastian Andrzej Siewior <[email protected]>
+ * Copyright (c) 2026 Valentin Liu <[email protected]>
  */
 
 #include <env.h>
@@ -130,6 +131,28 @@ static void android_boot_image_v3_v4_parse_hdr(const struct andr_boot_img_hdr_v3
 	data->boot_img_total_size = end - map_to_sysmem(hdr);
 }
 
+static void android_init_boot_image_v4_parse_hdr(const struct andr_boot_img_hdr_v3 *hdr,
+						 struct andr_image_data *data)
+{
+	ulong end;
+
+	/*
+	 * The header takes a full page, the remaining components are aligned
+	 * on page boundary.
+	 */
+	end = (ulong)hdr;
+	end += ANDR_GKI_PAGE_SIZE;
+	end += ALIGN(hdr->kernel_size, ANDR_GKI_PAGE_SIZE);
+	data->ramdisk_ptr = end;
+	data->ramdisk_size = hdr->ramdisk_size;
+	data->boot_ramdisk_size = hdr->ramdisk_size;
+	end += ALIGN(hdr->ramdisk_size, ANDR_GKI_PAGE_SIZE);
+
+	end += ALIGN(hdr->signature_size, ANDR_GKI_PAGE_SIZE);
+
+	data->init_boot_img_total_size = end - (ulong)hdr;
+}
+
 static void android_vendor_boot_image_v3_v4_parse_hdr(const struct andr_vnd_boot_img_hdr
 						      *hdr, struct andr_image_data *data,
 						      bool write_trailer)
@@ -263,6 +286,27 @@ bool android_image_get_bootimg_size(const void *hdr, u32 *boot_img_size)
 	return true;
 }
 
+bool android_image_get_init_bootimg_size(const void *hdr, u32 *init_boot_img_size)
+{
+	struct andr_image_data data;
+
+	if (!hdr || !init_boot_img_size) {
+		printf("hdr or init_boot_img_size can't be NULL\n");
+		return false;
+	}
+
+	if (!is_android_init_boot_image_header(hdr)) {
+		printf("Incorrect init boot image header\n");
+		return false;
+	}
+
+	android_init_boot_image_v4_parse_hdr(hdr, &data);
+
+	*init_boot_img_size = data.init_boot_img_total_size;
+
+	return true;
+}
+
 bool android_image_get_vendor_bootimg_size(const void *hdr, u32 *vendor_boot_img_size)
 {
 	struct andr_image_data data;
@@ -326,6 +370,24 @@ bool android_image_get_data(const void *boot_hdr, const void *vendor_boot_hdr,
 	return true;
 }
 
+bool android_image_get_data_v4(const void *boot_hdr, const void *vendor_boot_hdr,
+			       const void *init_boot_hdr, struct andr_image_data *data)
+{
+	if (!android_image_get_data(boot_hdr, vendor_boot_hdr, data)) {
+		printf("An error happened while calling android_image_get_data().\n");
+		return false;
+	}
+
+	if (!is_android_init_boot_image_header(init_boot_hdr)) {
+		printf("Incorrect init boot image header\n");
+		return false;
+	}
+
+	android_init_boot_image_v4_parse_hdr(init_boot_hdr, data);
+
+	return true;
+}
+
 static ulong android_image_get_kernel_addr(struct andr_image_data *img_data,
 					   ulong comp)
 {
@@ -466,6 +528,11 @@ bool is_android_vendor_boot_image_header(const void *vendor_boot_img)
 	return !memcmp(VENDOR_BOOT_MAGIC, vendor_boot_img, ANDR_VENDOR_BOOT_MAGIC_SIZE);
 }
 
+bool is_android_init_boot_image_header(const void *init_boot_img)
+{
+	return !memcmp(ANDR_BOOT_MAGIC, init_boot_img, ANDR_BOOT_MAGIC_SIZE);
+}
+
 bool is_android_boot_image_header(const void *hdr)
 {
 	return !memcmp(ANDR_BOOT_MAGIC, hdr, ANDR_BOOT_MAGIC_SIZE);
diff --git a/cmd/abootimg.c b/cmd/abootimg.c
index eae3e643b60..b32872bba7f 100644
--- a/cmd/abootimg.c
+++ b/cmd/abootimg.c
@@ -2,6 +2,7 @@
 /*
  * (C) Copyright 2020
  * Sam Protsenko <[email protected]>
+ * Copyright (c) 2026 Valentin Liu <[email protected]>
  */
 
 #include <android_image.h>
@@ -33,6 +34,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 ec9fafa0fa0..f1306ae0a8f 100644
--- a/doc/develop/bootstd/overview.rst
+++ b/doc/develop/bootstd/overview.rst
@@ -293,6 +293,9 @@ script_offset_f
 script_size_f
     Size of the script to load, e.g. 0x2000
 
+init_boot_comp_addr_r
+    Address to which to load the init_boot Android image, e.g. 0xd0000000
+
 vendor_boot_comp_addr_r
     Address to which to load the vendor_boot Android image, e.g. 0xe0000000
 
diff --git a/include/android_image.h b/include/android_image.h
index a2d80499ba3..134b5ed74d6 100644
--- a/include/android_image.h
+++ b/include/android_image.h
@@ -357,6 +357,7 @@ struct andr_image_data {
 	ulong tags_addr;  /* physical addr for kernel tags */
 	u32 header_version;  /* version of the boot image header */
 	u32 boot_img_total_size;  /* boot image size */
+	u32 init_boot_img_total_size;  /* init boot image size */
 	u32 vendor_boot_img_total_size;  /* vendor boot image size */
 };
 
diff --git a/include/image.h b/include/image.h
index 6edcb1995bf..b323b5f880e 100644
--- a/include/image.h
+++ b/include/image.h
@@ -2015,6 +2015,30 @@ struct andr_image_data;
  */
 bool android_image_get_bootimg_size(const void *hdr, u32 *boot_img_size);
 
+/**
+ * android_image_get_init_bootimg_size() - Extract size of Android init_boot image
+ *
+ * This is used to extract the size of an Android init_boot image
+ * from init_boot image header.
+ *
+ * @hdr: Pointer to init_boot image header
+ * @init_boot_img_size: On exit returns the size in bytes of the init_boot image
+ * Return: true if succeeded, false otherwise
+ */
+bool android_image_get_bootimg_size(const void *hdr, u32 *init_boot_img_size);
+
+/**
+ * android_image_get_init_bootimg_size() - Extract size of Android init_boot image
+ *
+ * This is used to extract the size of an Android init_boot image
+ * from init_boot image header.
+ *
+ * @hdr: Pointer to init_boot image header
+ * @init_boot_img_size: On exit returns the size in bytes of the init_boot image
+ * Return: true if succeeded, false otherwise
+ */
+bool android_image_get_init_bootimg_size(const void *hdr, u32 *init_boot_img_size);
+
 /**
  * android_image_get_vendor_bootimg_size() - Extract size of Android vendor-boot image
  *
@@ -2041,6 +2065,23 @@ bool android_image_get_vendor_bootimg_size(const void *hdr, u32 *vendor_boot_img
 bool android_image_get_data(const void *boot_hdr, const void *vendor_boot_hdr,
 			    struct andr_image_data *data);
 
+/**
+ * android_image_get_data_v4() - Parse Android header version 4 boot images
+ *
+ * This is used to parse boot, vendor-boot and init boot header into
+ * andr_image_data generic structure.
+ * The Android 13 and newer has splited parameters from boot and
+ * vendor_boot images to the init_boot image.
+ *
+ * @boot_hdr: Pointer to boot image header
+ * @vendor_boot_hdr: Pointer to vendor boot image header
+ * @init_boot_hdr: Pointer to init boot image header
+ * @data: Pointer to generic boot format structure
+ * Return: true if succeeded, false otherwise
+ */
+bool android_image_get_data_v4(const void *boot_hdr, const void *vendor_boot_hdr,
+			       const void *init_boot_hdr, struct andr_image_data *data);
+
 struct andr_boot_img_hdr_v0;
 
 /**
@@ -2167,6 +2208,17 @@ bool android_image_print_dtb_contents(ulong hdr_addr);
  */
 bool is_android_boot_image_header(const void *hdr);
 
+/**
+ * is_android_init_boot_image_header() - Check the magic of init boot image
+ *
+ * This checks the header of Android init boot image and verifies the
+ * magic is "ANDROID!" (same with the boot image)
+ *
+ * @init_boot_img: Pointer to boot image
+ * Return: non-zero if the magic is correct, zero otherwise
+ */
+bool is_android_init_boot_image_header(const void *init_boot_img);
+
 /**
  * is_android_vendor_boot_image_header() - Check the magic of vendor boot image
  *
@@ -2199,6 +2251,13 @@ void set_abootimg_addr(ulong addr);
  */
 ulong get_ainit_bootimg_addr(void);
 
+/**
+ * set_ainit_bootimg_addr() - Set Android init boot image address
+ *
+ * Return: no returned results
+ */
+void set_ainit_bootimg_addr(ulong addr);
+
 /**
  * get_avendor_bootimg_addr() - Get Android vendor boot image address
  *
-- 
2.53.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.