// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2011 Sebastian Andrzej Siewior */ #include #include #include #include #include #include #include #include #include #include #define ANDROID_IMAGE_DEFAULT_KERNEL_ADDR 0x10008000 int do_sunxi_flash(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]); struct boot_img_hdr *get_init_boot_hdr_addr(const struct andr_img_hdr *hdr, uint part_offset, uint part_size); static char andr_tmp_str[ANDR_BOOT_ARGS_SIZE + 1]; static struct vendor_boot_img_hdr *vendor_hdr; static struct boot_img_hdr *init_boot_hdr; /* static char img_cmdline[ANDR_BOOT_ARGS_SIZE + ANDR_BOOT_EXTRA_ARGS_SIZE]; */ static char *img_cmdline; int android_image_is_vendor_bootconfig_used( const struct vendor_boot_img_hdr *vendor_hdr) { return vendor_hdr->vendor_bootconfig_size > 0; } int android_image_get_vendor_bootconfig(const struct andr_img_hdr *hdr, ulong *rd_data, ulong *rd_len) { ulong bconfig_start; ulong bconfig_len; if ((!strncmp(hdr->magic, ANDR_BOOT_MAGIC, ANDR_BOOT_MAGIC_SIZE)) && (hdr->unused >= 0x3)) { vendor_hdr = get_vendor_hdr_addr(); if (vendor_hdr == 0) { pr_err("err: vendor_hdr=0\n"); return -1; } if (!android_image_is_vendor_bootconfig_used(vendor_hdr)) { pr_err("vendor bootconfig is not use"); return -1; } bconfig_start = (unsigned long)vendor_hdr; bconfig_start += ALIGN(VENDOR_BOOT_HEAD_SIZE, vendor_hdr->page_size) + ALIGN(vendor_hdr->vendor_ramdisk_size, vendor_hdr->page_size) + ALIGN(vendor_hdr->dtb_size, vendor_hdr->page_size) + ALIGN(vendor_hdr->vendor_ramdisk_table_size, vendor_hdr->page_size); bconfig_len = vendor_hdr->vendor_bootconfig_size; bconfig_len += addBootConfigTrailer(bconfig_start, bconfig_len); *rd_data = bconfig_start; *rd_len = bconfig_len; return 0; } return -1; } int android_image_get_vendor_get_end(const struct vendor_boot_img_hdr *vendor_hdr) { uint end = -1; if (strncmp((const char *)vendor_hdr->magic, VENDOR_BOOT_MAGIC, VENDOR_BOOT_MAGIC_SIZE)) { pr_err("vendor magic err\n"); return -1; } end = (unsigned long)vendor_hdr; end += ALIGN(VENDOR_BOOT_HEAD_SIZE, vendor_hdr->page_size); end += ALIGN(vendor_hdr->vendor_ramdisk_size, vendor_hdr->page_size); end += ALIGN(vendor_hdr->dtb_size, vendor_hdr->page_size); if (android_image_is_vendor_bootconfig_used(vendor_hdr)) { end += ALIGN(vendor_hdr->vendor_ramdisk_table_size, vendor_hdr->page_size); end += ALIGN(vendor_hdr->vendor_bootconfig_size, vendor_hdr->page_size); } /* tick_printf("vendor end:0x%x\n", end); */ return end; } struct vendor_boot_img_hdr *get_vendor_hdr_addr(void) { static int already_vendor; uint part_offset, part_size; if (!already_vendor) { already_vendor = 1; if (sunxi_partition_get_info_byname("vendor_boot", &part_offset, &part_size)) { pr_err("no %s partition is found\n", "vendor_boot"); return NULL; } else { #ifdef CONFIG_SUNXI_VENDOR_BOOT_ADDR sunxi_flash_read(part_offset, ALIGN(VENDOR_BOOT_HEAD_SIZE, 2048)/512, (char *)CONFIG_SUNXI_VENDOR_BOOT_ADDR); part_size = android_image_get_vendor_get_end((const struct vendor_boot_img_hdr *)CONFIG_SUNXI_VENDOR_BOOT_ADDR); if (part_size > 0) { /* part_size = vendor_boot_end - vendor_boot_start*/ part_size = ALIGN(part_size - CONFIG_SUNXI_VENDOR_BOOT_ADDR, 512)/512; /* pr_err("part_offset:0x%x, part_size:0x%x\n", part_offset, part_size); */ sunxi_flash_read(part_offset, part_size, (char *)CONFIG_SUNXI_VENDOR_BOOT_ADDR); vendor_hdr = (vendor_boot_img_hdr *)CONFIG_SUNXI_VENDOR_BOOT_ADDR; } else { vendor_hdr = NULL; } #else return NULL; #endif } } return vendor_hdr; } #ifdef CONFIG_SUNXI_ANDROID_OVERLAY int dtbo_idx[10]; int strtoint(char *str) { char *p = str; int val = 0; while (*p != '\0') { val = val * 10 + (*p - '0'); p++; } return val; } void get_andriod_dtbo_idx(const char *cmdline, const char *name) { char value[10] = {0}; char *p = NULL; int i = 0; int count = 0; if (cmdline == NULL) { pr_msg("%s cmdline is NULL", __func__); return; } memset((void *)dtbo_idx, 0xf5, sizeof(dtbo_idx)); p = (char *)cmdline; /* compare if it is the last character int the string*/ while (*p != '\0') { /*compare if the end of each key*/ if ((*p == ' ') || (*p == 0xa)) { /* Find the address of next key in the string */ p++; /* Search for the specified key:name */ if (!strncmp(p, name, strlen(name))) { p += strlen(name); /* Find the address offset of the value of key:name in the string */ if (*p++ != '=') { continue; } /* compare if it is the end of key:name */ while ((*p != '\0') && (*p != ' ') && (*p != 0xa)) { if (*p != ',') { value[i] = *p; i++; } else { value[i] = '\0'; i = 0; /*save vaule to dtbo_idx*/ dtbo_idx[count] = strtoint(value); pr_msg("line:%d dtbo_idx= %d\n", __LINE__, dtbo_idx[count]); count++; } p++; } value[i] = '\0'; i = 0; dtbo_idx[count] = strtoint(value); pr_msg("dtbo_idx= %d\n", __LINE__, dtbo_idx[count]); return; } } else { p++; } } pr_msg("%s can't find", name); return; } #endif static ulong android_image_get_kernel_addr(const struct andr_img_hdr *hdr) { /* * All the Android tools that generate a boot.img use this * address as the default. * * Even though it doesn't really make a lot of sense, and it * might be valid on some platforms, we treat that adress as * the default value for this field, and try to execute the * kernel in place in such a case. * * Otherwise, we will return the actual value set by the user. */ if ((!strncmp(hdr->magic, ANDR_BOOT_MAGIC, ANDR_BOOT_MAGIC_SIZE)) && (hdr->unused >= 0x3)) { vendor_hdr = get_vendor_hdr_addr(); if (vendor_hdr != NULL) { return vendor_hdr->kernel_addr; } else { return 0; } } else { if (hdr->kernel_addr == ANDROID_IMAGE_DEFAULT_KERNEL_ADDR) return (ulong)hdr + hdr->page_size; return hdr->kernel_addr; } } /** * android_image_get_kernel() - processes kernel part of Android boot images * @hdr: Pointer to 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. * @os_len: Pointer to a ulong variable, will hold os data length. * * This function returns the os image's start address and length. Also, * it appends the kernel command line to the bootargs env variable. * * 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, ulong *os_data, ulong *os_len) { u32 kernel_addr = android_image_get_kernel_addr(hdr); struct boot_img_hdr *hdr_v3; u32 page_size = hdr->page_size; u32 kernel_size = hdr->kernel_size; char *name = (char *)hdr->name; img_cmdline = (char *)hdr->cmdline; ulong bconfig_start; ulong bconfig_len; char end_flag = 0; if ((!strncmp(hdr->magic, ANDR_BOOT_MAGIC, ANDR_BOOT_MAGIC_SIZE)) && (hdr->unused >= 0x3)) { vendor_hdr = get_vendor_hdr_addr(); hdr_v3 = (boot_img_hdr *)hdr; if (android_image_is_vendor_bootconfig_used(vendor_hdr)) { android_image_get_vendor_bootconfig(hdr, &bconfig_start, &bconfig_len); img_cmdline = (char *)bconfig_start; end_flag = 0xa; } else { img_cmdline = (char *)vendor_hdr->cmdline; end_flag = ' '; } if (vendor_hdr != NULL) { page_size = vendor_hdr->page_size*2; name = (char *)vendor_hdr->name; } kernel_size = ALIGN(hdr_v3->kernel_size, page_size); } /* * 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. */ #ifdef CONFIG_SUNXI_ANDROID_OVERLAY char *dtboidx = env_get("dtbo_idx"); /* if env set dtbo_idx, replace image dtbo_idx, and update cmdline. */ /* * |... dtbo_idx=${old_value} ...| * v v v * |... idx p ...| */ static char image_cmdline[512] = {0}; if (dtboidx != NULL) { char *p = NULL; int idx = 0; strncpy(image_cmdline, img_cmdline, strlen(img_cmdline)); p = img_cmdline; /* compare if it is the last character int the string*/ do { /* Search for the specified key: androidboot.dtbo_idx */ if (!strncmp(p, ANDR_BOOT_DTBO_MAIGC, strlen(ANDR_BOOT_DTBO_MAIGC))) { p += strlen(ANDR_BOOT_DTBO_MAIGC); /* Find the address offset of the value of androidboot.dtbo_idx key in the string */ if (*p++ == '=') { idx = p - img_cmdline; /* Find the address offset of the next key of the androidboot.dtbo_idx */ while (*p != '\0' && *p != end_flag) p++; break; } } /* Find the address offset of the first character of each key in the string */ while (*p++ != end_flag) ; } while (*p != '\0' && idx == 0); /*compare whether the androidboot.dtbo_idx key exists */ if (idx != 0) { image_cmdline[idx] = '\0'; strcat(image_cmdline, dtboidx); strcat(image_cmdline, p); } img_cmdline = image_cmdline; } get_andriod_dtbo_idx(img_cmdline, ANDR_BOOT_DTBO_MAIGC); #endif if (end_flag == 0xa) { img_cmdline = (char *)vendor_hdr->cmdline; } strncpy(andr_tmp_str, 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); debug("Kernel load addr 0x%08x size %u KiB\n", kernel_addr, DIV_ROUND_UP(kernel_size, 1024)); int len = 0; if (*img_cmdline) { debug("Kernel command line: %s\n", img_cmdline); len += strlen(img_cmdline); } char *bootargs = env_get("bootargs"); if (bootargs) len += strlen(bootargs); char *newbootargs = malloc(len + 2); if (!newbootargs) { puts("Error: malloc in android_image_get_kernel failed!\n"); return -ENOMEM; } *newbootargs = '\0'; if (bootargs) { strcpy(newbootargs, bootargs); strcat(newbootargs, " "); } if (*img_cmdline) strcat(newbootargs, img_cmdline); env_set("bootargs", newbootargs); if (os_data) { *os_data = (ulong)hdr; *os_data += page_size; } if (os_len) *os_len = kernel_size; return 0; } int android_image_check_header(const struct andr_img_hdr *hdr) { return memcmp(ANDR_BOOT_MAGIC, hdr->magic, ANDR_BOOT_MAGIC_SIZE); } ulong android_image_get_end_by_avbfooter(void) { #ifdef CONFIG_SUNXI_AVB u32 start_block; u32 part_sectors; AvbFooter footer; uint8_t *avb_footer_buf; uint8_t avb_footer_sector[512]; char *part_name = env_get("boot_from_partion"); if (!part_name) return 0; if (sunxi_partition_get_info_byname(part_name, &start_block, &part_sectors)) { return 0; } sunxi_flash_read(start_block + part_sectors - 1, 1, avb_footer_sector); avb_footer_buf = &avb_footer_sector[512 - AVB_FOOTER_SIZE]; if (!avb_footer_validate_and_byteswap((const AvbFooter *)avb_footer_buf, &footer)) { return 0; } return round_down(footer.vbmeta_offset, 512); #else return 0; #endif } ulong android_image_get_end(const struct andr_img_hdr *hdr) { ulong end; struct boot_img_hdr *hdr_v3; /* * The header takes a full page, the remaining components are aligned * on page boundary */ end = (ulong)hdr; if ((!strncmp(hdr->magic, ANDR_BOOT_MAGIC, ANDR_BOOT_MAGIC_SIZE)) && (hdr->unused >= 0x3)) { vendor_hdr = get_vendor_hdr_addr(); if (vendor_hdr != NULL) { hdr_v3 = (boot_img_hdr *)hdr; end += ALIGN(hdr_v3->header_size, vendor_hdr->page_size*2); end += ALIGN(hdr_v3->kernel_size, vendor_hdr->page_size*2); end += ALIGN(hdr_v3->ramdisk_size, vendor_hdr->page_size*2); } debug("hdr_v3 end:0x%x\n", (uint)end); } else { 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); end += ALIGN(hdr->recovery_dtbo_size, hdr->page_size); end += ALIGN(hdr->dtb_size, hdr->page_size); } return end; } ulong android_image_get_kload(const struct andr_img_hdr *hdr) { return android_image_get_kernel_addr(hdr); } int android_image_get_ramdisk(const struct andr_img_hdr *hdr, ulong *rd_data, ulong *rd_len) { struct boot_img_hdr *hdr_v3; u32 page_size = hdr->page_size; u32 kernel_size = hdr->kernel_size; u32 ramdisk_size = hdr->ramdisk_size; u32 ramdisk_addr = hdr->ramdisk_addr; uint part_offset, part_size; struct boot_img_hdr *init_boot = NULL; if (!sunxi_partition_get_info_byname("init_boot", &part_offset, &part_size)) { printf("ramdisk use init boot\n"); init_boot = get_init_boot_hdr_addr( hdr, part_offset, part_size); hdr = (struct andr_img_hdr *)init_boot; } if ((!strncmp(hdr->magic, ANDR_BOOT_MAGIC, ANDR_BOOT_MAGIC_SIZE)) && (hdr->unused >= 0x3)) { vendor_hdr = get_vendor_hdr_addr(); hdr_v3 = (boot_img_hdr *)hdr; kernel_size = hdr_v3->kernel_size; if (vendor_hdr != 0) { page_size = vendor_hdr->page_size * 2; ramdisk_addr = vendor_hdr->ramdisk_addr; } ramdisk_size = hdr_v3->ramdisk_size; } if (!ramdisk_size) { *rd_data = *rd_len = 0; return -1; } debug("RAM disk load addr 0x%08x size %u KiB\n", ramdisk_addr, DIV_ROUND_UP(ramdisk_size, 1024)); *rd_data = (unsigned long)hdr; *rd_data += page_size; *rd_data += ALIGN(kernel_size, page_size); *rd_len = ramdisk_size; debug("rd_data:0x%lx, rd_len:0x%lx\n", *rd_data, *rd_len); return 0; } struct boot_img_hdr *get_init_boot_hdr_addr(const struct andr_img_hdr *hdr, uint part_offset, uint part_size) { static int already_initboot; ulong initboot_start; ulong initboot_end; ulong vendor_end; //struct vendor_boot_img_hdr *vendor_hdr = NULL; if (!already_initboot) { already_initboot = 1; if (part_size > 0) { vendor_hdr = get_vendor_hdr_addr(); vendor_end = android_image_get_vendor_get_end(vendor_hdr); if (vendor_end > 0) { initboot_start = ALIGN(vendor_end, 2048); sunxi_flash_read(part_offset, 2048 / 512, (char *)initboot_start); initboot_end = android_image_get_end( (void *)initboot_start); part_size = ALIGN(initboot_end - initboot_start, 512) / 512; /*printf("hdr:0x%lx, part_offset:0x%x, part_size:0x%x\n", (ulong)hdr, part_offset, part_size);*/ sunxi_flash_read(part_offset, part_size, (char *)initboot_start); init_boot_hdr = (struct boot_img_hdr *)initboot_start; } else { return NULL; } } else { return NULL; } } return init_boot_hdr; } int android_image_get_vendor_ramdisk(const struct andr_img_hdr *hdr, ulong *rd_data, ulong *rd_len) { vendor_hdr = get_vendor_hdr_addr(); if (vendor_hdr != NULL) { if (!vendor_hdr->vendor_ramdisk_size) { *rd_data = *rd_len = 0; return -1; } *rd_data = (unsigned long)vendor_hdr; *rd_data += ALIGN(VENDOR_BOOT_HEAD_SIZE, vendor_hdr->page_size); *rd_len = vendor_hdr->vendor_ramdisk_size; debug("rd_data:0x%lx rd_len:0x%lx\n", *rd_data, *rd_len); } return 0; } int android_image_get_second(const struct andr_img_hdr *hdr, ulong *second_data, ulong *second_len) { if (!hdr->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); printf("second address is 0x%lx\n",*second_data); *second_len = hdr->second_size; return 0; } int android_image_get_recovery_dtbo(const struct andr_img_hdr *hdr, ulong *recovery_dtbo_data, ulong *recovery_dtbo_len) { if (!hdr->recovery_dtbo_size) { *recovery_dtbo_data = *recovery_dtbo_len = 0; return -1; } *recovery_dtbo_data = (unsigned long)hdr; *recovery_dtbo_data += hdr->page_size; *recovery_dtbo_data += ALIGN(hdr->kernel_size, hdr->page_size); *recovery_dtbo_data += ALIGN(hdr->ramdisk_size, hdr->page_size); *recovery_dtbo_data += ALIGN(hdr->second_size, hdr->page_size); printf("recovery_dtbo address is 0x%lx\n", *recovery_dtbo_data); *recovery_dtbo_len = hdr->recovery_dtbo_size; return 0; } int android_image_get_dtb(const struct andr_img_hdr *hdr, ulong *dtb_data, ulong *dtb_len) { u32 part_start; if (!strncmp(hdr->magic, ANDR_BOOT_MAGIC, ANDR_BOOT_MAGIC_SIZE)) { if ((hdr->unused == 0x3)) { vendor_hdr = get_vendor_hdr_addr(); if ((vendor_hdr == NULL) && (!vendor_hdr->dtb_size)) { *dtb_data = *dtb_len = 0; pr_err("err: dtb_len=0\n"); return -1; } *dtb_data = (unsigned long)vendor_hdr; *dtb_data += ALIGN(VENDOR_BOOT_HEAD_SIZE, vendor_hdr->page_size); *dtb_data += ALIGN(vendor_hdr->vendor_ramdisk_size, vendor_hdr->page_size); *dtb_len = ALIGN(vendor_hdr->dtb_size, vendor_hdr->page_size); } else { *dtb_data = (unsigned long)hdr; *dtb_data += hdr->page_size; *dtb_data += ALIGN(hdr->kernel_size, hdr->page_size); *dtb_data += ALIGN(hdr->ramdisk_size, hdr->page_size); *dtb_data += ALIGN(hdr->second_size, hdr->page_size); *dtb_data += ALIGN(hdr->recovery_dtbo_size, hdr->page_size); *dtb_len = hdr->dtb_size; char *boot_name = env_get("boot_partition"); if (boot_name == NULL) { part_start = sunxi_partition_get_offset_byname("boot"); } else { part_start = sunxi_partition_get_offset_byname(boot_name); } if (part_start != 0) { sunxi_flash_read(part_start + (*dtb_data - (unsigned long)hdr)/512, ALIGN(hdr->dtb_size, 512)/512, (char *)(*dtb_data)); } } } debug("dtb address is 0x%lx\n", *dtb_data); return fdt_check_header((void *)*dtb_data); } #if !defined(CONFIG_SPL_BUILD) /** * android_print_contents - prints out the contents of the Android format image * @hdr: pointer to the Android format image header * * android_print_contents() formats a multi line Android image contents * description. * The routine prints out Android image properties * * returns: * no returned results */ void android_print_contents(const struct andr_img_hdr *hdr) { const char * const p = IMAGE_INDENT_STRING; /* os_version = ver << 11 | lvl */ u32 os_ver = hdr->os_version >> 11; u32 os_lvl = hdr->os_version & ((1U << 11) - 1); printf("%skernel size: %x\n", p, hdr->kernel_size); printf("%skernel address: %x\n", p, hdr->kernel_addr); printf("%sramdisk size: %x\n", p, hdr->ramdisk_size); printf("%sramdisk addrress: %x\n", p, hdr->ramdisk_addr); printf("%ssecond size: %x\n", p, hdr->second_size); printf("%ssecond address: %x\n", p, hdr->second_addr); printf("%stags address: %x\n", p, hdr->tags_addr); printf("%spage size: %x\n", p, hdr->page_size); printf("%sdtbo size: %x\n", p, hdr->recovery_dtbo_size); printf("%sdtbo address: %llx\n", p, hdr->recovery_dtbo_offset); printf("%sdtb size: %x\n", p, hdr->dtb_size); printf("%sdtb addr: %llx\n", p, hdr->dtb_addr); /* ver = A << 14 | B << 7 | C (7 bits for each of A, B, C) * lvl = ((Y - 2000) & 127) << 4 | M (7 bits for Y, 4 bits for M) */ printf("%sos_version: %x (ver: %u.%u.%u, level: %u.%u)\n", p, hdr->os_version, (os_ver >> 7) & 0x7F, (os_ver >> 14) & 0x7F, os_ver & 0x7F, (os_lvl >> 4) + 2000, os_lvl & 0x0F); printf("%sname: %s\n", p, hdr->name); printf("%scmdline: %s\n", p, hdr->cmdline); } #endif