blob: d794eb2c635af1631d596b61dcd2004b88635e8f [file] [log] [blame]
/*
* Copyright 2018 NXP
*
* SPDX-License-Identifier: GPL-2.0+
* derived from u-boot's mkimage utility
*
*/
#include "mkimage_common.h"
#include <inttypes.h>
#include <stdio.h>
#define OCRAM_START 0x00100000
#define OCRAM_END 0x00400000
#define IV_MAX_LEN 32
#define HASH_MAX_LEN 64
#define MAX_NUM_IMGS 6
#define MAX_NUM_SRK_RECORDS 4
#define IVT_HEADER_TAG_B0 0x87
#define IVT_VERSION_B0 0x00
#define IMG_FLAG_HASH_SHA256 0x000
#define IMG_FLAG_HASH_SHA384 0x100
#define IMG_FLAG_HASH_SHA512 0x200
#define IMG_FLAG_ENCRYPTED_MASK 0x400
#define IMG_FLAG_ENCRYPTED_SHIFT 0x0A
#define IMG_FLAG_BOOTFLAGS_MASK 0xFFFF0000
#define IMG_FLAG_BOOTFLAGS_SHIFT 0x10
#define IMG_ARRAY_ENTRY_SIZE 128
#define HEADER_IMG_ARRAY_OFFSET 0x10
#define HASH_STR_SHA_256 "sha256"
#define HASH_STR_SHA_384 "sha384"
#define HASH_STR_SHA_512 "sha512"
#define HASH_TYPE_SHA_256 256
#define HASH_TYPE_SHA_384 384
#define HASH_TYPE_SHA_512 512
#define IMAGE_HASH_ALGO_DEFAULT 384
#define IMAGE_PADDING_DEFAULT 0x1000
#define DCD_ENTRY_ADDR_IN_SCFW 0x240
#define CONTAINER_ALIGNMENT 0x400
#define CONTAINER_FLAGS_DEFAULT 0x10
#define CONTAINER_FUSE_DEFAULT 0x0
#define SIGNATURE_BLOCK_HEADER_LENGTH 0x10
#define MAX_NUM_OF_CONTAINER 2
#define FIRST_CONTAINER_HEADER_LENGTH 0x400
#define BOOT_IMG_META_MU_RID_SHIFT 10
#define BOOT_IMG_META_PART_ID_SHIFT 20
#define IMAGE_A35_DEFAULT_META(PART) (((PART == 0 ) ? PARTITION_ID_AP : PART) << BOOT_IMG_META_PART_ID_SHIFT |\
SC_R_MU_0A << BOOT_IMG_META_MU_RID_SHIFT | \
SC_R_A35_0)
#define IMAGE_A53_DEFAULT_META(PART) (((PART == 0 ) ? PARTITION_ID_AP : PART) << BOOT_IMG_META_PART_ID_SHIFT |\
SC_R_MU_0A << BOOT_IMG_META_MU_RID_SHIFT | \
SC_R_A53_0)
#define IMAGE_A72_DEFAULT_META(PART) (((PART == 0 ) ? PARTITION_ID_AP : PART) << BOOT_IMG_META_PART_ID_SHIFT |\
SC_R_MU_0A << BOOT_IMG_META_MU_RID_SHIFT | \
SC_R_A72_0)
#define IMAGE_M4_0_DEFAULT_META(PART) (((PART == 0) ? PARTITION_ID_M4 : PART) << BOOT_IMG_META_PART_ID_SHIFT |\
SC_R_M4_0_MU_1A << BOOT_IMG_META_MU_RID_SHIFT | \
SC_R_M4_0_PID0)
#define IMAGE_M4_1_DEFAULT_META(PART) (((PART == 0) ? PARTITION_ID_M4 : PART) << BOOT_IMG_META_PART_ID_SHIFT |\
SC_R_M4_1_MU_1A << BOOT_IMG_META_MU_RID_SHIFT | \
SC_R_M4_1_PID0)
#define CONTAINER_IMAGE_ARRAY_START_OFFSET 0x2000
uint32_t scfw_flags = 0;
typedef struct {
uint8_t version;
uint16_t length;
uint8_t tag;
uint16_t srk_table_offset;
uint16_t cert_offset;
uint16_t blob_offset;
uint16_t signature_offset;
uint32_t reserved;
} __attribute__((packed)) sig_blk_hdr_t;
typedef struct {
uint32_t offset;
uint32_t size;
uint64_t dst;
uint64_t entry;
uint32_t hab_flags;
uint32_t meta;
uint8_t hash[HASH_MAX_LEN];
uint8_t iv[IV_MAX_LEN];
} __attribute__((packed)) boot_img_t;
typedef struct {
uint8_t version;
uint16_t length;
uint8_t tag;
uint32_t flags;
uint16_t sw_version;
uint8_t fuse_version;
uint8_t num_images;
uint16_t sig_blk_offset;
uint16_t reserved;
boot_img_t img[MAX_NUM_IMGS];
sig_blk_hdr_t sig_blk_hdr;
uint32_t sigblk_size;
uint32_t padding;
} __attribute__((packed)) flash_header_v3_t;
typedef struct {
flash_header_v3_t fhdr[MAX_NUM_OF_CONTAINER];
dcd_v2_t dcd_table;
} __attribute__((packed)) imx_header_v3_t;
uint32_t custom_partition = 0;
static void copy_file_aligned (int ifd, const char *datafile, int offset, int align)
{
int dfd;
struct stat sbuf;
unsigned char *ptr;
uint8_t zeros[0x4000];
int size;
int ret;
if (align > 0x4000) {
fprintf (stderr, "Wrong alignment requested %d\n",
align);
exit (EXIT_FAILURE);
}
memset(zeros, 0, sizeof(zeros));
if ((dfd = open(datafile, O_RDONLY|O_BINARY)) < 0) {
fprintf (stderr, "Can't open %s: %s\n",
datafile, strerror(errno));
exit (EXIT_FAILURE);
}
if (fstat(dfd, &sbuf) < 0) {
fprintf (stderr, "Can't stat %s: %s\n",
datafile, strerror(errno));
exit (EXIT_FAILURE);
}
if(sbuf.st_size == 0)
goto close;
ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, dfd, 0);
if (ptr == MAP_FAILED) {
fprintf (stderr, "Can't read %s: %s\n",
datafile, strerror(errno));
exit (EXIT_FAILURE);
}
size = sbuf.st_size;
ret = lseek(ifd, offset, SEEK_SET);
if (ret < 0) {
fprintf(stderr, "%s: lseek error %s\n",
__func__, strerror(errno));
exit(EXIT_FAILURE);
}
if (write(ifd, ptr, size) != size) {
fprintf (stderr, "Write error %s\n",
strerror(errno));
exit (EXIT_FAILURE);
}
align = ALIGN(size, align) - size;
if (write(ifd, (char *)&zeros, align) != align) {
fprintf(stderr, "Write error: %s\n",
strerror(errno));
exit(EXIT_FAILURE);
}
(void) munmap((void *)ptr, sbuf.st_size);
close:
(void) close (dfd);
}
static void set_imx_hdr_v3(imx_header_v3_t *imxhdr, uint32_t dcd_len,
uint32_t flash_offset, uint32_t hdr_base, uint32_t cont_id)
{
flash_header_v3_t *fhdr_v3 = &imxhdr->fhdr[cont_id];
/* Set magic number */
fhdr_v3->tag = IVT_HEADER_TAG_B0;
fhdr_v3->version = IVT_VERSION_B0;
}
void set_image_hash(boot_img_t *img, char *filename, uint32_t hash_type)
{
FILE *fp = NULL;
char sha_command[512];
char hash[2 * HASH_MAX_LEN + 1];
if (img->size == 0)
sprintf(sha_command, "sha%dsum /dev/null", hash_type);
else
sprintf(sha_command, "dd if=/dev/zero of=tmp_pad bs=%d count=1;\
dd if=\'%s\' of=tmp_pad conv=notrunc;\
sha%dsum tmp_pad; rm -f tmp_pad",
img->size, filename, hash_type);
switch(hash_type) {
case HASH_TYPE_SHA_256:
img->hab_flags |= IMG_FLAG_HASH_SHA256;
break;
case HASH_TYPE_SHA_384:
img->hab_flags |= IMG_FLAG_HASH_SHA384;
break;
case HASH_TYPE_SHA_512:
img->hab_flags |= IMG_FLAG_HASH_SHA512;
break;
default:
fprintf(stderr, "Wrong hash type selected (%d) !!!\n\n",
hash_type);
exit(EXIT_FAILURE);
break;
}
memset(img->hash, 0, HASH_MAX_LEN);
fp = popen(sha_command, "r");
if (fp == NULL) {
fprintf(stderr, "Failed to run command hash\n" );
exit(EXIT_FAILURE);
}
if(fgets(hash, hash_type / 4 + 1, fp) == NULL) {
fprintf(stderr, "Failed to hash file: %s\n", filename);
exit(EXIT_FAILURE);
}
for(int i = 0; i < strlen(hash)/2; i++){
sscanf(hash + 2*i, "%02hhx", &img->hash[i]);
}
pclose(fp);
}
#define append(p, s, l) do {memcpy(p, (uint8_t *)s, l); p += l; } while (0)
uint8_t *flatten_container_header(imx_header_v3_t *imx_header,
uint8_t containers_count,
uint32_t *size_out, uint32_t file_offset)
{
uint8_t *flat = NULL;
uint8_t *ptr = NULL;
uint16_t size = 0;
/* Compute size of all container headers */
for (int i = 0; i < containers_count; i++) {
flash_header_v3_t *container = &imx_header->fhdr[i];
container->sig_blk_offset = HEADER_IMG_ARRAY_OFFSET +
container->num_images * IMG_ARRAY_ENTRY_SIZE;
container->length = HEADER_IMG_ARRAY_OFFSET +
(IMG_ARRAY_ENTRY_SIZE * container->num_images) + sizeof(sig_blk_hdr_t);
/* Print info needed by CST to sign the container header */
fprintf(stdout, "CST: CONTAINER %d offset: 0x%x\n", i, file_offset + size);
fprintf(stdout, "CST: CONTAINER %d: Signature Block: offset is at 0x%x\n", i,
file_offset + size + container->length - SIGNATURE_BLOCK_HEADER_LENGTH);
size += ALIGN(container->length, container->padding);
}
flat = calloc(size, sizeof(uint8_t));
if (!flat) {
fprintf(stderr, "Failed to allocate memory (%d)\n", size);
exit(EXIT_FAILURE);
}
ptr = flat;
*size_out = size;
for (int i = 0; i < containers_count; i++) {
flash_header_v3_t *container = &imx_header->fhdr[i];
uint32_t container_start_offset = ptr - flat;
/* Append container header */
append(ptr, container, HEADER_IMG_ARRAY_OFFSET);
/* Adjust images offset to start from container headers start */
for (int j = 0; j < container->num_images; j++) {
container->img[j].offset -= container_start_offset + file_offset;
}
/* Append each image array entry */
for (int j = 0; j < container->num_images; j++) {
append(ptr, &container->img[j], sizeof(boot_img_t));
}
append(ptr, &container->sig_blk_hdr, sizeof(sig_blk_hdr_t));
/* Padding for container (if necessary) */
ptr += ALIGN(container->length, container->padding) - container->length;
}
return flat;
}
uint64_t read_dcd_offset(char *filename)
{
int dfd;
struct stat sbuf;
uint8_t *ptr;
uint64_t offset = 0;
dfd = open(filename, O_RDONLY|O_BINARY);
if (dfd < 0) {
fprintf(stderr, "Can't open %s: %s\n", filename, strerror(errno));
exit(EXIT_FAILURE);
}
if (fstat(dfd, &sbuf) < 0) {
fprintf(stderr, "Can't stat %s: %s\n", filename, strerror(errno));
exit(EXIT_FAILURE);
}
ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, dfd, 0);
if (ptr == MAP_FAILED) {
fprintf(stderr, "Can't read %s: %s\n", filename, strerror(errno));
exit(EXIT_FAILURE);
}
offset = *(uint32_t *)(ptr + DCD_ENTRY_ADDR_IN_SCFW);
(void) munmap((void *)ptr, sbuf.st_size);
(void) close(dfd);
return offset;
}
uint32_t get_hash_algo(char *images_hash)
{
uint32_t hash_algo = IMAGE_HASH_ALGO_DEFAULT;
if (NULL != images_hash) {
if (0 == strcmp(images_hash, HASH_STR_SHA_256)) {
hash_algo = HASH_TYPE_SHA_256;
}
else if (0 == strcmp(images_hash, HASH_STR_SHA_384)) {
hash_algo = HASH_TYPE_SHA_384;
}
else if (0 == strcmp(images_hash, HASH_STR_SHA_512)) {
hash_algo = HASH_TYPE_SHA_512;
}
else {
fprintf(stderr,
"\nERROR: %s is an invalid hash argument\n"
" Expected values: %s, %s, %s\n\n",
images_hash, HASH_STR_SHA_256, HASH_STR_SHA_384, HASH_STR_SHA_512);
exit(EXIT_FAILURE);
}
}
fprintf(stdout, "Hash of the images = sha%d\n", hash_algo);
return hash_algo;
}
void set_image_array_entry(flash_header_v3_t *container, soc_type_t soc,
const image_t *image_stack, uint32_t offset,
uint32_t size, char *tmp_filename, bool dcd_skip, char *images_hash)
{
uint64_t entry = image_stack->entry;
uint64_t core = image_stack->ext;
uint32_t meta;
char *tmp_name = "";
option_type_t type = image_stack->option;
boot_img_t *img = &container->img[container->num_images];
img->offset = offset; /* Is re-adjusted later */
img->size = size;
set_image_hash(img, tmp_filename, get_hash_algo(images_hash));
switch(type) {
case SECO:
img->hab_flags |= IMG_TYPE_SECO;
img->hab_flags |= CORE_SECO << BOOT_IMG_FLAGS_CORE_SHIFT;
tmp_name = "SECO";
img->dst = 0x20C00000;
img->entry = 0x20000000;
break;
case AP:
if (soc == QX && core == CORE_CA35)
meta = IMAGE_A35_DEFAULT_META(custom_partition);
else if (soc == QM && core == CORE_CA53)
meta = IMAGE_A53_DEFAULT_META(custom_partition);
else if (soc == QM && core == CORE_CA72)
meta = IMAGE_A72_DEFAULT_META(custom_partition);
else {
fprintf(stderr, "Error: invalid AP core id: %" PRIi64 "\n", core);
exit(EXIT_FAILURE);
}
img->hab_flags |= IMG_TYPE_EXEC;
img->hab_flags |= CORE_CA53 << BOOT_IMG_FLAGS_CORE_SHIFT; /* On B0, only core id = 4 is valid */
tmp_name = "AP";
img->dst = entry;
img->entry = entry;
img->meta = meta;
custom_partition = 0;
break;
case M4:
if (core == 0) {
core = CORE_CM4_0;
meta = IMAGE_M4_0_DEFAULT_META(custom_partition);
} else if (core == 1) {
core = CORE_CM4_1;
meta = IMAGE_M4_1_DEFAULT_META(custom_partition);
} else {
fprintf(stderr, "Error: invalid m4 core id: %" PRIi64 "\n", core);
exit(EXIT_FAILURE);
}
img->hab_flags |= IMG_TYPE_EXEC;
img->hab_flags |= core << BOOT_IMG_FLAGS_CORE_SHIFT;
tmp_name = "M4";
if ((entry & 0x7) != 0)
{
fprintf(stderr, "\n\nWarning: M4 Destination address is not 8 byte aligned\n\n");
}
img->dst = entry;
img->entry = entry;
img->meta = meta;
custom_partition = 0;
break;
case DATA:
img->hab_flags |= IMG_TYPE_DATA;
img->hab_flags |= CORE_CA35 << BOOT_IMG_FLAGS_CORE_SHIFT;
tmp_name = "DATA";
img->dst = entry;
break;
case MSG_BLOCK:
img->hab_flags |= IMG_TYPE_DATA;
img->hab_flags |= CORE_CA35 << BOOT_IMG_FLAGS_CORE_SHIFT;
img->meta = core << BOOT_IMG_META_MU_RID_SHIFT;
tmp_name = "MSG_BLOCK";
img->dst = entry;
break;
case SCFW:
img->hab_flags |= scfw_flags & 0xFFFF0000;
img->hab_flags |= IMG_TYPE_EXEC;
img->hab_flags |= CORE_SC << BOOT_IMG_FLAGS_CORE_SHIFT;
tmp_name = "SCFW";
img->dst = 0x1FFE0000;
img->entry = 0x1FFE0000;
/* Lets add the DCD now */
if (!dcd_skip) {
container->num_images++;
img = &container->img[container->num_images];
img->hab_flags |= IMG_TYPE_DCD_DDR;
img->hab_flags |= CORE_SC << BOOT_IMG_FLAGS_CORE_SHIFT;
set_image_hash(img, "/dev/null", IMAGE_HASH_ALGO_DEFAULT);
img->offset = offset + img->size;
img->entry = read_dcd_offset(tmp_filename);
img->dst = img->entry - 1;
}
break;
default:
fprintf(stderr, "unrecognized image type (%d)\n", type);
exit(EXIT_FAILURE);
}
fprintf(stdout, "%s file_offset = 0x%x size = 0x%x\n", tmp_name, offset, size);
container->num_images++;
}
void set_container(flash_header_v3_t *container, uint16_t sw_version,
uint32_t alignment, uint32_t flags, uint16_t fuse_version)
{
container->sig_blk_hdr.tag = 0x90;
container->sig_blk_hdr.length = sizeof(sig_blk_hdr_t);
container->sw_version = sw_version;
container->padding = alignment;
container->fuse_version = fuse_version;
container->flags = flags;
printf("flags: 0x%x\n", container->flags);
}
int get_container_image_start_pos(image_t *image_stack, uint32_t align)
{
image_t *img_sp = image_stack;
/*8K total container header*/
int file_off = CONTAINER_IMAGE_ARRAY_START_OFFSET, ofd = -1;
flash_header_v3_t header;
while (img_sp->option != NO_IMG) {
if (img_sp->option == APPEND) {
ofd = open(img_sp->filename, O_RDONLY);
if (ofd < 0) {
printf("Failure open first container file %s\n", img_sp->filename);
break;
}
if(read(ofd, &header, sizeof(header)) != sizeof(header)) {
printf("Failure Read header \n");
exit(EXIT_FAILURE);
}
close(ofd);
if (header.tag != IVT_HEADER_TAG_B0) {
printf("header tag missmatched \n");
} else if (header.num_images == 0) {
printf("image num is 0 \n");
} else {
file_off += header.img[header.num_images - 1].size;
file_off = ALIGN(file_off, align);
}
}
img_sp++;
}
return file_off;
}
int build_container_qx_qm_b0(soc_type_t soc, uint32_t sector_size, uint32_t ivt_offset, char *out_file,
bool emmc_fastboot, image_t *image_stack, bool dcd_skip, uint8_t fuse_version,
uint16_t sw_version, char *images_hash)
{
int file_off, ofd = -1;
unsigned int dcd_len = 0;
static imx_header_v3_t imx_header;
image_t *img_sp = image_stack;
struct stat sbuf;
char *tmp_filename = NULL;
uint32_t size = 0;
uint32_t file_padding = 0;
int ret;
int container = -1;
int cont_img_count = 0; /* indexes to arrange the container */
memset((char *)&imx_header, 0, sizeof(imx_header_v3_t));
if (image_stack == NULL) {
fprintf(stderr, "Empty image stack ");
exit(EXIT_FAILURE);
}
if (soc == QX)
fprintf(stdout, "Platform:\ti.MX8QXP B0\n");
else if (soc == QM)
fprintf(stdout, "Platform:\ti.MX8QM B0\n");
set_imx_hdr_v3(&imx_header, dcd_len, ivt_offset, INITIAL_LOAD_ADDR_SCU_ROM, 0);
set_imx_hdr_v3(&imx_header, 0, ivt_offset, INITIAL_LOAD_ADDR_AP_ROM, 1);
printf("ivt_offset:\t%d\n", ivt_offset);
file_off = get_container_image_start_pos(image_stack, sector_size);
printf("container image offset (aligned):%x\n", file_off);
/* step through image stack and generate the header */
img_sp = image_stack;
while (img_sp->option != NO_IMG) { /* stop once we reach null terminator */
switch (img_sp->option) {
case AP:
case M4:
case SCFW:
case DATA:
case MSG_BLOCK:
if (container < 0) {
fprintf(stderr, "No container found\n");
exit(EXIT_FAILURE);
}
check_file(&sbuf, img_sp->filename);
tmp_filename = img_sp->filename;
set_image_array_entry(&imx_header.fhdr[container],
soc,
img_sp,
file_off,
ALIGN(sbuf.st_size, sector_size),
tmp_filename,
dcd_skip,
images_hash);
img_sp->src = file_off;
file_off += ALIGN(sbuf.st_size, sector_size);
cont_img_count++;
break;
case SECO:
if (container < 0) {
fprintf(stderr, "No container found\n");
exit(EXIT_FAILURE);
}
check_file(&sbuf, img_sp->filename);
tmp_filename = img_sp->filename;
set_image_array_entry(&imx_header.fhdr[container],
soc,
img_sp,
file_off,
sbuf.st_size,
tmp_filename,
dcd_skip,
"sha384");
img_sp->src = file_off;
file_off += sbuf.st_size;
cont_img_count++;
break;
case NEW_CONTAINER:
container++;
set_container(&imx_header.fhdr[container], sw_version,
CONTAINER_ALIGNMENT,
CONTAINER_FLAGS_DEFAULT,
fuse_version);
cont_img_count = 0; /* reset img count when moving to new container */
scfw_flags = 0;
break;
case APPEND:
/* nothing to do here, the container is appended in the output */
break;
case FLAG:
/* override the flags for scfw in current container */
scfw_flags = img_sp->entry & 0xFFFF0000;/* mask off bottom 16 bits */
break;
case FILEOFF:
if (file_off > img_sp->dst)
{
fprintf(stderr, "FILEOFF address less than current file offset!!!\n");
exit(EXIT_FAILURE);
}
if (img_sp->dst != ALIGN(img_sp->dst, sector_size))
{
fprintf(stderr, "FILEOFF address is not aligned to sector size!!!\n");
exit(EXIT_FAILURE);
}
file_off = img_sp->dst;
break;
case PARTITION: /* keep custom partition until next executable image */
custom_partition = img_sp->entry; /* use a global var for default behaviour */
break;
default:
fprintf(stderr, "unrecognized option in input stack (%d)\n", img_sp->option);
exit(EXIT_FAILURE);
}
img_sp++;/* advance index */
}
/* Open output file */
ofd = open(out_file, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, 0666);
if (ofd < 0) {
fprintf(stderr, "%s: Can't open: %s\n",
out_file, strerror(errno));
exit(EXIT_FAILURE);
}
/* Append container (if specified) */
img_sp = image_stack;
do {
if (img_sp->option == APPEND) {
copy_file(ofd, img_sp->filename, 0, 0);
file_padding += FIRST_CONTAINER_HEADER_LENGTH;
}
img_sp++;
} while (img_sp->option != NO_IMG);
/* Add padding or skip appended container */
ret = lseek(ofd, file_padding, SEEK_SET);
if (ret < 0) {
fprintf(stderr, "%s: lseek error %s\n",
__func__, strerror(errno));
exit(EXIT_FAILURE);
}
/* Note: Image offset are not contained in the image */
uint8_t *tmp = flatten_container_header(&imx_header, container + 1, &size, file_padding);
/* Write image header */
if (write(ofd, tmp, size) != size) {
fprintf(stderr, "error writing image hdr\n");
exit(1);
}
/* Clean-up memory used by the headers */
free(tmp);
if (emmc_fastboot)
ivt_offset = 0;/*set ivt offset to 0 if emmc */
/* step through the image stack again this time copying images to final bin */
img_sp = image_stack;
while (img_sp->option != NO_IMG) { /* stop once we reach null terminator */
if (img_sp->option == M4 || img_sp->option == AP || img_sp->option == DATA || img_sp->option == SCD ||
img_sp->option == SCFW || img_sp->option == SECO || img_sp->option == MSG_BLOCK) {
copy_file_aligned(ofd, img_sp->filename, img_sp->src, sector_size);
}
img_sp++;
}
/* Close output file */
close(ofd);
return 0;
}