mirror of
				https://source.denx.de/u-boot/u-boot.git
				synced 2025-11-03 01:41:44 +01:00 
			
		
		
		
	__swab32() is a Linux specific macro defined in linux/swab.h. Let's use the compiler equivalent builtin function __builtin_bswap32() for better portability. Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
		
			
				
	
	
		
			1018 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1018 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (C) 2018 Alexander Graf <agraf@suse.de>
 | 
						|
 *
 | 
						|
 * SPDX-License-Identifier:	GPL-2.0+
 | 
						|
 */
 | 
						|
 | 
						|
#include "imagetool.h"
 | 
						|
#include "mkimage.h"
 | 
						|
#include "zynqmpimage.h"
 | 
						|
#include <elf.h>
 | 
						|
#include <image.h>
 | 
						|
 | 
						|
struct bif_entry {
 | 
						|
	const char *filename;
 | 
						|
	uint64_t flags;
 | 
						|
	uint64_t dest_cpu;
 | 
						|
	uint64_t exp_lvl;
 | 
						|
	uint64_t dest_dev;
 | 
						|
	uint64_t load;
 | 
						|
	uint64_t entry;
 | 
						|
	size_t offset;
 | 
						|
};
 | 
						|
 | 
						|
enum bif_flag {
 | 
						|
	BIF_FLAG_AESKEYFILE,
 | 
						|
	BIF_FLAG_INIT,
 | 
						|
	BIF_FLAG_UDF_BH,
 | 
						|
	BIF_FLAG_HEADERSIGNATURE,
 | 
						|
	BIF_FLAG_PPKFILE,
 | 
						|
	BIF_FLAG_PSKFILE,
 | 
						|
	BIF_FLAG_SPKFILE,
 | 
						|
	BIF_FLAG_SSKFILE,
 | 
						|
	BIF_FLAG_SPKSIGNATURE,
 | 
						|
	BIF_FLAG_FSBL_CONFIG,
 | 
						|
	BIF_FLAG_AUTH_PARAMS,
 | 
						|
	BIF_FLAG_KEYSRC_ENCRYPTION,
 | 
						|
	BIF_FLAG_PMUFW_IMAGE,
 | 
						|
	BIF_FLAG_BOOTLOADER,
 | 
						|
	BIF_FLAG_TZ,
 | 
						|
	BIF_FLAG_BH_KEY_IV,
 | 
						|
	BIF_FLAG_BH_KEYFILE,
 | 
						|
	BIF_FLAG_PUF_FILE,
 | 
						|
	BIF_FLAG_AARCH32,
 | 
						|
	BIF_FLAG_PART_OWNER_UBOOT,
 | 
						|
 | 
						|
	/* Internal flags */
 | 
						|
	BIF_FLAG_BIT_FILE,
 | 
						|
	BIF_FLAG_ELF_FILE,
 | 
						|
	BIF_FLAG_BIN_FILE,
 | 
						|
};
 | 
						|
 | 
						|
struct bif_flags {
 | 
						|
	const char name[32];
 | 
						|
	uint64_t flag;
 | 
						|
	char *(*parse)(char *line, struct bif_entry *bf);
 | 
						|
};
 | 
						|
 | 
						|
struct bif_file_type {
 | 
						|
	const char name[32];
 | 
						|
	uint32_t header;
 | 
						|
	int (*add)(struct bif_entry *bf);
 | 
						|
};
 | 
						|
 | 
						|
struct bif_output {
 | 
						|
	size_t data_len;
 | 
						|
	char *data;
 | 
						|
	struct image_header_table *imgheader;
 | 
						|
	struct zynqmp_header *header;
 | 
						|
	struct partition_header *last_part;
 | 
						|
};
 | 
						|
 | 
						|
struct bif_output bif_output;
 | 
						|
 | 
						|
static uint32_t zynqmp_csum(void *start, void *end)
 | 
						|
{
 | 
						|
	uint32_t checksum = 0;
 | 
						|
	uint32_t *ptr32 = start;
 | 
						|
 | 
						|
	while (ptr32 != end) {
 | 
						|
		checksum += le32_to_cpu(*ptr32);
 | 
						|
		ptr32++;
 | 
						|
	}
 | 
						|
 | 
						|
	return ~checksum;
 | 
						|
}
 | 
						|
 | 
						|
