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

Reply via email to