summaryrefslogtreecommitdiffstats
path: root/arch
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 /arch
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 'arch')
-rw-r--r--arch/riscv/Makefile1
-rw-r--r--arch/riscv/boot/Makefile3
-rw-r--r--arch/riscv/boot/entry.c29
-rw-r--r--arch/riscv/boot/entry.h15
-rw-r--r--arch/riscv/boot/start.c185
-rw-r--r--arch/riscv/boot/uncompress.c74
-rw-r--r--arch/riscv/include/asm/barebox-riscv.h104
-rw-r--r--arch/riscv/include/asm/common.h10
-rw-r--r--arch/riscv/include/asm/elf.h7
-rw-r--r--arch/riscv/include/asm/linkage.h4
-rw-r--r--arch/riscv/include/asm/sections.h15
-rw-r--r--arch/riscv/lib/Makefile3
-rw-r--r--arch/riscv/lib/barebox.lds.S14
-rw-r--r--arch/riscv/lib/pbl.lds.S80
-rw-r--r--arch/riscv/lib/reloc.c66
-rw-r--r--arch/riscv/lib/runtime-offset.S12
-rw-r--r--arch/riscv/lib/sections.c9
-rw-r--r--arch/riscv/lib/setupc.S55
18 files changed, 682 insertions, 4 deletions
diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile
index d9cefe32c0..df2b5bb681 100644
--- a/arch/riscv/Makefile
+++ b/arch/riscv/Makefile
@@ -43,6 +43,7 @@ endif
common-y += $(MACH)
common-y += arch/riscv/boards/
common-y += arch/riscv/lib/
+common-y += arch/riscv/boot/
common-$(CONFIG_OFTREE) += arch/riscv/dts/
diff --git a/arch/riscv/boot/Makefile b/arch/riscv/boot/Makefile
new file mode 100644
index 0000000000..70e5b11beb
--- /dev/null
+++ b/arch/riscv/boot/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-y += start.o
+pbl-y += entry.o uncompress.o
diff --git a/arch/riscv/boot/entry.c b/arch/riscv/boot/entry.c
new file mode 100644
index 0000000000..eb286423d8
--- /dev/null
+++ b/arch/riscv/boot/entry.c
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <types.h>
+
+#include <asm/barebox-riscv.h>
+
+#include "entry.h"
+#include <debug_ll.h>
+
+/*
+ * Main RISC-V entry point. Call this with the memory region you can
+ * spare for barebox. This doesn't necessarily have to be the full
+ * SDRAM. The currently running binary can be inside or outside of
+ * this region. The PBL can be running inside or outside of this
+ * region.
+ *
+ * -> membase + memsize
+ * STACK_SIZE - stack
+ * 128KiB - early memory space
+ * -> maximum end of barebox binary
+ */
+
+void __noreturn __naked barebox_riscv_entry(unsigned long membase,
+ unsigned long memsize, void *boarddata)
+{
+ unsigned long stack_top = riscv_mem_stack_top(membase, membase + memsize);
+ asm volatile ("move sp, %0" : : "r"(stack_top));
+ barebox_pbl_start(membase, memsize, boarddata);
+}
+
diff --git a/arch/riscv/boot/entry.h b/arch/riscv/boot/entry.h
new file mode 100644
index 0000000000..b3a24d2783
--- /dev/null
+++ b/arch/riscv/boot/entry.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ENTRY_H__
+#define __ENTRY_H__
+
+#include <common.h>
+
+void __noreturn barebox_non_pbl_start(unsigned long membase,
+ unsigned long memsize,
+ void *boarddata);
+
+void __noreturn barebox_pbl_start(unsigned long membase,
+ unsigned long memsize,
+ void *boarddata);
+
+#endif
diff --git a/arch/riscv/boot/start.c b/arch/riscv/boot/start.c
new file mode 100644
index 0000000000..05f6c6231f
--- /dev/null
+++ b/arch/riscv/boot/start.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2010 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+
+#define pr_fmt(fmt) "start.c: " fmt
+
+#include <common.h>
+#include <init.h>
+#include <linux/sizes.h>
+#include <of.h>
+#include <asm/barebox-riscv.h>
+#include <asm-generic/memory_layout.h>
+#include <asm/sections.h>
+#include <asm/unaligned.h>
+#include <linux/kasan.h>
+#include <memory.h>
+#include <uncompress.h>
+#include <malloc.h>
+#include <compressed-dtb.h>
+
+#include <debug_ll.h>
+
+#include "entry.h"
+
+unsigned long riscv_stack_top;
+static unsigned long riscv_barebox_size;
+static unsigned long riscv_endmem;
+static void *barebox_boarddata;
+static unsigned long barebox_boarddata_size;
+
+void *barebox_riscv_boot_dtb(void)
+{
+ void *dtb;
+ void *data;
+ int ret;
+ struct barebox_boarddata_compressed_dtb *compressed_dtb;
+ static void *boot_dtb;
+
+ if (boot_dtb)
+ return boot_dtb;
+
+ if (barebox_boarddata && blob_is_fdt(barebox_boarddata)) {
+ pr_debug("%s: using barebox_boarddata\n", __func__);
+ return barebox_boarddata;
+ }
+
+ if (!fdt_blob_can_be_decompressed(barebox_boarddata))
+ return NULL;
+
+ compressed_dtb = barebox_boarddata;
+
+ pr_debug("%s: using compressed_dtb\n", __func__);
+
+ dtb = malloc(compressed_dtb->datalen_uncompressed);
+ if (!dtb)
+ return NULL;
+
+ data = compressed_dtb + 1;
+
+ ret = uncompress(data, compressed_dtb->datalen, NULL, NULL, dtb, NULL, NULL);
+ if (ret) {
+ pr_err("uncompressing dtb failed\n");
+ free(dtb);
+ return NULL;
+ }
+
+ boot_dtb = dtb;
+
+ return boot_dtb;
+}
+
+static inline unsigned long riscv_mem_boarddata(unsigned long membase,
+ unsigned long endmem,
+ unsigned long size)
+{
+ unsigned long mem;
+
+ mem = riscv_mem_barebox_image(membase, endmem, riscv_barebox_size);
+ mem -= ALIGN(size, 64);
+
+ return mem;
+}
+
+unsigned long riscv_mem_ramoops_get(void)
+{
+ return riscv_mem_ramoops(0, riscv_stack_top);
+}
+EXPORT_SYMBOL_GPL(riscv_mem_ramoops_get);
+
+unsigned long riscv_mem_endmem_get(void)
+{
+ return riscv_endmem;
+}
+EXPORT_SYMBOL_GPL(riscv_mem_endmem_get);
+
+static int barebox_memory_areas_init(void)
+{
+ if(barebox_boarddata)
+ request_sdram_region("board data", (unsigned long)barebox_boarddata,
+ barebox_boarddata_size);
+
+ return 0;
+}
+device_initcall(barebox_memory_areas_init);
+
+/*
+ * First function in the uncompressed image. We get here from
+ * the pbl. The stack already has been set up by the pbl.
+ */
+__noreturn __no_sanitize_address __section(.text_entry)
+void barebox_non_pbl_start(unsigned long membase, unsigned long memsize, void *boarddata)
+{
+ unsigned long endmem = membase + memsize;
+ unsigned long malloc_start, malloc_end;
+ unsigned long barebox_size = barebox_image_size + MAX_BSS_SIZE;
+ unsigned long barebox_base = riscv_mem_barebox_image(membase, endmem, barebox_size);
+
+ relocate_to_current_adr();
+
+ setup_c();
+
+ barrier();
+
+ pr_debug("memory at 0x%08lx, size 0x%08lx\n", membase, memsize);
+
+ riscv_endmem = endmem;
+ riscv_stack_top = riscv_mem_stack_top(membase, endmem);
+ riscv_barebox_size = barebox_size;
+ malloc_end = barebox_base;
+
+ if (boarddata) {
+ uint32_t totalsize = 0;
+ const char *name;
+
+ if (blob_is_fdt(boarddata)) {
+ totalsize = get_unaligned_be32(boarddata + 4);
+ name = "DTB";
+ } else if (blob_is_compressed_fdt(boarddata)) {
+ struct barebox_boarddata_compressed_dtb *bd = boarddata;
+ totalsize = bd->datalen + sizeof(*bd);
+ name = "Compressed DTB";
+ }
+
+ if (totalsize) {
+ unsigned long mem = riscv_mem_boarddata(membase, endmem, totalsize);
+ pr_debug("found %s in boarddata, copying to 0x%08lx\n", name, mem);
+ barebox_boarddata = memcpy((void *)mem, boarddata, totalsize);
+ barebox_boarddata_size = totalsize;
+ malloc_end = mem;
+ }
+ }
+
+ /*
+ * Maximum malloc space is the Kconfig value if given
+ * or 1GB.
+ */
+ if (MALLOC_SIZE > 0) {
+ malloc_start = malloc_end - MALLOC_SIZE;
+ if (malloc_start < membase)
+ malloc_start = membase;
+ } else {
+ malloc_start = malloc_end - (malloc_end - membase) / 2;
+ if (malloc_end - malloc_start > SZ_1G)
+ malloc_start = malloc_end - SZ_1G;
+ }
+
+ pr_debug("initializing malloc pool at 0x%08lx (size 0x%08lx)\n",
+ malloc_start, malloc_end - malloc_start);
+
+ mem_malloc_init((void *)malloc_start, (void *)malloc_end - 1);
+
+ pr_debug("starting barebox...\n");
+
+ start_barebox();
+}
+
+void start(unsigned long membase, unsigned long memsize, void *boarddata);
+/*
+ * First function in the uncompressed image. We get here from
+ * the pbl. The stack already has been set up by the pbl.
+ */
+void __no_sanitize_address __section(.text_entry) start(unsigned long membase,
+ unsigned long memsize, void *boarddata)
+{
+ barebox_non_pbl_start(membase, memsize, boarddata);
+}
diff --git a/arch/riscv/boot/uncompress.c b/arch/riscv/boot/uncompress.c
new file mode 100644
index 0000000000..cf268bece1
--- /dev/null
+++ b/arch/riscv/boot/uncompress.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2010-2013 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+// SPDX-FileCopyrightText: 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
+
+/* uncompress.c - uncompressor code for self extracing pbl image */
+
+#define pr_fmt(fmt) "uncompress.c: " fmt
+
+#include <common.h>
+#include <init.h>
+#include <linux/sizes.h>
+#include <pbl.h>
+#include <asm/barebox-riscv.h>
+#include <asm-generic/memory_layout.h>
+#include <asm/sections.h>
+#include <asm/unaligned.h>
+
+#include <debug_ll.h>
+
+#include "entry.h"
+
+unsigned long free_mem_ptr;
+unsigned long free_mem_end_ptr;
+
+extern unsigned char input_data[];
+extern unsigned char input_data_end[];
+
+void __noreturn barebox_pbl_start(unsigned long membase, unsigned long memsize,
+ void *fdt)
+{
+ uint32_t pg_len, uncompressed_len;
+ void __noreturn (*barebox)(unsigned long, unsigned long, void *);
+ unsigned long endmem = membase + memsize;
+ unsigned long barebox_base;
+ void *pg_start, *pg_end;
+ unsigned long pc = get_pc();
+
+ pg_start = input_data + get_runtime_offset();
+ pg_end = input_data_end + get_runtime_offset();
+
+ /*
+ * If we run from inside the memory just relocate the binary
+ * to the current address. Otherwise it may be a readonly location.
+ * Copy and relocate to the start of the memory in this case.
+ */
+ if (pc > membase && pc - membase < memsize)
+ relocate_to_current_adr();
+ else
+ relocate_to_adr(membase);
+
+ pg_len = pg_end - pg_start;
+ uncompressed_len = get_unaligned((const u32 *)(pg_start + pg_len - 4));
+
+ barebox_base = riscv_mem_barebox_image(membase, endmem,
+ uncompressed_len + MAX_BSS_SIZE);
+
+ setup_c();
+
+ pr_debug("memory at 0x%08lx, size 0x%08lx\n", membase, memsize);
+
+ free_mem_ptr = riscv_mem_early_malloc(membase, endmem);
+ free_mem_end_ptr = riscv_mem_early_malloc_end(membase, endmem);
+
+ pr_debug("uncompressing barebox binary at 0x%p (size 0x%08x) to 0x%08lx (uncompressed size: 0x%08x)\n",
+ pg_start, pg_len, barebox_base, uncompressed_len);
+
+ pbl_barebox_uncompress((void*)barebox_base, pg_start, pg_len);
+
+ barebox = (void *)barebox_base;
+
+ pr_debug("jumping to uncompressed image at 0x%p. dtb=0x%p\n", barebox, fdt);
+
+ barebox(membase, memsize, fdt);
+}
diff --git a/arch/riscv/include/asm/barebox-riscv.h b/arch/riscv/include/asm/barebox-riscv.h
new file mode 100644
index 0000000000..980165dc8a
--- /dev/null
+++ b/arch/riscv/include/asm/barebox-riscv.h
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * (C) Copyright 2002
+ * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Marius Groeger <mgroeger@sysgo.de>
+ *
+ * (C) Copyright 2002
+ * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Alex Zuepke <azu@sysgo.de>
+ */
+
+#ifndef _BAREBOX_RISCV_H_
+#define _BAREBOX_RISCV_H_
+
+#include <linux/sizes.h>
+#include <asm-generic/memory_layout.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/compiler.h>
+#include <asm/sections.h>
+
+unsigned long get_runtime_offset(void);
+
+void setup_c(void);
+void relocate_to_current_adr(void);
+void relocate_to_adr(unsigned long target);
+void __noreturn __naked barebox_riscv_entry(unsigned long membase, unsigned long memsize,
+ void *boarddata);
+
+unsigned long riscv_mem_ramoops_get(void);
+unsigned long riscv_mem_endmem_get(void);
+
+void *barebox_riscv_boot_dtb(void);
+
+static inline unsigned long riscv_mem_stack_top(unsigned long membase,
+ unsigned long endmem)
+{
+ return endmem - SZ_2M;
+}
+
+static inline unsigned long riscv_mem_stack(unsigned long membase,
+ unsigned long endmem)
+{
+ return riscv_mem_stack_top(membase, endmem) - STACK_SIZE;
+}
+
+static inline unsigned long riscv_mem_early_malloc(unsigned long membase,
+ unsigned long endmem)
+{
+ return riscv_mem_stack(membase, endmem) - SZ_128K;
+}
+
+static inline unsigned long riscv_mem_early_malloc_end(unsigned long membase,
+ unsigned long endmem)
+{
+ return riscv_mem_stack(membase, endmem);
+}
+
+static inline unsigned long riscv_mem_ramoops(unsigned long membase,
+ unsigned long endmem)
+{
+ endmem = riscv_mem_stack(membase, endmem);
+#ifdef CONFIG_FS_PSTORE_RAMOOPS
+ endmem -= CONFIG_FS_PSTORE_RAMOOPS_SIZE;
+ endmem = ALIGN_DOWN(endmem, SZ_4K);
+#endif
+
+ return endmem;
+}
+
+static inline unsigned long riscv_mem_barebox_image(unsigned long membase,
+ unsigned long endmem,
+ unsigned long size)
+{
+ endmem = riscv_mem_ramoops(membase, endmem);
+
+ return ALIGN_DOWN(endmem - size, SZ_1M);
+}
+
+#define ENTRY_FUNCTION(name, arg0, arg1, arg2) \
+ void name (ulong r0, ulong r1, ulong r2); \
+ static void __##name(ulong, ulong, ulong); \
+ void __naked __noreturn __section(.text_head_entry_##name) name \
+ (ulong a0, ulong a1, ulong a2) \
+ { \
+ __barebox_riscv_head(); \
+ __##name(a0, a1, a2); \
+ } \
+ static void __naked __noreturn noinline __##name \
+ (ulong arg0, ulong arg1, ulong arg2)
+
+
+/*
+ * When using compressed images in conjunction with relocatable images
+ * the PBL code must pick a suitable place where to uncompress the barebox
+ * image. For doing this the PBL code must know the size of the final
+ * image including the BSS segment. The BSS size is unknown to the PBL
+ * code, so define a maximum BSS size here.
+ */
+#define MAX_BSS_SIZE SZ_1M
+
+#define barebox_image_size (__image_end - __image_start)
+
+#endif /* _BAREBOX_RISCV_H_ */
diff --git a/arch/riscv/include/asm/common.h b/arch/riscv/include/asm/common.h
index bc8a17e30b..a0982c548f 100644
--- a/arch/riscv/include/asm/common.h
+++ b/arch/riscv/include/asm/common.h
@@ -1,6 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* SPDX-FileCopyrightText: Copyright (c) 2021 Ahmad Fatoum, Pengutronix */
#ifndef ASM_RISCV_COMMON_H
#define ASM_RISCV_COMMON_H
-/* nothing special yet */
+#include <linux/compiler.h>
+
+static __always_inline unsigned long get_pc(void)
+{
+label:
+ return (unsigned long)&&label;
+}
#endif /* ASM_RISCV_COMMON_H */
diff --git a/arch/riscv/include/asm/elf.h b/arch/riscv/include/asm/elf.h
index 7134fa0582..adb8ec8f6e 100644
--- a/arch/riscv/include/asm/elf.h
+++ b/arch/riscv/include/asm/elf.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ASM_RISCV_ELF_H__
#define __ASM_RISCV_ELF_H__
@@ -8,4 +9,10 @@
#define ELF_CLASS ELFCLASS32
#endif
+/* Relocation types used by the dynamic linker */
+#define R_RISCV_NONE 0
+#define R_RISCV_32 1
+#define R_RISCV_64 2
+#define R_RISCV_RELATIVE 3
+
#endif /* __ASM_RISCV_ELF_H__ */
diff --git a/arch/riscv/include/asm/linkage.h b/arch/riscv/include/asm/linkage.h
index 9e88ba23cd..c6801294f3 100644
--- a/arch/riscv/include/asm/linkage.h
+++ b/arch/riscv/include/asm/linkage.h
@@ -9,4 +9,8 @@
#define __ALIGN .balign 4
#define __ALIGN_STR ".balign 4"
+#define ENDPROC(name) \
+ .type name, %function; \
+ END(name)
+
#endif /* _ASM_RISCV_LINKAGE_H */
diff --git a/arch/riscv/include/asm/sections.h b/arch/riscv/include/asm/sections.h
index 2b8c516038..b5fbba8f16 100644
--- a/arch/riscv/include/asm/sections.h
+++ b/arch/riscv/include/asm/sections.h
@@ -1 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __ASM_SECTIONS_H
+#define __ASM_SECTIONS_H
+
+#ifndef __ASSEMBLY__
#include <asm-generic/sections.h>
+#include <linux/types.h>
+
+extern char __rel_dyn_start[];
+extern char __rel_dyn_end[];
+extern char __dynsym_start[];
+extern char __dynsym_end[];
+
+#endif
+
+#endif /* __ASM_SECTIONS_H */
diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile
index b983ca8cdc..5f57d9fcd2 100644
--- a/arch/riscv/lib/Makefile
+++ b/arch/riscv/lib/Makefile
@@ -1,5 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+
extra-y += barebox.lds
obj-y += riscv_timer.o
+obj-pbl-y += sections.o setupc.o reloc.o sections.o runtime-offset.o
obj-$(CONFIG_HAS_ARCH_SJLJ) += setjmp.o longjmp.o
obj-$(CONFIG_RISCV_OPTIMZED_STRING_FUNCTIONS) += memcpy.o memset.o memmove.o
diff --git a/arch/riscv/lib/barebox.lds.S b/arch/riscv/lib/barebox.lds.S
index 342769890b..c8a331c577 100644
--- a/arch/riscv/lib/barebox.lds.S
+++ b/arch/riscv/lib/barebox.lds.S
@@ -43,10 +43,18 @@ SECTIONS
.barebox_imd : { BAREBOX_IMD }
- . = ALIGN(8);
- .got : { *(.got*) }
+ /DISCARD/ : { *(.rela.plt*) }
+ .rela.dyn : {
+ __rel_dyn_start = .;
+ *(.rel*)
+ __rel_dyn_end = .;
+ }
- .rela.dyn : { *(.rela*) }
+ .dynsym : {
+ __dynsym_start = .;
+ *(.dynsym)
+ __dynsym_end = .;
+ }
_edata = .;
. = ALIGN(8);
diff --git a/arch/riscv/lib/pbl.lds.S b/arch/riscv/lib/pbl.lds.S
new file mode 100644
index 0000000000..881faac340
--- /dev/null
+++ b/arch/riscv/lib/pbl.lds.S
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* SPDX-FileCopyrightText: 2012 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix */
+
+#include <linux/sizes.h>
+#include <asm-generic/barebox.lds.h>
+#include <asm-generic/memory_layout.h>
+
+OUTPUT_ARCH(riscv)
+SECTIONS
+{
+ . = 0x0;
+
+ .image_start : { *(.__image_start) }
+
+ . = ALIGN(4);
+ ._text : { *(._text) }
+ .text :
+ {
+ _stext = .;
+ *(.text_head_entry*)
+ __bare_init_start = .;
+ *(.text_bare_init*)
+ __bare_init_end = .;
+ *(.text*)
+ }
+
+ BAREBOX_BARE_INIT_SIZE
+ BAREBOX_PBL_SIZE
+
+ . = ALIGN(4);
+ .rodata : { *(.rodata*) }
+
+ .barebox_imd : { BAREBOX_IMD }
+
+ _etext = .; /* End of text and rodata section */
+
+ .data : { *(.data*) }
+
+ __shasum_start = .;
+ .shasum : {
+ KEEP(*(.shasum))
+ }
+ __shasum_end = .;
+
+ /DISCARD/ : { *(.rela.plt*) }
+ .rela.dyn : {
+ __rel_dyn_start = .;
+ *(.rela*)
+ __rel_dyn_end = .;
+ }
+
+ .dynsym : {
+ __dynsym_start = .;
+ *(.dynsym)
+ __dynsym_end = .;
+ }
+
+ pbl_code_size = .;
+
+ .__bss_start : { *(.__bss_start) }
+ .bss : { *(.bss*) }
+ .__bss_stop : { *(.__bss_stop) }
+ _end = .;
+
+ pbl_memory_size = .;
+
+ . = ALIGN(4);
+ __piggydata_start = .;
+ .piggydata : {
+ *(.piggydata)
+ }
+ __piggydata_end = .;
+
+ .image_end : { KEEP(*(.__image_end)) }
+
+ pbl_image_size = .;
+
+ _barebox_image_size = __image_end;
+ _barebox_pbl_size = __bss_start;
+}
diff --git a/arch/riscv/lib/reloc.c b/arch/riscv/lib/reloc.c
new file mode 100644
index 0000000000..2fc8818cd6
--- /dev/null
+++ b/arch/riscv/lib/reloc.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-FileCopyrightText: Copyright (c) 2021 Ahmad Fatoum, Pengutronix
+
+#include <common.h>
+#include <linux/linkage.h>
+#include <asm/sections.h>
+#include <asm/barebox-riscv.h>
+#include <debug_ll.h>
+#include <asm-generic/module.h>
+
+#include <elf.h>
+
+#if __riscv_xlen == 64
+#define Elf_Rela Elf64_Rela
+#define R_RISCV_ABSOLUTE R_RISCV_64
+#define DYNSYM_ENTRY(dynsym, rela) dynsym[ELF_R_SYM(rela->r_info) * 3 + 1]
+#elif __riscv_xlen == 32
+#define Elf_Rela Elf32_Rela
+#define R_RISCV_ABSOLUTE R_RISCV_32
+#define DYNSYM_ENTRY(dynsym, rela) dynsym[ELF_R_SYM(rela->r_info) * 4 + 1]
+#else
+#error unknown riscv target
+#endif
+
+#define RISC_R_TYPE(x) ((x) & 0xFF)
+
+void relocate_to_current_adr(void)
+{
+ unsigned long offset;
+ unsigned long *dynsym;
+ void *dstart, *dend;
+ Elf_Rela *rela;
+
+ /* Get offset between linked address and runtime address */
+ offset = get_runtime_offset();
+ if (!offset)
+ return;
+
+ dstart = __rel_dyn_start + offset;
+ dend = __rel_dyn_end + offset;
+ dynsym = (void *)__dynsym_start + offset;
+
+ for (rela = dstart; (void *)rela < dend; rela++) {
+ unsigned long *fixup;
+
+ fixup = (unsigned long *)(rela->r_offset + offset);
+
+ switch (RISC_R_TYPE(rela->r_info)) {
+ case R_RISCV_RELATIVE:
+ *fixup = rela->r_addend + offset;
+ break;
+ case R_RISCV_ABSOLUTE:
+ *fixup = DYNSYM_ENTRY(dynsym, rela) + rela->r_addend + offset;
+ break;
+ default:
+ putc_ll('>');
+ puthex_ll(rela->r_info);
+ putc_ll(' ');
+ puthex_ll(rela->r_offset);
+ putc_ll(' ');
+ puthex_ll(rela->r_addend);
+ putc_ll('\n');
+ panic("");
+ }
+ }
+}
diff --git a/arch/riscv/lib/runtime-offset.S b/arch/riscv/lib/runtime-offset.S
new file mode 100644
index 0000000000..f6a0406289
--- /dev/null
+++ b/arch/riscv/lib/runtime-offset.S
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* SPDX-FileCopyrightText: Copyright (c) 2021 Ahmad Fatoum, Pengutronix */
+#include <linux/linkage.h>
+#include <asm/sections.h>
+
+.section ".text_bare_init","ax"
+ENTRY(get_runtime_offset)
+ lla a0, _text /* load addr */
+ la a1, _text /* link addr */
+ sub a0, a0, a1
+ ret
+ENDPROC(get_runtime_offset)
diff --git a/arch/riscv/lib/sections.c b/arch/riscv/lib/sections.c
new file mode 100644
index 0000000000..e23a41dcf5
--- /dev/null
+++ b/arch/riscv/lib/sections.c
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <asm/sections.h>
+#include <linux/types.h>
+
+char _text[0] __attribute__((section("._text")));
+char __bss_start[0] __attribute__((section(".__bss_start")));
+char __bss_stop[0] __attribute__((section(".__bss_stop")));
+char __image_start[0] __attribute__((section(".__image_start")));
+char __image_end[0] __attribute__((section(".__image_end")));
diff --git a/arch/riscv/lib/setupc.S b/arch/riscv/lib/setupc.S
new file mode 100644
index 0000000000..5fdd81c2c3
--- /dev/null
+++ b/arch/riscv/lib/setupc.S
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* SPDX-FileCopyrightText: Copyright (c) 2021 Ahmad Fatoum, Pengutronix */
+
+#include <linux/linkage.h>
+#include <asm/sections.h>
+#include <asm/asm.h>
+
+/*
+ * setup_c: clear bss
+ */
+.section .text.setup_c
+ENTRY(setup_c)
+ lla a0, __bss_start
+ li a1, 0
+ lla a2, __bss_stop
+ sub a2, a2, a0
+ j __memset
+ENDPROC(setup_c)
+
+/*
+ * void relocate_to_adr(unsigned long targetadr)
+ *
+ * Copy binary to targetadr, relocate code and continue
+ * executing at new address.
+ */
+.section .text.relocate_to_adr
+ENTRY(relocate_to_adr)
+ /* a0: target address */
+ addi sp, sp, -SZREG * 2
+ lla a1, _text /* a1: source address */
+
+ /* adjust return address */
+ sub ra, ra, a1 /* sub address where we are actually running */
+ add ra, ra, a0 /* add address where we are going to run */
+ REG_S ra, (SZREG * 2)(sp)
+
+ beq a0, a1, copied /* skip if already at new address */
+
+ lla a2, copied
+ sub a2, a2, a1
+ add a2, a2, a0
+ REG_S a2, (SZREG * 1)(sp)
+
+ lla a2, __bss_start
+ sub a2, a2, a1 /* a2: size */
+
+ jal __memcpy
+
+ REG_L a0, (SZREG * 1)(sp)
+ jr a0 /* jump to relocated address */
+copied:
+ REG_L ra, (SZREG * 2)(sp)
+ addi sp, sp, SZREG * 2
+ j relocate_to_current_adr /* relocate binary */
+ENDPROC(relocate_to_adr)