summaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
authorAhmad Fatoum <a.fatoum@pengutronix.de>2021-03-22 14:39:12 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2021-03-23 12:16:26 +0100
commit410544558f35542dbcd07ebf0c8fb09d7e844a93 (patch)
treea9dead1c667f8eecf91522234f24ffd7430be2b7 /scripts
parentc9067a3266f8f8c8651a2b063d7cfc9edb3d1770 (diff)
downloadbarebox-410544558f35542dbcd07ebf0c8fb09d7e844a93.tar.gz
barebox-410544558f35542dbcd07ebf0c8fb09d7e844a93.tar.xz
RISC-V: implement PBL and relocation support
Given that we only support a single RISC-V board, this puts us in a good position to make this a multi-image-only architecture. This commit adds the necessary bits. It's highly inspired by the ARM PBL support. Notable difference is that for relocations to be generated, it was necessary to compile with -fpic. The relocation code assumes the relocation entries to preprocessed. This is done at build-time by means of the prelink-riscv script imported from U-Boot. Actual migration to -fpic and prelinking is done along with porting erizo in a follow-up commit. Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'scripts')
-rw-r--r--scripts/.gitignore1
-rw-r--r--scripts/Makefile1
-rw-r--r--scripts/Makefile.lib11
-rw-r--r--scripts/prelink-riscv.c122
-rw-r--r--scripts/prelink-riscv.inc123
5 files changed, 258 insertions, 0 deletions
diff --git a/scripts/.gitignore b/scripts/.gitignore
index 7c9a3f5571..9577d568ed 100644
--- a/scripts/.gitignore
+++ b/scripts/.gitignore
@@ -32,3 +32,4 @@ mips-relocs
rsatoc
stm32image
mvebuimg
+prelink-riscv
diff --git a/scripts/Makefile b/scripts/Makefile
index 744f4dd0e7..4dc70815b7 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -24,6 +24,7 @@ hostprogs-always-$(CONFIG_ARCH_SOCFPGA) += socfpga_mkimage
hostprogs-always-$(CONFIG_ARCH_MXS) += mxsimage mxsboot
hostprogs-always-$(CONFIG_ARCH_LAYERSCAPE) += pblimage
hostprogs-always-$(CONFIG_ARCH_STM32MP) += stm32image
+hostprogs-always-$(CONFIG_RISCV) += prelink-riscv
KBUILD_HOSTCFLAGS += -I$(srctree)/scripts/include/
HOSTLDLIBS_mxsimage = `pkg-config --libs openssl`
HOSTCFLAGS_omap3-usb-loader.o = `pkg-config --cflags libusb-1.0`
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 2844d29be6..319ac19975 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -247,6 +247,17 @@ $(obj)/%:: $(src)/%_shipped
# and add target to extra-y so that we know we have to
# read in the saved command line
+# Prelinking
+# ---------------------------------------------------------------------------
+
+ifneq ($(CONFIG_RISCV),)
+quiet_cmd_prelink__ = PRELINK $@
+ cmd_prelink__ = $(objtree)/scripts/prelink-riscv $@
+endif
+
+quiet_cmd_prelink__ ?=
+ cmd_prelink__ ?=
+
# Linking
# ---------------------------------------------------------------------------
diff --git a/scripts/prelink-riscv.c b/scripts/prelink-riscv.c
new file mode 100644
index 0000000000..c185d3981c
--- /dev/null
+++ b/scripts/prelink-riscv.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Andes Technology
+ * Chih-Mao Chen <cmchen@andestech.com>
+ *
+ * Statically process runtime relocations on RISC-V ELF images
+ * so that it can be directly executed when loaded at LMA
+ * without fixup. Both RV32 and RV64 are supported.
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <elf.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "compiler.h"
+
+#ifndef EM_RISCV
+#define EM_RISCV 243
+#endif
+
+#ifndef R_RISCV_32
+#define R_RISCV_32 1
+#endif
+
+#ifndef R_RISCV_64
+#define R_RISCV_64 2
+#endif
+
+#ifndef R_RISCV_RELATIVE
+#define R_RISCV_RELATIVE 3
+#endif
+
+const char *argv0;
+
+#define die(fmt, ...) \
+ do { \
+ fprintf(stderr, "%s: " fmt "\n", argv0, ## __VA_ARGS__); \
+ exit(EXIT_FAILURE); \
+ } while (0)
+
+#define PRELINK_BYTEORDER le
+#define PRELINK_INC_BITS 32
+#include "prelink-riscv.inc"
+#undef PRELINK_BYTEORDER
+#undef PRELINK_INC_BITS
+
+#define PRELINK_BYTEORDER le
+#define PRELINK_INC_BITS 64
+#include "prelink-riscv.inc"
+#undef PRELINK_BYTEORDER
+#undef PRELINK_INC_BITS
+
+#define PRELINK_BYTEORDER be
+#define PRELINK_INC_BITS 32
+#include "prelink-riscv.inc"
+#undef PRELINK_BYTEORDER
+#undef PRELINK_INC_BITS
+
+#define PRELINK_BYTEORDER be
+#define PRELINK_INC_BITS 64
+#include "prelink-riscv.inc"
+#undef PRELINK_BYTEORDER
+#undef PRELINK_INC_BITS
+
+int main(int argc, const char *const *argv)
+{
+ argv0 = argv[0];
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s <u-boot>\n", argv0);
+ exit(EXIT_FAILURE);
+ }
+
+ int fd = open(argv[1], O_RDWR, 0);
+
+ if (fd < 0)
+ die("Cannot open %s: %s", argv[1], strerror(errno));
+
+ struct stat st;
+
+ if (fstat(fd, &st) < 0)
+ die("Cannot stat %s: %s", argv[1], strerror(errno));
+
+ void *data =
+ mmap(0, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+
+ if (data == MAP_FAILED)
+ die("Cannot mmap %s: %s", argv[1], strerror(errno));
+
+ close(fd);
+
+ unsigned char *e_ident = (unsigned char *)data;
+
+ if (memcmp(e_ident, ELFMAG, SELFMAG) != 0)
+ die("Invalid ELF file %s", argv[1]);
+
+ bool is64 = e_ident[EI_CLASS] == ELFCLASS64;
+ bool isbe = e_ident[EI_DATA] == ELFDATA2MSB;
+
+ if (is64) {
+ if (isbe)
+ prelink_be64(data);
+ else
+ prelink_le64(data);
+ } else {
+ if (isbe)
+ prelink_be32(data);
+ else
+ prelink_le32(data);
+ }
+
+ return 0;
+}
diff --git a/scripts/prelink-riscv.inc b/scripts/prelink-riscv.inc
new file mode 100644
index 0000000000..f2b5467f5b
--- /dev/null
+++ b/scripts/prelink-riscv.inc
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Andes Technology
+ * Chih-Mao Chen <cmchen@andestech.com>
+ *
+ * Statically process runtime relocations on RISC-V ELF images
+ * so that it can be directly executed when loaded at LMA
+ * without fixup. Both RV32 and RV64 are supported.
+ */
+
+#define CONCAT_IMPL(x, y) x##y
+#define CONCAT(x, y) CONCAT_IMPL(x, y)
+#define CONCAT3(x, y, z) CONCAT(CONCAT(x, y), z)
+
+#define prelink_bonn CONCAT3(prelink_, PRELINK_BYTEORDER, PRELINK_INC_BITS)
+#define uintnn_t CONCAT3(uint, PRELINK_INC_BITS, _t)
+#define get_offset_bonn CONCAT3(get_offset_, PRELINK_BYTEORDER, PRELINK_INC_BITS)
+#define Elf_Ehdr CONCAT3(Elf, PRELINK_INC_BITS, _Ehdr)
+#define Elf_Phdr CONCAT3(Elf, PRELINK_INC_BITS, _Phdr)
+#define Elf_Rela CONCAT3(Elf, PRELINK_INC_BITS, _Rela)
+#define Elf_Sym CONCAT3(Elf, PRELINK_INC_BITS, _Sym)
+#define Elf_Dyn CONCAT3(Elf, PRELINK_INC_BITS, _Dyn)
+#define Elf_Addr CONCAT3(Elf, PRELINK_INC_BITS, _Addr)
+#define ELF_R_TYPE CONCAT3(ELF, PRELINK_INC_BITS, _R_TYPE)
+#define ELF_R_SYM CONCAT3(ELF, PRELINK_INC_BITS, _R_SYM)
+#define target16_to_cpu CONCAT(PRELINK_BYTEORDER, 16_to_cpu)
+#define target32_to_cpu CONCAT(PRELINK_BYTEORDER, 32_to_cpu)
+#define target64_to_cpu CONCAT(PRELINK_BYTEORDER, 64_to_cpu)
+#define targetnn_to_cpu CONCAT3(PRELINK_BYTEORDER, PRELINK_INC_BITS, _to_cpu)
+#define cpu_to_target32 CONCAT3(cpu_to_, PRELINK_BYTEORDER, 32)
+#define cpu_to_target64 CONCAT3(cpu_to_, PRELINK_BYTEORDER, 64)
+
+static void* get_offset_bonn (void* data, Elf_Phdr* phdrs, size_t phnum, Elf_Addr addr)
+{
+ Elf_Phdr *p;
+
+ for (p = phdrs; p < phdrs + phnum; ++p)
+ if (targetnn_to_cpu(p->p_vaddr) <= addr && targetnn_to_cpu(p->p_vaddr) + targetnn_to_cpu(p->p_memsz) > addr)
+ return data + targetnn_to_cpu(p->p_offset) + (addr - targetnn_to_cpu(p->p_vaddr));
+
+ return NULL;
+}
+
+static void prelink_bonn(void *data)
+{
+ Elf_Ehdr *ehdr = data;
+ Elf_Phdr *p;
+ Elf_Dyn *dyn;
+ Elf_Rela *r;
+
+ if (target16_to_cpu(ehdr->e_machine) != EM_RISCV)
+ die("Machine type is not RISC-V");
+
+ Elf_Phdr *phdrs = data + targetnn_to_cpu(ehdr->e_phoff);
+
+ Elf_Dyn *dyns = NULL;
+ for (p = phdrs; p < phdrs + target16_to_cpu(ehdr->e_phnum); ++p) {
+ if (target32_to_cpu(p->p_type) == PT_DYNAMIC) {
+ dyns = data + targetnn_to_cpu(p->p_offset);
+ break;
+ }
+ }
+
+ if (dyns == NULL)
+ die("No dynamic section found");
+
+ Elf_Rela *rela_dyn = NULL;
+ size_t rela_count = 0;
+ Elf_Sym *dynsym = NULL;
+ for (dyn = dyns;; ++dyn) {
+ if (targetnn_to_cpu(dyn->d_tag) == DT_NULL)
+ break;
+ else if (targetnn_to_cpu(dyn->d_tag) == DT_RELA)
+ rela_dyn = get_offset_bonn(data, phdrs, target16_to_cpu(ehdr->e_phnum), + targetnn_to_cpu(dyn->d_un.d_ptr));
+ else if (targetnn_to_cpu(dyn->d_tag) == DT_RELASZ)
+ rela_count = targetnn_to_cpu(dyn->d_un.d_val) / sizeof(Elf_Rela);
+ else if (targetnn_to_cpu(dyn->d_tag) == DT_SYMTAB)
+ dynsym = get_offset_bonn(data, phdrs, target16_to_cpu(ehdr->e_phnum), + targetnn_to_cpu(dyn->d_un.d_ptr));
+
+ }
+
+ if (rela_dyn == NULL)
+ die("No .rela.dyn found");
+
+ if (dynsym == NULL)
+ die("No .dynsym found");
+
+ for (r = rela_dyn; r < rela_dyn + rela_count; ++r) {
+ void* buf = get_offset_bonn(data, phdrs, target16_to_cpu(ehdr->e_phnum), targetnn_to_cpu(r->r_offset));
+
+ if (buf == NULL)
+ continue;
+
+ if (ELF_R_TYPE(targetnn_to_cpu(r->r_info)) == R_RISCV_RELATIVE)
+ *((uintnn_t*) buf) = r->r_addend;
+ else if (ELF_R_TYPE(targetnn_to_cpu(r->r_info)) == R_RISCV_32)
+ *((uint32_t*) buf) = cpu_to_target32(targetnn_to_cpu(dynsym[ELF_R_SYM(targetnn_to_cpu(r->r_info))].st_value) + targetnn_to_cpu(r->r_addend));
+ else if (ELF_R_TYPE(targetnn_to_cpu(r->r_info)) == R_RISCV_64)
+ *((uint64_t*) buf) = cpu_to_target64(targetnn_to_cpu(dynsym[ELF_R_SYM(targetnn_to_cpu(r->r_info))].st_value) + targetnn_to_cpu(r->r_addend));
+ }
+}
+
+#undef prelink_bonn
+#undef uintnn_t
+#undef get_offset_bonn
+#undef Elf_Ehdr
+#undef Elf_Phdr
+#undef Elf_Rela
+#undef Elf_Sym
+#undef Elf_Dyn
+#undef Elf_Addr
+#undef ELF_R_TYPE
+#undef ELF_R_SYM
+#undef target16_to_cpu
+#undef target32_to_cpu
+#undef target64_to_cpu
+#undef targetnn_to_cpu
+#undef cpu_to_target32
+#undef cpu_to_target64
+
+#undef CONCAT_IMPL
+#undef CONCAT
+#undef CONCAT3