static int zynqmpbif_check_params(struct image_tool_params *params)
 | 
						|
{
 | 
						|
	if (!params)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	if (params->addr != 0x0) {
 | 
						|
		fprintf(stderr, "Error: Load Address can not be specified.\n");
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	if (params->eflag) {
 | 
						|
		fprintf(stderr, "Error: Entry Point can not be specified.\n");
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	return !(params->lflag || params->dflag);
 | 
						|
}
 | 
						|
 | 
						|
static int zynqmpbif_check_image_types(uint8_t type)
 | 
						|
{
 | 
						|
	return (type == IH_TYPE_ZYNQMPBIF) ? EXIT_SUCCESS : EXIT_FAILURE;
 | 
						|
}
 | 
						|
 | 
						|
static char *parse_dest_cpu(char *line, struct bif_entry *bf)
 | 
						|
{
 | 
						|
	uint64_t i;
 | 
						|
 | 
						|
	for (i = 0; i < ARRAY_SIZE(dest_cpus); i++) {
 | 
						|
		if (!strncmp(line, dest_cpus[i], strlen(dest_cpus[i]))) {
 | 
						|
			bf->dest_cpu = i << PART_ATTR_DEST_CPU_SHIFT;
 | 
						|
			return line + strlen(dest_cpus[i]);
 | 
						|
		}
 | 
						|
 | 
						|
		/* a5x can also be written as a53 */
 | 
						|
		if (!strncmp(dest_cpus[i], "a5x", 3)) {
 | 
						|
			char a53[] = "a53-X";
 | 
						|
 | 
						|
			a53[4] = dest_cpus[i][4];
 | 
						|
			if (!strncmp(line, a53, strlen(a53))) {
 | 
						|
				bf->dest_cpu = i << PART_ATTR_DEST_CPU_SHIFT;
 | 
						|
				return line + strlen(a53);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return line;
 | 
						|
}
 | 
						|
 | 
						|
static char *parse_el(char *line, struct bif_entry *bf)
 | 
						|
{
 | 
						|
	const char *dest_els[] = { "none", "el-0", "el-1", "el-2", "el-3" };
 | 
						|
	int i;
 | 
						|
 | 
						|
	for (i = 0; i < ARRAY_SIZE(dest_els); i++) {
 | 
						|
		if (!strncmp(line, dest_els[i], strlen(dest_els[i]))) {
 | 
						|
			bf->exp_lvl = i;
 | 
						|
			return line + strlen(dest_els[i]);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return line;
 | 
						|
}
 | 
						|
 | 
						|
static char *parse_load(char *line, struct bif_entry *bf)
 | 
						|
{
 | 
						|
	char *endptr;
 | 
						|
 | 
						|
	bf->load = strtoll(line, &endptr, 0);
 | 
						|
 | 
						|
	return endptr;
 | 
						|
}
 | 
						|
 | 
						|
static char *parse_entry(char *line, struct bif_entry *bf)
 | 
						|
{
 | 
						|
	char *endptr;
 | 
						|
 | 
						|
	bf->entry = strtoll(line, &endptr, 0);
 | 
						|
 | 
						|
	return endptr;
 | 
						|
}
 | 
						|
 | 
						|
static char *parse_offset(char *line, struct bif_entry *bf)
 | 
						|
{
 | 
						|
	char *endptr;
 | 
						|
 | 
						|
	bf->offset = strtoll(line, &endptr, 0);
 | 
						|
 | 
						|
	return endptr;
 | 
						|
}
 | 
						|
 | 
						|
static char *parse_partition_owner(char *line, struct bif_entry *bf)
 | 
						|
{
 | 
						|
	char *endptr = NULL;
 | 
						|
 | 
						|
	if (!strncmp(line, "fsbl", 4)) {
 | 
						|
		endptr = line + 4;
 | 
						|
	} else if (!strncmp(line, "uboot", 5)) {
 | 
						|
		bf->flags |= 1ULL << BIF_FLAG_PART_OWNER_UBOOT;
 | 
						|
		endptr = line + 5;
 | 
						|
	} else {
 | 
						|
		printf("ERROR: Unknown partition type '%s'\n", line);
 | 
						|
	}
 | 
						|
 | 
						|
	return endptr;
 | 
						|
}
 | 
						|
 | 
						|
static const struct bif_flags bif_flags[] = {
 | 
						|
	{ "fsbl_config", BIF_FLAG_FSBL_CONFIG },
 | 
						|
	{ "trustzone", BIF_FLAG_TZ },
 | 
						|
	{ "pmufw_image", BIF_FLAG_PMUFW_IMAGE },
 | 
						|
	{ "bootloader", BIF_FLAG_BOOTLOADER },
 | 
						|
	{ "destination_cpu=", 0, parse_dest_cpu },
 | 
						|
	{ "exception_level=", 0, parse_el },
 | 
						|
	{ "load=", 0, parse_load },
 | 
						|
	{ "startup=", 0, parse_entry },
 | 
						|
	{ "offset=", 0, parse_offset },
 | 
						|
	{ "partition_owner=", 0, parse_partition_owner },
 | 
						|
};
 | 
						|
 | 
						|
static char *read_full_file(const char *filename, size_t *size)
 | 
						|
{
 | 
						|
	char *buf, *bufp;
 | 
						|
	struct stat sbuf;
 | 
						|
	int len = 0, r, fd;
 | 
						|
 | 
						|
	fd = open(filename, O_RDONLY);
 | 
						|
	if (fd < 0)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	if (fstat(fd, &sbuf) < 0)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	if (size)
 | 
						|
		*size = sbuf.st_size;
 | 
						|
 | 
						|
	buf = malloc(sbuf.st_size);
 | 
						|
	if (!buf)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	bufp = buf;
 | 
						|
	while (len < sbuf.st_size) {
 | 
						|
		r = read(fd, bufp, sbuf.st_size - len);
 | 
						|
		if (r < 0)
 | 
						|
			return NULL;
 | 
						|
		len += r;
 | 
						|
		bufp += r;
 | 
						|
	}
 | 
						|
 | 
						|
	close(fd);
 | 
						|
 | 
						|
	return buf;
 | 
						|
}
 | 
						|
 | 
						|
static int bif_add_blob(const void *data, size_t len, size_t *offset)
 | 
						|
{
 | 
						|
	size_t new_size;
 | 
						|
	uintptr_t header_off;
 | 
						|
	uintptr_t last_part_off;
 | 
						|
	uintptr_t imgheader_off;
 | 
						|
	uintptr_t old_data = (uintptr_t)bif_output.data;
 | 
						|
	void *new_data;
 | 
						|
 | 
						|
	header_off = (uintptr_t)bif_output.header - old_data;
 | 
						|
	last_part_off = (uintptr_t)bif_output.last_part - old_data;
 | 
						|
	imgheader_off = (uintptr_t)bif_output.imgheader - old_data;
 | 
						|
 | 
						|
	if (offset && *offset) {
 | 
						|
		/* Pad to a given offset */
 | 
						|
		if (bif_output.data_len > *offset) {
 | 
						|
			printf("Can not pad to offset %zx\n", *offset);
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
 | 
						|
		bif_output.data_len = *offset;
 | 
						|
	}
 | 
						|
 | 
						|
	new_size = ROUND(bif_output.data_len + len, 64);
 | 
						|
	new_data = realloc(bif_output.data, new_size);
 | 
						|
	memcpy(new_data + bif_output.data_len, data, len);
 | 
						|
	if (offset)
 | 
						|
		*offset = bif_output.data_len;
 | 
						|
	bif_output.data = new_data;
 | 
						|
	bif_output.data_len = new_size;
 | 
						|
 | 
						|
	/* Readjust internal pointers */
 | 
						|
	if (bif_output.header)
 | 
						|
		bif_output.header = new_data + header_off;
 | 
						|
	if (bif_output.last_part)
 | 
						|
		bif_output.last_part = new_data + last_part_off;
 | 
						|
	if (bif_output.imgheader)
 | 
						|
		bif_output.imgheader = new_data + imgheader_off;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int bif_init(void)
 | 
						|
{
 | 
						|
	struct zynqmp_header header = { { 0 } };
 | 
						|
	int r;
 | 
						|
 | 
						|
	zynqmpimage_default_header(&header);
 | 
						|
 | 
						|
	r = bif_add_blob(&header, sizeof(header), NULL);
 | 
						|
	if (r)
 | 
						|
		return r;
 | 
						|
 | 
						|
	bif_output.header = (void *)bif_output.data;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int bif_add_pmufw(struct bif_entry *bf, const char *data, size_t len)
 | 
						|
{
 | 
						|
	int r;
 | 
						|
 | 
						|
	if (bif_output.header->image_offset) {
 | 
						|
		printf("PMUFW expected before bootloader in your .bif file!\n");
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	r = bif_add_blob(data, len, &bf->offset);
 | 
						|
	if (r)
 | 
						|
		return r;
 | 
						|
 | 
						|
	len = ROUND(len, 64);
 | 
						|
	bif_output.header->pfw_image_length = cpu_to_le32(len);
 | 
						|
	bif_output.header->total_pfw_image_length = cpu_to_le32(len);
 | 
						|
	bif_output.header->image_offset = cpu_to_le32(bf->offset);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int bif_add_part(struct bif_entry *bf, const char *data, size_t len)
 | 
						|
{
 | 
						|
	size_t parthdr_offset = 0;
 | 
						|
	size_t len_padded = ROUND(len, 4);
 | 
						|
 | 
						|
	struct partition_header parthdr = {
 | 
						|
		.len_enc = cpu_to_le32(len_padded / 4),
 | 
						|
		.len_unenc = cpu_to_le32(len_padded / 4),
 | 
						|
		.len = cpu_to_le32(len_padded / 4),
 | 
						|
		.entry_point = cpu_to_le64(bf->entry),
 | 
						|
		.load_address = cpu_to_le64(bf->load),
 | 
						|
	};
 | 
						|
	int r;
 | 
						|
	uint32_t csum;
 | 
						|
 | 
						|
	if (len < len_padded) {
 | 
						|
		char *newdata = malloc(len_padded);
 | 
						|
		memcpy(newdata, data, len);
 | 
						|
		memset(newdata + len, 0, len_padded - len);
 | 
						|
		data = newdata;
 | 
						|
	}
 | 
						|
 | 
						|
	if (bf->flags & (1ULL << BIF_FLAG_PMUFW_IMAGE))
 | 
						|
		return bif_add_pmufw(bf, data, len);
 | 
						|
 | 
						|
	r = bif_add_blob(data, len, &bf->offset);
 | 
						|
	if (r)
 | 
						|
		return r;
 | 
						|
 | 
						|
	parthdr.offset = cpu_to_le32(bf->offset / 4);
 | 
						|
 | 
						|
	if (bf->flags & (1ULL << BIF_FLAG_BOOTLOADER)) {
 | 
						|
		if (bif_output.last_part) {
 | 
						|
			printf("ERROR: Bootloader expected before others\n");
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
 | 
						|
		parthdr.offset = cpu_to_le32(bif_output.header->image_offset);
 | 
						|
		parthdr.len = cpu_to_le32((bf->offset + len -
 | 
						|
			bif_output.header->image_offset) / 4);
 | 
						|
		parthdr.len_enc = parthdr.len;
 | 
						|
		parthdr.len_unenc = parthdr.len;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Normalize EL */
 | 
						|
	bf->exp_lvl = bf->exp_lvl ? bf->exp_lvl - 1 : 3;
 | 
						|
	parthdr.attributes |= bf->exp_lvl << PART_ATTR_TARGET_EL_SHIFT;
 | 
						|
	parthdr.attributes |= bf->dest_dev;
 | 
						|
	parthdr.attributes |= bf->dest_cpu;
 | 
						|
	if (bf->flags & (1ULL << BIF_FLAG_TZ))
 | 
						|
		parthdr.attributes |= PART_ATTR_TZ_SECURE;
 | 
						|
	if (bf->flags & (1ULL << BIF_FLAG_PART_OWNER_UBOOT))
 | 
						|
		parthdr.attributes |= PART_ATTR_PART_OWNER_UBOOT;
 | 
						|
	switch (bf->dest_cpu) {
 | 
						|
	case PART_ATTR_DEST_CPU_NONE:
 | 
						|
	case PART_ATTR_DEST_CPU_A53_0:
 | 
						|
	case PART_ATTR_DEST_CPU_A53_1:
 | 
						|
	case PART_ATTR_DEST_CPU_A53_2:
 | 
						|
	case PART_ATTR_DEST_CPU_A53_3:
 | 
						|
		if (bf->flags & (1ULL << BIF_FLAG_AARCH32))
 | 
						|
			parthdr.attributes |= PART_ATTR_A53_EXEC_AARCH32;
 | 
						|
	}
 | 
						|
 | 
						|
	csum = zynqmp_csum(&parthdr, &parthdr.checksum);
 | 
						|
	parthdr.checksum = cpu_to_le32(csum);
 | 
						|
 | 
						|
	r = bif_add_blob(&parthdr, sizeof(parthdr), &parthdr_offset);
 | 
						|
	if (r)
 | 
						|
		return r;
 | 
						|
 | 
						|
	/* Add image header table if not there yet */
 | 
						|
	if (!bif_output.imgheader) {
 | 
						|
		size_t imghdr_off = 0;
 | 
						|
		struct image_header_table imghdr = {
 | 
						|
			.version = cpu_to_le32(0x01020000),
 | 
						|
			.nr_parts = 0,
 | 
						|
		};
 | 
						|
 | 
						|
		r = bif_add_blob(&imghdr, sizeof(imghdr), &imghdr_off);
 | 
						|
		if (r)
 | 
						|
			return r;
 | 
						|
 | 
						|
		bif_output.header->image_header_table_offset = imghdr_off;
 | 
						|
		bif_output.imgheader = (void *)(bif_output.data + imghdr_off);
 | 
						|
	}
 | 
						|
 | 
						|
	bif_output.imgheader->nr_parts = cpu_to_le32(le32_to_cpu(
 | 
						|
		bif_output.imgheader->nr_parts) + 1);
 | 
						|
 | 
						|
	/* Link to this partition header */
 | 
						|
	if (bif_output.last_part) {
 | 
						|
		bif_output.last_part->next_partition_offset =
 | 
						|
			cpu_to_le32(parthdr_offset / 4);
 | 
						|
 | 
						|
		/* Recalc checksum of last_part */
 | 
						|
		csum = zynqmp_csum(bif_output.last_part,
 | 
						|
				   &bif_output.last_part->checksum);
 | 
						|
		bif_output.last_part->checksum = cpu_to_le32(csum);
 | 
						|
	} else {
 | 
						|
		bif_output.imgheader->partition_header_offset =
 | 
						|
			cpu_to_le32(parthdr_offset / 4);
 | 
						|
	}
 | 
						|
	bif_output.last_part = (void *)(bif_output.data + parthdr_offset);
 | 
						|
 | 
						|
	if (bf->flags & (1ULL << BIF_FLAG_BOOTLOADER)) {
 | 
						|
		bif_output.header->image_load = cpu_to_le32(bf->load);
 | 
						|
		if (!bif_output.header->image_offset)
 | 
						|
			bif_output.header->image_offset =
 | 
						|
				cpu_to_le32(bf->offset);
 | 
						|
		bif_output.header->image_size = cpu_to_le32(len_padded);
 | 
						|
		bif_output.header->image_stored_size = cpu_to_le32(len_padded);
 | 
						|
 | 
						|
		bif_output.header->image_attributes &= ~HEADER_CPU_SELECT_MASK;
 | 
						|
		switch (bf->dest_cpu) {
 | 
						|
		default:
 | 
						|
		case PART_ATTR_DEST_CPU_A53_0:
 | 
						|
			if (bf->flags & BIF_FLAG_AARCH32)
 | 
						|
				bif_output.header->image_attributes |=
 | 
						|
					HEADER_CPU_SELECT_A53_32BIT;
 | 
						|
			else
 | 
						|
				bif_output.header->image_attributes |=
 | 
						|
					HEADER_CPU_SELECT_A53_64BIT;
 | 
						|
			break;
 | 
						|
		case PART_ATTR_DEST_CPU_R5_0:
 | 
						|
			bif_output.header->image_attributes |=
 | 
						|
				HEADER_CPU_SELECT_R5_SINGLE;
 | 
						|
			break;
 | 
						|
		case PART_ATTR_DEST_CPU_R5_L:
 | 
						|
			bif_output.header->image_attributes |=
 | 
						|
				HEADER_CPU_SELECT_R5_DUAL;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Add .bit bitstream */
 | 
						|
static int bif_add_bit(struct bif_entry *bf)
 | 
						|
{
 | 
						|
	char *bit = read_full_file(bf->filename, NULL);
 | 
						|
	char *bitbin;
 | 
						|
	uint8_t initial_header[] = { 0x00, 0x09, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f,
 | 
						|
				     0xf0, 0x0f, 0xf0, 0x00, 0x00, 0x01, 0x61 };
 | 
						|
	uint16_t len;
 | 
						|
	uint32_t bitlen;
 | 
						|
	int i;
 | 
						|
 | 
						|
	if (!bit)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	/* Skip initial header */
 | 
						|
	if (memcmp(bit, initial_header, sizeof(initial_header)))
 | 
						|
		return -1;
 | 
						|
 | 
						|
	bit += sizeof(initial_header);
 | 
						|
 | 
						|
	/* Design name */
 | 
						|
	len = be16_to_cpu(*(uint16_t *)bit);
 | 
						|
	bit += sizeof(uint16_t);
 | 
						|
	debug("Design: %s\n", bit);
 | 
						|
	bit += len;
 | 
						|
 | 
						|
	/* Device identifier */
 | 
						|
	if (*bit != 'b')
 | 
						|
		return -1;
 | 
						|
	bit++;
 | 
						|
	len = be16_to_cpu(*(uint16_t *)bit);
 | 
						|
	bit += sizeof(uint16_t);
 | 
						|
	debug("Device: %s\n", bit);
 | 
						|
	bit += len;
 | 
						|
 | 
						|
	/* Date */
 | 
						|
	if (*bit != 'c')
 | 
						|
		return -1;
 | 
						|
	bit++;
 | 
						|
	len = be16_to_cpu(*(uint16_t *)bit);
 | 
						|
	bit += sizeof(uint16_t);
 | 
						|
	debug("Date: %s\n", bit);
 | 
						|
	bit += len;
 | 
						|
 | 
						|
	/* Time */
 | 
						|
	if (*bit != 'd')
 | 
						|
		return -1;
 | 
						|
	bit++;
 | 
						|
	len = be16_to_cpu(*(uint16_t *)bit);
 | 
						|
	bit += sizeof(uint16_t);
 | 
						|
	debug("Time: %s\n", bit);
 | 
						|
	bit += len;
 | 
						|
 | 
						|
	/* Bitstream length */
 | 
						|
	if (*bit != 'e')
 | 
						|
		return -1;
 | 
						|
	bit++;
 | 
						|
	bitlen = be32_to_cpu(*(uint32_t *)bit);
 | 
						|
	bit += sizeof(uint32_t);
 | 
						|
	bitbin = bit;
 | 
						|
 | 
						|
	debug("Bitstream Length: 0x%x\n", bitlen);
 | 
						|
	for (i = 0; i < bitlen; i += sizeof(uint32_t)) {
 | 
						|
		uint32_t *bitbin32 = (uint32_t *)&bitbin[i];
 | 
						|
		*bitbin32 = __builtin_bswap32(*bitbin32);
 | 
						|
	}
 | 
						|
 | 
						|
	if (!bf->dest_dev)
 | 
						|
		bf->dest_dev = PART_ATTR_DEST_DEVICE_PL;
 | 
						|
 | 
						|
	bf->load = 0xffffffff;
 | 
						|
	bf->entry = 0;
 | 
						|
 | 
						|
	bf->flags |= 1ULL << BIF_FLAG_BIT_FILE;
 | 
						|
	return bif_add_part(bf, bit, bitlen);
 | 
						|
}
 | 
						|
 | 
						|
/* Add .bin bitstream */
 | 
						|
static int bif_add_bin(struct bif_entry *bf)
 | 
						|
{
 | 
						|
	size_t size;
 | 
						|
	char *bin = read_full_file(bf->filename, &size);
 | 
						|
 | 
						|
	if (!bf->dest_dev)
 | 
						|
		bf->dest_dev = PART_ATTR_DEST_DEVICE_PS;
 | 
						|
 | 
						|
	bf->flags |= 1ULL << BIF_FLAG_BIN_FILE;
 | 
						|
	return bif_add_part(bf, bin, size);
 | 
						|
}
 | 
						|
 | 
						|
/* Add elf file */
 | 
						|
static char *elf2flat64(char *elf, size_t *flat_size, size_t *load_addr)
 | 
						|
{
 | 
						|
	Elf64_Ehdr *ehdr;
 | 
						|
	Elf64_Shdr *shdr;
 | 
						|
	size_t min_addr = -1, max_addr = 0;
 | 
						|
	char *flat;
 | 
						|
	int i;
 | 
						|
 | 
						|
	ehdr = (void *)elf;
 | 
						|
	shdr = (void *)(elf + le64_to_cpu(ehdr->e_shoff));
 | 
						|
 | 
						|
	/* Look for smallest / biggest address */
 | 
						|
	for (i = 0; i < le64_to_cpu(ehdr->e_shnum); i++, shdr++) {
 | 
						|
		if (!shdr->sh_size || !shdr->sh_addr ||
 | 
						|
		    !(shdr->sh_flags & SHF_ALLOC) ||
 | 
						|
		    (shdr->sh_type == SHT_NOBITS))
 | 
						|
			continue;
 | 
						|
 | 
						|
		if (le64_to_cpu(shdr->sh_addr) < min_addr)
 | 
						|
			min_addr = le64_to_cpu(shdr->sh_addr);
 | 
						|
		if ((le64_to_cpu(shdr->sh_addr) + le64_to_cpu(shdr->sh_size)) >
 | 
						|
			max_addr)
 | 
						|
			max_addr = le64_to_cpu(shdr->sh_addr) +
 | 
						|
				   le64_to_cpu(shdr->sh_size);
 | 
						|
	}
 | 
						|
 | 
						|
	*load_addr = min_addr;
 | 
						|
	*flat_size = max_addr - min_addr;
 | 
						|
	flat = calloc(1, *flat_size);
 | 
						|
	if (!flat)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	shdr = (void *)(elf + le64_to_cpu(ehdr->e_shoff));
 | 
						|
	for (i = 0; i < le64_to_cpu(ehdr->e_shnum); i++, shdr++) {
 | 
						|
		char *dst = flat + le64_to_cpu(shdr->sh_addr) - min_addr;
 | 
						|
		char *src = elf + le64_to_cpu(shdr->sh_offset);
 | 
						|
 | 
						|
		if (!shdr->sh_size || !shdr->sh_addr ||
 | 
						|
		    !(shdr->sh_flags & SHF_ALLOC))
 | 
						|
			continue;
 | 
						|
 | 
						|
		if (shdr->sh_type != SHT_NOBITS)
 | 
						|
			memcpy(dst, src, le64_to_cpu(shdr->sh_size));
 | 
						|
	}
 | 
						|
 | 
						|
	return flat;
 | 
						|
}
 | 
						|
 | 
						|
static char *elf2flat32(char *elf, size_t *flat_size, size_t *load_addr)
 | 
						|
{
 | 
						|
	Elf32_Ehdr *ehdr;
 | 
						|
	Elf32_Shdr *shdr;
 | 
						|
	size_t min_addr = -1, max_addr = 0;
 | 
						|
	char *flat;
 | 
						|
	int i;
 | 
						|
 | 
						|
	ehdr = (void *)elf;
 | 
						|
	shdr = (void *)(elf + le32_to_cpu(ehdr->e_shoff));
 | 
						|
 | 
						|
	/* Look for smallest / biggest address */
 | 
						|
	for (i = 0; i < le32_to_cpu(ehdr->e_shnum); i++, shdr++) {
 | 
						|
		if (!shdr->sh_size || !shdr->sh_addr ||
 | 
						|
		    !(shdr->sh_flags & SHF_ALLOC) ||
 | 
						|
		    (shdr->sh_type == SHT_NOBITS))
 | 
						|
			continue;
 | 
						|
 | 
						|
		if (le32_to_cpu(shdr->sh_addr) < min_addr)
 | 
						|
			min_addr = le32_to_cpu(shdr->sh_addr);
 | 
						|
		if ((le32_to_cpu(shdr->sh_addr) + le32_to_cpu(shdr->sh_size)) >
 | 
						|
			max_addr)
 | 
						|
			max_addr = le32_to_cpu(shdr->sh_addr) +
 | 
						|
				   le32_to_cpu(shdr->sh_size);
 | 
						|
	}
 | 
						|
 | 
						|
	*load_addr = min_addr;
 | 
						|
	*flat_size = max_addr - min_addr;
 | 
						|
	flat = calloc(1, *flat_size);
 | 
						|
	if (!flat)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	shdr = (void *)(elf + le32_to_cpu(ehdr->e_shoff));
 | 
						|
	for (i = 0; i < le32_to_cpu(ehdr->e_shnum); i++, shdr++) {
 | 
						|
		char *dst = flat + le32_to_cpu(shdr->sh_addr) - min_addr;
 | 
						|
		char *src = elf + le32_to_cpu(shdr->sh_offset);
 | 
						|
 | 
						|
		if (!shdr->sh_size || !shdr->sh_addr ||
 | 
						|
		    !(shdr->sh_flags & SHF_ALLOC))
 | 
						|
			continue;
 | 
						|
 | 
						|
		if (shdr->sh_type != SHT_NOBITS)
 | 
						|
			memcpy(dst, src, le32_to_cpu(shdr->sh_size));
 | 
						|
	}
 | 
						|
 | 
						|
	return flat;
 | 
						|
}
 | 
						|
 | 
						|
static int bif_add_elf(struct bif_entry *bf)
 | 
						|
{
 | 
						|
	size_t size;
 | 
						|
	size_t elf_size;
 | 
						|
	char *elf;
 | 
						|
	char *flat;
 | 
						|
	size_t load_addr;
 | 
						|
	Elf32_Ehdr *ehdr32;
 | 
						|
	Elf64_Ehdr *ehdr64;
 | 
						|
 | 
						|
	elf = read_full_file(bf->filename, &elf_size);
 | 
						|
	if (!elf)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	ehdr32 = (void *)elf;
 | 
						|
	ehdr64 = (void *)elf;
 | 
						|
 | 
						|
	switch (ehdr32->e_ident[EI_CLASS]) {
 | 
						|
	case ELFCLASS32:
 | 
						|
		flat = elf2flat32(elf, &size, &load_addr);
 | 
						|
		bf->entry = le32_to_cpu(ehdr32->e_entry);
 | 
						|
		bf->flags |= 1ULL << BIF_FLAG_AARCH32;
 | 
						|
		break;
 | 
						|
	case ELFCLASS64:
 | 
						|
		flat = elf2flat64(elf, &size, &load_addr);
 | 
						|
		bf->entry = le64_to_cpu(ehdr64->e_entry);
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		printf("Unknown ELF class: %d\n", ehdr32->e_ident[EI_CLASS]);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!flat)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	bf->load = load_addr;
 | 
						|
	if (!bf->dest_dev)
 | 
						|
		bf->dest_dev = PART_ATTR_DEST_DEVICE_PS;
 | 
						|
 | 
						|
	bf->flags |= 1ULL << BIF_FLAG_ELF_FILE;
 | 
						|
	return bif_add_part(bf, flat, size);
 | 
						|
}
 | 
						|
 | 
						|
static const struct bif_file_type bif_file_types[] = {
 | 
						|
	{
 | 
						|
		.name = "bitstream (.bit)",
 | 
						|
		.header = 0x00090ff0,
 | 
						|
		.add = bif_add_bit,
 | 
						|
	},
 | 
						|
 | 
						|
	{
 | 
						|
		.name = "ELF",
 | 
						|
		.header = 0x7f454c46,
 | 
						|
		.add = bif_add_elf,
 | 
						|
	},
 | 
						|
 | 
						|
	/* Anything else is a .bin file */
 | 
						|
	{
 | 
						|
		.name = ".bin",
 | 
						|
		.add = bif_add_bin,
 | 
						|
	},
 | 
						|
};
 | 
						|
 | 
						|
static int bif_fsbl_config(struct bif_entry *fsbl_config,
 | 
						|
			   struct bif_entry *entries, int nr_entries)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	int config_set = 0;
 | 
						|
	struct {
 | 
						|
		const char *name;
 | 
						|
		uint64_t flags;
 | 
						|
		uint64_t dest_cpu;
 | 
						|
	} configs[] = {
 | 
						|
		{ .name = "a5x_x64", .dest_cpu = PART_ATTR_DEST_CPU_A53_0 },
 | 
						|
		{ .name = "a53_x64", .dest_cpu = PART_ATTR_DEST_CPU_A53_0 },
 | 
						|
		{ .name = "a5x_x32", .dest_cpu = PART_ATTR_DEST_CPU_A53_0,
 | 
						|
				     .flags = 1ULL << BIF_FLAG_AARCH32 },
 | 
						|
		{ .name = "a53_x32", .dest_cpu = PART_ATTR_DEST_CPU_A53_0,
 | 
						|
				     .flags = 1ULL << BIF_FLAG_AARCH32 },
 | 
						|
		{ .name = "r5_single", .dest_cpu = PART_ATTR_DEST_CPU_R5_0 },
 | 
						|
		{ .name = "r5_dual", .dest_cpu = PART_ATTR_DEST_CPU_R5_L },
 | 
						|
	};
 | 
						|
 | 
						|
	/* Set target CPU of bootloader entry */
 | 
						|
	for (i = 0; i < nr_entries; i++) {
 | 
						|
		struct bif_entry *b = &entries[i];
 | 
						|
		const char *config_attr = fsbl_config->filename;
 | 
						|
		int j;
 | 
						|
 | 
						|
		if (!(b->flags & (1ULL << BIF_FLAG_BOOTLOADER)))
 | 
						|
			continue;
 | 
						|
 | 
						|
		for (j = 0; j < ARRAY_SIZE(configs); j++) {
 | 
						|
			if (!strncmp(config_attr, configs[j].name,
 | 
						|
				     strlen(configs[j].name))) {
 | 
						|
				b->dest_cpu = configs[j].dest_cpu;
 | 
						|
				b->flags |= configs[j].flags;
 | 
						|
				config_set = 1;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if (!config_set) {
 | 
						|
			printf("ERROR: Unsupported fsbl_config: %s\n",
 | 
						|
			       config_attr);
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (!config_set) {
 | 
						|
		printf("ERROR: fsbl_config w/o bootloader\n");
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static const struct bif_flags *find_flag(char *str)
 | 
						|
{
 | 
						|
	const struct bif_flags *bf;
 | 
						|
	int i;
 | 
						|
 | 
						|
	for (i = 0; i < ARRAY_SIZE(bif_flags); i++) {
 | 
						|
		bf = &bif_flags[i];
 | 
						|
		if (!strncmp(bf->name, str, strlen(bf->name)))
 | 
						|
			return bf;
 | 
						|
	}
 | 
						|
 | 
						|
	printf("ERROR: Flag '%s' not found\n", str);
 | 
						|
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
static int bif_open_file(struct bif_entry *entry)
 | 
						|
{
 | 
						|
	int fd = open(entry->filename, O_RDONLY);
 | 
						|
 | 
						|
	if (fd < 0)
 | 
						|
		printf("Error opening file %s\n", entry->filename);
 | 
						|
 | 
						|
	return fd;
 | 
						|
}
 | 
						|
 | 
						|
static const struct bif_file_type *get_file_type(struct bif_entry *entry)
 | 
						|
{
 | 
						|
	int fd = bif_open_file(entry);
 | 
						|
	uint32_t header;
 | 
						|
	int i;
 | 
						|
 | 
						|
	if (fd < 0)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	if (read(fd, &header, sizeof(header)) != sizeof(header)) {
 | 
						|
		printf("Error reading file %s", entry->filename);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	close(fd);
 | 
						|
 | 
						|
	for (i = 0; i < ARRAY_SIZE(bif_file_types); i++) {
 | 
						|
		const struct bif_file_type *type = &bif_file_types[i];
 | 
						|
 | 
						|
		if (!type->header)
 | 
						|
			return type;
 | 
						|
		if (type->header == be32_to_cpu(header))
 | 
						|
			return type;
 | 
						|
	}
 | 
						|
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
#define NEXT_CHAR(str, chr) ({		\
 | 
						|
	char *_n = strchr(str, chr);	\
 | 
						|
	if (!_n)			\
 | 
						|
		goto err;		\
 | 
						|
	_n;				\
 | 
						|
})
 | 
						|
 | 
						|
static char *skip_whitespace(char *str)
 | 
						|
{
 | 
						|
	while (*str == ' ' || *str == '\t')
 | 
						|
		str++;
 | 
						|
 | 
						|
	return str;
 | 
						|
}
 | 
						|
 | 
						|
int zynqmpbif_copy_image(int outfd, struct image_tool_params *mparams)
 | 
						|
{
 | 
						|
	char *bif, *bifp, *bifpn;
 | 
						|
	char *line;
 | 
						|
	struct bif_entry entries[32] = { { 0 } };
 | 
						|
	int nr_entries = 0;
 | 
						|
	struct bif_entry *entry = entries;
 | 
						|
	size_t len;
 | 
						|
	int i;
 | 
						|
	uint32_t csum;
 | 
						|
	int bldr = -1;
 | 
						|
 | 
						|
	bif_init();
 | 
						|
 | 
						|
	/* Read .bif input file */
 | 
						|
	bif = read_full_file(mparams->datafile, NULL);
 | 
						|
	if (!bif)
 | 
						|
		goto err;
 | 
						|
 | 
						|
	/* Interpret .bif file */
 | 
						|
	bifp = bif;
 | 
						|
 | 
						|
	/* A bif description starts with a { section */
 | 
						|
	bifp = NEXT_CHAR(bifp, '{') + 1;
 | 
						|
 | 
						|
	/* Read every line */
 | 
						|
	while (1) {
 | 
						|
		bifpn = NEXT_CHAR(bifp, '\n');
 | 
						|
 | 
						|
		if (bifpn[-1] == '\r')
 | 
						|
			bifpn[-1] = '\0';
 | 
						|
 | 
						|
		*bifpn = '\0';
 | 
						|
		bifpn++;
 | 
						|
		line = bifp;
 | 
						|
 | 
						|
		line = skip_whitespace(line);
 | 
						|
 | 
						|
		/* Attributes? */
 | 
						|
		if (*line == '[') {
 | 
						|
			line++;
 | 
						|
			while (1) {
 | 
						|
				const struct bif_flags *bf;
 | 
						|
 | 
						|
				line = skip_whitespace(line);
 | 
						|
				bf = find_flag(line);
 | 
						|
				if (!bf)
 | 
						|
					goto err;
 | 
						|
 | 
						|
				line += strlen(bf->name);
 | 
						|
				if (bf->parse)
 | 
						|
					line = bf->parse(line, entry);
 | 
						|
				else
 | 
						|
					entry->flags |= 1ULL << bf->flag;
 | 
						|
 | 
						|
				if (!line)
 | 
						|
					goto err;
 | 
						|
 | 
						|
				/* Go to next attribute or quit */
 | 
						|
				if (*line == ']') {
 | 
						|
					line++;
 | 
						|
					break;
 | 
						|
				}
 | 
						|
				if (*line == ',')
 | 
						|
					line++;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/* End of image description */
 | 
						|
		if (*line == '}')
 | 
						|
			break;
 | 
						|
 | 
						|
		if (*line) {
 | 
						|
			line = skip_whitespace(line);
 | 
						|
			entry->filename = line;
 | 
						|
			nr_entries++;
 | 
						|
			entry++;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Use next line */
 | 
						|
		bifp = bifpn;
 | 
						|
	}
 | 
						|
 | 
						|
	for (i = 0; i < nr_entries; i++) {
 | 
						|
		debug("Entry flags=%#lx name=%s\n", entries[i].flags,
 | 
						|
		      entries[i].filename);
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Some entries are actually configuration option for other ones,
 | 
						|
	 * let's apply them in an intermediate step.
 | 
						|
	 */
 | 
						|
	for (i = 0; i < nr_entries; i++) {
 | 
						|
		struct bif_entry *entry = &entries[i];
 | 
						|
 | 
						|
		if (entry->flags & (1ULL << BIF_FLAG_FSBL_CONFIG))
 | 
						|
			if (bif_fsbl_config(entry, entries, nr_entries))
 | 
						|
				goto err;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Make sure PMUFW comes before bootloader */
 | 
						|
	for (i = 0; i < nr_entries; i++) {
 | 
						|
		struct bif_entry *entry = &entries[i];
 | 
						|
 | 
						|
		if (entry->flags & (1ULL << BIF_FLAG_BOOTLOADER))
 | 
						|
			bldr = i;
 | 
						|
		if (entry->flags & (1ULL << BIF_FLAG_PMUFW_IMAGE)) {
 | 
						|
			if (bldr >= 0) {
 | 
						|
				struct bif_entry tmp = *entry;
 | 
						|
 | 
						|
				*entry = entries[bldr];
 | 
						|
				entries[bldr] = tmp;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	for (i = 0; i < nr_entries; i++) {
 | 
						|
		struct bif_entry *entry = &entries[i];
 | 
						|
		const struct bif_file_type *type;
 | 
						|
		int r;
 | 
						|
 | 
						|
		if (entry->flags & (1ULL << BIF_FLAG_FSBL_CONFIG))
 | 
						|
			continue;
 | 
						|
 | 
						|
		type = get_file_type(entry);
 | 
						|
		if (!type)
 | 
						|
			goto err;
 | 
						|
 | 
						|
		debug("type=%s file=%s\n", type->name, entry->filename);
 | 
						|
		r = type->add(entry);
 | 
						|
		if (r)
 | 
						|
			goto err;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Calculate checksums */
 | 
						|
	csum = zynqmp_csum(&bif_output.header->width_detection,
 | 
						|
			   &bif_output.header->checksum);
 | 
						|
	bif_output.header->checksum = cpu_to_le32(csum);
 | 
						|
 | 
						|
	if (bif_output.imgheader) {
 | 
						|
		csum = zynqmp_csum(bif_output.imgheader,
 | 
						|
				   &bif_output.imgheader->checksum);
 | 
						|
		bif_output.imgheader->checksum = cpu_to_le32(csum);
 | 
						|
	}
 | 
						|
 | 
						|
	/* Write headers and components */
 | 
						|
	if (lseek(outfd, 0, SEEK_SET) != 0)
 | 
						|
		goto err;
 | 
						|
 | 
						|
	len = bif_output.data_len;
 | 
						|
	bifp = bif_output.data;
 | 
						|
	while (len) {
 | 
						|
		int r;
 | 
						|
 | 
						|
		r = write(outfd, bifp, len);
 | 
						|
		if (r < 0)
 | 
						|
			goto err;
 | 
						|
		len -= r;
 | 
						|
		bifp += r;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
 | 
						|
err:
 | 
						|
	fprintf(stderr, "Error: Failed to create image.\n");
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
/* Needs to be stubbed out so we can print after creation */
 | 
						|
static void zynqmpbif_set_header(void *ptr, struct stat *sbuf, int ifd,
 | 
						|
				 struct image_tool_params *params)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
static struct zynqmp_header zynqmpimage_header;
 | 
						|
 | 
						|
U_BOOT_IMAGE_TYPE(
 | 
						|
	zynqmpbif,
 | 
						|
	"Xilinx ZynqMP Boot Image support (bif)",
 | 
						|
	sizeof(struct zynqmp_header),
 | 
						|
	(void *)&zynqmpimage_header,
 | 
						|
	zynqmpbif_check_params,
 | 
						|
	NULL,
 | 
						|
	zynqmpimage_print_header,
 | 
						|
	zynqmpbif_set_header,
 | 
						|
	NULL,
 | 
						|
	zynqmpbif_check_image_types,
 | 
						|
	NULL,
 | 
						|
	NULL
 | 
						|
);
 |