diff options
author | Tom Rini <trini@konsulko.com> | 2023-04-05 10:40:05 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2023-04-05 10:40:05 -0400 |
commit | 25eeda170c5e533ca0e3837c8b2d7404cdd749d1 (patch) | |
tree | 3ed815ab6105a6655fe25c91dace40c47336f8c6 | |
parent | 698c2bd364ce4122a0d0db82b5a8d842186b2fa4 (diff) | |
parent | f4449397551a82f0c1d9714d648f1efb90d56962 (diff) | |
download | u-boot-25eeda170c5e533ca0e3837c8b2d7404cdd749d1.zip u-boot-25eeda170c5e533ca0e3837c8b2d7404cdd749d1.tar.gz u-boot-25eeda170c5e533ca0e3837c8b2d7404cdd749d1.tar.bz2 |
Merge branch '2023-04-04-android-image-v3-v4-support'
To quote the author:
* This is based on Roman Stratiienko's work to support boot image header version 3 and 4.
* This supports the new boot image headers v3, v4 and bootconfig feature.
https://source.android.com/docs/core/architecture/bootloader/boot-image-header
https://source.android.com/docs/core/architecture/bootloader/implementing-bootconfig
- Tested on Amlogic Khadas vim3l, a reference board for Android Open Source Project
https://www.khadas.com/vim3l
And on AM625 Texas Instruments board with 5.10 linux kernel
Main changes :
- New partition : vendor boot, with a specific vendor ramdisk
- DTB is stored in the vendor boot partition
- The generic ramdisk is placed after the vendor ramdisk
- Bootconfig feature support
Here is a link to see the related android boot flow changes on KHADAS vim3l as an example:
https://gitlab.baylibre.com/baylibre/amlogic/atv/u-boot/-/commits/souajih/BootImagev4/
-rw-r--r-- | boot/bootm.c | 37 | ||||
-rw-r--r-- | boot/image-android.c | 461 | ||||
-rw-r--r-- | boot/image-board.c | 19 | ||||
-rw-r--r-- | boot/image-fdt.c | 5 | ||||
-rw-r--r-- | cmd/abootimg.c | 75 | ||||
-rw-r--r-- | doc/android/boot-image.rst | 13 | ||||
-rw-r--r-- | drivers/fastboot/fb_mmc.c | 19 | ||||
-rw-r--r-- | include/android_image.h | 228 | ||||
-rw-r--r-- | include/image.h | 173 | ||||
-rw-r--r-- | test/py/tests/test_android/test_abootimg.py | 136 | ||||
-rw-r--r-- | tools/docker/Dockerfile | 4 |
11 files changed, 1009 insertions, 161 deletions
diff --git a/boot/bootm.c b/boot/bootm.c index 2eec60e..4144ff3 100644 --- a/boot/bootm.c +++ b/boot/bootm.c @@ -113,6 +113,10 @@ static int bootm_find_os(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { const void *os_hdr; +#ifdef CONFIG_ANDROID_BOOT_IMAGE + const void *vendor_boot_img; + const void *boot_img; +#endif bool ep_found = false; int ret; @@ -181,14 +185,23 @@ static int bootm_find_os(struct cmd_tbl *cmdtp, int flag, int argc, #endif #ifdef CONFIG_ANDROID_BOOT_IMAGE case IMAGE_FORMAT_ANDROID: + boot_img = os_hdr; + vendor_boot_img = NULL; + if (IS_ENABLED(CONFIG_CMD_ABOOTIMG)) { + boot_img = map_sysmem(get_abootimg_addr(), 0); + vendor_boot_img = map_sysmem(get_avendor_bootimg_addr(), 0); + } images.os.type = IH_TYPE_KERNEL; - images.os.comp = android_image_get_kcomp(os_hdr); + images.os.comp = android_image_get_kcomp(boot_img, vendor_boot_img); images.os.os = IH_OS_LINUX; - - images.os.end = android_image_get_end(os_hdr); - images.os.load = android_image_get_kload(os_hdr); + images.os.end = android_image_get_end(boot_img, vendor_boot_img); + images.os.load = android_image_get_kload(boot_img, vendor_boot_img); images.ep = images.os.load; ep_found = true; + if (IS_ENABLED(CONFIG_CMD_ABOOTIMG)) { + unmap_sysmem(vendor_boot_img); + unmap_sysmem(boot_img); + } break; #endif default: @@ -889,6 +902,10 @@ static const void *boot_get_kernel(struct cmd_tbl *cmdtp, int flag, int argc, int os_noffset; #endif +#ifdef CONFIG_ANDROID_BOOT_IMAGE + const void *boot_img; + const void *vendor_boot_img; +#endif img_addr = genimg_get_kernel_addr_fit(argc < 1 ? NULL : argv[0], &fit_uname_config, &fit_uname_kernel); @@ -964,10 +981,20 @@ static const void *boot_get_kernel(struct cmd_tbl *cmdtp, int flag, int argc, #endif #ifdef CONFIG_ANDROID_BOOT_IMAGE case IMAGE_FORMAT_ANDROID: + boot_img = buf; + vendor_boot_img = NULL; + if (IS_ENABLED(CONFIG_CMD_ABOOTIMG)) { + boot_img = map_sysmem(get_abootimg_addr(), 0); + vendor_boot_img = map_sysmem(get_avendor_bootimg_addr(), 0); + } printf("## Booting Android Image at 0x%08lx ...\n", img_addr); - if (android_image_get_kernel(buf, images->verify, + if (android_image_get_kernel(boot_img, vendor_boot_img, images->verify, os_data, os_len)) return NULL; + if (IS_ENABLED(CONFIG_CMD_ABOOTIMG)) { + unmap_sysmem(vendor_boot_img); + unmap_sysmem(boot_img); + } break; #endif default: diff --git a/boot/image-android.c b/boot/image-android.c index 2628db3..88e40bc 100644 --- a/boot/image-android.c +++ b/boot/image-android.c @@ -18,7 +18,193 @@ static char andr_tmp_str[ANDR_BOOT_ARGS_SIZE + 1]; -static ulong android_image_get_kernel_addr(const struct andr_img_hdr *hdr) +static ulong checksum(const unsigned char *buffer, ulong size) +{ + ulong sum = 0; + + for (ulong i = 0; i < size; i++) + sum += buffer[i]; + return sum; +} + +static bool is_trailer_present(ulong bootconfig_end_addr) +{ + return !strncmp((char *)(bootconfig_end_addr - BOOTCONFIG_MAGIC_SIZE), + BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_SIZE); +} + +static ulong add_trailer(ulong bootconfig_start_addr, ulong bootconfig_size) +{ + ulong end; + ulong sum; + + if (!bootconfig_start_addr) + return -1; + if (!bootconfig_size) + return 0; + + end = bootconfig_start_addr + bootconfig_size; + if (is_trailer_present(end)) + return 0; + + memcpy((void *)(end), &bootconfig_size, BOOTCONFIG_SIZE_SIZE); + sum = checksum((unsigned char *)bootconfig_start_addr, bootconfig_size); + memcpy((void *)(end + BOOTCONFIG_SIZE_SIZE), &sum, + BOOTCONFIG_CHECKSUM_SIZE); + memcpy((void *)(end + BOOTCONFIG_SIZE_SIZE + BOOTCONFIG_CHECKSUM_SIZE), + BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_SIZE); + + return BOOTCONFIG_TRAILER_SIZE; +} + +static void android_boot_image_v3_v4_parse_hdr(const struct andr_boot_img_hdr_v3 *hdr, + struct andr_image_data *data) +{ + ulong end; + + data->kcmdline = hdr->cmdline; + data->header_version = hdr->header_version; + data->ramdisk_ptr = env_get_ulong("ramdisk_addr_r", 16, 0); + + /* + * The header takes a full page, the remaining components are aligned + * on page boundary. + */ + end = (ulong)hdr; + end += ANDR_GKI_PAGE_SIZE; + data->kernel_ptr = end; + data->kernel_size = hdr->kernel_size; + end += ALIGN(hdr->kernel_size, ANDR_GKI_PAGE_SIZE); + data->ramdisk_size = hdr->ramdisk_size; + data->boot_ramdisk_size = hdr->ramdisk_size; + end += ALIGN(hdr->ramdisk_size, ANDR_GKI_PAGE_SIZE); + + if (hdr->header_version > 3) + end += ALIGN(hdr->signature_size, ANDR_GKI_PAGE_SIZE); + + data->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) +{ + ulong end; + + /* + * The header takes a full page, the remaining components are aligned + * on page boundary. + */ + data->kcmdline_extra = hdr->cmdline; + data->tags_addr = hdr->tags_addr; + data->image_name = hdr->name; + data->kernel_addr = hdr->kernel_addr; + data->ramdisk_addr = hdr->ramdisk_addr; + data->dtb_load_addr = hdr->dtb_addr; + data->bootconfig_size = hdr->bootconfig_size; + end = (ulong)hdr; + end += hdr->page_size; + if (hdr->vendor_ramdisk_size) { + data->vendor_ramdisk_ptr = end; + data->vendor_ramdisk_size = hdr->vendor_ramdisk_size; + data->ramdisk_size += hdr->vendor_ramdisk_size; + end += ALIGN(hdr->vendor_ramdisk_size, hdr->page_size); + } + + data->dtb_ptr = end; + data->dtb_size = hdr->dtb_size; + + end += ALIGN(hdr->dtb_size, hdr->page_size); + end += ALIGN(hdr->vendor_ramdisk_table_size, hdr->page_size); + data->bootconfig_addr = end; + if (hdr->bootconfig_size) { + data->bootconfig_size += add_trailer(data->bootconfig_addr, + data->bootconfig_size); + data->ramdisk_size += data->bootconfig_size; + } + end += ALIGN(data->bootconfig_size, hdr->page_size); + data->vendor_boot_img_total_size = end - (ulong)hdr; +} + +static void android_boot_image_v0_v1_v2_parse_hdr(const struct andr_boot_img_hdr_v0 *hdr, + struct andr_image_data *data) +{ + ulong end; + + data->image_name = hdr->name; + data->kcmdline = hdr->cmdline; + data->kernel_addr = hdr->kernel_addr; + data->ramdisk_addr = hdr->ramdisk_addr; + data->header_version = hdr->header_version; + data->dtb_load_addr = hdr->dtb_addr; + + end = (ulong)hdr; + + /* + * The header takes a full page, the remaining components are aligned + * on page boundary + */ + + end += hdr->page_size; + + data->kernel_ptr = end; + data->kernel_size = hdr->kernel_size; + end += ALIGN(hdr->kernel_size, hdr->page_size); + + data->ramdisk_ptr = end; + data->ramdisk_size = hdr->ramdisk_size; + end += ALIGN(hdr->ramdisk_size, hdr->page_size); + + data->second_ptr = end; + data->second_size = hdr->second_size; + end += ALIGN(hdr->second_size, hdr->page_size); + + if (hdr->header_version >= 1) { + data->recovery_dtbo_ptr = end; + data->recovery_dtbo_size = hdr->recovery_dtbo_size; + end += ALIGN(hdr->recovery_dtbo_size, hdr->page_size); + } + + if (hdr->header_version >= 2) { + data->dtb_ptr = end; + data->dtb_size = hdr->dtb_size; + end += ALIGN(hdr->dtb_size, hdr->page_size); + } + + data->boot_img_total_size = end - (ulong)hdr; +} + +bool android_image_get_data(const void *boot_hdr, const void *vendor_boot_hdr, + struct andr_image_data *data) +{ + if (!boot_hdr || !data) { + printf("boot_hdr or data params can't be NULL\n"); + return false; + } + + if (!is_android_boot_image_header(boot_hdr)) { + printf("Incorrect boot image header\n"); + return false; + } + + if (((struct andr_boot_img_hdr_v0 *)boot_hdr)->header_version > 2) { + if (!vendor_boot_hdr) { + printf("For boot header v3+ vendor boot image has to be provided\n"); + return false; + } + if (!is_android_vendor_boot_image_header(vendor_boot_hdr)) { + printf("Incorrect vendor boot image header\n"); + return false; + } + android_boot_image_v3_v4_parse_hdr(boot_hdr, data); + android_vendor_boot_image_v3_v4_parse_hdr(vendor_boot_hdr, data); + } else { + android_boot_image_v0_v1_v2_parse_hdr(boot_hdr, data); + } + + return true; +} + +static ulong android_image_get_kernel_addr(struct andr_image_data *img_data) { /* * All the Android tools that generate a boot.img use this @@ -31,23 +217,25 @@ static ulong android_image_get_kernel_addr(const struct andr_img_hdr *hdr) * * Otherwise, we will return the actual value set by the user. */ - if (hdr->kernel_addr == ANDROID_IMAGE_DEFAULT_KERNEL_ADDR) - return (ulong)hdr + hdr->page_size; + if (img_data->kernel_addr == ANDROID_IMAGE_DEFAULT_KERNEL_ADDR) + return img_data->kernel_ptr; /* * abootimg creates images where all load addresses are 0 * and we need to fix them. */ - if (hdr->kernel_addr == 0 && hdr->ramdisk_addr == 0) + if (img_data->kernel_addr == 0 && img_data->ramdisk_addr == 0) return env_get_ulong("kernel_addr_r", 16, 0); - return hdr->kernel_addr; + return img_data->kernel_addr; } /** * android_image_get_kernel() - processes kernel part of Android boot images - * @hdr: Pointer to image header, which is at the start + * @hdr: Pointer to boot image header, which is at the start * of the image. + * @vendor_boot_img: Pointer to vendor boot image header, which is at the + * start of the image. * @verify: Checksum verification flag. Currently unimplemented. * @os_data: Pointer to a ulong variable, will hold os data start * address. @@ -59,30 +247,42 @@ static ulong android_image_get_kernel_addr(const struct andr_img_hdr *hdr) * Return: Zero, os start address and length on success, * otherwise on failure. */ -int android_image_get_kernel(const struct andr_img_hdr *hdr, int verify, +int android_image_get_kernel(const void *hdr, + const void *vendor_boot_img, int verify, ulong *os_data, ulong *os_len) { - u32 kernel_addr = android_image_get_kernel_addr(hdr); - const struct legacy_img_hdr *ihdr = (const struct legacy_img_hdr *) - ((uintptr_t)hdr + hdr->page_size); + struct andr_image_data img_data = {0}; + u32 kernel_addr; + const struct legacy_img_hdr *ihdr; + + if (!android_image_get_data(hdr, vendor_boot_img, &img_data)) + return -EINVAL; + + kernel_addr = android_image_get_kernel_addr(&img_data); + ihdr = (const struct legacy_img_hdr *)img_data.kernel_ptr; /* * Not all Android tools use the id field for signing the image with * sha1 (or anything) so we don't check it. It is not obvious that the * string is null terminated so we take care of this. */ - strncpy(andr_tmp_str, hdr->name, ANDR_BOOT_NAME_SIZE); + strlcpy(andr_tmp_str, img_data.image_name, ANDR_BOOT_NAME_SIZE); andr_tmp_str[ANDR_BOOT_NAME_SIZE] = '\0'; if (strlen(andr_tmp_str)) printf("Android's image name: %s\n", andr_tmp_str); printf("Kernel load addr 0x%08x size %u KiB\n", - kernel_addr, DIV_ROUND_UP(hdr->kernel_size, 1024)); + kernel_addr, DIV_ROUND_UP(img_data.kernel_size, 1024)); int len = 0; - if (*hdr->cmdline) { - printf("Kernel command line: %s\n", hdr->cmdline); - len += strlen(hdr->cmdline); + if (*img_data.kcmdline) { + printf("Kernel command line: %s\n", img_data.kcmdline); + len += strlen(img_data.kcmdline); + } + + if (img_data.kcmdline_extra) { + printf("Kernel extra command line: %s\n", img_data.kcmdline_extra); + len += strlen(img_data.kcmdline_extra); } char *bootargs = env_get("bootargs"); @@ -100,8 +300,14 @@ int android_image_get_kernel(const struct andr_img_hdr *hdr, int verify, strcpy(newbootargs, bootargs); strcat(newbootargs, " "); } - if (*hdr->cmdline) - strcat(newbootargs, hdr->cmdline); + + if (*img_data.kcmdline) + strcat(newbootargs, img_data.kcmdline); + + if (img_data.kcmdline_extra) { + strcat(newbootargs, " "); + strcat(newbootargs, img_data.kcmdline_extra); + } env_set("bootargs", newbootargs); @@ -109,56 +315,63 @@ int android_image_get_kernel(const struct andr_img_hdr *hdr, int verify, if (image_get_magic(ihdr) == IH_MAGIC) { *os_data = image_get_data(ihdr); } else { - *os_data = (ulong)hdr; - *os_data += hdr->page_size; + *os_data = img_data.kernel_ptr; } } if (os_len) { if (image_get_magic(ihdr) == IH_MAGIC) *os_len = image_get_data_size(ihdr); else - *os_len = hdr->kernel_size; + *os_len = img_data.kernel_size; } return 0; } -int android_image_check_header(const struct andr_img_hdr *hdr) +bool is_android_vendor_boot_image_header(const void *vendor_boot_img) { - return memcmp(ANDR_BOOT_MAGIC, hdr->magic, ANDR_BOOT_MAGIC_SIZE); + return !memcmp(VENDOR_BOOT_MAGIC, vendor_boot_img, ANDR_VENDOR_BOOT_MAGIC_SIZE); } -ulong android_image_get_end(const struct andr_img_hdr *hdr) +bool is_android_boot_image_header(const void *hdr) { - ulong end; + return !memcmp(ANDR_BOOT_MAGIC, hdr, ANDR_BOOT_MAGIC_SIZE); +} - /* - * The header takes a full page, the remaining components are aligned - * on page boundary - */ - end = (ulong)hdr; - end += hdr->page_size; - end += ALIGN(hdr->kernel_size, hdr->page_size); - end += ALIGN(hdr->ramdisk_size, hdr->page_size); - end += ALIGN(hdr->second_size, hdr->page_size); +ulong android_image_get_end(const struct andr_boot_img_hdr_v0 *hdr, + const void *vendor_boot_img) +{ + struct andr_image_data img_data; - if (hdr->header_version >= 1) - end += ALIGN(hdr->recovery_dtbo_size, hdr->page_size); + if (!android_image_get_data(hdr, vendor_boot_img, &img_data)) + return -EINVAL; - if (hdr->header_version >= 2) - end += ALIGN(hdr->dtb_size, hdr->page_size); + if (img_data.header_version > 2) + return 0; - return end; + return img_data.boot_img_total_size; } -ulong android_image_get_kload(const struct andr_img_hdr *hdr) +ulong android_image_get_kload(const void *hdr, + const void *vendor_boot_img) { - return android_image_get_kernel_addr(hdr); + struct andr_image_data img_data; + + if (!android_image_get_data(hdr, vendor_boot_img, &img_data)) + return -EINVAL; + + return android_image_get_kernel_addr(&img_data); } -ulong android_image_get_kcomp(const struct andr_img_hdr *hdr) +ulong android_image_get_kcomp(const void *hdr, + const void *vendor_boot_img) { - const void *p = (void *)((uintptr_t)hdr + hdr->page_size); + struct andr_image_data img_data; + const void *p; + + if (!android_image_get_data(hdr, vendor_boot_img, &img_data)) + return -EINVAL; + p = (const void *)img_data.kernel_ptr; if (image_get_magic((struct legacy_img_hdr *)p) == IH_MAGIC) return image_get_comp((struct legacy_img_hdr *)p); else if (get_unaligned_le32(p) == LZ4F_MAGIC) @@ -167,41 +380,66 @@ ulong android_image_get_kcomp(const struct andr_img_hdr *hdr) return image_decomp_type(p, sizeof(u32)); } -int android_image_get_ramdisk(const struct andr_img_hdr *hdr, +int android_image_get_ramdisk(const void *hdr, const void *vendor_boot_img, ulong *rd_data, ulong *rd_len) { - if (!hdr->ramdisk_size) { + struct andr_image_data img_data = {0}; + ulong ramdisk_ptr; + + if (!android_image_get_data(hdr, vendor_boot_img, &img_data)) + return -EINVAL; + + if (!img_data.ramdisk_size) { *rd_data = *rd_len = 0; return -1; } + if (img_data.header_version > 2) { + ramdisk_ptr = img_data.ramdisk_ptr; + memcpy((void *)(ramdisk_ptr), (void *)img_data.vendor_ramdisk_ptr, + img_data.vendor_ramdisk_size); + memcpy((void *)(ramdisk_ptr + img_data.vendor_ramdisk_size), + (void *)img_data.ramdisk_ptr, + img_data.boot_ramdisk_size); + if (img_data.bootconfig_size) { + memcpy((void *) + (ramdisk_ptr + img_data.vendor_ramdisk_size + + img_data.boot_ramdisk_size), + (void *)img_data.bootconfig_addr, + img_data.bootconfig_size); + } + } - printf("RAM disk load addr 0x%08x size %u KiB\n", - hdr->ramdisk_addr, DIV_ROUND_UP(hdr->ramdisk_size, 1024)); + printf("RAM disk load addr 0x%08lx size %u KiB\n", + img_data.ramdisk_ptr, DIV_ROUND_UP(img_data.ramdisk_size, 1024)); - *rd_data = (unsigned long)hdr; - *rd_data += hdr->page_size; - *rd_data += ALIGN(hdr->kernel_size, hdr->page_size); + *rd_data = img_data.ramdisk_ptr; - *rd_len = hdr->ramdisk_size; + *rd_len = img_data.ramdisk_size; return 0; } -int android_image_get_second(const struct andr_img_hdr *hdr, - ulong *second_data, ulong *second_len) +int android_image_get_second(const void *hdr, ulong *second_data, ulong *second_len) { - if (!hdr->second_size) { + struct andr_image_data img_data; + + if (!android_image_get_data(hdr, NULL, &img_data)) + return -EINVAL; + + if (img_data.header_version > 2) { + printf("Second stage bootloader is only supported for boot image version <= 2\n"); + return -EOPNOTSUPP; + } + + if (!img_data.second_size) { *second_data = *second_len = 0; return -1; } - *second_data = (unsigned long)hdr; - *second_data += hdr->page_size; - *second_data += ALIGN(hdr->kernel_size, hdr->page_size); - *second_data += ALIGN(hdr->ramdisk_size, hdr->page_size); + *second_data = img_data.second_ptr; printf("second address is 0x%lx\n",*second_data); - *second_len = hdr->second_size; + *second_len = img_data.second_size; return 0; } @@ -226,19 +464,19 @@ int android_image_get_second(const struct andr_img_hdr *hdr, */ bool android_image_get_dtbo(ulong hdr_addr, ulong *addr, u32 *size) { - const struct andr_img_hdr *hdr; + const struct andr_boot_img_hdr_v0 *hdr; ulong dtbo_img_addr; bool ret = true; hdr = map_sysmem(hdr_addr, sizeof(*hdr)); - if (android_image_check_header(hdr)) { + if (!is_android_boot_image_header(hdr)) { printf("Error: Boot Image header is incorrect\n"); ret = false; goto exit; } - if (hdr->header_version < 1) { - printf("Error: header_version must be >= 1 to get dtbo\n"); + if (hdr->header_version != 1 && hdr->header_version != 2) { + printf("Error: header version must be >= 1 and <= 2 to get dtbo\n"); ret = false; goto exit; } @@ -269,18 +507,20 @@ exit: /** * android_image_get_dtb_img_addr() - Get the address of DTB area in boot image. * @hdr_addr: Boot image header address + * @vhdr_addr: Vendor Boot image header address * @addr: Will contain the address of DTB area in boot image * * Return: true on success or false on fail. */ -static bool android_image_get_dtb_img_addr(ulong hdr_addr, ulong *addr) +static bool android_image_get_dtb_img_addr(ulong hdr_addr, ulong vhdr_addr, ulong *addr) { - const struct andr_img_hdr *hdr; + const struct andr_boot_img_hdr_v0 *hdr; + const struct andr_vnd_boot_img_hdr *v_hdr; ulong dtb_img_addr; bool ret = true; hdr = map_sysmem(hdr_addr, sizeof(*hdr)); - if (android_image_check_header(hdr)) { + if (!is_android_boot_image_header(hdr)) { printf("Error: Boot Image header is incorrect\n"); ret = false; goto exit; @@ -292,22 +532,40 @@ static bool android_image_get_dtb_img_addr(ulong hdr_addr, ulong *addr) goto exit; } - if (hdr->dtb_size == 0) { - printf("Error: dtb_size is 0\n"); - ret = false; - goto exit; + if (hdr->header_version == 2) { + if (!hdr->dtb_size) { + printf("Error: dtb_size is 0\n"); + ret = false; + goto exit; + } + /* Calculate the address of DTB area in boot image */ + dtb_img_addr = hdr_addr; + dtb_img_addr += hdr->page_size; + dtb_img_addr += ALIGN(hdr->kernel_size, hdr->page_size); + dtb_img_addr += ALIGN(hdr->ramdisk_size, hdr->page_size); + dtb_img_addr += ALIGN(hdr->second_size, hdr->page_size); + dtb_img_addr += ALIGN(hdr->recovery_dtbo_size, hdr->page_size); + + *addr = dtb_img_addr; } - /* Calculate the address of DTB area in boot image */ - dtb_img_addr = hdr_addr; - dtb_img_addr += hdr->page_size; - dtb_img_addr += ALIGN(hdr->kernel_size, hdr->page_size); - dtb_img_addr += ALIGN(hdr->ramdisk_size, hdr->page_size); - dtb_img_addr += ALIGN(hdr->second_size, hdr->page_size); - dtb_img_addr += ALIGN(hdr->recovery_dtbo_size, hdr->page_size); - - *addr = dtb_img_addr; - + if (hdr->header_version > 2) { + v_hdr = map_sysmem(vhdr_addr, sizeof(*v_hdr)); + if (!v_hdr->dtb_size) { + printf("Error: dtb_size is 0\n"); + ret = false; + unmap_sysmem(v_hdr); + goto exit; + } + /* Calculate the address of DTB area in boot image */ + dtb_img_addr = vhdr_addr; + dtb_img_addr += v_hdr->page_size; + if (v_hdr->vendor_ramdisk_size) + dtb_img_addr += ALIGN(v_hdr->vendor_ramdisk_size, v_hdr->page_size); + *addr = dtb_img_addr; + unmap_sysmem(v_hdr); + goto exit; + } exit: unmap_sysmem(hdr); return ret; @@ -316,6 +574,7 @@ exit: /** * android_image_get_dtb_by_index() - Get address and size of blob in DTB area. * @hdr_addr: Boot image header address + * @vendor_boot_img: Pointer to vendor boot image header, which is at the start of the image. * @index: Index of desired DTB in DTB area (starting from 0) * @addr: If not NULL, will contain address to specified DTB * @size: If not NULL, will contain size of specified DTB @@ -325,20 +584,32 @@ exit: * * Return: true on success or false on error. */ -bool android_image_get_dtb_by_index(ulong hdr_addr, u32 index, ulong *addr, - u32 *size) +bool android_image_get_dtb_by_index(ulong hdr_addr, ulong vendor_boot_img, + u32 index, ulong *addr, u32 *size) { - const struct andr_img_hdr *hdr; - bool res; + struct andr_image_data img_data; + const struct andr_boot_img_hdr_v0 *hdr; + const struct andr_vnd_boot_img_hdr *vhdr; + + hdr = map_sysmem(hdr_addr, sizeof(*hdr)); + if (vendor_boot_img != -1) + vhdr = map_sysmem(vendor_boot_img, sizeof(*vhdr)); + if (!android_image_get_data(hdr, vhdr, &img_data)) { + if (vendor_boot_img != -1) + unmap_sysmem(vhdr); + unmap_sysmem(hdr); + return false; + } + if (vendor_boot_img != -1) + unmap_sysmem(vhdr); + unmap_sysmem(hdr); + ulong dtb_img_addr; /* address of DTB part in boot image */ u32 dtb_img_size; /* size of DTB payload in boot image */ ulong dtb_addr; /* address of DTB blob with specified index */ u32 i; /* index iterator */ - res = android_image_get_dtb_img_addr(hdr_addr, &dtb_img_addr); - if (!res) - return false; - + android_image_get_dtb_img_addr(hdr_addr, vendor_boot_img, &dtb_img_addr); /* Check if DTB area of boot image is in DTBO format */ if (android_dt_check_header(dtb_img_addr)) { return android_dt_get_fdt_by_index(dtb_img_addr, index, addr, @@ -346,9 +617,7 @@ bool android_image_get_dtb_by_index(ulong hdr_addr, u32 index, ulong *addr, } /* Find out the address of DTB with specified index in concat blobs */ - hdr = map_sysmem(hdr_addr, sizeof(*hdr)); - dtb_img_size = hdr->dtb_size; - unmap_sysmem(hdr); + dtb_img_size = img_data.dtb_size; i = 0; dtb_addr = dtb_img_addr; while (dtb_addr < dtb_img_addr + dtb_img_size) { @@ -393,8 +662,12 @@ bool android_image_get_dtb_by_index(ulong hdr_addr, u32 index, ulong *addr, * returns: * no returned results */ -void android_print_contents(const struct andr_img_hdr *hdr) +void android_print_contents(const struct andr_boot_img_hdr_v0 *hdr) { + if (hdr->header_version >= 3) { + printf("Content print is not supported for boot image header version > 2"); + return; + } const char * const p = IMAGE_INDENT_STRING; /* os_version = ver << 11 | lvl */ u32 os_ver = hdr->os_version >> 11; @@ -427,7 +700,7 @@ void android_print_contents(const struct andr_img_hdr *hdr) hdr->header_size); } - if (hdr->header_version >= 2) { + if (hdr->header_version == 2) { printf("%sdtb size: %x\n", p, hdr->dtb_size); printf("%sdtb addr: %llx\n", p, hdr->dtb_addr); } @@ -485,14 +758,14 @@ static bool android_image_print_dtb_info(const struct fdt_header *fdt, */ bool android_image_print_dtb_contents(ulong hdr_addr) { - const struct andr_img_hdr *hdr; + const struct andr_boot_img_hdr_v0 *hdr; bool res; ulong dtb_img_addr; /* address of DTB part in boot image */ u32 dtb_img_size; /* size of DTB payload in boot image */ ulong dtb_addr; /* address of DTB blob with specified index */ u32 i; /* index iterator */ - res = android_image_get_dtb_img_addr(hdr_addr, &dtb_img_addr); + res = android_image_get_dtb_img_addr(hdr_addr, 0, &dtb_img_addr); if (!res) return false; diff --git a/boot/image-board.c b/boot/image-board.c index 9bf7082..7dd0c32 100644 --- a/boot/image-board.c +++ b/boot/image-board.c @@ -284,7 +284,7 @@ int genimg_get_format(const void *img_addr) return IMAGE_FORMAT_FIT; } if (IS_ENABLED(CONFIG_ANDROID_BOOT_IMAGE) && - !android_image_check_header(img_addr)) + is_android_boot_image_header(img_addr)) return IMAGE_FORMAT_ANDROID; return IMAGE_FORMAT_INVALID; @@ -426,11 +426,22 @@ static int select_ramdisk(struct bootm_headers *images, const char *select, u8 a break; case IMAGE_FORMAT_ANDROID: if (IS_ENABLED(CONFIG_ANDROID_BOOT_IMAGE)) { - void *ptr = map_sysmem(images->os.start, 0); int ret; + if (IS_ENABLED(CONFIG_CMD_ABOOTIMG)) { + void *boot_img = map_sysmem(get_abootimg_addr(), 0); + void *vendor_boot_img = map_sysmem(get_avendor_bootimg_addr(), 0); + + ret = android_image_get_ramdisk(boot_img, vendor_boot_img, + rd_datap, rd_lenp); + unmap_sysmem(vendor_boot_img); + unmap_sysmem(boot_img); + } else { + void *ptr = map_sysmem(images->os.start, 0); + + ret = android_image_get_ramdisk(ptr, NULL, rd_datap, rd_lenp); + unmap_sysmem(ptr); + } - ret = android_image_get_ramdisk(ptr, rd_datap, rd_lenp); - unmap_sysmem(ptr); if (ret) return ret; done = true; diff --git a/boot/image-fdt.c b/boot/image-fdt.c index 714d05d..f10200f 100644 --- a/boot/image-fdt.c +++ b/boot/image-fdt.c @@ -529,14 +529,15 @@ int boot_get_fdt(int flag, int argc, char *const argv[], uint8_t arch, } #ifdef CONFIG_ANDROID_BOOT_IMAGE } else if (genimg_get_format(buf) == IMAGE_FORMAT_ANDROID) { - struct andr_img_hdr *hdr = buf; + void *hdr = buf; ulong fdt_data, fdt_len; u32 fdt_size, dtb_idx; /* * Firstly check if this android boot image has dtb field. */ dtb_idx = (u32)env_get_ulong("adtb_idx", 10, 0); - if (android_image_get_dtb_by_index((ulong)hdr, dtb_idx, &fdt_addr, &fdt_size)) { + if (android_image_get_dtb_by_index((ulong)hdr, 0, + dtb_idx, &fdt_addr, &fdt_size)) { fdt_blob = (char *)map_sysmem(fdt_addr, 0); if (fdt_check_header(fdt_blob)) goto no_fdt; diff --git a/cmd/abootimg.c b/cmd/abootimg.c index f48a9dc..2653b55 100644 --- a/cmd/abootimg.c +++ b/cmd/abootimg.c @@ -15,17 +15,28 @@ /* Please use abootimg_addr() macro to obtain the boot image address */ static ulong _abootimg_addr = -1; +static ulong _avendor_bootimg_addr = -1; + +ulong get_abootimg_addr(void) +{ + return (_abootimg_addr == -1 ? image_load_addr : _abootimg_addr); +} + +ulong get_avendor_bootimg_addr(void) +{ + return _avendor_bootimg_addr; +} static int abootimg_get_ver(int argc, char *const argv[]) { - const struct andr_img_hdr *hdr; + const struct andr_boot_img_hdr_v0 *hdr; int res = CMD_RET_SUCCESS; if (argc > 1) return CMD_RET_USAGE; hdr = map_sysmem(abootimg_addr(), sizeof(*hdr)); - if (android_image_check_header(hdr)) { + if (!is_android_boot_image_header(hdr)) { printf("Error: Boot Image header is incorrect\n"); res = CMD_RET_FAILURE; goto exit; @@ -65,33 +76,43 @@ static int abootimg_get_recovery_dtbo(int argc, char *const argv[]) static int abootimg_get_dtb_load_addr(int argc, char *const argv[]) { - const struct andr_img_hdr *hdr; - int res = CMD_RET_SUCCESS; - if (argc > 1) return CMD_RET_USAGE; + struct andr_image_data img_data = {0}; + const struct andr_boot_img_hdr_v0 *hdr; + const struct andr_vnd_boot_img_hdr *vhdr; hdr = map_sysmem(abootimg_addr(), sizeof(*hdr)); - if (android_image_check_header(hdr)) { - printf("Error: Boot Image header is incorrect\n"); - res = CMD_RET_FAILURE; - goto exit; + if (get_avendor_bootimg_addr() != -1) + vhdr = map_sysmem(get_avendor_bootimg_addr(), sizeof(*vhdr)); + + if (!android_image_get_data(hdr, vhdr, &img_data)) { + if (get_avendor_bootimg_addr() != -1) + unmap_sysmem(vhdr); + unmap_sysmem(hdr); + return CMD_RET_FAILURE; } - if (hdr->header_version < 2) { + if (get_avendor_bootimg_addr() != -1) + unmap_sysmem(vhdr); + unmap_sysmem(hdr); + + if (img_data.header_version < 2) { printf("Error: header_version must be >= 2 for this\n"); - res = CMD_RET_FAILURE; - goto exit; + return CMD_RET_FAILURE; + } + + if (!img_data.dtb_load_addr) { + printf("Error: failed to read dtb_load_addr\n"); + return CMD_RET_FAILURE; } if (argc == 0) - printf("%lx\n", (ulong)hdr->dtb_addr); + printf("%lx\n", (ulong)img_data.dtb_load_addr); else - env_set_hex(argv[0], (ulong)hdr->dtb_addr); + env_set_hex(argv[0], (ulong)img_data.dtb_load_addr); -exit: - unmap_sysmem(hdr); - return res; + return CMD_RET_SUCCESS; } static int abootimg_get_dtb_by_index(int argc, char *const argv[]) @@ -117,7 +138,8 @@ static int abootimg_get_dtb_by_index(int argc, char *const argv[]) return CMD_RET_FAILURE; } - if (!android_image_get_dtb_by_index(abootimg_addr(), num, + if (!android_image_get_dtb_by_index(abootimg_addr(), + get_avendor_bootimg_addr(), num, &addr, &size)) { return CMD_RET_FAILURE; } @@ -158,7 +180,7 @@ static int do_abootimg_addr(struct cmd_tbl *cmdtp, int flag, int argc, char *endp; ulong img_addr; - if (argc != 2) + if (argc < 2 || argc > 3) return CMD_RET_USAGE; img_addr = hextoul(argv[1], &endp); @@ -168,6 +190,17 @@ static int do_abootimg_addr(struct cmd_tbl *cmdtp, int flag, int argc, } _abootimg_addr = img_addr; + + if (argc == 3) { + img_addr = simple_strtoul(argv[2], &endp, 16); + if (*endp != '\0') { + printf("Error: Wrong vendor image address\n"); + return CMD_RET_FAILURE; + } + + _avendor_bootimg_addr = img_addr; + } + return CMD_RET_SUCCESS; } @@ -211,7 +244,7 @@ static int do_abootimg_dump(struct cmd_tbl *cmdtp, int flag, int argc, } static struct cmd_tbl cmd_abootimg_sub[] = { - U_BOOT_CMD_MKENT(addr, 2, 1, do_abootimg_addr, "", ""), + U_BOOT_CMD_MKENT(addr, 3, 1, do_abootimg_addr, "", ""), U_BOOT_CMD_MKENT(dump, 2, 1, do_abootimg_dump, "", ""), U_BOOT_CMD_MKENT(get, 5, 1, do_abootimg_get, "", ""), }; @@ -239,7 +272,7 @@ static int do_abootimg(struct cmd_tbl *cmdtp, int flag, int argc, U_BOOT_CMD( abootimg, CONFIG_SYS_MAXARGS, 0, do_abootimg, "manipulate Android Boot Image", - "addr <addr>\n" + "addr <boot_img_addr> [<vendor_boot_img_addr>]>\n" " - set the address in RAM where boot image is located\n" " ($loadaddr is used by default)\n" "abootimg dump dtb\n" diff --git a/doc/android/boot-image.rst b/doc/android/boot-image.rst index 71db025..c719b4d 100644 --- a/doc/android/boot-image.rst +++ b/doc/android/boot-image.rst @@ -27,11 +27,21 @@ next image headers: * v2: used in devices launched with Android 10; adds ``dtb`` field, which references payload containing DTB blobs (either concatenated one after the other, or in Android DTBO image format) +* v3: used in devices launched with Android 11; adds ``vendor_boot`` partition + and removes the second-stage bootloader and recovery image support. The new + ``vendor_boot`` partition holds the device tree blob (DTB) and a vendor ramdisk. + The generic ramdisk in ``boot`` partition is loaded immediately following + the vendor ramdisk. +* v4: used in devices launched with Android 12; provides a boot signature in boot + image header, supports multiple vendor ramdisk fragments in ``vendor_boot`` + partition. This version also adds a bootconfig section at the end of the vendor + boot image, this section contains boot configuration parameters known at build time + (see [9]_ for details). v2, v1 and v0 formats are backward compatible. The Android Boot Image format is represented by -:c:type:`struct andr_img_hdr <andr_img_hdr>` in U-Boot, and can be seen in +:c:type:`struct andr_image_data <andr_image_data>` in U-Boot, and can be seen in ``include/android_image.h``. U-Boot supports booting Android Boot Image and also has associated command @@ -153,3 +163,4 @@ References .. [6] :doc:`avb2` .. [7] https://source.android.com/devices/bootloader .. [8] https://connect.linaro.org/resources/san19/san19-217/ +.. [9] https://source.android.com/docs/core/architecture/bootloader/implementing-bootconfig diff --git a/drivers/fastboot/fb_mmc.c b/drivers/fastboot/fb_mmc.c index a06c590..9d25c40 100644 --- a/drivers/fastboot/fb_mmc.c +++ b/drivers/fastboot/fb_mmc.c @@ -287,7 +287,7 @@ static void fb_mmc_boot_ops(struct blk_desc *dev_desc, void *buffer, */ static lbaint_t fb_mmc_get_boot_header(struct blk_desc *dev_desc, struct disk_partition *info, - struct andr_img_hdr *hdr, + struct andr_boot_img_hdr_v0 *hdr, char *response) { ulong sector_size; /* boot partition sector size */ @@ -296,7 +296,7 @@ static lbaint_t fb_mmc_get_boot_header(struct blk_desc *dev_desc, /* Calculate boot image sectors count */ sector_size = info->blksz; - hdr_sectors = DIV_ROUND_UP(sizeof(struct andr_img_hdr), sector_size); + hdr_sectors = DIV_ROUND_UP(sizeof(struct andr_boot_img_hdr_v0), sector_size); if (hdr_sectors == 0) { pr_err("invalid number of boot sectors: 0\n"); fastboot_fail("invalid number of boot sectors: 0", response); @@ -313,8 +313,7 @@ static lbaint_t fb_mmc_get_boot_header(struct blk_desc *dev_desc, } /* Check boot header magic string */ - res = android_image_check_header(hdr); - if (res != 0) { + if (!is_android_boot_image_header(hdr)) { pr_err("bad boot image magic\n"); fastboot_fail("boot partition not initialized", response); return 0; @@ -338,7 +337,7 @@ static int fb_mmc_update_zimage(struct blk_desc *dev_desc, char *response) { uintptr_t hdr_addr; /* boot image header address */ - struct andr_img_hdr *hdr; /* boot image header */ + struct andr_boot_img_hdr_v0 *hdr; /* boot image header */ lbaint_t hdr_sectors; /* boot image header sectors */ u8 *ramdisk_buffer; u32 ramdisk_sector_start; @@ -361,7 +360,7 @@ static int fb_mmc_update_zimage(struct blk_desc *dev_desc, /* Put boot image header in fastboot buffer after downloaded zImage */ hdr_addr = (uintptr_t)download_buffer + ALIGN(download_bytes, PAGE_SIZE); - hdr = (struct andr_img_hdr *)hdr_addr; + hdr = (struct andr_boot_img_hdr_v0 *)hdr_addr; /* Read boot image header */ hdr_sectors = fb_mmc_get_boot_header(dev_desc, &info, hdr, response); @@ -371,6 +370,14 @@ static int fb_mmc_update_zimage(struct blk_desc *dev_desc, return -1; } + /* Check if boot image header version is 2 or less */ + if (hdr->header_version > 2) { + pr_err("zImage flashing supported only for boot images v2 and less\n"); + fastboot_fail("zImage flashing supported only for boot images v2 and less", + response); + return -EOPNOTSUPP; + } + /* Check if boot image has second stage in it (we don't support it) */ if (hdr->second_size > 0) { pr_err("moving second stage is not supported yet\n"); diff --git a/include/android_image.h b/include/android_image.h index 54d25af..d503c98 100644 --- a/include/android_image.h +++ b/include/android_image.h @@ -3,7 +3,7 @@ * This is from the Android Project, * Repository: https://android.googlesource.com/platform/system/tools/mkbootimg * File: include/bootimg/bootimg.h - * Commit: e55998a0f2b61b685d5eb4a486ca3a0c680b1a2f + * Commit: cce5b1923e3cd2fcb765b512610bdc5c42bc501d * * Copyright (C) 2007 The Android Open Source Project */ @@ -14,15 +14,70 @@ #include <linux/compiler.h> #include <linux/types.h> +#define ANDR_GKI_PAGE_SIZE 4096 #define ANDR_BOOT_MAGIC "ANDROID!" #define ANDR_BOOT_MAGIC_SIZE 8 #define ANDR_BOOT_NAME_SIZE 16 #define ANDR_BOOT_ARGS_SIZE 512 #define ANDR_BOOT_EXTRA_ARGS_SIZE 1024 +#define VENDOR_BOOT_MAGIC "VNDRBOOT" +#define ANDR_VENDOR_BOOT_MAGIC_SIZE 8 +#define ANDR_VENDOR_BOOT_ARGS_SIZE 2048 +#define ANDR_VENDOR_BOOT_NAME_SIZE 16 -/* The bootloader expects the structure of andr_img_hdr with header +#define BOOTCONFIG_MAGIC "#BOOTCONFIG\n" +#define BOOTCONFIG_MAGIC_SIZE 12 +#define BOOTCONFIG_SIZE_SIZE 4 +#define BOOTCONFIG_CHECKSUM_SIZE 4 +#define BOOTCONFIG_TRAILER_SIZE BOOTCONFIG_MAGIC_SIZE + \ + BOOTCONFIG_SIZE_SIZE + \ + BOOTCONFIG_CHECKSUM_SIZE + +struct andr_boot_img_hdr_v3 { + u8 magic[ANDR_BOOT_MAGIC_SIZE]; + + u32 kernel_size; /* size in bytes */ + u32 ramdisk_size; /* size in bytes */ + + u32 os_version; + + u32 header_size; /* size of boot image header in bytes */ + u32 reserved[4]; + u32 header_version; /* offset remains constant for version check */ + + u8 cmdline[ANDR_BOOT_ARGS_SIZE + ANDR_BOOT_EXTRA_ARGS_SIZE]; + /* for boot image header v4 only */ + u32 signature_size; /* size in bytes */ +}; + +struct andr_vnd_boot_img_hdr { + u8 magic[ANDR_VENDOR_BOOT_MAGIC_SIZE]; + u32 header_version; + u32 page_size; /* flash page size we assume */ + + u32 kernel_addr; /* physical load addr */ + u32 ramdisk_addr; /* physical load addr */ + + u32 vendor_ramdisk_size; /* size in bytes */ + + u8 cmdline[ANDR_VENDOR_BOOT_ARGS_SIZE]; + + u32 tags_addr; /* physical addr for kernel tags */ + + u8 name[ANDR_VENDOR_BOOT_NAME_SIZE]; /* asciiz product name */ + u32 header_size; /* size of vendor boot image header in bytes */ + u32 dtb_size; /* size of dtb image */ + u64 dtb_addr; /* physical load address */ + /* for boot image header v4 only */ + u32 vendor_ramdisk_table_size; /* size in bytes for the vendor ramdisk table */ + u32 vendor_ramdisk_table_entry_num; /* number of entries in the vendor ramdisk table */ + u32 vendor_ramdisk_table_entry_size; /* size in bytes for a vendor ramdisk table entry */ + u32 bootconfig_size; /* size in bytes for the bootconfig section */ +}; + +/* The bootloader expects the structure of andr_boot_img_hdr_v0 with header * version 0 to be as follows: */ -struct andr_img_hdr { +struct andr_boot_img_hdr_v0 { /* Must be ANDR_BOOT_MAGIC. */ char magic[ANDR_BOOT_MAGIC_SIZE]; @@ -136,4 +191,171 @@ struct andr_img_hdr { * else: jump to kernel_addr */ +/* When the boot image header has a version of 3, the structure of the boot + * image is as follows: + * + * +---------------------+ + * | boot header | 4096 bytes + * +---------------------+ + * | kernel | m pages + * +---------------------+ + * | ramdisk | n pages + * +---------------------+ + * + * m = (kernel_size + 4096 - 1) / 4096 + * n = (ramdisk_size + 4096 - 1) / 4096 + * + * Note that in version 3 of the boot image header, page size is fixed at 4096 bytes. + * + * The structure of the vendor boot image (introduced with version 3 and + * required to be present when a v3 boot image is used) is as follows: + * + * +---------------------+ + * | vendor boot header | o pages + * +---------------------+ + * | vendor ramdisk | p pages + * +---------------------+ + * | dtb | q pages + * +---------------------+ + * o = (2112 + page_size - 1) / page_size + * p = (vendor_ramdisk_size + page_size - 1) / page_size + * q = (dtb_size + page_size - 1) / page_size + * + * 0. all entities in the boot image are 4096-byte aligned in flash, all + * entities in the vendor boot image are page_size (determined by the vendor + * and specified in the vendor boot image header) aligned in flash + * 1. kernel, ramdisk, vendor ramdisk, and DTB are required (size != 0) + * 2. load the kernel and DTB at the specified physical address (kernel_addr, + * dtb_addr) + * 3. load the vendor ramdisk at ramdisk_addr + * 4. load the generic ramdisk immediately following the vendor ramdisk in + * memory + * 5. set up registers for kernel entry as required by your architecture + * 6. if the platform has a second stage bootloader jump to it (must be + * contained outside boot and vendor boot partitions), otherwise + * jump to kernel_addr + */ + +/* When the boot image header has a version of 4, the structure of the boot + * image is as follows: + * + * +---------------------+ + * | boot header | 4096 bytes + * +---------------------+ + * | kernel | m pages + * +---------------------+ + * | ramdisk | n pages + * +---------------------+ + * | boot signature | g pages + * +---------------------+ + * + * m = (kernel_size + 4096 - 1) / 4096 + * n = (ramdisk_size + 4096 - 1) / 4096 + * g = (signature_size + 4096 - 1) / 4096 + * + * Note that in version 4 of the boot image header, page size is fixed at 4096 + * bytes. + * + * The structure of the vendor boot image version 4, which is required to be + * present when a version 4 boot image is used, is as follows: + * + * +------------------------+ + * | vendor boot header | o pages + * +------------------------+ + * | vendor ramdisk section | p pages + * +------------------------+ + * | dtb | q pages + * +------------------------+ + * | vendor ramdisk table | r pages + * +------------------------+ + * | bootconfig | s pages + * +------------------------+ + * + * o = (2128 + page_size - 1) / page_size + * p = (vendor_ramdisk_size + page_size - 1) / page_size + * q = (dtb_size + page_size - 1) / page_size + * r = (vendor_ramdisk_table_size + page_size - 1) / page_size + * s = (vendor_bootconfig_size + page_size - 1) / page_size + * + * Note that in version 4 of the vendor boot image, multiple vendor ramdisks can + * be included in the vendor boot image. The bootloader can select a subset of + * ramdisks to load at runtime. To help the bootloader select the ramdisks, each + * ramdisk is tagged with a type tag and a set of hardware identifiers + * describing the board, soc or platform that this ramdisk is intended for. + * + * The vendor ramdisk section is consist of multiple ramdisk images concatenated + * one after another, and vendor_ramdisk_size is the size of the section, which + * is the total size of all the ramdisks included in the vendor boot image. + * + * The vendor ramdisk table holds the size, offset, type, name and hardware + * identifiers of each ramdisk. The type field denotes the type of its content. + * The vendor ramdisk names are unique. The hardware identifiers are specified + * in the board_id field in each table entry. The board_id field is consist of a + * vector of unsigned integer words, and the encoding scheme is defined by the + * hardware vendor. + * + * For the different type of ramdisks, there are: + * - VENDOR_RAMDISK_TYPE_NONE indicates the value is unspecified. + * - VENDOR_RAMDISK_TYPE_PLATFORM ramdisks contain platform specific bits, so + * the bootloader should always load these into memory. + * - VENDOR_RAMDISK_TYPE_RECOVERY ramdisks contain recovery resources, so + * the bootloader should load these when booting into recovery. + * - VENDOR_RAMDISK_TYPE_DLKM ramdisks contain dynamic loadable kernel + * modules. + * + * Version 4 of the vendor boot image also adds a bootconfig section to the end + * of the image. This section contains Boot Configuration parameters known at + * build time. The bootloader is responsible for placing this section directly + * after the generic ramdisk, followed by the bootconfig trailer, before + * entering the kernel. + * + * 0. all entities in the boot image are 4096-byte aligned in flash, all + * entities in the vendor boot image are page_size (determined by the vendor + * and specified in the vendor boot image header) aligned in flash + * 1. kernel, ramdisk, and DTB are required (size != 0) + * 2. load the kernel and DTB at the specified physical address (kernel_addr, + * dtb_addr) + * 3. load the vendor ramdisks at ramdisk_addr + * 4. load the generic ramdisk immediately following the vendor ramdisk in + * memory + * 5. load the bootconfig immediately following the generic ramdisk. Add + * additional bootconfig parameters followed by the bootconfig trailer. + * 6. set up registers for kernel entry as required by your architecture + * 7. if the platform has a second stage bootloader jump to it (must be + * contained outside boot and vendor boot partitions), otherwise + * jump to kernel_addr + */ + +/* Private struct */ +struct andr_image_data { + ulong kernel_ptr; /* kernel address */ + u32 kernel_size; /* size in bytes */ + u32 ramdisk_size; /* size in bytes */ + ulong vendor_ramdisk_ptr; /* vendor ramdisk address */ + u32 vendor_ramdisk_size; /* vendor ramdisk size*/ + u32 boot_ramdisk_size; /* size in bytes */ + ulong second_ptr; /* secondary bootloader address */ + u32 second_size; /* secondary bootloader size */ + ulong dtb_ptr; /* address of dtb image */ + u32 dtb_size; /* size of dtb image */ + ulong recovery_dtbo_ptr; /* size in bytes for recovery DTBO/ACPIO image */ + u32 recovery_dtbo_size; /* offset to recovery dtbo/acpio in boot image */ + + const char *kcmdline; /* boot kernel cmdline */ + const char *kcmdline_extra; /* vendor-boot extra kernel cmdline */ + const char *image_name; /* asciiz product name */ + + ulong bootconfig_addr; /* bootconfig image address */ + ulong bootconfig_size; /* bootconfig image size */ + + u32 kernel_addr; /* physical load addr */ + ulong ramdisk_addr; /* physical load addr */ + ulong ramdisk_ptr; /* ramdisk address */ + ulong dtb_load_addr; /* physical load address for DTB image */ + 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 vendor_boot_img_total_size; /* vendor boot image size */ +}; + #endif diff --git a/include/image.h b/include/image.h index 7717a4c..456197d 100644 --- a/include/image.h +++ b/include/image.h @@ -1733,25 +1733,174 @@ struct cipher_algo { int fit_image_cipher_get_algo(const void *fit, int noffset, char **algo); struct cipher_algo *image_get_cipher_algo(const char *full_name); +struct andr_image_data; -struct andr_img_hdr; -int android_image_check_header(const struct andr_img_hdr *hdr); -int android_image_get_kernel(const struct andr_img_hdr *hdr, int verify, +/** + * android_image_get_data() - Parse Android boot images + * + * This is used to parse boot and vendor-boot header into + * andr_image_data generic structure. + * + * @boot_hdr: Pointer to boot image header + * @vendor_boot_hdr: Pointer to vendor boot image header + * @data: Pointer to generic boot format structure + * Return: true if succeeded, false otherwise + */ +bool android_image_get_data(const void *boot_hdr, const void *vendor_boot_hdr, + struct andr_image_data *data); + +struct andr_boot_img_hdr_v0; + +/** + * android_image_get_kernel() - Processes kernel part of Android boot images + * + * This function returns the os image's start address and length. Also, + * it appends the kernel command line to the bootargs env variable. + * + * @hdr: Pointer to image header, which is at the start + * of the image. + * @vendor_boot_img : Pointer to vendor boot image header + * @verify: Checksum verification flag. Currently unimplemented. + * @os_data: Pointer to a ulong variable, will hold os data start + * address. + * @os_len: Pointer to a ulong variable, will hold os data length. + * Return: Zero, os start address and length on success, + * otherwise on failure. + */ +int android_image_get_kernel(const void *hdr, + const void *vendor_boot_img, int verify, ulong *os_data, ulong *os_len); -int android_image_get_ramdisk(const struct andr_img_hdr *hdr, + +/** + * android_image_get_ramdisk() - Extracts the ramdisk load address and its size + * + * This extracts the load address of the ramdisk and its size + * + * @hdr: Pointer to image header + * @vendor_boot_img : Pointer to vendor boot image header + * @rd_data: Pointer to a ulong variable, will hold ramdisk address + * @rd_len: Pointer to a ulong variable, will hold ramdisk length + * Return: 0 if succeeded, -1 if ramdisk size is 0 + */ +int android_image_get_ramdisk(const void *hdr, const void *vendor_boot_img, ulong *rd_data, ulong *rd_len); -int android_image_get_second(const struct andr_img_hdr *hdr, - ulong *second_data, ulong *second_len); + +/** + * android_image_get_second() - Extracts the secondary bootloader address + * and its size + * + * This extracts the address of the secondary bootloader and its size + * + * @hdr: Pointer to image header + * @second_data: Pointer to a ulong variable, will hold secondary bootloader address + * @second_len : Pointer to a ulong variable, will hold secondary bootloader length + * Return: 0 if succeeded, -1 if secondary bootloader size is 0 + */ +int android_image_get_second(const void *hdr, ulong *second_data, ulong *second_len); bool android_image_get_dtbo(ulong hdr_addr, ulong *addr, u32 *size); -bool android_image_get_dtb_by_index(ulong hdr_addr, u32 index, ulong *addr, - u32 *size); -ulong android_image_get_end(const struct andr_img_hdr *hdr); -ulong android_image_get_kload(const struct andr_img_hdr *hdr); -ulong android_image_get_kcomp(const struct andr_img_hdr *hdr); -void android_print_contents(const struct andr_img_hdr *hdr); + +/** + * android_image_get_dtb_by_index() - Get address and size of blob in DTB area. + * @hdr_addr: Boot image header address + * @vendor_boot_img: Pointer to vendor boot image header, which is at the start of the image. + * @index: Index of desired DTB in DTB area (starting from 0) + * @addr: If not NULL, will contain address to specified DTB + * @size: If not NULL, will contain size of specified DTB + * + * Get the address and size of DTB blob by its index in DTB area of Android + * Boot Image in RAM. + * + * Return: true on success or false on error. + */ +bool android_image_get_dtb_by_index(ulong hdr_addr, ulong vendor_boot_img, + u32 index, ulong *addr, u32 *size); + +/** + * android_image_get_end() - Get the end of Android boot image + * + * This returns the end address of Android boot image address + * + * @hdr: Pointer to image header + * @vendor_boot_img : Pointer to vendor boot image header + * Return: The end address of Android boot image + */ +ulong android_image_get_end(const struct andr_boot_img_hdr_v0 *hdr, + const void *vendor_boot_img); + +/** + * android_image_get_kload() - Get the kernel load address + * + * This returns the kernel load address. The load address is extracted + * from the boot image header or the "kernel_addr_r" environment variable + * + * @hdr: Pointer to image header + * @vendor_boot_img : Pointer to vendor boot image header + * Return: The kernel load address + */ +ulong android_image_get_kload(const void *hdr, + const void *vendor_boot_img); + +/** + * android_image_get_kcomp() - Get kernel compression type + * + * This gets the kernel compression type from the boot image header + * + * @hdr: Pointer to image header + * @vendor_boot_img : Pointer to vendor boot image header + * Return: Kernel compression type + */ +ulong android_image_get_kcomp(const void *hdr, + const void *vendor_boot_img); + +/** + * android_print_contents() - Prints out the contents of the Android format image + * + * This formats a multi line Android image contents description. + * The routine prints out Android image properties + * + * @hdr: Pointer to the Android format image header + * Return: no returned results + */ +void android_print_contents(const struct andr_boot_img_hdr_v0 *hdr); bool android_image_print_dtb_contents(ulong hdr_addr); /** + * is_android_boot_image_header() - Check the magic of boot image + * + * This checks the header of Android boot image and verifies the + * magic is "ANDROID!" + * + * @hdr: Pointer to boot image + * Return: non-zero if the magic is correct, zero otherwise + */ +bool is_android_boot_image_header(const void *hdr); + +/** + * is_android_vendor_boot_image_header() - Check the magic of vendor boot image + * + * This checks the header of Android vendor boot image and verifies the magic + * is "VNDRBOOT" + * + * @vendor_boot_img: Pointer to boot image + * Return: non-zero if the magic is correct, zero otherwise + */ +bool is_android_vendor_boot_image_header(const void *vendor_boot_img); + +/** + * get_abootimg_addr() - Get Android boot image address + * + * Return: Android boot image address + */ +ulong get_abootimg_addr(void); + +/** + * get_avendor_bootimg_addr() - Get Android vendor boot image address + * + * Return: Android vendor boot image address + */ +ulong get_avendor_bootimg_addr(void); + +/** * board_fit_config_name_match() - Check for a matching board name * * This is used when SPL loads a FIT containing multiple device tree files diff --git a/test/py/tests/test_android/test_abootimg.py b/test/py/tests/test_android/test_abootimg.py index 43a7099..6a8ff34 100644 --- a/test/py/tests/test_android/test_abootimg.py +++ b/test/py/tests/test_android/test_abootimg.py @@ -32,6 +32,23 @@ Now one can obtain original boot.img from this hex dump like this: $ xxd -r -p boot.img.gz.hex boot.img.gz $ gunzip -9 boot.img.gz + +For boot image header version 4, these tests rely on two images that are generated +using the same steps above : + +1- boot.img : + $ mkbootimg --kernel ./kernel --ramdisk ./ramdisk.img \ + --cmdline "cmdline test" --dtb ./dtb.img \ + --os_version R --os_patch_level 2019-06-05 \ + --header_version 4 --output ./boot.img + +2- vendor_boot.img + $ mkbootimg --kernel ./kernel --ramdisk ./ramdisk.img \ + --cmdline "cmdline test" --dtb ./dtb.img \ + --os_version R --os_patch_level 2019-06-05 \ + --pagesize 4096 --vendor_ramdisk ./ramdisk.img \ + --header_version 4 --vendor_boot ./vboot.img \ + """ # boot.img.gz hex dump @@ -44,6 +61,24 @@ b7762ffff07d345446c1281805e8a0868d81e117a45e111c0d8dc101b253 9c03c41a0c90f17fe85400986d82452b6c3680198a192a0ce17c3610ae34 d4a9820881a70f3873f35352731892f3730b124b32937252a96bb9119ae5 463a5546f82c1f05a360148c8251300a462e000085bf67f200200000""" + +# boot img v4 hex dump +boot_img_hex = """1f8b080827b0cd630203626f6f742e696d6700edd8bd0d82601885d1d7c4 +58d8c808b88195bd098d8d246e40e42b083f1aa0717be99d003d277916b8 +e5bddc8a7b792d8e8788c896ce9b88d32ebe6c971e7ddd3543cae734cd01 +c0ffc84c0000b0766d1a87d4e5afeadd3dab7a6f10000000f84163d5d7cd +d43a000000000000000060c53e7544995700400000""" + +# vendor boot image v4 hex dump +vboot_img_hex = """1f8b0808baaecd63020376626f6f742e696d6700edd8310b824018c6f1b3 +222a08f41b3436b4280dcdd19c11d16ee9109d18d59042d047ec8b04cd0d +d19d5a4345534bf6ffc173ef29272f38e93b1d0ec67dd79d548462aa1cd2 +d5d20b0000f8438678f90c18d584b8a4bbb3a557991ecb2a0000f80d6b2f +f4179b656be5c532f2fc066f040000000080e23936af2755f62a3d918df1 +db2a7ab67f9ffdeb7df7cda3465ecb79c4ce7e5c577562bb9364b74449a5 +1e467e20c53c0a57de763193c1779b3b4fcd9d4ee27c6a0e00000000c0ff +309ffea7010000000040f1dc004129855400400000""" + # Expected response for "abootimg dtb_dump" command dtb_dump_resp="""## DTB area contents (concat format): - DTB #0: @@ -56,15 +91,21 @@ dtb_dump_resp="""## DTB area contents (concat format): (DTB)compatible = y2,z2""" # Address in RAM where to load the boot image ('abootimg' looks in $loadaddr) loadaddr = 0x1000 +# Address in RAM where to load the vendor boot image ('abootimg' looks in $vloadaddr) +vloadaddr= 0x10000 # Expected DTB #1 offset from the boot image start address dtb1_offset = 0x187d +# Expected DTB offset from the vendor boot image start address +dtb2_offset = 0x207d # DTB #1 start address in RAM dtb1_addr = loadaddr + dtb1_offset +# DTB #2 start address in RAM +dtb2_addr = vloadaddr + dtb2_offset class AbootimgTestDiskImage(object): """Disk image used by abootimg tests.""" - def __init__(self, u_boot_console): + def __init__(self, u_boot_console, image_name, hex_img): """Initialize a new AbootimgDiskImage object. Args: @@ -74,13 +115,13 @@ class AbootimgTestDiskImage(object): Nothing. """ - gz_hex = u_boot_console.config.persistent_data_dir + '/boot.img.gz.hex' - gz = u_boot_console.config.persistent_data_dir + '/boot.img.gz' + gz_hex = u_boot_console.config.persistent_data_dir + '/' + image_name + '.gz.hex' + gz = u_boot_console.config.persistent_data_dir + '/' + image_name + '.gz' - filename = 'boot.img' + filename = image_name persistent = u_boot_console.config.persistent_data_dir + '/' + filename self.path = u_boot_console.config.result_dir + '/' + filename - + u_boot_console.log.action('persistent is ' + persistent) with u_boot_utils.persistent_file_helper(u_boot_console.log, persistent): if os.path.exists(persistent): u_boot_console.log.action('Disk image file ' + persistent + @@ -89,19 +130,17 @@ class AbootimgTestDiskImage(object): u_boot_console.log.action('Generating ' + persistent) f = open(gz_hex, "w") - f.write(img_hex) + f.write(hex_img) f.close() - cmd = ('xxd', '-r', '-p', gz_hex, gz) u_boot_utils.run_and_log(u_boot_console, cmd) - cmd = ('gunzip', '-9', gz) u_boot_utils.run_and_log(u_boot_console, cmd) cmd = ('cp', persistent, self.path) u_boot_utils.run_and_log(u_boot_console, cmd) -gtdi = None +gtdi1 = None @pytest.fixture(scope='function') def abootimg_disk_image(u_boot_console): """pytest fixture to provide a AbootimgTestDiskImage object to tests. @@ -109,10 +148,36 @@ def abootimg_disk_image(u_boot_console): function-scoped. However, we don't need to actually do any function-scope work, so this simply returns the same object over and over each time.""" - global gtdi - if not gtdi: - gtdi = AbootimgTestDiskImage(u_boot_console) - return gtdi + global gtdi1 + if not gtdi1: + gtdi1 = AbootimgTestDiskImage(u_boot_console, 'boot.img', img_hex) + return gtdi1 + +gtdi2 = None +@pytest.fixture(scope='function') +def abootimgv4_disk_image_vboot(u_boot_console): + """pytest fixture to provide a AbootimgTestDiskImage object to tests. + This is function-scoped because it uses u_boot_console, which is also + function-scoped. However, we don't need to actually do any function-scope + work, so this simply returns the same object over and over each time.""" + + global gtdi2 + if not gtdi2: + gtdi2 = AbootimgTestDiskImage(u_boot_console, 'vendor_boot.img', vboot_img_hex) + return gtdi2 + +gtdi3 = None +@pytest.fixture(scope='function') +def abootimgv4_disk_image_boot(u_boot_console): + """pytest fixture to provide a AbootimgTestDiskImage object to tests. + This is function-scoped because it uses u_boot_console, which is also + function-scoped. However, we don't need to actually do any function-scope + work, so this simply returns the same object over and over each time.""" + + global gtdi3 + if not gtdi3: + gtdi3 = AbootimgTestDiskImage(u_boot_console, 'bootv4.img', boot_img_hex) + return gtdi3 @pytest.mark.boardspec('sandbox') @pytest.mark.buildconfigspec('android_boot_image') @@ -157,3 +222,48 @@ def test_abootimg(abootimg_disk_image, u_boot_console): u_boot_console.run_command('fdt get value v / model') response = u_boot_console.run_command('env print v') assert response == 'v=x2' + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('android_boot_image') +@pytest.mark.buildconfigspec('cmd_abootimg') +@pytest.mark.buildconfigspec('cmd_fdt') +@pytest.mark.requiredtool('xxd') +@pytest.mark.requiredtool('gunzip') +def test_abootimgv4(abootimgv4_disk_image_vboot, abootimgv4_disk_image_boot, u_boot_console): + """Test the 'abootimg' command with boot image header v4.""" + + cons = u_boot_console + cons.log.action('Loading disk image to RAM...') + cons.run_command('setenv loadaddr 0x%x' % (loadaddr)) + cons.run_command('setenv vloadaddr 0x%x' % (vloadaddr)) + cons.run_command('host load hostfs - 0x%x %s' % (vloadaddr, + abootimgv4_disk_image_vboot.path)) + cons.run_command('host load hostfs - 0x%x %s' % (loadaddr, + abootimgv4_disk_image_boot.path)) + cons.run_command('abootimg addr 0x%x 0x%x' % (loadaddr, vloadaddr)) + cons.log.action('Testing \'abootimg get ver\'...') + response = cons.run_command('abootimg get ver') + assert response == "4" + cons.run_command('abootimg get ver v') + response = cons.run_command('env print v') + assert response == 'v=4' + + cons.log.action('Testing \'abootimg get recovery_dtbo\'...') + response = cons.run_command('abootimg get recovery_dtbo a') + assert response == 'Error: header version must be >= 1 and <= 2 to get dtbo' + + cons.log.action('Testing \'abootimg get dtb_load_addr\'...') + cons.run_command('abootimg get dtb_load_addr a') + response = cons.run_command('env print a') + assert response == 'a=11f00000' + + cons.log.action('Testing \'abootimg get dtb --index\'...') + cons.run_command('abootimg get dtb --index=1 dtb2_start') + response = cons.run_command('env print dtb2_start') + correct_str = "dtb2_start=%x" % (dtb2_addr) + assert response == correct_str + + cons.run_command('fdt addr $dtb2_start') + cons.run_command('fdt get value v / model') + response = cons.run_command('env print v') + assert response == 'v=x2' diff --git a/tools/docker/Dockerfile b/tools/docker/Dockerfile index 801bebf..9804b55 100644 --- a/tools/docker/Dockerfile +++ b/tools/docker/Dockerfile @@ -284,3 +284,7 @@ RUN /bin/echo -e "\n[toolchain-prefix]\nxtensa = /opt/2020.07/xtensa-dc233c-elf/ RUN /bin/echo -e "\n[toolchain-alias]\nsh = sh2" >> ~/.buildman RUN /bin/echo -e "\nsandbox = x86_64" >> ~/.buildman RUN /bin/echo -e "\nx86 = i386" >> ~/.buildman; + +# Add mkbootimg tool +RUN git clone https://android.googlesource.com/platform/system/tools/mkbootimg /home/uboot/mkbootimg +ENV PYTHONPATH "${PYTHONPATH}:/home/uboot/mkbootimg" |