diff mbox series

[v2,3/3] tools: zynqmpimage: Add bif support

Message ID 20180412134158.69300-4-agraf@suse.de
State Superseded
Headers show
Series tools: zynqmpimage: Support partitions | expand

Commit Message

Alexander Graf April 12, 2018, 1:41 p.m. UTC
The officially described way to generate boot.bin files for ZynqMP is to
describe the contents of the target binary using a file of the "bif" format.
This file then links to other files that all get packed into a bootable image.

This patch adds support to read such a .bif file and generate a respective
ZynqMP boot.bin file that can include the normal image and pmu files, but also
supports image partitions now. This makes it a handy replacement for the
proprietary "bootgen" utility that is currently used to generate boot.bin
files with FSBL.

Signed-off-by: Alexander Graf <agraf@suse.de>
---
 common/image.c      |   1 +
 include/image.h     |   1 +
 tools/Makefile      |   1 +
 tools/imagetool.h   |   1 +
 tools/mkimage.c     |   3 +
 tools/zynqmpbif.c   | 839 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 tools/zynqmpimage.c |   4 +-
 tools/zynqmpimage.h |   3 +
 8 files changed, 851 insertions(+), 2 deletions(-)
 create mode 100644 tools/zynqmpbif.c
diff mbox series

Patch

