[U-Boot] [PATCH 1/4] add make_fit_atf in tools
Vicente Bergas
vicencb at gmail.com
Fri Jun 1 16:46:03 UTC 2018
From: Vicente Bergas <vicencb at gmail.com>
make_fit_atf generates an .its file based on U-Boot and ATF .elf files.
It also extracts all loadable sections from ATF.
It tries to somewhat mimic the behaviour of
arch/arm/mach-rockchip/make_fit_atf.py
Signed-off-by: Vicente Bergas <vicencb at gmail.com>
---
tools/Makefile | 2 +
tools/make_fit_atf.c | 360 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 362 insertions(+)
create mode 100644 tools/make_fit_atf.c
diff --git a/tools/Makefile b/tools/Makefile
index 5dd33ed4d5..a282f04f63 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -56,6 +56,8 @@ mkenvimage-objs := mkenvimage.o os_support.o lib/crc32.o
hostprogs-y += dumpimage mkimage
hostprogs-$(CONFIG_FIT_SIGNATURE) += fit_info fit_check_sign
+hostprogs-$(CONFIG_SPL_ATF) += make_fit_atf
+
hostprogs-$(CONFIG_CMD_BOOTEFI_SELFTEST) += file2include
FIT_SIG_OBJS-$(CONFIG_FIT_SIGNATURE) := common/image-sig.o
diff --git a/tools/make_fit_atf.c b/tools/make_fit_atf.c
new file mode 100644
index 0000000000..91101aaf8a
--- /dev/null
+++ b/tools/make_fit_atf.c
@@ -0,0 +1,360 @@
+// SPDX-License-Identifier: 0BSD
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static char const *dtb_basename (char const *path) {
+ enum { FILE_SEPARATOR = '/' };
+ size_t ln = strlen(path);
+ if (!ln || path[ln - 1] == FILE_SEPARATOR) {
+ fprintf(stderr, "dtb file name error '%s'.\n", path);
+ return NULL;
+ }
+ char const *base = path + ln - 1;
+ while (base > path && *base != FILE_SEPARATOR) --base;
+ if (*base == FILE_SEPARATOR) ++base;
+ return base;
+}
+
+/*
+ http://www.sco.com/developers/gabi/latest/ch4.intro.html
+ http://www.sco.com/developers/gabi/latest/ch4.eheader.html
+ http://www.sco.com/developers/gabi/latest/ch4.sheader.html
+ http://www.sco.com/developers/gabi/latest/ch5.pheader.html
+*/
+enum { EI_MAG0, EI_MAG1, EI_MAG2, EI_MAG3, EI_CLASS, EI_DATA, EI_VERSION, EI_NIDENT = 16 };
+enum { ELFMAG0 = 0x7F, ELFMAG1 = 'E', ELFMAG2 = 'L', ELFMAG3 = 'F' };
+enum { ELFCLASSNONE, ELFCLASS32, ELFCLASS64 };
+enum { ELFDATANONE, ELFDATA2LSB, ELFDATA2MSB };
+enum { ET_NONE, ET_REL, ET_EXEC, ET_DYN, ET_CORE };
+enum { EM_NONE, EM_ARM = 40, EM_AARCH64 = 183 };
+enum { EV_NONE, EV_CURRENT };
+enum { PT_NULL, PT_LOAD, PT_DYNAMIC, PT_INTERP, PT_NOTE, PT_SHLIB, PT_PHDR, PT_TLS };
+enum { SHT_NULL, SHT_PROGBITS, SHT_SYMTAB, SHT_STRTAB, SHT_RELA, SHT_HASH, SHT_DYNAMIC, SHT_NOTE, SHT_NOBITS };
+enum { SHF_ALLOC = 2 };
+typedef struct {
+ uint8_t e_ident[EI_NIDENT];
+ uint16_t e_type;
+ uint16_t e_machine;
+ uint32_t e_version;
+ uint64_t e_entry;
+ uint64_t e_phoff;
+ uint64_t e_shoff;
+ uint32_t e_flags;
+ uint16_t e_ehsize;
+ uint16_t e_phentsize;
+ uint16_t e_phnum;
+ uint16_t e_shentsize;
+ uint16_t e_shnum;
+ uint16_t e_shstrndx;
+} Elf64_Ehdr;
+typedef struct {
+ uint32_t p_type;
+ uint32_t p_flags;
+ uint64_t p_offset;
+ uint64_t p_vaddr;
+ uint64_t p_paddr;
+ uint64_t p_filesz;
+ uint64_t p_memsz;
+ uint64_t p_align;
+} Elf64_Phdr;
+typedef struct {
+ uint32_t sh_name;
+ uint32_t sh_type;
+ uint64_t sh_flags;
+ uint64_t sh_addr;
+ uint64_t sh_offset;
+ uint64_t sh_size;
+ uint32_t sh_link;
+ uint32_t sh_info;
+ uint64_t sh_addralign;
+ uint64_t sh_entsize;
+} Elf64_Shdr;
+
+static FILE *elf_open(char const *file_name, Elf64_Ehdr *eh) {
+ FILE *f = fopen(file_name, "rb");
+ if (!f) {
+ fprintf(stderr, "Error openning '%s': %s.\n", file_name, strerror(errno));
+ return 0;
+ }
+ if (fread(eh, EI_NIDENT, 1, f) != 1) {
+ if (feof(f)) {
+ fprintf(stderr, "Not ELF file.\n");
+ return 0;
+ }
+ fprintf(stderr, "Error reading '%s'.\n", file_name);
+ return 0;
+ }
+ if (
+ eh->e_ident[EI_MAG0] != ELFMAG0 ||
+ eh->e_ident[EI_MAG1] != ELFMAG1 ||
+ eh->e_ident[EI_MAG2] != ELFMAG2 ||
+ eh->e_ident[EI_MAG3] != ELFMAG3
+ ) {
+ fprintf(stderr, "Not ELF file.\n");
+ return 0;
+ }
+ if (
+ eh->e_ident[EI_CLASS] != ELFCLASS64 ||
+ eh->e_ident[EI_DATA] != ELFDATA2LSB ||
+ eh->e_ident[EI_VERSION] != EV_CURRENT
+ ) {
+ fprintf(stderr, "Only version %u 64-bit little-endian ELF files supported.\n", EV_CURRENT);
+ return 0;
+ }
+ if (!*(uint8_t *)&(uint32_t){1}) {
+ fprintf(stderr, "Only little-endian hosts supported.\n");
+ return 0;
+ }
+ if (fread(((void *)eh) + EI_NIDENT, sizeof(*eh) - EI_NIDENT, 1, f) != 1) {
+ fprintf(stderr, "Error reading '%s'.\n", file_name);
+ return 0;
+ }
+
+ if (
+ eh->e_version != EV_CURRENT ||
+ eh->e_ehsize != sizeof(Elf64_Ehdr) ||
+ eh->e_phentsize != sizeof(Elf64_Phdr) ||
+ eh->e_shentsize != sizeof(Elf64_Shdr)
+ ) {
+ fprintf(stderr, "Unexpected ELF file.\n");
+ return 0;
+ }
+ if (eh->e_machine != EM_AARCH64) {
+ fprintf(stderr, "Only arm64 supported.\n");
+ return 0;
+ }
+ return f;
+}
+
+enum { GOTO_EH, GOTO_PH, GOTO_SH };
+static int elf_goto(char const *file_name, FILE *elf, Elf64_Ehdr const *eh, unsigned where) {
+ uint64_t seek = 0;
+ switch (where) {
+ case GOTO_EH: break;
+ case GOTO_PH: seek = eh->e_phoff; break;
+ case GOTO_SH: seek = eh->e_shoff; break;
+ }
+ if (fseek(elf, seek, SEEK_SET)) {
+ fprintf(stderr, "Error seeking '%s'.\n", file_name);
+ return -1;
+ }
+ return 0;
+}
+
+static int elf_next_ph(char const *file_name, FILE *elf, Elf64_Phdr *ph) {
+ if (fread(ph, sizeof(Elf64_Phdr), 1, elf) != 1) {
+ fprintf(stderr, "Error reading '%s'.\n", file_name);
+ return -1;
+ }
+ return 0;
+}
+
+static int elf_next_sh(char const *file_name, FILE *elf, Elf64_Shdr *sh) {
+ if (fread(sh, sizeof(Elf64_Shdr), 1, elf) != 1) {
+ fprintf(stderr, "Error reading '%s'.\n", file_name);
+ return -1;
+ }
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ Elf64_Ehdr eh;
+ Elf64_Phdr ph;
+ Elf64_Shdr sh;
+ FILE *fi;
+ FILE *fo = stdout;
+ char *uboot = "u-boot";
+ char *bl31 = "bl31.elf";
+ void *buffer = 0;
+ size_t buffer_len = 0;
+ uint64_t addr;
+ int cnt;
+
+ if (!argc) { return -1; }
+ --argc; ++argv;
+
+ while (argc) {
+ if (argv[0][0] != '-') { break; }
+ char opt = argv[0][1];
+ --argc; ++argv;
+ if (argv[-1][2]) {
+err_option:
+ fprintf(stderr, "Error with option '%s'.\n", argv[-1]);
+usage:
+ fprintf(stderr, "make_fit_atf [-h] [-o out_file] [-u u_boot_file] [-b bl31_file] dtb_file[s]\n");
+ return -1;
+ }
+ switch (opt) {
+ default : goto err_option;
+ case 'h': goto usage;
+ case 'u': uboot = argv[0]; break;
+ case 'b': bl31 = argv[0]; break;
+ case 'o':
+ if (fo != stdout) {
+ fprintf(stderr, "Maximum one -o option.\n");
+ return -1;
+ }
+ fo = fopen(argv[0], "w");
+ if (!fo) {
+ fprintf(stderr, "Could not open '%s' for writing: %s.\n", argv[0], strerror(errno));
+ return -1;
+ }
+ }
+ --argc; ++argv;
+ }
+
+ fi = elf_open(uboot, &eh);
+ if (!fi) { return -1; }
+ if (elf_goto(uboot, fi, &eh, GOTO_PH)) { return -1; }
+ cnt = 0;
+ for (unsigned i = 0; i < eh.e_phnum; ++i) {
+ if (elf_next_ph(uboot, fi, &ph)) { return -1; }
+ if (ph.p_type != PT_LOAD) { continue; }
+ if (ph.p_vaddr != ph.p_paddr) {
+ fprintf(stderr, "Virtual address not supported.\n");
+ return -1;
+ }
+ addr = ph.p_vaddr;
+ ++cnt;
+ }
+ if (cnt != 1) {
+ fprintf(stderr, "Only one loadable segment expected.\n");
+ return -1;
+ }
+ fclose(fi);
+
+ fprintf(fo,
+ "/dts-v1/;\n"
+ "\n"
+ "/ {\n"
+ " description = \"Configuration to load ATF before U-Boot\";\n"
+ " #address-cells = <1>;\n"
+ "\n"
+ " images {\n"
+ " uboot {\n"
+ " description = \"U-Boot\";\n"
+ " data = /incbin/(\"u-boot-nodtb.bin\");\n"
+ " type = \"standalone\";\n"
+ " os = \"U-Boot\";\n"
+ " arch = \"arm64\";\n"
+ " compression = \"none\";\n"
+ " load = <0x%08lX>;\n"
+ " };\n"
+ , addr
+ );
+
+ fi = elf_open(bl31, &eh);
+ if (!fi) { return -1; }
+ if (elf_goto(bl31, fi, &eh, GOTO_SH)) { return -1; }
+ cnt = 0;
+ int fw_entry = -1;
+ for (unsigned i = 0; i < eh.e_shnum; ++i) {
+ if (elf_next_sh(bl31, fi, &sh)) { return -1; }
+ if (sh.sh_type != SHT_PROGBITS || !(sh.sh_flags & SHF_ALLOC)) { continue; }
+ ++cnt;
+ fprintf(fo,
+ " atf%u {\n"
+ " description = \"ARM Trusted Firmware\";\n"
+ " data = /incbin/(\"bl31_0x%08lX.bin\");\n"
+ " type = \"firmware\";\n"
+ " arch = \"arm64\";\n"
+ " os = \"arm-trusted-firmware\";\n"
+ " compression = \"none\";\n"
+ " load = <0x%08lX>;\n"
+ , cnt
+ , sh.sh_addr
+ , sh.sh_addr
+ );
+ if (sh.sh_addr <= eh.e_entry && eh.e_entry < sh.sh_addr + sh.sh_size) {
+ fprintf(fo, " entry = <0x%08lX>;\n", eh.e_entry);
+ fw_entry = cnt;
+ }
+ fprintf(fo, " };\n");
+
+ if (1) {
+ if (buffer_len < sh.sh_size) {
+ if (buffer_len) { free(buffer); }
+ buffer_len = sh.sh_size;
+ buffer = malloc(buffer_len);
+ if (!buffer) {
+ fprintf(stderr, "%s.\n", strerror(errno));
+ return -1;
+ }
+ }
+ long current = ftell(fi);
+ int sk = fseek(fi, sh.sh_offset, SEEK_SET);
+ size_t ln = fread(buffer, sh.sh_size, 1, fi);
+ int sk2 = fseek(fi, current, SEEK_SET);
+ if (current < 0 || sk || ln != 1 || sk2) {
+ fprintf(stderr, "Could not read '%s': %s.\n", bl31, strerror(errno));
+ return -1;
+ }
+
+ char fname[32];
+ sprintf(fname, "bl31_0x%08lX.bin", sh.sh_addr);
+ FILE *f = fopen(fname, "wb");
+ if (f) {
+ ln = fwrite(buffer, sh.sh_size, 1, f);
+ fclose(f);
+ }
+ if (!f || ln != 1) {
+ fprintf(stderr, "Could not write to '%s': %s.\n", fname, strerror(errno));
+ return -1;
+ }
+ }
+ }
+ fclose(fi);
+
+ for (int j = 0; j < argc; ++j) {
+ fprintf(fo,
+ " fdt%d {\n"
+ " description = \"%s\";\n"
+ " data = /incbin/(\"%s\");\n"
+ " type = \"flat_dt\";\n"
+ " compression = \"none\";\n"
+ " };\n"
+ , j + 1
+ , dtb_basename(argv[j])
+ , argv[j]
+ );
+ }
+ fprintf(fo, " };\n" );
+
+ if (argc) {
+ fprintf(fo,
+ " configurations {\n"
+ " default = \"config1\";\n"
+ );
+ for (int j = 0; j < argc; ++j) {
+ fprintf(fo,
+ " config%d {\n"
+ " description = \"%s\";\n"
+ , j + 1
+ , dtb_basename(argv[j])
+ );
+ if (fw_entry >= 0) {
+ fprintf(fo, " firmware = \"atf%d\";\n", fw_entry);
+ }
+ fprintf(fo, " loadables = \"uboot\"");
+ for (int i = 1; i <= cnt; ++i) {
+ if (i != fw_entry) {
+ fprintf(fo, ",\"atf%u\"", i);
+ }
+ }
+ fprintf(fo,
+ ";\n"
+ " fdt = \"fdt%u\";\n"
+ " };\n"
+ , j + 1
+ );
+ }
+ fprintf(fo, " };\n");
+ }
+
+ fprintf(fo, "};\n");
+ return 0;
+}
--
2.17.1
More information about the U-Boot
mailing list