| // SPDX-License-Identifier: GPL-2.0-or-later |
| // Copyright Contributors to the U-Boot project. |
| |
| #include "imagetool.h" |
| #include <inttypes.h> |
| #include <u-boot/sha256.h> |
| |
| /* |
| * Image contain data in the following order: |
| * Nonce 16 byte |
| * Header 64 byte |
| * Digest 32 byte |
| * Padding align up to 4K |
| * Payload |
| */ |
| |
| #define HEADER_MAGIC 0x4c4d4140 /* @AML */ |
| #define HEADER_OFFSET 0x10 /* 16 */ |
| #define HEADER_SIZE 0x40 /* 64 */ |
| #define PAYLOAD_OFFSET 0x1000 /* 4096 */ |
| |
| struct amlimage_header { |
| uint32_t magic; |
| uint32_t total_size; |
| uint8_t header_size; |
| uint8_t root_key_index; |
| uint8_t version_major; |
| uint8_t version_minor; |
| uint32_t padding1; |
| uint32_t digest_type; |
| uint32_t digest_offset; |
| uint32_t digest_size; |
| uint32_t data_offset; |
| uint32_t key_type; |
| uint32_t key_offset; |
| uint32_t key_size; |
| uint32_t data_size; |
| uint32_t payload_type; |
| uint32_t payload_offset; |
| uint32_t payload_size; |
| uint32_t padding2; |
| } __packed; |
| |
| struct amlimage_variant { |
| const char *name; |
| const struct amlimage_header hdr; |
| }; |
| |
| #define VARIANT(name, major, minor, size) \ |
| { name, { .magic = HEADER_MAGIC, .header_size = HEADER_SIZE, \ |
| .version_major = major, .version_minor = minor, \ |
| .payload_size = size, } } |
| |
| static const struct amlimage_variant variants[] = { |
| VARIANT("gxbb", 1, 0, 0xb000), |
| VARIANT("gxl", 1, 1, 0xb000), |
| VARIANT("gxm", 1, 1, 0xb000), |
| VARIANT("axg", 1, 1, 0xb000), |
| VARIANT("g12a", 1, 1, 0xf000), |
| VARIANT("g12b", 1, 1, 0xf000), |
| VARIANT("sm1", 1, 1, 0xf000), |
| }; |
| |
| static const struct amlimage_variant *amlimage_get_variant(const char *name) |
| { |
| if (!name) |
| return NULL; |
| |
| for (int i = 0; i < ARRAY_SIZE(variants); i++) |
| if (!strcmp(name, variants[i].name)) |
| return &variants[i]; |
| |
| return NULL; |
| } |
| |
| static int amlimage_check_params(struct image_tool_params *params) |
| { |
| const struct amlimage_variant *variant = |
| amlimage_get_variant(params->imagename); |
| int datafile_size; |
| |
| if (params->lflag || params->iflag) |
| return EXIT_SUCCESS; |
| |
| if (!variant) { |
| fprintf(stderr, "%s: unsupported image name: %s\n", |
| params->cmdname, params->imagename); |
| exit(EXIT_FAILURE); |
| } |
| |
| datafile_size = imagetool_get_filesize(params, params->datafile); |
| if (datafile_size < 0) { |
| exit(EXIT_FAILURE); |
| } else if (datafile_size > variant->hdr.payload_size) { |
| fprintf(stderr, "%s: datafile is too large (%#x > %#x)\n", |
| params->cmdname, datafile_size, |
| variant->hdr.payload_size); |
| exit(EXIT_FAILURE); |
| } |
| |
| return EXIT_SUCCESS; |
| } |
| |
| static int amlimage_verify_header(unsigned char *buf, int size, |
| struct image_tool_params *params) |
| { |
| const struct amlimage_header *hdr = (void *)buf + HEADER_OFFSET; |
| |
| if (size >= HEADER_OFFSET + HEADER_SIZE + SHA256_SUM_LEN && |
| hdr->magic == HEADER_MAGIC && hdr->header_size == HEADER_SIZE && |
| hdr->version_major == 1 && hdr->version_minor <= 1) |
| return 0; |
| |
| return -1; |
| } |
| |
| static void amlimage_print_header(const void *buf, |
| struct image_tool_params *params) |
| { |
| const struct amlimage_header *hdr = buf + HEADER_OFFSET; |
| uint8_t digest[SHA256_SUM_LEN]; |
| sha256_context ctx; |
| bool valid; |
| |
| printf("Amlogic Boot Image %" PRIu8 ".%" PRIu8 "\n", |
| hdr->version_major, hdr->version_minor); |
| printf("Total size: %" PRIu32 "\n", hdr->total_size); |
| printf("Digest %" PRIu32 ": %" PRIu32 " @ 0x%" PRIx32 "\n", |
| hdr->digest_type, hdr->digest_size, hdr->digest_offset); |
| printf("Key %" PRIu32 ": %" PRIu32 " @ 0x%" PRIx32 "\n", |
| hdr->key_type, hdr->key_size, hdr->key_offset); |
| printf("Payload %" PRIu32 ": %" PRIu32 " @ 0x%" PRIx32 "\n", |
| hdr->payload_type, hdr->payload_size, hdr->payload_offset); |
| |
| if (hdr->digest_type == 0) { |
| /* sha256 digest (normal boot) */ |
| sha256_starts(&ctx); |
| |
| /* Header and data is used as input for sha256 digest */ |
| sha256_update(&ctx, (void *)hdr, hdr->header_size); |
| sha256_update(&ctx, (void *)hdr + hdr->data_offset, hdr->data_size); |
| sha256_finish(&ctx, digest); |
| |
| valid = !memcmp((void *)hdr + hdr->digest_offset, |
| digest, SHA256_SUM_LEN); |
| |
| printf("Data: %" PRIu32 " @ 0x%" PRIx32 " - %s\n", |
| hdr->data_size, hdr->data_offset, valid ? "OK" : "BAD"); |
| } else { |
| /* RSA (secure boot) */ |
| printf("Data: %" PRIu32 " @ 0x%" PRIx32 " - Secure Boot\n", |
| hdr->data_size, hdr->data_offset); |
| } |
| } |
| |
| static void amlimage_set_header(void *buf, struct stat *sbuf, int ifd, |
| struct image_tool_params *params) |
| { |
| struct amlimage_header *hdr = buf + HEADER_OFFSET; |
| sha256_context ctx; |
| |
| /* Use header size as initial size */ |
| hdr->total_size = hdr->header_size; |
| |
| /* Use sha256 digest (normal boot) */ |
| hdr->digest_type = 0; |
| /* The sha256 digest is stored directly following the header */ |
| hdr->digest_offset = hdr->total_size; |
| /* Unknown if this is used as block size instead of digest size */ |
| hdr->digest_size = 512; |
| hdr->total_size += hdr->digest_size; |
| |
| /* Use key as padding so that payload ends up 4K aligned in TZRAM */ |
| hdr->key_type = 0; |
| hdr->key_offset = hdr->total_size; |
| hdr->key_size = PAYLOAD_OFFSET - HEADER_OFFSET - hdr->key_offset; |
| hdr->total_size += hdr->key_size; |
| |
| /* With padding above payload will have a 0x1000 offset in TZRAM */ |
| hdr->payload_type = 0; |
| hdr->payload_offset = hdr->total_size; |
| /* Payload size has already been copied from the variant header */ |
| hdr->total_size += hdr->payload_size; |
| |
| /* Set the data range to be used as input for sha256 digest */ |
| hdr->data_offset = hdr->digest_offset + SHA256_SUM_LEN; |
| hdr->data_size = hdr->total_size - hdr->data_offset; |
| |
| sha256_starts(&ctx); |
| /* Header and data is used as input for sha256 digest */ |
| sha256_update(&ctx, (void *)hdr, hdr->header_size); |
| sha256_update(&ctx, (void *)hdr + hdr->data_offset, hdr->data_size); |
| /* Write sha256 digest to the 32 bytes directly following the header */ |
| sha256_finish(&ctx, (void *)hdr + hdr->digest_offset); |
| } |
| |
| static int amlimage_extract_subimage(void *buf, |
| struct image_tool_params *params) |
| { |
| const struct amlimage_header *hdr = buf + HEADER_OFFSET; |
| |
| /* Save payload as the subimage */ |
| return imagetool_save_subimage(params->outfile, |
| (ulong)hdr + hdr->payload_offset, |
| hdr->payload_size); |
| } |
| |
| static int amlimage_check_image_type(uint8_t type) |
| { |
| if (type == IH_TYPE_AMLIMAGE) |
| return EXIT_SUCCESS; |
| |
| return EXIT_FAILURE; |
| } |
| |
| static int amlimage_vrec_header(struct image_tool_params *params, |
| struct image_type_params *tparams) |
| { |
| const struct amlimage_variant *variant = |
| amlimage_get_variant(params->imagename); |
| const struct amlimage_header *hdr = &variant->hdr; |
| |
| /* Use payload offset as header size, datafile will be appended */ |
| tparams->header_size = PAYLOAD_OFFSET; |
| |
| tparams->hdr = calloc(1, tparams->header_size); |
| if (!tparams->hdr) { |
| fprintf(stderr, "%s: Can't alloc header: %s\n", |
| params->cmdname, strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| |
| /* Start with a copy of the variant header */ |
| memcpy(tparams->hdr + HEADER_OFFSET, hdr, hdr->header_size); |
| |
| /* Pad up to payload size of the variant header */ |
| return hdr->payload_size - params->file_size; |
| } |
| |
| /* |
| * amlimage parameters |
| */ |
| U_BOOT_IMAGE_TYPE( |
| amlimage, |
| "Amlogic Boot Image", |
| 0, |
| NULL, |
| amlimage_check_params, |
| amlimage_verify_header, |
| amlimage_print_header, |
| amlimage_set_header, |
| amlimage_extract_subimage, |
| amlimage_check_image_type, |
| NULL, |
| amlimage_vrec_header |
| ); |