diff --git a/common/image.c b/common/image.c
index e1c50eb25d..f30dfa229b 100644
--- a/common/image.c
+++ b/common/image.c
@@ -159,6 +159,7 @@  static const table_entry_t uimage_type[] = {
 	{	IH_TYPE_VYBRIDIMAGE, "vybridimage",  "Vybrid Boot Image", },
 	{	IH_TYPE_ZYNQIMAGE,  "zynqimage",  "Xilinx Zynq Boot Image" },
 	{	IH_TYPE_ZYNQMPIMAGE, "zynqmpimage", "Xilinx ZynqMP Boot Image" },
+	{	IH_TYPE_ZYNQMPBIF,  "zynqmpbif",  "Xilinx ZynqMP Boot Image (bif)" },
 	{	IH_TYPE_FPGA,       "fpga",       "FPGA Image" },
 	{       IH_TYPE_TEE,        "tee",        "Trusted Execution Environment Image",},
 	{	IH_TYPE_FIRMWARE_IVT, "firmware_ivt", "Firmware with HABv4 IVT" },
diff --git a/include/image.h b/include/image.h
index a579c5f509..c5af912aeb 100644
--- a/include/image.h
+++ b/include/image.h
@@ -269,6 +269,7 @@  enum {
 	IH_TYPE_RKSPI,			/* Rockchip SPI image		*/
 	IH_TYPE_ZYNQIMAGE,		/* Xilinx Zynq Boot Image */
 	IH_TYPE_ZYNQMPIMAGE,		/* Xilinx ZynqMP Boot Image */
+	IH_TYPE_ZYNQMPBIF,		/* Xilinx ZynqMP Boot Image (bif) */
 	IH_TYPE_FPGA,			/* FPGA Image */
 	IH_TYPE_VYBRIDIMAGE,	/* VYBRID .vyb Image */
 	IH_TYPE_TEE,            /* Trusted Execution Environment OS Image */
diff --git a/tools/Makefile b/tools/Makefile
index 8143c25666..204685ec9e 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -113,6 +113,7 @@  dumpimage-mkimage-objs := aisimage.o \
 			ublimage.o \
 			zynqimage.o \
 			zynqmpimage.o \
+			zynqmpbif.o \
 			$(LIBFDT_OBJS) \
 			gpimage.o \
 			gpimage-common.o \
diff --git a/tools/imagetool.h b/tools/imagetool.h
index e67de9b5ad..d78a9458f4 100644
--- a/tools/imagetool.h
+++ b/tools/imagetool.h
@@ -232,6 +232,7 @@  time_t imagetool_get_source_date(
 
 
 void pbl_load_uboot(int fd, struct image_tool_params *mparams);
+void zynqmpbif_copy_image(int fd, struct image_tool_params *mparams);
 
 #define ___cat(a, b) a ## b
 #define __cat(a, b) ___cat(a, b)
diff --git a/tools/mkimage.c b/tools/mkimage.c
index 4e561820e7..72183f5f2b 100644
--- a/tools/mkimage.c
+++ b/tools/mkimage.c
@@ -514,6 +514,9 @@  int main(int argc, char **argv)
 		} else if (params.type == IH_TYPE_PBLIMAGE) {
 			/* PBL has special Image format, implements its' own */
 			pbl_load_uboot(ifd, &params);
+		} else if (params.type == IH_TYPE_ZYNQMPBIF) {
+			/* Image file is meta, walk through actual targets */
+			zynqmpbif_copy_image(ifd, &params);
 		} else {
 			copy_file(ifd, params.datafile, pad_len);
 		}
diff --git a/tools/zynqmpbif.c b/tools/zynqmpbif.c
new file mode 100644
index 0000000000..47218873eb
--- /dev/null
+++ b/tools/zynqmpbif.c
@@ -0,0 +1,839 @@ 
+/*
+ * 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;
+};
+
+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,
+
+	/* 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)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(dest_cpus); i++) {
+		if (!strncmp(line, dest_cpus[i], strlen(dest_cpus[i]))) {
+			bf->dest_cpu = i;
+			return line + strlen(dest_cpus[i]);
+		}
+	}
+
+	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 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 },
+};
+
+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;
+
+	bufp = buf = malloc(sbuf.st_size);
+	if (!buf)
+		return NULL;
+
+	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 = ROUND(bif_output.data_len + len, 64);
+	uintptr_t header_off;
+	uintptr_t last_part_off;
+	uintptr_t imgheader_off;
+	uintptr_t old_data = (uintptr_t)bif_output.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;
+
+	bif_output.data = realloc(bif_output.data, new_size);
+	memcpy(bif_output.data + bif_output.data_len, data, len);
+	if (offset)
+		*offset = bif_output.data_len;
+	bif_output.data_len = new_size;
+
+	/* Readjust internal pointers */
+	if (bif_output.header)
+		bif_output.header = (void*)(bif_output.data + header_off);
+	if (bif_output.last_part)
+		bif_output.last_part = (void*)(bif_output.data + last_part_off);
+	if (bif_output.imgheader)
+		bif_output.imgheader = (void*)(bif_output.data + imgheader_off);
+
+	return 0;
+}
+
+static int bif_init(void)
+{
+	struct zynqmp_header header;
+	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)
+{
+	size_t offset;
+
+	if (bif_output.header->image_offset) {
+		printf("PMUFW expected before bootloader in your .bif file!\n");
+		return -1;
+	}
+
+	bif_add_blob(data, len, &offset);
+	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(offset);
+
+	return 0;
+}
+
+static int bif_add_part(struct bif_entry *bf, const char *data, size_t len)
+{
+	size_t parthdr_offset, part_offset;
+	struct partition_header parthdr = {
+		.len_enc = cpu_to_le32(len / 4),
+		.len_unenc = cpu_to_le32(len / 4),
+		.len = cpu_to_le32(len / 4),
+		.entry_point = cpu_to_le64(bf->entry),
+		.load_address = cpu_to_le64(bf->load),
+	};
+	int r;
+	uint32_t csum;
+
+	if (bf->flags & (1ULL << BIF_FLAG_PMUFW_IMAGE))
+		return bif_add_pmufw(bf, data, len);
+
+	bif_add_blob(data, len, &part_offset);
+	parthdr.offset = cpu_to_le32(part_offset / 4);
+
+	if (bf->flags & (1ULL << BIF_FLAG_BOOTLOADER)) {
+		if (bif_output.last_part) {
+			printf("ERROR: Bootloader needs to come as first non-PMU partition");
+			return -1;
+		}
+
+		parthdr.offset = cpu_to_le32(bif_output.header->image_offset);
+		parthdr.len = cpu_to_le32((part_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 << PART_ATTR_DEST_CPU_SHIFT;
+	if (bf->flags & (1ULL << BIF_FLAG_TZ))
+		parthdr.attributes |= PART_ATTR_TZ_SECURE;
+
+	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;
+		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(part_offset);
+		bif_output.header->image_size = cpu_to_le32(len);
+		bif_output.header->image_stored_size = cpu_to_le32(len);
+	}
+
+	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);
+	printf("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);
+	printf("Device: %s\n", bit);
+	bit += len;
+
+	/* Date */
+	if (*bit != 'c')
+		return -1;
+	bit++;
+	len = be16_to_cpu(*(uint16_t*)bit);
+	bit += sizeof(uint16_t);
+	printf("Date: %s\n", bit);
+	bit += len;
+
+	/* Time */
+	if (*bit != 'd')
+		return -1;
+	bit++;
+	len = be16_to_cpu(*(uint16_t*)bit);
+	bit += sizeof(uint16_t);
+	printf("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;
+
+	printf("Bitstream Length: 0x%x\n", bitlen);
+	for (i = 0; i < bitlen; i += sizeof(uint32_t)) {
+		uint32_t *bitbin32 = (uint32_t*)&bitbin[i];
+		*bitbin32 = __swab32(*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++) {
+		if (!shdr->sh_size || !shdr->sh_addr ||
+		    !(shdr->sh_flags & SHF_ALLOC) ||
+		    (shdr->sh_type == SHT_NOBITS)) {
+			shdr++;
+			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);
+
+		shdr++;
+	}
+
+	*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++) {
+		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)) {
+			shdr++;
+			continue;
+		}
+
+		if (shdr->sh_type != SHT_NOBITS) {
+
+			memcpy(dst, src, le64_to_cpu(shdr->sh_size));
+		}
+		shdr++;
+	}
+
+	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++) {
+		if (!shdr->sh_size || !shdr->sh_addr ||
+		    !(shdr->sh_flags & SHF_ALLOC) ||
+		    (shdr->sh_type == SHT_NOBITS)) {
+			shdr++;
+			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);
+
+		shdr++;
+	}
+
+	*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++) {
+		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)) {
+			shdr++;
+			continue;
+		}
+
+		if (shdr->sh_type != SHT_NOBITS) {
+
+			memcpy(dst, src, le32_to_cpu(shdr->sh_size));
+		}
+		shdr++;
+	}
+
+	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);
+		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 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;
+}
+
+void 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;
+
+	bif_init();
+
+	/* Read .bif input file */
+	bifp = 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');
+
+		*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;
+
+				/* 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);
+	}
+
+	for (i = 0; i < nr_entries; i++) {
+		struct bif_entry *entry = &entries[i];
+		const struct bif_file_type *type;
+		int r;
+
+		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;
+
+err:
+	fprintf(stderr, "Error: Failed to create image.\n");
+}
+
+/* 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
+);
diff --git a/tools/zynqmpimage.c b/tools/zynqmpimage.c
index 8f4766f077..145391de3e 100644
--- a/tools/zynqmpimage.c
+++ b/tools/zynqmpimage.c
@@ -87,7 +87,7 @@  static uint32_t zynqmpimage_checksum(struct zynqmp_header *ptr)
 	return cpu_to_le32(checksum);
 }
 
-static void zynqmpimage_default_header(struct zynqmp_header *ptr)
+void zynqmpimage_default_header(struct zynqmp_header *ptr)
 {
 	int i;
 
@@ -211,7 +211,7 @@  static void print_partition(const void *ptr, const struct partition_header *ph)
 	printf("    Checksum   : 0x%08x\n", le32_to_cpu(ph->checksum));
 }
 
-static void zynqmpimage_print_header(const void *ptr)
+void zynqmpimage_print_header(const void *ptr)
 {
 	struct zynqmp_header *zynqhdr = (struct zynqmp_header *)ptr;
 	int i;
diff --git a/tools/zynqmpimage.h b/tools/zynqmpimage.h
index f3b5c195ad..b421e4f94c 100644
--- a/tools/zynqmpimage.h
+++ b/tools/zynqmpimage.h
@@ -129,4 +129,7 @@  struct zynqmp_header {
 	uint32_t __reserved4[66]; /* 0x9c0 */
 };
 
+void zynqmpimage_default_header(struct zynqmp_header *ptr);
+void zynqmpimage_print_header(const void *ptr);
+
 #endif /* _ZYNQMPIMAGE_H_ */