From patchwork Thu Apr 12 13:48:23 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Graf X-Patchwork-Id: 133280 Delivered-To: patch@linaro.org Received: by 10.46.84.29 with SMTP id i29csp1706352ljb; Thu, 12 Apr 2018 06:49:30 -0700 (PDT) X-Google-Smtp-Source: AIpwx4/3n33bXgsf6NEN6ZJi1JTwacQx9yE3DGFFIJaclgjcrsYTQnciNO3pyD8lDPLD8XwoJErH X-Received: by 10.80.132.171 with SMTP id 40mr4288594edq.235.1523540970020; Thu, 12 Apr 2018 06:49:30 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1523540970; cv=none; d=google.com; s=arc-20160816; b=LiBg41a2qZVo7SyB41myH93th/gHuy5w/6v5/1SGRj8Dw4tjsdHjIpD6fJZN0AdtVf HY20X1YP4D+9wqSfUSJENb/OCXAKinuyzWawaD8KQTUAzZ1lYNemSUhTD4vYVNMXjjTG NaPfB30BWtgmAFi1VmV1FlElDV2pxnzH5Cz/yC7luZ47i9eNhT0R8qFclIwOIKKgFCWw 7Gwr1JMe2i+QyoGcAOIMmeXfhoXwIXRyAj3PLvEitTOSXwabbxOgrswL7VhJili48Ciw x7juJwCpNQoLOSBQdg815g+cvA1dTT+ReoFGiRYKZOSqWRNn+XHeGnN9cJR5zu4UWSC6 jmaA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:in-reply-to:message-id:date :to:from:arc-authentication-results; bh=qbAV2HPMyFJDRieD6oeUsAmeonPflqCH9dqUCjIXvok=; b=MIrnTngGy5iS3KlvD4HsmKXsmvKo9Wtlb+gg5vVbnGZwgpXxMUAXkHyKHHH7HZ5jIU mSRM27duqR50FpdzlSQH1hSCT98BmMsaVJRDPe5OwveQ7g4ByH7dZ9An6Lkj2oIBeUg1 Zu/hj0XwgP0/xcM6DZ6KeGe8NIUfeutGRDO2wrOrImgBlJwpoRYT2ZT5gqLoywUz35AF cl51Gqa8+xgjbqUUwXaeqiDqfnn+9aWL4X0WkVY5vxerNrrzKTOB/6ZAoxkFXccFzY1H UdVSUJ1jyxsXED12yb4ohLthOvozwbCBQgp8dkxkZ3uzdeX619ufMFGYDWc6we6L5EO5 wxTw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of u-boot-bounces@lists.denx.de designates 81.169.180.215 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de Return-Path: Received: from lists.denx.de (dione.denx.de. [81.169.180.215]) by mx.google.com with ESMTP id m1si2202194edb.388.2018.04.12.06.49.29; Thu, 12 Apr 2018 06:49:30 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of u-boot-bounces@lists.denx.de designates 81.169.180.215 as permitted sender) client-ip=81.169.180.215; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of u-boot-bounces@lists.denx.de designates 81.169.180.215 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de Received: by lists.denx.de (Postfix, from userid 105) id 85413C21C38; Thu, 12 Apr 2018 13:49:02 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=none autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 22AC7C21DA2; Thu, 12 Apr 2018 13:48:26 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id B30E8C21C2F; Thu, 12 Apr 2018 13:48:24 +0000 (UTC) Received: from mx2.suse.de (mx2.suse.de [195.135.220.15]) by lists.denx.de (Postfix) with ESMTPS id 3A2C7C21C38 for ; Thu, 12 Apr 2018 13:48:24 +0000 (UTC) X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay1.suse.de (charybdis-ext.suse.de [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id EAC14ADD3; Thu, 12 Apr 2018 13:48:23 +0000 (UTC) From: Alexander Graf To: u-boot@lists.denx.de Date: Thu, 12 Apr 2018 15:48:23 +0200 Message-Id: <20180412134823.77239-4-agraf@suse.de> X-Mailer: git-send-email 2.12.3 In-Reply-To: <20180412134823.77239-1-agraf@suse.de> References: <20180412134823.77239-1-agraf@suse.de> Subject: [U-Boot] [PATCH v3 3/3] tools: zynqmpimage: Add bif support X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" 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 --- 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); #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 + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "imagetool.h" +#include "mkimage.h" +#include "zynqmpimage.h" +#include +#include + +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_ */