Message ID | 20180413121852.38942-5-agraf@suse.de |
---|---|
State | Accepted |
Commit | 6915dcf35987d654b491524f151e56b91e0d0ec9 |
Headers | show |
Series | tools: zynqmpimage: Support partitions | expand |
Michael or Alex, Could someone add a ZynqMP README documenting the process required to use U-Boot for the ZynqMP with the open tools? I looked in board/xilinx/zynqmp and doc/ and a few other places but couldn't see any docs for either that or the closed tools. Peter On Fri, Apr 13, 2018 at 1:18 PM, Alexander Graf <agraf@suse.de> wrote: > 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> > > --- > > v2 -> v3: > > - zero initialize header > - reduce default debug verbosity > > v3 -> v4: > > - add error handling > - add fsbl_config support > - add aarch32 support > - allow a5x to be written as a53 > - add offset support > - add support for partition_owner > - ensure pmufw comes before bootloader > - simplify fsbl_config > - add non-a53 boot support > - checkpatch fixes > --- > common/image.c | 1 + > include/image.h | 1 + > tools/Makefile | 1 + > tools/imagetool.h | 1 + > tools/mkimage.c | 7 + > tools/zynqmpbif.c | 1008 +++++++++++++++++++++++++++++++++++++++++++++++++++ > tools/zynqmpimage.c | 4 +- > tools/zynqmpimage.h | 7 + > 8 files changed, 1028 insertions(+), 2 deletions(-) > create mode 100644 tools/zynqmpbif.c > > 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..6a7e7386f7 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); > +int 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..fe861f5405 100644 > --- a/tools/mkimage.c > +++ b/tools/mkimage.c > @@ -514,6 +514,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, ¶ms); > + } else if (params.type == IH_TYPE_ZYNQMPBIF) { > + /* Image file is meta, walk through actual targets */ > + int ret; > + > + ret = zynqmpbif_copy_image(ifd, ¶ms); > + 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 > index 0000000000..6c8f66055d > --- /dev/null > +++ b/tools/zynqmpbif.c > @@ -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 > +); > diff --git a/tools/zynqmpimage.c b/tools/zynqmpimage.c > index 3bd23b9bf8..288816d6cd 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; > > @@ -210,7 +210,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..7a57681709 100644 > --- a/tools/zynqmpimage.h > +++ b/tools/zynqmpimage.h > @@ -19,7 +19,11 @@ > #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_ */ > -- > 2.12.3 > > _______________________________________________ > U-Boot mailing list > U-Boot@lists.denx.de > https://lists.denx.de/listinfo/u-boot
Hi Peter, I'm not sure how much documentation you want. Basically mkimage becomes a replacement for bootgen in any official Xilinx documentation. So any Xilinx wiki like http://www.wiki.xilinx.com/Prepare+boot+image or even the official .bif documentation: https://www.xilinx.com/support/documentation/user_guides/ug1137-zynq-ultrascale-mpsoc-swdev.pdf apply. The only difference is that the command line arguments are different. But mkimage takes a .bif file as input and generates a boot.bin file as output. Alex On 13.06.18 08:49, Peter Robinson wrote: > Michael or Alex, > > Could someone add a ZynqMP README documenting the process required to > use U-Boot for the ZynqMP with the open tools? I looked in > board/xilinx/zynqmp and doc/ and a few other places but couldn't see > any docs for either that or the closed tools. > > Peter > > On Fri, Apr 13, 2018 at 1:18 PM, Alexander Graf <agraf@suse.de> wrote: >> 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> >> >> --- >> >> v2 -> v3: >> >> - zero initialize header >> - reduce default debug verbosity >> >> v3 -> v4: >> >> - add error handling >> - add fsbl_config support >> - add aarch32 support >> - allow a5x to be written as a53 >> - add offset support >> - add support for partition_owner >> - ensure pmufw comes before bootloader >> - simplify fsbl_config >> - add non-a53 boot support >> - checkpatch fixes >> --- >> common/image.c | 1 + >> include/image.h | 1 + >> tools/Makefile | 1 + >> tools/imagetool.h | 1 + >> tools/mkimage.c | 7 + >> tools/zynqmpbif.c | 1008 +++++++++++++++++++++++++++++++++++++++++++++++++++ >> tools/zynqmpimage.c | 4 +- >> tools/zynqmpimage.h | 7 + >> 8 files changed, 1028 insertions(+), 2 deletions(-) >> create mode 100644 tools/zynqmpbif.c >> >> 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..6a7e7386f7 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); >> +int 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..fe861f5405 100644 >> --- a/tools/mkimage.c >> +++ b/tools/mkimage.c >> @@ -514,6 +514,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, ¶ms); >> + } else if (params.type == IH_TYPE_ZYNQMPBIF) { >> + /* Image file is meta, walk through actual targets */ >> + int ret; >> + >> + ret = zynqmpbif_copy_image(ifd, ¶ms); >> + 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 >> index 0000000000..6c8f66055d >> --- /dev/null >> +++ b/tools/zynqmpbif.c >> @@ -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 >> +); >> diff --git a/tools/zynqmpimage.c b/tools/zynqmpimage.c >> index 3bd23b9bf8..288816d6cd 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; >> >> @@ -210,7 +210,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..7a57681709 100644 >> --- a/tools/zynqmpimage.h >> +++ b/tools/zynqmpimage.h >> @@ -19,7 +19,11 @@ >> #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_ */ >> -- >> 2.12.3 >> >> _______________________________________________ >> U-Boot mailing list >> U-Boot@lists.denx.de >> https://lists.denx.de/listinfo/u-boot >
Hi Alex On Wed, Jun 13, 2018 at 11:07 AM, Alexander Graf <agraf@suse.de> wrote: > Hi Peter, > > I'm not sure how much documentation you want. Basically mkimage becomes > a replacement for bootgen in any official Xilinx documentation. So any > Xilinx wiki like > > http://www.wiki.xilinx.com/Prepare+boot+image > > or even the official .bif documentation: > > > https://www.xilinx.com/support/documentation/user_guides/ug1137-zynq-ultrascale-mpsoc-swdev.pdf > > apply. The only difference is that the command line arguments are > different. But mkimage takes a .bif file as input and generates a > boot.bin file as output. Thanks, good resources, a readme like doc/README.chromium outlining the process for people wanting to get started would likely be a good thing in general with links to those sorts of other resources would be useful for zynqmp like most of the other device categories. I was kind of surprised actually that zynqmp didn't have some sort of related docs to deal with it already. Thanks > On 13.06.18 08:49, Peter Robinson wrote: >> Michael or Alex, >> >> Could someone add a ZynqMP README documenting the process required to >> use U-Boot for the ZynqMP with the open tools? I looked in >> board/xilinx/zynqmp and doc/ and a few other places but couldn't see >> any docs for either that or the closed tools. >> >> Peter >> >> On Fri, Apr 13, 2018 at 1:18 PM, Alexander Graf <agraf@suse.de> wrote: >>> 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> >>> >>> --- >>> >>> v2 -> v3: >>> >>> - zero initialize header >>> - reduce default debug verbosity >>> >>> v3 -> v4: >>> >>> - add error handling >>> - add fsbl_config support >>> - add aarch32 support >>> - allow a5x to be written as a53 >>> - add offset support >>> - add support for partition_owner >>> - ensure pmufw comes before bootloader >>> - simplify fsbl_config >>> - add non-a53 boot support >>> - checkpatch fixes >>> --- >>> common/image.c | 1 + >>> include/image.h | 1 + >>> tools/Makefile | 1 + >>> tools/imagetool.h | 1 + >>> tools/mkimage.c | 7 + >>> tools/zynqmpbif.c | 1008 +++++++++++++++++++++++++++++++++++++++++++++++++++ >>> tools/zynqmpimage.c | 4 +- >>> tools/zynqmpimage.h | 7 + >>> 8 files changed, 1028 insertions(+), 2 deletions(-) >>> create mode 100644 tools/zynqmpbif.c >>> >>> 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..6a7e7386f7 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); >>> +int 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..fe861f5405 100644 >>> --- a/tools/mkimage.c >>> +++ b/tools/mkimage.c >>> @@ -514,6 +514,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, ¶ms); >>> + } else if (params.type == IH_TYPE_ZYNQMPBIF) { >>> + /* Image file is meta, walk through actual targets */ >>> + int ret; >>> + >>> + ret = zynqmpbif_copy_image(ifd, ¶ms); >>> + 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 >>> index 0000000000..6c8f66055d >>> --- /dev/null >>> +++ b/tools/zynqmpbif.c >>> @@ -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 >>> +); >>> diff --git a/tools/zynqmpimage.c b/tools/zynqmpimage.c >>> index 3bd23b9bf8..288816d6cd 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; >>> >>> @@ -210,7 +210,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..7a57681709 100644 >>> --- a/tools/zynqmpimage.h >>> +++ b/tools/zynqmpimage.h >>> @@ -19,7 +19,11 @@ >>> #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_ */ >>> -- >>> 2.12.3 >>> >>> _______________________________________________ >>> U-Boot mailing list >>> U-Boot@lists.denx.de >>> https://lists.denx.de/listinfo/u-boot >>
Hi, > Hi Alex > > On Wed, Jun 13, 2018 at 11:07 AM, Alexander Graf <agraf@suse.de> wrote: >> Hi Peter, >> >> I'm not sure how much documentation you want. Basically mkimage becomes >> a replacement for bootgen in any official Xilinx documentation. So any >> Xilinx wiki like >> >> http://www.wiki.xilinx.com/Prepare+boot+image >> >> or even the official .bif documentation: >> >> >> https://www.xilinx.com/support/documentation/user_guides/ug1137-zynq-ultrascale-mpsoc-swdev.pdf >> >> apply. The only difference is that the command line arguments are >> different. But mkimage takes a .bif file as input and generates a >> boot.bin file as output. > > Thanks, good resources, a readme like doc/README.chromium outlining > the process for people wanting to get started would likely be a good > thing in general with links to those sorts of other resources would be > useful for zynqmp like most of the other device categories. I was kind > of surprised actually that zynqmp didn't have some sort of related > docs to deal with it already. Xilinx has wiki.xilinx.com pages to cover xilinx differences. Definitely feel free to compose one and I am happy to review it. Thanks, Michal
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..6a7e7386f7 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); +int 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..fe861f5405 100644 --- a/tools/mkimage.c +++ b/tools/mkimage.c @@ -514,6 +514,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, ¶ms); + } else if (params.type == IH_TYPE_ZYNQMPBIF) { + /* Image file is meta, walk through actual targets */ + int ret; + + ret = zynqmpbif_copy_image(ifd, ¶ms); + 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 index 0000000000..6c8f66055d --- /dev/null +++ b/tools/zynqmpbif.c @@ -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 +); diff --git a/tools/zynqmpimage.c b/tools/zynqmpimage.c index 3bd23b9bf8..288816d6cd 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; @@ -210,7 +210,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..7a57681709 100644 --- a/tools/zynqmpimage.h +++ b/tools/zynqmpimage.h @@ -19,7 +19,11 @@ #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_ */
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> --- v2 -> v3: - zero initialize header - reduce default debug verbosity v3 -> v4: - add error handling - add fsbl_config support - add aarch32 support - allow a5x to be written as a53 - add offset support - add support for partition_owner - ensure pmufw comes before bootloader - simplify fsbl_config - add non-a53 boot support - checkpatch fixes --- common/image.c | 1 + include/image.h | 1 + tools/Makefile | 1 + tools/imagetool.h | 1 + tools/mkimage.c | 7 + tools/zynqmpbif.c | 1008 +++++++++++++++++++++++++++++++++++++++++++++++++++ tools/zynqmpimage.c | 4 +- tools/zynqmpimage.h | 7 + 8 files changed, 1028 insertions(+), 2 deletions(-) create mode 100644 tools/zynqmpbif.c