]> git.sur5r.net Git - u-boot/commitdiff
tools: zynqmpimage: Add bif support
authorAlexander Graf <agraf@suse.de>
Fri, 13 Apr 2018 12:18:52 +0000 (14:18 +0200)
committerMichal Simek <michal.simek@xilinx.com>
Fri, 11 May 2018 07:23:44 +0000 (09:23 +0200)
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>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
common/image.c
include/image.h
tools/Makefile
tools/imagetool.h
tools/mkimage.c
tools/zynqmpbif.c [new file with mode: 0644]
tools/zynqmpimage.c
tools/zynqmpimage.h

index 4eae585218b8d41bd15cb8636d932610e123d61b..87d15010c0c2917dca91a50bf1ca3b6e805ea43f 100644 (file)
@@ -158,6 +158,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" },
index 6a22c98b4aecceeb21ef2b693ac91c67abe31d96..8fa75a5a5a322faa9240bb2ce8b80679f0b0db4b 100644 (file)
@@ -268,6 +268,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 */
index 1185bf5b36f6864c9c769534fd94790153868290..5dd33ed4d5611b9740bad90fcd25516b18f01dfe 100644 (file)
@@ -111,6 +111,7 @@ dumpimage-mkimage-objs := aisimage.o \
                        ublimage.o \
                        zynqimage.o \
                        zynqmpimage.o \
+                       zynqmpbif.o \
                        $(LIBFDT_OBJS) \
                        gpimage.o \
                        gpimage-common.o \
index ef2429e050728024ab82376345418a9fee82ec8c..d191b9cfe709637a479b41697046f59b7263e2fe 100644 (file)
@@ -231,6 +231,7 @@ time_t imagetool_get_source_date(
 
 
 void pbl_load_uboot(int fd, struct image_tool_params *mparams);
+int zynqmpbif_copy_image(int fd, struct image_tool_params *mparams);
 
 #define ___cat(a, b) a ## b
 #define __cat(a, b) ___cat(a, b)
index 64ad131860b08c7b2d5d2e772c1e55095967be46..e0d4d20be499cd1e869466e9594269034251fba9 100644 (file)
@@ -515,6 +515,13 @@ 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 */
+                       int ret;
+
+                       ret = zynqmpbif_copy_image(ifd, &params);
+                       if (ret)
+                               return ret;
                } else {
                        copy_file(ifd, params.datafile, pad_len);
                }
diff --git a/tools/zynqmpbif.c b/tools/zynqmpbif.c
new file mode 100644 (file)
index 0000000..6c8f660
--- /dev/null
@@ -0,0 +1,1008 @@
+/*
+ * 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;
+       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);
+
+       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);
+               bif_output.header->image_stored_size = cpu_to_le32(len);
+
+               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 = __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++, 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
+);
index 870a6b5710f876bff98c042ad298ff310a43b015..19b2f02ff150bf751316edff33004590a9e1d2fa 100644 (file)
@@ -86,7 +86,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;
 
@@ -209,7 +209,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;
index f3b5c195ad9b8d7180d7db73ebb72fffc74404aa..7a57681709cef50b52b89afca8595de0b95f0544 100644 (file)
 #define HEADER_REGINIT_NULL (cpu_to_le32(0xffffffff))
 #define HEADER_WIDTHDETECTION (cpu_to_le32(0xaa995566))
 #define HEADER_IMAGEIDENTIFIER (cpu_to_le32(0x584c4e58))
+#define HEADER_CPU_SELECT_MASK         (0x3 << 10)
+#define HEADER_CPU_SELECT_R5_SINGLE    (0x0 << 10)
+#define HEADER_CPU_SELECT_A53_32BIT    (0x1 << 10)
 #define HEADER_CPU_SELECT_A53_64BIT    (0x2 << 10)
+#define HEADER_CPU_SELECT_R5_DUAL      (0x3 << 10)
 
 enum {
        ENCRYPTION_EFUSE = 0xa5c3c5a3,
@@ -129,4 +133,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_ */