Message ID | 20180412134823.77239-4-agraf@suse.de |
---|---|
State | New |
Headers | show |
Series | tools: zynqmpimage: Support partitions | expand |
On 12.4.2018 15:48, Alexander Graf 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 > --- > common/image.c | 1 + > include/image.h | 1 + > tools/Makefile | 1 + > tools/imagetool.h | 1 + > tools/mkimage.c | 3 + > tools/zynqmpbif.c | 839 ++++++++++++++++++++++++++++++++++++++++++++++++++++ > tools/zynqmpimage.c | 4 +- > tools/zynqmpimage.h | 3 + > 8 files changed, 851 insertions(+), 2 deletions(-) > create mode 100644 tools/zynqmpbif.c > > diff --git a/common/image.c b/common/image.c > index e1c50eb25d..f30dfa229b 100644 > --- a/common/image.c > +++ b/common/image.c > @@ -159,6 +159,7 @@ static const table_entry_t uimage_type[] = { > { IH_TYPE_VYBRIDIMAGE, "vybridimage", "Vybrid Boot Image", }, > { IH_TYPE_ZYNQIMAGE, "zynqimage", "Xilinx Zynq Boot Image" }, > { IH_TYPE_ZYNQMPIMAGE, "zynqmpimage", "Xilinx ZynqMP Boot Image" }, > + { IH_TYPE_ZYNQMPBIF, "zynqmpbif", "Xilinx ZynqMP Boot Image (bif)" }, > { IH_TYPE_FPGA, "fpga", "FPGA Image" }, > { IH_TYPE_TEE, "tee", "Trusted Execution Environment Image",}, > { IH_TYPE_FIRMWARE_IVT, "firmware_ivt", "Firmware with HABv4 IVT" }, > diff --git a/include/image.h b/include/image.h > index a579c5f509..c5af912aeb 100644 > --- a/include/image.h > +++ b/include/image.h > @@ -269,6 +269,7 @@ enum { > IH_TYPE_RKSPI, /* Rockchip SPI image */ > IH_TYPE_ZYNQIMAGE, /* Xilinx Zynq Boot Image */ > IH_TYPE_ZYNQMPIMAGE, /* Xilinx ZynqMP Boot Image */ > + IH_TYPE_ZYNQMPBIF, /* Xilinx ZynqMP Boot Image (bif) */ > IH_TYPE_FPGA, /* FPGA Image */ > IH_TYPE_VYBRIDIMAGE, /* VYBRID .vyb Image */ > IH_TYPE_TEE, /* Trusted Execution Environment OS Image */ > diff --git a/tools/Makefile b/tools/Makefile > index 8143c25666..204685ec9e 100644 > --- a/tools/Makefile > +++ b/tools/Makefile > @@ -113,6 +113,7 @@ dumpimage-mkimage-objs := aisimage.o \ > ublimage.o \ > zynqimage.o \ > zynqmpimage.o \ > + zynqmpbif.o \ > $(LIBFDT_OBJS) \ > gpimage.o \ > gpimage-common.o \ > diff --git a/tools/imagetool.h b/tools/imagetool.h > index e67de9b5ad..d78a9458f4 100644 > --- a/tools/imagetool.h > +++ b/tools/imagetool.h > @@ -232,6 +232,7 @@ time_t imagetool_get_source_date( > > > void pbl_load_uboot(int fd, struct image_tool_params *mparams); > +void zynqmpbif_copy_image(int fd, struct image_tool_params *mparams); This should return value which you will check > > #define ___cat(a, b) a ## b > #define __cat(a, b) ___cat(a, b) > diff --git a/tools/mkimage.c b/tools/mkimage.c > index 4e561820e7..72183f5f2b 100644 > --- a/tools/mkimage.c > +++ b/tools/mkimage.c > @@ -514,6 +514,9 @@ int main(int argc, char **argv) > } else if (params.type == IH_TYPE_PBLIMAGE) { > /* PBL has special Image format, implements its' own */ > pbl_load_uboot(ifd, ¶ms); > + } else if (params.type == IH_TYPE_ZYNQMPBIF) { > + /* Image file is meta, walk through actual targets */ > + zynqmpbif_copy_image(ifd, ¶ms); The same here because if there is for example comment in bif format which is permitted than you need to error out. > } else { > copy_file(ifd, params.datafile, pad_len); > } > diff --git a/tools/zynqmpbif.c b/tools/zynqmpbif.c > new file mode 100644 > index 0000000000..d60eff1d8b > --- /dev/null > +++ b/tools/zynqmpbif.c > @@ -0,0 +1,839 @@ > +/* > + * Copyright (C) 2018 Alexander Graf <agraf@suse.de> > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#include "imagetool.h" > +#include "mkimage.h" > +#include "zynqmpimage.h" > +#include <elf.h> > +#include <image.h> > + > +struct bif_entry { > + const char *filename; > + uint64_t flags; > + uint64_t dest_cpu; > + uint64_t exp_lvl; > + uint64_t dest_dev; > + uint64_t load; > + uint64_t entry; > +}; > + > +enum bif_flag { > + BIF_FLAG_AESKEYFILE, > + BIF_FLAG_INIT, > + BIF_FLAG_UDF_BH, > + BIF_FLAG_HEADERSIGNATURE, > + BIF_FLAG_PPKFILE, > + BIF_FLAG_PSKFILE, > + BIF_FLAG_SPKFILE, > + BIF_FLAG_SSKFILE, > + BIF_FLAG_SPKSIGNATURE, > + BIF_FLAG_FSBL_CONFIG, > + BIF_FLAG_AUTH_PARAMS, > + BIF_FLAG_KEYSRC_ENCRYPTION, > + BIF_FLAG_PMUFW_IMAGE, > + BIF_FLAG_BOOTLOADER, > + BIF_FLAG_TZ, > + BIF_FLAG_BH_KEY_IV, > + BIF_FLAG_BH_KEYFILE, > + BIF_FLAG_PUF_FILE, > + > + /* Internal flags */ > + BIF_FLAG_BIT_FILE, > + BIF_FLAG_ELF_FILE, > + BIF_FLAG_BIN_FILE, > +}; > + > +struct bif_flags { > + const char name[32]; > + uint64_t flag; > + char *(*parse)(char *line, struct bif_entry *bf); > +}; > + > +struct bif_file_type { > + const char name[32]; > + uint32_t header; > + int (*add)(struct bif_entry *bf); > +}; > + > +struct bif_output { > + size_t data_len; > + char *data; > + struct image_header_table *imgheader; > + struct zynqmp_header *header; > + struct partition_header *last_part; > +}; > + > +struct bif_output bif_output; > + > +static uint32_t zynqmp_csum(void *start, void *end) > +{ > + uint32_t checksum = 0; > + uint32_t *ptr32 = start; > + > + while (ptr32 != end) { > + checksum += le32_to_cpu(*ptr32); > + ptr32++; > + } > + > + return ~checksum; > +} > + > +static int zynqmpbif_check_params(struct image_tool_params *params) > +{ > + if (!params) > + return 0; > + > + if (params->addr != 0x0) { > + fprintf(stderr, "Error: Load Address can not be specified.\n"); > + return -1; > + } > + > + if (params->eflag) { > + fprintf(stderr, "Error: Entry Point can not be specified.\n"); > + return -1; > + } > + > + return !(params->lflag || params->dflag); > +} > + > +static int zynqmpbif_check_image_types(uint8_t type) > +{ > + return (type == IH_TYPE_ZYNQMPBIF) ? EXIT_SUCCESS : EXIT_FAILURE; > +} > + > +static char *parse_dest_cpu(char *line, struct bif_entry *bf) > +{ > + int i; > + > + for (i = 0; i < ARRAY_SIZE(dest_cpus); i++) { > + if (!strncmp(line, dest_cpus[i], strlen(dest_cpus[i]))) { > + bf->dest_cpu = i; > + return line + strlen(dest_cpus[i]); > + } > + } > + > + return line; > +} > + > +static char *parse_el(char *line, struct bif_entry *bf) > +{ > + const char *dest_els[] = { "none", "el-0", "el-1", "el-2", "el-3" }; > + int i; > + > + for (i = 0; i < ARRAY_SIZE(dest_els); i++) { > + if (!strncmp(line, dest_els[i], strlen(dest_els[i]))) { > + bf->exp_lvl = i; > + return line + strlen(dest_els[i]); > + } > + } > + > + return line; > +} > + > +static char *parse_load(char *line, struct bif_entry *bf) > +{ > + char *endptr; > + > + bf->load = strtoll(line, &endptr, 0); > + > + return endptr; > +} > + > +static char *parse_entry(char *line, struct bif_entry *bf) > +{ > + char *endptr; > + > + bf->entry = strtoll(line, &endptr, 0); > + > + return endptr; > +} > + > +static const struct bif_flags bif_flags[] = { > + { "fsbl_config", BIF_FLAG_FSBL_CONFIG }, > + { "trustzone", BIF_FLAG_TZ }, > + { "pmufw_image", BIF_FLAG_PMUFW_IMAGE }, > + { "bootloader", BIF_FLAG_BOOTLOADER }, > + { "destination_cpu=", 0, parse_dest_cpu }, > + { "exception_level=", 0, parse_el }, > + { "load=", 0, parse_load }, > + { "startup=", 0, parse_entry }, > +}; > + > +static char *read_full_file(const char *filename, size_t *size) > +{ > + char *buf, *bufp; > + struct stat sbuf; > + int len = 0, r, fd; > + > + fd = open(filename, O_RDONLY); > + if (fd < 0) > + return NULL; > + > + if (fstat(fd, &sbuf) < 0) > + return NULL; > + > + if (size) > + *size = sbuf.st_size; > + > + bufp = buf = malloc(sbuf.st_size); > + if (!buf) > + return NULL; > + > + while (len < sbuf.st_size) { > + r = read(fd, bufp, sbuf.st_size - len); > + if (r < 0) > + return NULL; > + len += r; > + bufp += r; > + } > + > + close(fd); > + > + return buf; > +} > + > +static int bif_add_blob(const void *data, size_t len, size_t *offset) > +{ > + size_t new_size = ROUND(bif_output.data_len + len, 64); > + uintptr_t header_off; > + uintptr_t last_part_off; > + uintptr_t imgheader_off; > + uintptr_t old_data = (uintptr_t)bif_output.data; > + > + header_off = (uintptr_t)bif_output.header - old_data; > + last_part_off = (uintptr_t)bif_output.last_part - old_data; > + imgheader_off = (uintptr_t)bif_output.imgheader - old_data; > + > + bif_output.data = realloc(bif_output.data, new_size); > + memcpy(bif_output.data + bif_output.data_len, data, len); > + if (offset) > + *offset = bif_output.data_len; > + bif_output.data_len = new_size; > + > + /* Readjust internal pointers */ > + if (bif_output.header) > + bif_output.header = (void*)(bif_output.data + header_off); > + if (bif_output.last_part) > + bif_output.last_part = (void*)(bif_output.data + last_part_off); > + if (bif_output.imgheader) > + bif_output.imgheader = (void*)(bif_output.data + imgheader_off); > + > + return 0; > +} > + > +static int bif_init(void) > +{ > + struct zynqmp_header header = { { 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) > +{ > + size_t offset; > + > + if (bif_output.header->image_offset) { > + printf("PMUFW expected before bootloader in your .bif file!\n"); > + return -1; > + } > + > + bif_add_blob(data, len, &offset); > + len = ROUND(len, 64); > + bif_output.header->pfw_image_length = cpu_to_le32(len); > + bif_output.header->total_pfw_image_length = cpu_to_le32(len); > + bif_output.header->image_offset = cpu_to_le32(offset); > + > + return 0; > +} > + > +static int bif_add_part(struct bif_entry *bf, const char *data, size_t len) > +{ > + size_t parthdr_offset, part_offset; > + struct partition_header parthdr = { > + .len_enc = cpu_to_le32(len / 4), > + .len_unenc = cpu_to_le32(len / 4), > + .len = cpu_to_le32(len / 4), > + .entry_point = cpu_to_le64(bf->entry), > + .load_address = cpu_to_le64(bf->load), > + }; > + int r; > + uint32_t csum; > + > + if (bf->flags & (1ULL << BIF_FLAG_PMUFW_IMAGE)) > + return bif_add_pmufw(bf, data, len); > + > + bif_add_blob(data, len, &part_offset); > + parthdr.offset = cpu_to_le32(part_offset / 4); > + > + if (bf->flags & (1ULL << BIF_FLAG_BOOTLOADER)) { > + if (bif_output.last_part) { > + printf("ERROR: Bootloader needs to come as first non-PMU partition"); > + return -1; > + } > + > + parthdr.offset = cpu_to_le32(bif_output.header->image_offset); > + parthdr.len = cpu_to_le32((part_offset + len - > + bif_output.header->image_offset) / 4); > + parthdr.len_enc = parthdr.len; > + parthdr.len_unenc = parthdr.len; > + } > + > + /* Normalize EL */ > + bf->exp_lvl = bf->exp_lvl ? bf->exp_lvl - 1 : 3; > + parthdr.attributes |= bf->exp_lvl << PART_ATTR_TARGET_EL_SHIFT; > + parthdr.attributes |= bf->dest_dev; > + parthdr.attributes |= bf->dest_cpu << PART_ATTR_DEST_CPU_SHIFT; > + if (bf->flags & (1ULL << BIF_FLAG_TZ)) > + parthdr.attributes |= PART_ATTR_TZ_SECURE; > + > + csum = zynqmp_csum(&parthdr, &parthdr.checksum); > + parthdr.checksum = cpu_to_le32(csum); > + > + r = bif_add_blob(&parthdr, sizeof(parthdr), &parthdr_offset); > + if (r) > + return r; > + > + /* Add image header table if not there yet */ > + if (!bif_output.imgheader) { > + size_t imghdr_off; > + struct image_header_table imghdr = { > + .version = cpu_to_le32(0x01020000), > + .nr_parts = 0, > + }; > + > + r = bif_add_blob(&imghdr, sizeof(imghdr), &imghdr_off); > + if (r) > + return r; > + > + bif_output.header->image_header_table_offset = imghdr_off; > + bif_output.imgheader = (void*)(bif_output.data + imghdr_off); > + } > + > + bif_output.imgheader->nr_parts = cpu_to_le32(le32_to_cpu( > + bif_output.imgheader->nr_parts) + 1); > + > + /* Link to this partition header */ > + if (bif_output.last_part) { > + bif_output.last_part->next_partition_offset = > + cpu_to_le32(parthdr_offset / 4); > + > + /* Recalc checksum of last_part */ > + csum = zynqmp_csum(bif_output.last_part, > + &bif_output.last_part->checksum); > + bif_output.last_part->checksum = cpu_to_le32(csum); > + } else { > + bif_output.imgheader->partition_header_offset = > + cpu_to_le32(parthdr_offset / 4); > + } > + bif_output.last_part = (void*)(bif_output.data + parthdr_offset); > + > + if (bf->flags & (1ULL << BIF_FLAG_BOOTLOADER)) { > + bif_output.header->image_load = cpu_to_le32(bf->load); > + if (!bif_output.header->image_offset) > + bif_output.header->image_offset = > + cpu_to_le32(part_offset); > + bif_output.header->image_size = cpu_to_le32(len); > + bif_output.header->image_stored_size = cpu_to_le32(len); > + } > + > + return 0; > +} > + > +/* Add .bit bitstream */ > +static int bif_add_bit(struct bif_entry *bf) > +{ > + char *bit = read_full_file(bf->filename, NULL); > + char *bitbin; > + uint8_t initial_header[] = { 0x00, 0x09, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, > + 0xf0, 0x0f, 0xf0, 0x00, 0x00, 0x01, 0x61 }; > + uint16_t len; > + uint32_t bitlen; > + int i; > + > + if (!bit) > + return -1; > + > + /* Skip initial header */ > + if (memcmp(bit, initial_header, sizeof(initial_header))) > + return -1; > + > + bit += sizeof(initial_header); > + > + /* Design name */ > + len = be16_to_cpu(*(uint16_t*)bit); > + bit += sizeof(uint16_t); > + 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++) { > + if (!shdr->sh_size || !shdr->sh_addr || > + !(shdr->sh_flags & SHF_ALLOC) || > + (shdr->sh_type == SHT_NOBITS)) { > + shdr++; > + continue; > + } > + > + if (le64_to_cpu(shdr->sh_addr) < min_addr) > + min_addr = le64_to_cpu(shdr->sh_addr); > + if ((le64_to_cpu(shdr->sh_addr) + le64_to_cpu(shdr->sh_size)) > > + max_addr) > + max_addr = le64_to_cpu(shdr->sh_addr) + > + le64_to_cpu(shdr->sh_size); > + > + shdr++; > + } > + > + *load_addr = min_addr; > + *flat_size = max_addr - min_addr; > + flat = calloc(1, *flat_size); > + if (!flat) > + return NULL; > + > + shdr = (void*)(elf + le64_to_cpu(ehdr->e_shoff)); > + for (i = 0; i < le64_to_cpu(ehdr->e_shnum); i++) { > + char *dst = flat + le64_to_cpu(shdr->sh_addr) - min_addr; > + char *src = elf + le64_to_cpu(shdr->sh_offset); > + > + if (!shdr->sh_size || !shdr->sh_addr || > + !(shdr->sh_flags & SHF_ALLOC)) { > + shdr++; > + continue; > + } > + > + if (shdr->sh_type != SHT_NOBITS) { > + > + memcpy(dst, src, le64_to_cpu(shdr->sh_size)); > + } > + shdr++; > + } > + > + return flat; > +} > + > +static char *elf2flat32(char *elf, size_t *flat_size, size_t *load_addr) > +{ > + Elf32_Ehdr *ehdr; > + Elf32_Shdr *shdr; > + size_t min_addr = -1, max_addr = 0; > + char *flat; > + int i; > + > + ehdr = (void*)elf; > + shdr = (void*)(elf + le32_to_cpu(ehdr->e_shoff)); > + > + /* Look for smallest / biggest address */ > + for (i = 0; i < le32_to_cpu(ehdr->e_shnum); i++) { > + if (!shdr->sh_size || !shdr->sh_addr || > + !(shdr->sh_flags & SHF_ALLOC) || > + (shdr->sh_type == SHT_NOBITS)) { > + shdr++; > + continue; > + } > + > + if (le32_to_cpu(shdr->sh_addr) < min_addr) > + min_addr = le32_to_cpu(shdr->sh_addr); > + if ((le32_to_cpu(shdr->sh_addr) + le32_to_cpu(shdr->sh_size)) > > + max_addr) > + max_addr = le32_to_cpu(shdr->sh_addr) + > + le32_to_cpu(shdr->sh_size); > + > + shdr++; > + } > + > + *load_addr = min_addr; > + *flat_size = max_addr - min_addr; > + flat = calloc(1, *flat_size); > + if (!flat) > + return NULL; > + > + shdr = (void*)(elf + le32_to_cpu(ehdr->e_shoff)); > + for (i = 0; i < le32_to_cpu(ehdr->e_shnum); i++) { > + char *dst = flat + le32_to_cpu(shdr->sh_addr) - min_addr; > + char *src = elf + le32_to_cpu(shdr->sh_offset); > + > + if (!shdr->sh_size || !shdr->sh_addr || > + !(shdr->sh_flags & SHF_ALLOC)) { > + shdr++; > + continue; > + } > + > + if (shdr->sh_type != SHT_NOBITS) { > + > + memcpy(dst, src, le32_to_cpu(shdr->sh_size)); > + } > + shdr++; > + } > + > + return flat; > +} > + > +static int bif_add_elf(struct bif_entry *bf) > +{ > + size_t size; > + size_t elf_size; > + char *elf; > + char *flat; > + size_t load_addr; > + Elf32_Ehdr *ehdr32; > + Elf64_Ehdr *ehdr64; > + > + elf = read_full_file(bf->filename, &elf_size); > + if (!elf) > + return -1; > + > + ehdr32 = (void*)elf; > + ehdr64 = (void*)elf; > + > + switch (ehdr32->e_ident[EI_CLASS]) { > + case ELFCLASS32: > + flat = elf2flat32(elf, &size, &load_addr); > + bf->entry = le32_to_cpu(ehdr32->e_entry); > + break; > + case ELFCLASS64: > + flat = elf2flat64(elf, &size, &load_addr); > + bf->entry = le64_to_cpu(ehdr64->e_entry); > + break; > + default: > + printf("Unknown ELF class: %d\n", ehdr32->e_ident[EI_CLASS]); > + return -1; > + } > + > + if (!flat) > + return -1; > + > + bf->load = load_addr; > + if (!bf->dest_dev) > + bf->dest_dev = PART_ATTR_DEST_DEVICE_PS; > + > + bf->flags |= 1ULL << BIF_FLAG_ELF_FILE; > + return bif_add_part(bf, flat, size); > +} > + > +static const struct bif_file_type bif_file_types[] = { > + { > + .name = "bitstream (.bit)", > + .header = 0x00090ff0, > + .add = bif_add_bit, > + }, > + > + { > + .name = "ELF", > + .header = 0x7f454c46, > + .add = bif_add_elf, > + }, > + > + /* Anything else is a .bin file */ > + { > + .name = ".bin", > + .add = bif_add_bin, > + }, > +}; > + > +static const struct bif_flags *find_flag(char *str) > +{ > + const struct bif_flags *bf; > + int i; > + > + for (i = 0; i < ARRAY_SIZE(bif_flags); i++) { > + bf = &bif_flags[i]; > + if (!strncmp(bf->name, str, strlen(bf->name))) > + return bf; > + } > + > + printf("ERROR: Flag '%s' not found\n", str); > + > + return NULL; > +} > + > +static int bif_open_file(struct bif_entry *entry) > +{ > + int fd = open(entry->filename, O_RDONLY); > + > + if (fd < 0) > + printf("Error opening file %s\n", entry->filename); > + > + return fd; > +} > + > +static const struct bif_file_type *get_file_type(struct bif_entry *entry) > +{ > + int fd = bif_open_file(entry); > + uint32_t header; > + int i; > + > + if (fd < 0) > + return NULL; > + > + if (read(fd, &header, sizeof(header)) != sizeof(header)) { > + printf("Error reading file %s", entry->filename); > + return NULL; > + } > + > + close(fd); > + > + for (i = 0; i < ARRAY_SIZE(bif_file_types); i++) { > + const struct bif_file_type *type = &bif_file_types[i]; > + > + if (!type->header) > + return type; > + if (type->header == be32_to_cpu(header)) > + return type; > + } > + > + return NULL; > +} > + > +#define NEXT_CHAR(str, chr) ({ \ > + char *_n = strchr(str, chr); \ > + if (!_n) \ > + goto err; \ > + _n; \ > +}) > + > +static char *skip_whitespace(char *str) > +{ > + while (*str == ' ' || *str == '\t') > + str++; > + > + return str; > +} > + > +void zynqmpbif_copy_image(int outfd, struct image_tool_params *mparams) > +{ > + char *bif, *bifp, *bifpn; > + char *line; > + struct bif_entry entries[32] = { { 0 } }; > + int nr_entries = 0; > + struct bif_entry *entry = entries; > + size_t len; > + int i; > + uint32_t csum; > + > + bif_init(); > + > + /* Read .bif input file */ > + bifp = bif = read_full_file(mparams->datafile, NULL); > + if (!bif) > + goto err; > + > + /* Interpret .bif file */ > + bifp = bif; > + > + /* A bif description starts with a { section */ > + bifp = NEXT_CHAR(bifp, '{') + 1; > + > + /* Read every line */ > + while (1) { > + bifpn = NEXT_CHAR(bifp, '\n'); > + > + *bifpn = '\0'; > + bifpn++; > + line = bifp; > + > + line = skip_whitespace(line); > + > + /* Attributes? */ > + if (*line == '[') { > + line++; > + while (1) { > + const struct bif_flags *bf; > + > + line = skip_whitespace(line); > + bf = find_flag(line); > + if (!bf) > + goto err; > + > + line += strlen(bf->name); > + if (bf->parse) > + line = bf->parse(line, entry); > + else > + entry->flags |= 1ULL << bf->flag; > + > + /* Go to next attribute or quit */ > + if (*line == ']') { > + line++; > + break; > + } > + if (*line == ',') > + line++; > + } > + } > + > + /* End of image description */ > + if (*line == '}') > + break; > + > + if (*line) { > + line = skip_whitespace(line); > + entry->filename = line; > + nr_entries++; > + entry++; > + } > + > + /* Use next line */ > + bifp = bifpn; > + } > + > + for (i = 0; i < nr_entries; i++) { > + debug("Entry flags=%#lx name=%s\n", entries[i].flags, > + entries[i].filename); > + } > + > + for (i = 0; i < nr_entries; i++) { > + struct bif_entry *entry = &entries[i]; > + const struct bif_file_type *type; > + int r; > + > + type = get_file_type(entry); > + if (!type) > + goto err; > + > + debug("type=%s file=%s\n", type->name, entry->filename); > + r = type->add(entry); > + if (r) > + goto err; > + } > + > + /* Calculate checksums */ > + csum = zynqmp_csum(&bif_output.header->width_detection, > + &bif_output.header->checksum); > + bif_output.header->checksum = cpu_to_le32(csum); > + > + if (bif_output.imgheader) { > + csum = zynqmp_csum(bif_output.imgheader, > + &bif_output.imgheader->checksum); > + bif_output.imgheader->checksum = cpu_to_le32(csum); > + } > + > + /* Write headers and components */ > + if (lseek(outfd, 0, SEEK_SET) != 0) > + goto err; > + > + len = bif_output.data_len; > + bifp = bif_output.data; > + while (len) { > + int r; > + r = write(outfd, bifp, len); > + if (r < 0) > + goto err; > + len -= r; > + bifp += r; > + } > + > + return; > + > +err: > + fprintf(stderr, "Error: Failed to create image.\n"); > +} > + > +/* Needs to be stubbed out so we can print after creation */ > +static void zynqmpbif_set_header(void *ptr, struct stat *sbuf, int ifd, > + struct image_tool_params *params) > +{ > +} > + > +static struct zynqmp_header zynqmpimage_header; > + > +U_BOOT_IMAGE_TYPE( > + zynqmpbif, > + "Xilinx ZynqMP Boot Image support (bif)", > + sizeof(struct zynqmp_header), > + (void *)&zynqmpimage_header, > + zynqmpbif_check_params, > + NULL, > + zynqmpimage_print_header, > + zynqmpbif_set_header, > + NULL, > + zynqmpbif_check_image_types, > + NULL, > + NULL > +); > diff --git a/tools/zynqmpimage.c b/tools/zynqmpimage.c > index 8f4766f077..145391de3e 100644 > --- a/tools/zynqmpimage.c > +++ b/tools/zynqmpimage.c > @@ -87,7 +87,7 @@ static uint32_t zynqmpimage_checksum(struct zynqmp_header *ptr) > return cpu_to_le32(checksum); > } > > -static void zynqmpimage_default_header(struct zynqmp_header *ptr) > +void zynqmpimage_default_header(struct zynqmp_header *ptr) > { > int i; > > @@ -211,7 +211,7 @@ static void print_partition(const void *ptr, const struct partition_header *ph) > printf(" Checksum : 0x%08x\n", le32_to_cpu(ph->checksum)); > } > > -static void zynqmpimage_print_header(const void *ptr) > +void zynqmpimage_print_header(const void *ptr) > { > struct zynqmp_header *zynqhdr = (struct zynqmp_header *)ptr; > int i; > diff --git a/tools/zynqmpimage.h b/tools/zynqmpimage.h > index f3b5c195ad..b421e4f94c 100644 > --- a/tools/zynqmpimage.h > +++ b/tools/zynqmpimage.h > @@ -129,4 +129,7 @@ struct zynqmp_header { > uint32_t __reserved4[66]; /* 0x9c0 */ > }; > > +void zynqmpimage_default_header(struct zynqmp_header *ptr); > +void zynqmpimage_print_header(const void *ptr); > + > #endif /* _ZYNQMPIMAGE_H_ */ > Also please run checkpatch over 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..d78a9458f4 100644 --- a/tools/imagetool.h +++ b/tools/imagetool.h @@ -232,6 +232,7 @@ time_t imagetool_get_source_date( void pbl_load_uboot(int fd, struct image_tool_params *mparams); +void zynqmpbif_copy_image(int fd, struct image_tool_params *mparams); #define ___cat(a, b) a ## b #define __cat(a, b) ___cat(a, b) diff --git a/tools/mkimage.c b/tools/mkimage.c index 4e561820e7..72183f5f2b 100644 --- a/tools/mkimage.c +++ b/tools/mkimage.c @@ -514,6 +514,9 @@ int main(int argc, char **argv) } else if (params.type == IH_TYPE_PBLIMAGE) { /* PBL has special Image format, implements its' own */ pbl_load_uboot(ifd, ¶ms); + } else if (params.type == IH_TYPE_ZYNQMPBIF) { + /* Image file is meta, walk through actual targets */ + zynqmpbif_copy_image(ifd, ¶ms); } else { copy_file(ifd, params.datafile, pad_len); } diff --git a/tools/zynqmpbif.c b/tools/zynqmpbif.c new file mode 100644 index 0000000000..d60eff1d8b --- /dev/null +++ b/tools/zynqmpbif.c @@ -0,0 +1,839 @@ +/* + * Copyright (C) 2018 Alexander Graf <agraf@suse.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "imagetool.h" +#include "mkimage.h" +#include "zynqmpimage.h" +#include <elf.h> +#include <image.h> + +struct bif_entry { + const char *filename; + uint64_t flags; + uint64_t dest_cpu; + uint64_t exp_lvl; + uint64_t dest_dev; + uint64_t load; + uint64_t entry; +}; + +enum bif_flag { + BIF_FLAG_AESKEYFILE, + BIF_FLAG_INIT, + BIF_FLAG_UDF_BH, + BIF_FLAG_HEADERSIGNATURE, + BIF_FLAG_PPKFILE, + BIF_FLAG_PSKFILE, + BIF_FLAG_SPKFILE, + BIF_FLAG_SSKFILE, + BIF_FLAG_SPKSIGNATURE, + BIF_FLAG_FSBL_CONFIG, + BIF_FLAG_AUTH_PARAMS, + BIF_FLAG_KEYSRC_ENCRYPTION, + BIF_FLAG_PMUFW_IMAGE, + BIF_FLAG_BOOTLOADER, + BIF_FLAG_TZ, + BIF_FLAG_BH_KEY_IV, + BIF_FLAG_BH_KEYFILE, + BIF_FLAG_PUF_FILE, + + /* Internal flags */ + BIF_FLAG_BIT_FILE, + BIF_FLAG_ELF_FILE, + BIF_FLAG_BIN_FILE, +}; + +struct bif_flags { + const char name[32]; + uint64_t flag; + char *(*parse)(char *line, struct bif_entry *bf); +}; + +struct bif_file_type { + const char name[32]; + uint32_t header; + int (*add)(struct bif_entry *bf); +}; + +struct bif_output { + size_t data_len; + char *data; + struct image_header_table *imgheader; + struct zynqmp_header *header; + struct partition_header *last_part; +}; + +struct bif_output bif_output; + +static uint32_t zynqmp_csum(void *start, void *end) +{ + uint32_t checksum = 0; + uint32_t *ptr32 = start; + + while (ptr32 != end) { + checksum += le32_to_cpu(*ptr32); + ptr32++; + } + + return ~checksum; +} + +static int zynqmpbif_check_params(struct image_tool_params *params) +{ + if (!params) + return 0; + + if (params->addr != 0x0) { + fprintf(stderr, "Error: Load Address can not be specified.\n"); + return -1; + } + + if (params->eflag) { + fprintf(stderr, "Error: Entry Point can not be specified.\n"); + return -1; + } + + return !(params->lflag || params->dflag); +} + +static int zynqmpbif_check_image_types(uint8_t type) +{ + return (type == IH_TYPE_ZYNQMPBIF) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +static char *parse_dest_cpu(char *line, struct bif_entry *bf) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dest_cpus); i++) { + if (!strncmp(line, dest_cpus[i], strlen(dest_cpus[i]))) { + bf->dest_cpu = i; + return line + strlen(dest_cpus[i]); + } + } + + return line; +} + +static char *parse_el(char *line, struct bif_entry *bf) +{ + const char *dest_els[] = { "none", "el-0", "el-1", "el-2", "el-3" }; + int i; + + for (i = 0; i < ARRAY_SIZE(dest_els); i++) { + if (!strncmp(line, dest_els[i], strlen(dest_els[i]))) { + bf->exp_lvl = i; + return line + strlen(dest_els[i]); + } + } + + return line; +} + +static char *parse_load(char *line, struct bif_entry *bf) +{ + char *endptr; + + bf->load = strtoll(line, &endptr, 0); + + return endptr; +} + +static char *parse_entry(char *line, struct bif_entry *bf) +{ + char *endptr; + + bf->entry = strtoll(line, &endptr, 0); + + return endptr; +} + +static const struct bif_flags bif_flags[] = { + { "fsbl_config", BIF_FLAG_FSBL_CONFIG }, + { "trustzone", BIF_FLAG_TZ }, + { "pmufw_image", BIF_FLAG_PMUFW_IMAGE }, + { "bootloader", BIF_FLAG_BOOTLOADER }, + { "destination_cpu=", 0, parse_dest_cpu }, + { "exception_level=", 0, parse_el }, + { "load=", 0, parse_load }, + { "startup=", 0, parse_entry }, +}; + +static char *read_full_file(const char *filename, size_t *size) +{ + char *buf, *bufp; + struct stat sbuf; + int len = 0, r, fd; + + fd = open(filename, O_RDONLY); + if (fd < 0) + return NULL; + + if (fstat(fd, &sbuf) < 0) + return NULL; + + if (size) + *size = sbuf.st_size; + + bufp = buf = malloc(sbuf.st_size); + if (!buf) + return NULL; + + while (len < sbuf.st_size) { + r = read(fd, bufp, sbuf.st_size - len); + if (r < 0) + return NULL; + len += r; + bufp += r; + } + + close(fd); + + return buf; +} + +static int bif_add_blob(const void *data, size_t len, size_t *offset) +{ + size_t new_size = ROUND(bif_output.data_len + len, 64); + uintptr_t header_off; + uintptr_t last_part_off; + uintptr_t imgheader_off; + uintptr_t old_data = (uintptr_t)bif_output.data; + + header_off = (uintptr_t)bif_output.header - old_data; + last_part_off = (uintptr_t)bif_output.last_part - old_data; + imgheader_off = (uintptr_t)bif_output.imgheader - old_data; + + bif_output.data = realloc(bif_output.data, new_size); + memcpy(bif_output.data + bif_output.data_len, data, len); + if (offset) + *offset = bif_output.data_len; + bif_output.data_len = new_size; + + /* Readjust internal pointers */ + if (bif_output.header) + bif_output.header = (void*)(bif_output.data + header_off); + if (bif_output.last_part) + bif_output.last_part = (void*)(bif_output.data + last_part_off); + if (bif_output.imgheader) + bif_output.imgheader = (void*)(bif_output.data + imgheader_off); + + return 0; +} + +static int bif_init(void) +{ + struct zynqmp_header header = { { 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) +{ + size_t offset; + + if (bif_output.header->image_offset) { + printf("PMUFW expected before bootloader in your .bif file!\n"); + return -1; + } + + bif_add_blob(data, len, &offset); + len = ROUND(len, 64); + bif_output.header->pfw_image_length = cpu_to_le32(len); + bif_output.header->total_pfw_image_length = cpu_to_le32(len); + bif_output.header->image_offset = cpu_to_le32(offset); + + return 0; +} + +static int bif_add_part(struct bif_entry *bf, const char *data, size_t len) +{ + size_t parthdr_offset, part_offset; + struct partition_header parthdr = { + .len_enc = cpu_to_le32(len / 4), + .len_unenc = cpu_to_le32(len / 4), + .len = cpu_to_le32(len / 4), + .entry_point = cpu_to_le64(bf->entry), + .load_address = cpu_to_le64(bf->load), + }; + int r; + uint32_t csum; + + if (bf->flags & (1ULL << BIF_FLAG_PMUFW_IMAGE)) + return bif_add_pmufw(bf, data, len); + + bif_add_blob(data, len, &part_offset); + parthdr.offset = cpu_to_le32(part_offset / 4); + + if (bf->flags & (1ULL << BIF_FLAG_BOOTLOADER)) { + if (bif_output.last_part) { + printf("ERROR: Bootloader needs to come as first non-PMU partition"); + return -1; + } + + parthdr.offset = cpu_to_le32(bif_output.header->image_offset); + parthdr.len = cpu_to_le32((part_offset + len - + bif_output.header->image_offset) / 4); + parthdr.len_enc = parthdr.len; + parthdr.len_unenc = parthdr.len; + } + + /* Normalize EL */ + bf->exp_lvl = bf->exp_lvl ? bf->exp_lvl - 1 : 3; + parthdr.attributes |= bf->exp_lvl << PART_ATTR_TARGET_EL_SHIFT; + parthdr.attributes |= bf->dest_dev; + parthdr.attributes |= bf->dest_cpu << PART_ATTR_DEST_CPU_SHIFT; + if (bf->flags & (1ULL << BIF_FLAG_TZ)) + parthdr.attributes |= PART_ATTR_TZ_SECURE; + + csum = zynqmp_csum(&parthdr, &parthdr.checksum); + parthdr.checksum = cpu_to_le32(csum); + + r = bif_add_blob(&parthdr, sizeof(parthdr), &parthdr_offset); + if (r) + return r; + + /* Add image header table if not there yet */ + if (!bif_output.imgheader) { + size_t imghdr_off; + struct image_header_table imghdr = { + .version = cpu_to_le32(0x01020000), + .nr_parts = 0, + }; + + r = bif_add_blob(&imghdr, sizeof(imghdr), &imghdr_off); + if (r) + return r; + + bif_output.header->image_header_table_offset = imghdr_off; + bif_output.imgheader = (void*)(bif_output.data + imghdr_off); + } + + bif_output.imgheader->nr_parts = cpu_to_le32(le32_to_cpu( + bif_output.imgheader->nr_parts) + 1); + + /* Link to this partition header */ + if (bif_output.last_part) { + bif_output.last_part->next_partition_offset = + cpu_to_le32(parthdr_offset / 4); + + /* Recalc checksum of last_part */ + csum = zynqmp_csum(bif_output.last_part, + &bif_output.last_part->checksum); + bif_output.last_part->checksum = cpu_to_le32(csum); + } else { + bif_output.imgheader->partition_header_offset = + cpu_to_le32(parthdr_offset / 4); + } + bif_output.last_part = (void*)(bif_output.data + parthdr_offset); + + if (bf->flags & (1ULL << BIF_FLAG_BOOTLOADER)) { + bif_output.header->image_load = cpu_to_le32(bf->load); + if (!bif_output.header->image_offset) + bif_output.header->image_offset = + cpu_to_le32(part_offset); + bif_output.header->image_size = cpu_to_le32(len); + bif_output.header->image_stored_size = cpu_to_le32(len); + } + + return 0; +} + +/* Add .bit bitstream */ +static int bif_add_bit(struct bif_entry *bf) +{ + char *bit = read_full_file(bf->filename, NULL); + char *bitbin; + uint8_t initial_header[] = { 0x00, 0x09, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, + 0xf0, 0x0f, 0xf0, 0x00, 0x00, 0x01, 0x61 }; + uint16_t len; + uint32_t bitlen; + int i; + + if (!bit) + return -1; + + /* Skip initial header */ + if (memcmp(bit, initial_header, sizeof(initial_header))) + return -1; + + bit += sizeof(initial_header); + + /* Design name */ + len = be16_to_cpu(*(uint16_t*)bit); + bit += sizeof(uint16_t); + 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++) { + if (!shdr->sh_size || !shdr->sh_addr || + !(shdr->sh_flags & SHF_ALLOC) || + (shdr->sh_type == SHT_NOBITS)) { + shdr++; + continue; + } + + if (le64_to_cpu(shdr->sh_addr) < min_addr) + min_addr = le64_to_cpu(shdr->sh_addr); + if ((le64_to_cpu(shdr->sh_addr) + le64_to_cpu(shdr->sh_size)) > + max_addr) + max_addr = le64_to_cpu(shdr->sh_addr) + + le64_to_cpu(shdr->sh_size); + + shdr++; + } + + *load_addr = min_addr; + *flat_size = max_addr - min_addr; + flat = calloc(1, *flat_size); + if (!flat) + return NULL; + + shdr = (void*)(elf + le64_to_cpu(ehdr->e_shoff)); + for (i = 0; i < le64_to_cpu(ehdr->e_shnum); i++) { + char *dst = flat + le64_to_cpu(shdr->sh_addr) - min_addr; + char *src = elf + le64_to_cpu(shdr->sh_offset); + + if (!shdr->sh_size || !shdr->sh_addr || + !(shdr->sh_flags & SHF_ALLOC)) { + shdr++; + continue; + } + + if (shdr->sh_type != SHT_NOBITS) { + + memcpy(dst, src, le64_to_cpu(shdr->sh_size)); + } + shdr++; + } + + return flat; +} + +static char *elf2flat32(char *elf, size_t *flat_size, size_t *load_addr) +{ + Elf32_Ehdr *ehdr; + Elf32_Shdr *shdr; + size_t min_addr = -1, max_addr = 0; + char *flat; + int i; + + ehdr = (void*)elf; + shdr = (void*)(elf + le32_to_cpu(ehdr->e_shoff)); + + /* Look for smallest / biggest address */ + for (i = 0; i < le32_to_cpu(ehdr->e_shnum); i++) { + if (!shdr->sh_size || !shdr->sh_addr || + !(shdr->sh_flags & SHF_ALLOC) || + (shdr->sh_type == SHT_NOBITS)) { + shdr++; + continue; + } + + if (le32_to_cpu(shdr->sh_addr) < min_addr) + min_addr = le32_to_cpu(shdr->sh_addr); + if ((le32_to_cpu(shdr->sh_addr) + le32_to_cpu(shdr->sh_size)) > + max_addr) + max_addr = le32_to_cpu(shdr->sh_addr) + + le32_to_cpu(shdr->sh_size); + + shdr++; + } + + *load_addr = min_addr; + *flat_size = max_addr - min_addr; + flat = calloc(1, *flat_size); + if (!flat) + return NULL; + + shdr = (void*)(elf + le32_to_cpu(ehdr->e_shoff)); + for (i = 0; i < le32_to_cpu(ehdr->e_shnum); i++) { + char *dst = flat + le32_to_cpu(shdr->sh_addr) - min_addr; + char *src = elf + le32_to_cpu(shdr->sh_offset); + + if (!shdr->sh_size || !shdr->sh_addr || + !(shdr->sh_flags & SHF_ALLOC)) { + shdr++; + continue; + } + + if (shdr->sh_type != SHT_NOBITS) { + + memcpy(dst, src, le32_to_cpu(shdr->sh_size)); + } + shdr++; + } + + return flat; +} + +static int bif_add_elf(struct bif_entry *bf) +{ + size_t size; + size_t elf_size; + char *elf; + char *flat; + size_t load_addr; + Elf32_Ehdr *ehdr32; + Elf64_Ehdr *ehdr64; + + elf = read_full_file(bf->filename, &elf_size); + if (!elf) + return -1; + + ehdr32 = (void*)elf; + ehdr64 = (void*)elf; + + switch (ehdr32->e_ident[EI_CLASS]) { + case ELFCLASS32: + flat = elf2flat32(elf, &size, &load_addr); + bf->entry = le32_to_cpu(ehdr32->e_entry); + break; + case ELFCLASS64: + flat = elf2flat64(elf, &size, &load_addr); + bf->entry = le64_to_cpu(ehdr64->e_entry); + break; + default: + printf("Unknown ELF class: %d\n", ehdr32->e_ident[EI_CLASS]); + return -1; + } + + if (!flat) + return -1; + + bf->load = load_addr; + if (!bf->dest_dev) + bf->dest_dev = PART_ATTR_DEST_DEVICE_PS; + + bf->flags |= 1ULL << BIF_FLAG_ELF_FILE; + return bif_add_part(bf, flat, size); +} + +static const struct bif_file_type bif_file_types[] = { + { + .name = "bitstream (.bit)", + .header = 0x00090ff0, + .add = bif_add_bit, + }, + + { + .name = "ELF", + .header = 0x7f454c46, + .add = bif_add_elf, + }, + + /* Anything else is a .bin file */ + { + .name = ".bin", + .add = bif_add_bin, + }, +}; + +static const struct bif_flags *find_flag(char *str) +{ + const struct bif_flags *bf; + int i; + + for (i = 0; i < ARRAY_SIZE(bif_flags); i++) { + bf = &bif_flags[i]; + if (!strncmp(bf->name, str, strlen(bf->name))) + return bf; + } + + printf("ERROR: Flag '%s' not found\n", str); + + return NULL; +} + +static int bif_open_file(struct bif_entry *entry) +{ + int fd = open(entry->filename, O_RDONLY); + + if (fd < 0) + printf("Error opening file %s\n", entry->filename); + + return fd; +} + +static const struct bif_file_type *get_file_type(struct bif_entry *entry) +{ + int fd = bif_open_file(entry); + uint32_t header; + int i; + + if (fd < 0) + return NULL; + + if (read(fd, &header, sizeof(header)) != sizeof(header)) { + printf("Error reading file %s", entry->filename); + return NULL; + } + + close(fd); + + for (i = 0; i < ARRAY_SIZE(bif_file_types); i++) { + const struct bif_file_type *type = &bif_file_types[i]; + + if (!type->header) + return type; + if (type->header == be32_to_cpu(header)) + return type; + } + + return NULL; +} + +#define NEXT_CHAR(str, chr) ({ \ + char *_n = strchr(str, chr); \ + if (!_n) \ + goto err; \ + _n; \ +}) + +static char *skip_whitespace(char *str) +{ + while (*str == ' ' || *str == '\t') + str++; + + return str; +} + +void zynqmpbif_copy_image(int outfd, struct image_tool_params *mparams) +{ + char *bif, *bifp, *bifpn; + char *line; + struct bif_entry entries[32] = { { 0 } }; + int nr_entries = 0; + struct bif_entry *entry = entries; + size_t len; + int i; + uint32_t csum; + + bif_init(); + + /* Read .bif input file */ + bifp = bif = read_full_file(mparams->datafile, NULL); + if (!bif) + goto err; + + /* Interpret .bif file */ + bifp = bif; + + /* A bif description starts with a { section */ + bifp = NEXT_CHAR(bifp, '{') + 1; + + /* Read every line */ + while (1) { + bifpn = NEXT_CHAR(bifp, '\n'); + + *bifpn = '\0'; + bifpn++; + line = bifp; + + line = skip_whitespace(line); + + /* Attributes? */ + if (*line == '[') { + line++; + while (1) { + const struct bif_flags *bf; + + line = skip_whitespace(line); + bf = find_flag(line); + if (!bf) + goto err; + + line += strlen(bf->name); + if (bf->parse) + line = bf->parse(line, entry); + else + entry->flags |= 1ULL << bf->flag; + + /* Go to next attribute or quit */ + if (*line == ']') { + line++; + break; + } + if (*line == ',') + line++; + } + } + + /* End of image description */ + if (*line == '}') + break; + + if (*line) { + line = skip_whitespace(line); + entry->filename = line; + nr_entries++; + entry++; + } + + /* Use next line */ + bifp = bifpn; + } + + for (i = 0; i < nr_entries; i++) { + debug("Entry flags=%#lx name=%s\n", entries[i].flags, + entries[i].filename); + } + + for (i = 0; i < nr_entries; i++) { + struct bif_entry *entry = &entries[i]; + const struct bif_file_type *type; + int r; + + type = get_file_type(entry); + if (!type) + goto err; + + debug("type=%s file=%s\n", type->name, entry->filename); + r = type->add(entry); + if (r) + goto err; + } + + /* Calculate checksums */ + csum = zynqmp_csum(&bif_output.header->width_detection, + &bif_output.header->checksum); + bif_output.header->checksum = cpu_to_le32(csum); + + if (bif_output.imgheader) { + csum = zynqmp_csum(bif_output.imgheader, + &bif_output.imgheader->checksum); + bif_output.imgheader->checksum = cpu_to_le32(csum); + } + + /* Write headers and components */ + if (lseek(outfd, 0, SEEK_SET) != 0) + goto err; + + len = bif_output.data_len; + bifp = bif_output.data; + while (len) { + int r; + r = write(outfd, bifp, len); + if (r < 0) + goto err; + len -= r; + bifp += r; + } + + return; + +err: + fprintf(stderr, "Error: Failed to create image.\n"); +} + +/* Needs to be stubbed out so we can print after creation */ +static void zynqmpbif_set_header(void *ptr, struct stat *sbuf, int ifd, + struct image_tool_params *params) +{ +} + +static struct zynqmp_header zynqmpimage_header; + +U_BOOT_IMAGE_TYPE( + zynqmpbif, + "Xilinx ZynqMP Boot Image support (bif)", + sizeof(struct zynqmp_header), + (void *)&zynqmpimage_header, + zynqmpbif_check_params, + NULL, + zynqmpimage_print_header, + zynqmpbif_set_header, + NULL, + zynqmpbif_check_image_types, + NULL, + NULL +); diff --git a/tools/zynqmpimage.c b/tools/zynqmpimage.c index 8f4766f077..145391de3e 100644 --- a/tools/zynqmpimage.c +++ b/tools/zynqmpimage.c @@ -87,7 +87,7 @@ static uint32_t zynqmpimage_checksum(struct zynqmp_header *ptr) return cpu_to_le32(checksum); } -static void zynqmpimage_default_header(struct zynqmp_header *ptr) +void zynqmpimage_default_header(struct zynqmp_header *ptr) { int i; @@ -211,7 +211,7 @@ static void print_partition(const void *ptr, const struct partition_header *ph) printf(" Checksum : 0x%08x\n", le32_to_cpu(ph->checksum)); } -static void zynqmpimage_print_header(const void *ptr) +void zynqmpimage_print_header(const void *ptr) { struct zynqmp_header *zynqhdr = (struct zynqmp_header *)ptr; int i; diff --git a/tools/zynqmpimage.h b/tools/zynqmpimage.h index f3b5c195ad..b421e4f94c 100644 --- a/tools/zynqmpimage.h +++ b/tools/zynqmpimage.h @@ -129,4 +129,7 @@ struct zynqmp_header { uint32_t __reserved4[66]; /* 0x9c0 */ }; +void zynqmpimage_default_header(struct zynqmp_header *ptr); +void zynqmpimage_print_header(const void *ptr); + #endif /* _ZYNQMPIMAGE_H_ */
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 --- common/image.c | 1 + include/image.h | 1 + tools/Makefile | 1 + tools/imagetool.h | 1 + tools/mkimage.c | 3 + tools/zynqmpbif.c | 839 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/zynqmpimage.c | 4 +- tools/zynqmpimage.h | 3 + 8 files changed, 851 insertions(+), 2 deletions(-) create mode 100644 tools/zynqmpbif.c