* [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations
@ 2026-01-05 11:26 Sascha Hauer
2026-01-05 11:26 ` [PATCH 01/19] elf: Use memcmp to make suitable for PBL Sascha Hauer
` (19 more replies)
0 siblings, 20 replies; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 11:26 UTC (permalink / raw)
To: BAREBOX; +Cc: Claude Sonnet 4.5
Until now we linked the raw barebox proper binary into the PBL which
comes with a number of disadvantages. We rely on self-modifying code
to in barebox proper (relocate_to_current_adr()) and have no initialized
bss segment (setup_c()). Also we can only mark the .text and .rodata as
readonly during runtime of barebox proper.
This series overcomes this by linking a ELF image into the PBL. This
image is properly layed out, linked and initialized in the PBL. With
this barebox proper has a proper C environment and text/rodata
protection from the start.
As a bonus this series also adds initial MMU support for RISCV, also
based on loading the ELF image and configuring the MMU from the PBL.
This series also marks my start into AI assisted programming as you can
see in the Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
and 🤖 Generated with [Claude Code](https://claude.com/claude-code)
tags. Without it I wouldn't have started this series during my xmas
break, but with it it was actually quite fun to do; it felt like having
a programming team which I just had to delegate new tasks to while
having fun with my family myself ;)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
Sascha Hauer (19):
elf: Use memcmp to make suitable for PBL
elf: build for PBL as well
elf: add dynamic relocation support
ARM: implement elf_apply_relocations() for ELF relocation support
riscv: implement elf_apply_relocations() for ELF relocation support
elf: implement elf_load_inplace()
elf: create elf_open_binary_into()
Makefile: add barebox.elf build target
PBL: allow to link ELF image into PBL
mmu: add MAP_CACHED_RO mapping type
mmu: introduce pbl_remap_range()
ARM: use relative jumps in exception table
ARM: exceptions: make in-binary exception table const
ARM: linker script: create separate PT_LOAD segments for text, rodata, and data
ARM: link ELF image into PBL
ARM: PBL: setup MMU with proper permissions from ELF segments
riscv: link ELF image into PBL
riscv: linker script: create separate PT_LOAD segments for text, rodata, and data
riscv: add ELF segment-based memory protection with MMU
Makefile | 16 +-
arch/arm/Kconfig | 2 +
arch/arm/cpu/exceptions_32.S | 54 +++---
arch/arm/cpu/interrupts_32.c | 41 +++-
arch/arm/cpu/mmu-common.c | 66 +------
arch/arm/cpu/mmu-common.h | 3 +-
arch/arm/cpu/mmu_32.c | 19 +-
arch/arm/cpu/mmu_64.c | 10 +-
arch/arm/cpu/no-mmu.c | 11 +-
arch/arm/cpu/start.c | 4 -
arch/arm/cpu/uncompress.c | 46 ++++-
arch/arm/include/asm/barebox-arm.h | 4 +-
arch/arm/include/asm/barebox.lds.h | 14 +-
arch/arm/include/asm/elf.h | 11 ++
arch/arm/include/asm/sections.h | 1 +
arch/arm/lib/pbl.lds.S | 8 +-
arch/arm/lib32/Makefile | 1 +
arch/arm/lib32/barebox.lds.S | 39 ++--
arch/arm/lib32/elf_reloc.c | 105 ++++++++++
arch/arm/lib64/Makefile | 1 +
arch/arm/lib64/barebox.lds.S | 31 ++-
arch/arm/lib64/elf_reloc.c | 105 ++++++++++
arch/riscv/Kconfig | 18 ++
arch/riscv/Kconfig.socs | 1 -
arch/riscv/boot/start.c | 4 -
arch/riscv/boot/uncompress.c | 41 +++-
arch/riscv/cpu/Makefile | 1 +
arch/riscv/cpu/mmu.c | 386 +++++++++++++++++++++++++++++++++++++
arch/riscv/cpu/mmu.h | 144 ++++++++++++++
arch/riscv/include/asm/asm.h | 3 +-
arch/riscv/include/asm/mmu.h | 44 +++++
arch/riscv/lib/Makefile | 1 +
arch/riscv/lib/barebox.lds.S | 38 ++--
arch/riscv/lib/elf_reloc.c | 212 ++++++++++++++++++++
common/Makefile | 2 +-
common/elf.c | 367 +++++++++++++++++++++++++++++++++--
images/Makefile | 18 +-
images/piggy.S | 4 +
include/elf.h | 62 ++++++
include/mmu.h | 12 +-
include/pbl/mmu.h | 29 +++
lib/Makefile | 1 +
lib/elf_reloc.c | 15 ++
pbl/Kconfig | 8 +
pbl/Makefile | 1 +
pbl/mmu.c | 111 +++++++++++
scripts/prelink-riscv.inc | 9 +-
47 files changed, 1922 insertions(+), 202 deletions(-)
---
base-commit: e2d7e032281158b54541392b4d8108de204137a1
change-id: 20251227-pbl-load-elf-cb4cb0ceb7d8
Best regards,
--
Sascha Hauer <s.hauer@pengutronix.de>
^ permalink raw reply [flat|nested] 49+ messages in thread
* [PATCH 01/19] elf: Use memcmp to make suitable for PBL
2026-01-05 11:26 [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Sascha Hauer
@ 2026-01-05 11:26 ` Sascha Hauer
2026-01-05 11:46 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 02/19] elf: build for PBL as well Sascha Hauer
` (18 subsequent siblings)
19 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 11:26 UTC (permalink / raw)
To: BAREBOX; +Cc: Claude Sonnet 4.5
In preparation of adding ELF support to the PBL use memcmp rather than
strncmp for checking against the ELF magic. The other possibility would
be to add strncmp to the PBL, but for the sake of binary size use a
function that is already there.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
common/elf.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/common/elf.c b/common/elf.c
index c68ea0be3fa668d988b27530644bbb77eb62ff48..692323c6beab2dd8aae9a9e874fb4980152a74e0 100644
--- a/common/elf.c
+++ b/common/elf.c
@@ -201,7 +201,7 @@ static int load_elf_image_segments(struct elf_image *elf)
static int elf_check_image(struct elf_image *elf, void *buf)
{
- if (strncmp(buf, ELFMAG, SELFMAG)) {
+ if (memcmp(buf, ELFMAG, SELFMAG)) {
pr_err("ELF magic not found.\n");
return -EINVAL;
}
--
2.47.3
^ permalink raw reply [flat|nested] 49+ messages in thread
* [PATCH 02/19] elf: build for PBL as well
2026-01-05 11:26 [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Sascha Hauer
2026-01-05 11:26 ` [PATCH 01/19] elf: Use memcmp to make suitable for PBL Sascha Hauer
@ 2026-01-05 11:26 ` Sascha Hauer
2026-01-05 11:26 ` [PATCH 03/19] elf: add dynamic relocation support Sascha Hauer
` (17 subsequent siblings)
19 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 11:26 UTC (permalink / raw)
To: BAREBOX; +Cc: Claude Sonnet 4.5
We'll link barebox proper as an ELF image into the PBL later, so compile
ELF support for PBL as well.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
common/Makefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/common/Makefile b/common/Makefile
index d501a6a2755a113fac3ac632806d4a92b741d6e2..7afdb04be3b494cc8edcebb145c642c23f54503f 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -14,7 +14,7 @@ obj-y += misc.o
obj-pbl-y += memsize.o
obj-y += resource.o
obj-pbl-y += bootsource.o
-obj-$(CONFIG_ELF) += elf.o
+obj-pbl-$(CONFIG_ELF) += elf.o
obj-$(CONFIG_PE) += pe.o
obj-y += restart.o
obj-y += poweroff.o
--
2.47.3
^ permalink raw reply [flat|nested] 49+ messages in thread
* [PATCH 03/19] elf: add dynamic relocation support
2026-01-05 11:26 [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Sascha Hauer
2026-01-05 11:26 ` [PATCH 01/19] elf: Use memcmp to make suitable for PBL Sascha Hauer
2026-01-05 11:26 ` [PATCH 02/19] elf: build for PBL as well Sascha Hauer
@ 2026-01-05 11:26 ` Sascha Hauer
2026-01-05 14:05 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 04/19] ARM: implement elf_apply_relocations() for ELF " Sascha Hauer
` (16 subsequent siblings)
19 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 11:26 UTC (permalink / raw)
To: BAREBOX; +Cc: Claude Sonnet 4.5
Add support for applying dynamic relocations to ELF binaries. This allows
loading ET_DYN (position-independent) binaries and ET_EXEC binaries at
custom load addresses.
Key changes:
- Add elf_image.reloc_offset to track offset between vaddr and load address
- Implement elf_compute_load_offset() to calculate relocation offset
- Add elf_set_load_address() API to specify custom load address
- Implement elf_find_dynamic_segment() to locate PT_DYNAMIC
- Add elf_relocate() to apply relocations
- Provide weak default elf_apply_relocations() stub for unsupported architectures
- Add ELF dynamic section accessors
The relocation offset type is unsigned long to properly handle pointer
arithmetic and avoid casting issues.
Architecture-specific implementations should override the weak
elf_apply_relocations() function to handle their relocation types.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
---
common/elf.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
include/elf.h | 53 ++++++++++++++++
lib/Makefile | 1 +
lib/elf_reloc.c | 15 +++++
4 files changed, 251 insertions(+), 5 deletions(-)
diff --git a/common/elf.c b/common/elf.c
index 692323c6beab2dd8aae9a9e874fb4980152a74e0..fc2949c285ebb0c0740c68c551926da8d0bb8637 100644
--- a/common/elf.c
+++ b/common/elf.c
@@ -65,9 +65,63 @@ static void elf_release_regions(struct elf_image *elf)
}
}
+static int elf_compute_load_offset(struct elf_image *elf)
+{
+ void *buf = elf->hdr_buf;
+ void *phdr = buf + elf_hdr_e_phoff(elf, buf);
+ u64 min_vaddr = (u64)-1;
+ u64 min_paddr = (u64)-1;
+ int i;
+
+ /* Find lowest p_vaddr and p_paddr in PT_LOAD segments */
+ for (i = 0; i < elf_hdr_e_phnum(elf, buf); i++) {
+ if (elf_phdr_p_type(elf, phdr) == PT_LOAD) {
+ u64 vaddr = elf_phdr_p_vaddr(elf, phdr);
+ u64 paddr = elf_phdr_p_paddr(elf, phdr);
+
+ if (vaddr < min_vaddr)
+ min_vaddr = vaddr;
+ if (paddr < min_paddr)
+ min_paddr = paddr;
+ }
+ phdr += elf_size_of_phdr(elf);
+ }
+
+ /*
+ * Determine base load address:
+ * 1. If user specified load_address, use it
+ * 2. Otherwise for ET_EXEC, use NULL (segments use p_paddr directly)
+ * 3. For ET_DYN, use lowest p_paddr
+ */
+ if (elf->load_address) {
+ elf->base_load_addr = elf->load_address;
+ } else if (elf->type == ET_EXEC) {
+ elf->base_load_addr = NULL;
+ } else {
+ elf->base_load_addr = (void *)(phys_addr_t)min_paddr;
+ }
+
+ /*
+ * Calculate relocation offset:
+ * - For ET_EXEC with no custom load address: no offset needed
+ * - Otherwise: offset = base_load_addr - lowest_vaddr
+ */
+ if (elf->type == ET_EXEC && !elf->load_address) {
+ elf->reloc_offset = 0;
+ } else {
+ elf->reloc_offset = ((unsigned long)elf->base_load_addr - min_vaddr);
+ }
+
+ pr_debug("ELF load: type=%s, base=%p, offset=%08lx\n",
+ elf->type == ET_EXEC ? "ET_EXEC" : "ET_DYN",
+ elf->base_load_addr, elf->reloc_offset);
+
+ return 0;
+}
+
static int request_elf_segment(struct elf_image *elf, void *phdr)
{
- void *dst = (void *) (phys_addr_t) elf_phdr_p_paddr(elf, phdr);
+ void *dst;
int ret;
u64 p_memsz = elf_phdr_p_memsz(elf, phdr);
@@ -78,6 +132,20 @@ static int request_elf_segment(struct elf_image *elf, void *phdr)
if (!p_memsz)
return 0;
+ /*
+ * Calculate destination address:
+ * - If reloc_offset is set (custom load address or ET_DYN):
+ * dst = reloc_offset + p_vaddr
+ * - Otherwise (ET_EXEC, no custom address):
+ * dst = p_paddr (original behavior)
+ */
+ if (elf->reloc_offset) {
+ u64 p_vaddr = elf_phdr_p_vaddr(elf, phdr);
+ dst = (void *)(unsigned long)(elf->reloc_offset + p_vaddr);
+ } else {
+ dst = (void *)(unsigned long)elf_phdr_p_paddr(elf, phdr);
+ }
+
if (dst < elf->low_addr)
elf->low_addr = dst;
if (dst + p_memsz > elf->high_addr)
@@ -129,7 +197,18 @@ static int load_elf_to_memory(struct elf_image *elf)
p_offset = elf_phdr_p_offset(elf, r->phdr);
p_filesz = elf_phdr_p_filesz(elf, r->phdr);
p_memsz = elf_phdr_p_memsz(elf, r->phdr);
- dst = (void *) (phys_addr_t) elf_phdr_p_paddr(elf, r->phdr);
+
+ /*
+ * Calculate destination address (same logic as request_elf_segment):
+ * - If reloc_offset is set: dst = reloc_offset + p_vaddr
+ * - Otherwise: dst = p_paddr
+ */
+ if (elf->reloc_offset) {
+ u64 p_vaddr = elf_phdr_p_vaddr(elf, r->phdr);
+ dst = (void *)(unsigned long)(elf->reloc_offset + p_vaddr);
+ } else {
+ dst = (void *)(unsigned long)elf_phdr_p_paddr(elf, r->phdr);
+ }
pr_debug("Loading phdr offset 0x%llx to 0x%p (%llu bytes)\n",
p_offset, dst, p_filesz);
@@ -173,6 +252,11 @@ static int load_elf_image_segments(struct elf_image *elf)
if (!list_empty(&elf->list))
return -EINVAL;
+ /* Calculate load offset for ET_DYN */
+ ret = elf_compute_load_offset(elf);
+ if (ret)
+ return ret;
+
for (i = 0; i < elf_hdr_e_phnum(elf, buf) ; ++i) {
ret = request_elf_segment(elf, phdr);
if (ret)
@@ -201,6 +285,8 @@ static int load_elf_image_segments(struct elf_image *elf)
static int elf_check_image(struct elf_image *elf, void *buf)
{
+ u16 e_type;
+
if (memcmp(buf, ELFMAG, SELFMAG)) {
pr_err("ELF magic not found.\n");
return -EINVAL;
@@ -208,11 +294,14 @@ static int elf_check_image(struct elf_image *elf, void *buf)
elf->class = ((char *) buf)[EI_CLASS];
- if (elf_hdr_e_type(elf, buf) != ET_EXEC) {
- pr_err("Non EXEC ELF image.\n");
+ e_type = elf_hdr_e_type(elf, buf);
+ if (e_type != ET_EXEC && e_type != ET_DYN) {
+ pr_err("Unsupported ELF type: %u (only ET_EXEC and ET_DYN supported)\n", e_type);
return -ENOEXEC;
}
+ elf->type = e_type;
+
if (!elf_hdr_e_phnum(elf, buf)) {
pr_err("No phdr found.\n");
return -ENOEXEC;
@@ -338,9 +427,97 @@ struct elf_image *elf_open(const char *filename)
return elf_check_init(filename);
}
+void elf_set_load_address(struct elf_image *elf, void *addr)
+{
+ elf->load_address = addr;
+}
+
+static void *elf_find_dynamic_segment(struct elf_image *elf)
+{
+ void *buf = elf->hdr_buf;
+ void *phdr = buf + elf_hdr_e_phoff(elf, buf);
+ int i;
+
+ for (i = 0; i < elf_hdr_e_phnum(elf, buf); i++) {
+ if (elf_phdr_p_type(elf, phdr) == PT_DYNAMIC) {
+ u64 offset = elf_phdr_p_offset(elf, phdr);
+ void *dyn_addr;
+
+ /* If loaded from file, PT_DYNAMIC might not be in hdr_buf */
+ if (elf->filename) {
+ /*
+ * Calculate address in loaded memory using same logic
+ * as request_elf_segment
+ */
+ if (elf->reloc_offset) {
+ u64 p_vaddr = elf_phdr_p_vaddr(elf, phdr);
+ dyn_addr = (void *)(unsigned long)(elf->reloc_offset + p_vaddr);
+ } else {
+ u64 paddr = elf_phdr_p_paddr(elf, phdr);
+ dyn_addr = (void *)(phys_addr_t)paddr;
+ }
+ return dyn_addr;
+ } else {
+ /* Binary in memory, use offset */
+ return elf->hdr_buf + offset;
+ }
+ }
+ phdr += elf_size_of_phdr(elf);
+ }
+
+ return NULL; /* No PT_DYNAMIC segment */
+}
+
+static int elf_relocate(struct elf_image *elf)
+{
+ void *dyn_seg;
+
+ /*
+ * Relocations needed if:
+ * - ET_DYN (position-independent), OR
+ * - ET_EXEC with custom load address
+ */
+ if (elf->type == ET_EXEC && !elf->load_address)
+ return 0;
+
+ /* Find PT_DYNAMIC segment */
+ dyn_seg = elf_find_dynamic_segment(elf);
+ if (!dyn_seg) {
+ /*
+ * No PT_DYNAMIC segment found.
+ * For ET_DYN this is unusual but legal.
+ * For ET_EXEC with custom load address, this means no relocations
+ * can be applied - warn the user.
+ */
+ if (elf->type == ET_EXEC && elf->load_address) {
+ pr_warn("ET_EXEC loaded at custom address but no PT_DYNAMIC segment - "
+ "relocations cannot be applied\n");
+ } else {
+ pr_debug("No PT_DYNAMIC segment found\n");
+ }
+ return 0;
+ }
+
+ /* Call architecture-specific relocation handler */
+ return elf_apply_relocations(elf, dyn_seg);
+}
+
int elf_load(struct elf_image *elf)
{
- return load_elf_image_segments(elf);
+ int ret;
+
+ ret = load_elf_image_segments(elf);
+ if (ret)
+ return ret;
+
+ /* Apply relocations if needed */
+ ret = elf_relocate(elf);
+ if (ret) {
+ pr_err("Relocation failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
}
void elf_close(struct elf_image *elf)
diff --git a/include/elf.h b/include/elf.h
index 994db642b0789942530f6ef7fdffdd2218afd7b6..4b19c917639d0d655d9f7530f0311afa599f1a98 100644
--- a/include/elf.h
+++ b/include/elf.h
@@ -394,11 +394,15 @@ extern Elf64_Dyn _DYNAMIC [];
struct elf_image {
struct list_head list;
u8 class;
+ u16 type; /* ET_EXEC or ET_DYN */
u64 entry;
void *low_addr;
void *high_addr;
void *hdr_buf;
const char *filename;
+ void *load_address; /* User-specified load address (NULL = use p_paddr) */
+ void *base_load_addr; /* Calculated base address for ET_DYN */
+ unsigned long reloc_offset; /* Offset between p_vaddr and actual load address */
};
static inline size_t elf_get_mem_size(struct elf_image *elf)
@@ -411,6 +415,20 @@ struct elf_image *elf_open(const char *filename);
void elf_close(struct elf_image *elf);
int elf_load(struct elf_image *elf);
+/*
+ * Set the load address for the ELF file.
+ * Must be called before elf_load().
+ * If not set, ET_EXEC uses p_paddr, ET_DYN uses lowest p_paddr.
+ */
+void elf_set_load_address(struct elf_image *elf, void *addr);
+
+/*
+ * Architecture-specific relocation handler.
+ * Returns 0 on success, -ENOSYS if architecture doesn't support relocations,
+ * other negative error codes on failure.
+ */
+int elf_apply_relocations(struct elf_image *elf, void *dyn_seg);
+
#define ELF_GET_FIELD(__s, __field, __type) \
static inline __type elf_##__s##_##__field(struct elf_image *elf, void *arg) { \
if (elf->class == ELFCLASS32) \
@@ -426,10 +444,12 @@ ELF_GET_FIELD(hdr, e_phentsize, u16)
ELF_GET_FIELD(hdr, e_type, u16)
ELF_GET_FIELD(hdr, e_machine, u16)
ELF_GET_FIELD(phdr, p_paddr, u64)
+ELF_GET_FIELD(phdr, p_vaddr, u64)
ELF_GET_FIELD(phdr, p_filesz, u64)
ELF_GET_FIELD(phdr, p_memsz, u64)
ELF_GET_FIELD(phdr, p_type, u32)
ELF_GET_FIELD(phdr, p_offset, u64)
+ELF_GET_FIELD(phdr, p_flags, u32)
static inline unsigned long elf_size_of_phdr(struct elf_image *elf)
{
@@ -439,4 +459,37 @@ static inline unsigned long elf_size_of_phdr(struct elf_image *elf)
return sizeof(Elf64_Phdr);
}
+/* Dynamic section accessors */
+static inline s64 elf_dyn_d_tag(struct elf_image *elf, void *arg)
+{
+ if (elf->class == ELFCLASS32)
+ return (s64)((Elf32_Dyn *)arg)->d_tag;
+ else
+ return (s64)((Elf64_Dyn *)arg)->d_tag;
+}
+
+static inline u64 elf_dyn_d_val(struct elf_image *elf, void *arg)
+{
+ if (elf->class == ELFCLASS32)
+ return (u64)((Elf32_Dyn *)arg)->d_un.d_val;
+ else
+ return (u64)((Elf64_Dyn *)arg)->d_un.d_val;
+}
+
+static inline u64 elf_dyn_d_ptr(struct elf_image *elf, void *arg)
+{
+ if (elf->class == ELFCLASS32)
+ return (u64)((Elf32_Dyn *)arg)->d_un.d_ptr;
+ else
+ return (u64)((Elf64_Dyn *)arg)->d_un.d_ptr;
+}
+
+static inline unsigned long elf_size_of_dyn(struct elf_image *elf)
+{
+ if (elf->class == ELFCLASS32)
+ return sizeof(Elf32_Dyn);
+ else
+ return sizeof(Elf64_Dyn);
+}
+
#endif /* _LINUX_ELF_H */
diff --git a/lib/Makefile b/lib/Makefile
index 9ab4cad0359c0e8e5ad0c0785d8f45afc9c1d00e..11716ec420cb46799b56512429afdbf1c2bba7cc 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -24,6 +24,7 @@ obj-y += readkey.o
obj-y += kfifo.o
obj-y += libbb.o
obj-y += libgen.o
+obj-y += elf_reloc.o
obj-$(CONFIG_FIP) += fip.o tbbr_config.o
obj-$(CONFIG_JSMN) += jsmn.o
obj-y += stringlist.o
diff --git a/lib/elf_reloc.c b/lib/elf_reloc.c
new file mode 100644
index 0000000000000000000000000000000000000000..d9e09800a6a836e7a97f5d55a8120b2632c0c25e
--- /dev/null
+++ b/lib/elf_reloc.c
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <common.h>
+#include <elf.h>
+#include <errno.h>
+
+/*
+ * Weak default implementation for architectures that don't support
+ * ELF relocations yet. Can be overridden by arch-specific implementation.
+ */
+int __weak elf_apply_relocations(struct elf_image *elf, void *dyn_seg)
+{
+ pr_warn("ELF relocations not supported for this architecture\n");
+ return -ENOSYS;
+}
--
2.47.3
^ permalink raw reply [flat|nested] 49+ messages in thread
* [PATCH 04/19] ARM: implement elf_apply_relocations() for ELF relocation support
2026-01-05 11:26 [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Sascha Hauer
` (2 preceding siblings ...)
2026-01-05 11:26 ` [PATCH 03/19] elf: add dynamic relocation support Sascha Hauer
@ 2026-01-05 11:26 ` Sascha Hauer
2026-01-05 11:58 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 05/19] riscv: " Sascha Hauer
` (15 subsequent siblings)
19 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 11:26 UTC (permalink / raw)
To: BAREBOX; +Cc: Claude Sonnet 4.5
Implement architecture-specific ELF relocation handlers for ARM32 and ARM64.
ARM32 implementation (arch/arm/lib32/elf_reloc.c):
- Handles REL-format relocations (no explicit addend)
- Supports R_ARM_RELATIVE and R_ARM_ABS32 relocation types
- Addend is read from the target location
ARM64 implementation (arch/arm/lib64/elf_reloc.c):
- Handles RELA-format relocations (with explicit addend)
- Supports R_AARCH64_RELATIVE and R_AARCH64_ABS64 relocation types
- Addend is provided in relocation entry
Both implementations:
- Parse PT_DYNAMIC segment to locate relocation tables
- Validate relocation table format and entry sizes
- Apply relocations based on the computed load offset
- Return appropriate errors for unsupported relocation types
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
---
arch/arm/include/asm/elf.h | 11 +++++
arch/arm/lib32/Makefile | 1 +
arch/arm/lib32/elf_reloc.c | 105 +++++++++++++++++++++++++++++++++++++++++++++
arch/arm/lib64/Makefile | 1 +
arch/arm/lib64/elf_reloc.c | 105 +++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 223 insertions(+)
diff --git a/arch/arm/include/asm/elf.h b/arch/arm/include/asm/elf.h
index 4043e6fd5b991eb5cccb3fa0ea28d208006ee1fc..cceb92ee1a5f63c37b0e981c263676bd35a261c0 100644
--- a/arch/arm/include/asm/elf.h
+++ b/arch/arm/include/asm/elf.h
@@ -36,6 +36,17 @@ typedef struct user_fp elf_fpregset_t;
#define R_ARM_THM_CALL 10
#define R_ARM_THM_JUMP24 30
+/* Additional relocation types for dynamic linking */
+#define R_ARM_RELATIVE 23
+#define R_ARM_GLOB_DAT 21
+#define R_ARM_JUMP_SLOT 22
+
+#define R_AARCH64_NONE 0
+#define R_AARCH64_ABS64 257
+#define R_AARCH64_RELATIVE 1027
+#define R_AARCH64_GLOB_DAT 1025
+#define R_AARCH64_JUMP_SLOT 1026
+
/*
* These are used to set parameters in the core dumps.
*/
diff --git a/arch/arm/lib32/Makefile b/arch/arm/lib32/Makefile
index f76010e93350375a11e673d9b68fb1d216983404..579d8bc0f0d0f7f0edf5761530be614d36495e69 100644
--- a/arch/arm/lib32/Makefile
+++ b/arch/arm/lib32/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_BOOTM_OPTEE) += start-kernel-optee.o
obj-$(CONFIG_CMD_BOOTU) += bootu.o
obj-$(CONFIG_BOOT_ATAGS) += atags.o
obj-y += div0.o
+obj-pbl-$(CONFIG_ELF) += elf_reloc.o
obj-y += findbit.o
obj-y += io.o
obj-y += io-readsb.o
diff --git a/arch/arm/lib32/elf_reloc.c b/arch/arm/lib32/elf_reloc.c
new file mode 100644
index 0000000000000000000000000000000000000000..2b44270d965412ef348be7919022a607fa3fa020
--- /dev/null
+++ b/arch/arm/lib32/elf_reloc.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <common.h>
+#include <elf.h>
+#include <errno.h>
+#include <asm/elf.h>
+
+/*
+ * Parse dynamic section and extract relocation info for ARM32
+ */
+static int parse_dynamic_section(struct elf_image *elf, Elf32_Dyn *dyn,
+ Elf32_Rel **rel_out, u64 *relsz_out)
+{
+ Elf32_Rel *rel = NULL;
+ u64 relsz = 0, relent = 0;
+ int i;
+ phys_addr_t base = (phys_addr_t)elf->reloc_offset;
+
+ /* Iterate through dynamic entries until DT_NULL */
+ for (i = 0; dyn[i].d_tag != DT_NULL; i++) {
+ switch (dyn[i].d_tag) {
+ case DT_REL:
+ /* REL table address - needs to be adjusted by load offset */
+ rel = (Elf32_Rel *)(base + dyn[i].d_un.d_ptr);
+ break;
+ case DT_RELSZ:
+ relsz = dyn[i].d_un.d_val;
+ break;
+ case DT_RELENT:
+ relent = dyn[i].d_un.d_val;
+ break;
+ case DT_RELA:
+ pr_err("ARM32 uses REL, not RELA relocations\n");
+ return -EINVAL;
+ default:
+ break;
+ }
+ }
+
+ if (!rel || !relsz || relent != sizeof(Elf32_Rel)) {
+ pr_debug("No relocations or invalid relocation info\n");
+ return -EINVAL;
+ }
+
+ *rel_out = rel;
+ *relsz_out = relsz;
+ return 0;
+}
+
+/*
+ * Apply ARM32 ELF relocations
+ */
+int elf_apply_relocations(struct elf_image *elf, void *dyn_seg)
+{
+ Elf32_Dyn *dyn = dyn_seg;
+ Elf32_Rel *rel;
+ u64 relsz;
+ phys_addr_t base = (phys_addr_t)elf->reloc_offset;
+ int ret;
+
+ if (elf->class != ELFCLASS32) {
+ pr_err("Wrong ELF class for ARM32 relocation\n");
+ return -EINVAL;
+ }
+
+ ret = parse_dynamic_section(elf, dyn, &rel, &relsz);
+ if (ret)
+ return ret;
+
+ /* Apply each relocation */
+ while (relsz > 0) {
+ u32 *fixup_addr;
+ u32 reloc_type = ELF32_R_TYPE(rel->r_info);
+
+ /* Calculate address to fix up */
+ fixup_addr = (u32 *)(base + rel->r_offset);
+
+ switch (reloc_type) {
+ case R_ARM_NONE:
+ /* No operation */
+ break;
+
+ case R_ARM_RELATIVE:
+ /* B(P) = S + A */
+ /* For REL format: A = *fixup_addr, S = base */
+ *fixup_addr = *fixup_addr + base;
+ break;
+
+ case R_ARM_ABS32:
+ /* B(P) = (S + A) | T */
+ *fixup_addr = *fixup_addr + base;
+ break;
+
+ default:
+ pr_err("Unsupported ARM32 relocation type: %u at offset 0x%x\n",
+ reloc_type, rel->r_offset);
+ return -EINVAL;
+ }
+
+ rel++;
+ relsz -= sizeof(Elf32_Rel);
+ }
+
+ return 0;
+}
diff --git a/arch/arm/lib64/Makefile b/arch/arm/lib64/Makefile
index e86a2e5a2f3d6fa220179835a33ff1e1af358c9a..2890a41c37c676ab3e6f78ef6596447a06909651 100644
--- a/arch/arm/lib64/Makefile
+++ b/arch/arm/lib64/Makefile
@@ -3,6 +3,7 @@
obj-y += stacktrace.o
obj-$(CONFIG_ARM_LINUX) += armlinux.o
obj-y += div0.o
+obj-pbl-$(CONFIG_ELF) += elf_reloc.o
obj-$(CONFIG_ARM_OPTIMZED_STRING_FUNCTIONS) += memcpy.o
obj-$(CONFIG_ARM_OPTIMZED_STRING_FUNCTIONS) += memset.o string.o
extra-y += barebox.lds
diff --git a/arch/arm/lib64/elf_reloc.c b/arch/arm/lib64/elf_reloc.c
new file mode 100644
index 0000000000000000000000000000000000000000..22adb4cdafb37f7bd2939e84bc0c6e8133d2d998
--- /dev/null
+++ b/arch/arm/lib64/elf_reloc.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <common.h>
+#include <elf.h>
+#include <errno.h>
+#include <asm/elf.h>
+
+/*
+ * Parse dynamic section and extract relocation info for ARM64
+ */
+static int parse_dynamic_section(struct elf_image *elf, Elf64_Dyn *dyn,
+ Elf64_Rela **rela_out, u64 *relasz_out)
+{
+ Elf64_Rela *rela = NULL;
+ u64 relasz = 0, relaent = 0;
+ int i;
+ phys_addr_t base = (phys_addr_t)elf->reloc_offset;
+
+ /* Iterate through dynamic entries until DT_NULL */
+ for (i = 0; dyn[i].d_tag != DT_NULL; i++) {
+ switch (dyn[i].d_tag) {
+ case DT_RELA:
+ /* RELA table address - needs to be adjusted by load offset */
+ rela = (Elf64_Rela *)(base + dyn[i].d_un.d_ptr);
+ break;
+ case DT_RELASZ:
+ relasz = dyn[i].d_un.d_val;
+ break;
+ case DT_RELAENT:
+ relaent = dyn[i].d_un.d_val;
+ break;
+ case DT_REL:
+ pr_err("ARM64 uses RELA, not REL relocations\n");
+ return -EINVAL;
+ default:
+ break;
+ }
+ }
+
+ if (!rela || !relasz || relaent != sizeof(Elf64_Rela)) {
+ pr_debug("No relocations or invalid relocation info\n");
+ return -EINVAL;
+ }
+
+ *rela_out = rela;
+ *relasz_out = relasz;
+ return 0;
+}
+
+/*
+ * Apply ARM64 ELF relocations
+ */
+int elf_apply_relocations(struct elf_image *elf, void *dyn_seg)
+{
+ Elf64_Dyn *dyn = dyn_seg;
+ Elf64_Rela *rela;
+ u64 relasz;
+ phys_addr_t base = (phys_addr_t)elf->reloc_offset;
+ int ret;
+
+ if (elf->class != ELFCLASS64) {
+ pr_err("Wrong ELF class for ARM64 relocation\n");
+ return -EINVAL;
+ }
+
+ ret = parse_dynamic_section(elf, dyn, &rela, &relasz);
+ if (ret)
+ return ret;
+
+ /* Apply each relocation */
+ while (relasz > 0) {
+ u64 *fixup_addr;
+ u32 reloc_type = ELF64_R_TYPE(rela->r_info);
+
+ /* Calculate address to fix up */
+ fixup_addr = (u64 *)(base + rela->r_offset);
+
+ switch (reloc_type) {
+ case R_AARCH64_NONE:
+ /* No operation */
+ break;
+
+ case R_AARCH64_RELATIVE:
+ /* B(P) = Delta(S) + A */
+ /* For RELA format: A = r_addend, Delta(S) = base */
+ *fixup_addr = base + rela->r_addend;
+ break;
+
+ case R_AARCH64_ABS64:
+ /* B(P) = S + A */
+ *fixup_addr = base + rela->r_addend;
+ break;
+
+ default:
+ pr_err("Unsupported ARM64 relocation type: %u at offset 0x%llx\n",
+ reloc_type, rela->r_offset);
+ return -EINVAL;
+ }
+
+ rela++;
+ relasz -= sizeof(Elf64_Rela);
+ }
+
+ return 0;
+}
--
2.47.3
^ permalink raw reply [flat|nested] 49+ messages in thread
* [PATCH 05/19] riscv: implement elf_apply_relocations() for ELF relocation support
2026-01-05 11:26 [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Sascha Hauer
` (3 preceding siblings ...)
2026-01-05 11:26 ` [PATCH 04/19] ARM: implement elf_apply_relocations() for ELF " Sascha Hauer
@ 2026-01-05 11:26 ` Sascha Hauer
2026-01-05 11:26 ` [PATCH 06/19] elf: implement elf_load_inplace() Sascha Hauer
` (14 subsequent siblings)
19 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 11:26 UTC (permalink / raw)
To: BAREBOX; +Cc: Claude Sonnet 4.5
Add architecture-specific ELF relocation support for RISC-V,
enabling dynamic relocation of position-independent ELF binaries.
RISC-V implementation:
- Both RV32 and RV64 use RELA format with relocations:
* R_RISCV_NONE (0): No operation
* R_RISCV_32 (1): 32-bit absolute relocation (RV32)
* R_RISCV_64 (2): 64-bit absolute relocation (RV64)
* R_RISCV_RELATIVE (3): Base-relative adjustment
* Parse the PT_DYNAMIC section to find relocation tables
* Support both regular and PBL builds (obj-pbl-y)
* Follow the same pattern as ARM32/ARM64 implementations
* Validate ELF class matches architecture pointer size
* Return appropriate error codes for unsupported relocations
The relocation constants are added to arch/*/include/asm/elf.h for
use by the dynamic linker and bootloader code.
These implementations enable support for loading position-independent
ELF binaries in barebox PBL, which will be used when the compressed
payload is switched from raw binary to ELF format (similar to the
recent ARM changes).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
arch/riscv/lib/Makefile | 1 +
arch/riscv/lib/elf_reloc.c | 212 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 213 insertions(+)
diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile
index 693248080070338ce26952cb1d9a831937d7f388..ee53e4602126d2ce7da2cdc42f21f92b22232f1a 100644
--- a/arch/riscv/lib/Makefile
+++ b/arch/riscv/lib/Makefile
@@ -3,6 +3,7 @@
extra-y += barebox.lds
obj-y += dtb.o
+obj-pbl-$(CONFIG_ELF) += elf_reloc.o
obj-pbl-y += sections.o setupc.o reloc.o sections.o runtime-offset.o
obj-$(CONFIG_ARCH_HAS_SJLJ) += setjmp.o longjmp.o
obj-$(CONFIG_RISCV_OPTIMZED_STRING_FUNCTIONS) += memcpy.o memset.o memmove.o
diff --git a/arch/riscv/lib/elf_reloc.c b/arch/riscv/lib/elf_reloc.c
new file mode 100644
index 0000000000000000000000000000000000000000..11e44e2c816185d7b4722f2ead698b32bd562de0
--- /dev/null
+++ b/arch/riscv/lib/elf_reloc.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <common.h>
+#include <elf.h>
+#include <errno.h>
+#include <asm/elf.h>
+
+#if __SIZEOF_POINTER__ == 8
+
+/*
+ * Parse dynamic section and extract relocation info for RISC-V 64-bit
+ */
+static int parse_dynamic_section(struct elf_image *elf, Elf64_Dyn *dyn,
+ Elf64_Rela **rela_out, u64 *relasz_out)
+{
+ Elf64_Rela *rela = NULL;
+ u64 relasz = 0, relaent = 0;
+ int i;
+ phys_addr_t base = (phys_addr_t)elf->reloc_offset;
+
+ /* Iterate through dynamic entries until DT_NULL */
+ for (i = 0; dyn[i].d_tag != DT_NULL; i++) {
+ switch (dyn[i].d_tag) {
+ case DT_RELA:
+ /* RELA table address - needs to be adjusted by load offset */
+ rela = (Elf64_Rela *)(base + dyn[i].d_un.d_ptr);
+ break;
+ case DT_RELASZ:
+ relasz = dyn[i].d_un.d_val;
+ break;
+ case DT_RELAENT:
+ relaent = dyn[i].d_un.d_val;
+ break;
+ case DT_REL:
+ pr_err("RISC-V 64 uses RELA, not REL relocations\n");
+ return -EINVAL;
+ default:
+ break;
+ }
+ }
+
+ if (!rela || !relasz || relaent != sizeof(Elf64_Rela)) {
+ pr_debug("No relocations or invalid relocation info\n");
+ return -EINVAL;
+ }
+
+ *rela_out = rela;
+ *relasz_out = relasz;
+ return 0;
+}
+
+/*
+ * Apply RISC-V 64-bit ELF relocations
+ */
+int elf_apply_relocations(struct elf_image *elf, void *dyn_seg)
+{
+ Elf64_Dyn *dyn = dyn_seg;
+ Elf64_Rela *rela;
+ u64 relasz;
+ phys_addr_t base = (phys_addr_t)elf->reloc_offset;
+ int ret;
+
+ if (elf->class != ELFCLASS64) {
+ pr_err("Wrong ELF class for RISC-V 64 relocation\n");
+ return -EINVAL;
+ }
+
+ ret = parse_dynamic_section(elf, dyn, &rela, &relasz);
+ if (ret)
+ return ret;
+
+ /* Apply each relocation */
+ while (relasz > 0) {
+ u64 *fixup_addr;
+ u32 reloc_type = ELF64_R_TYPE(rela->r_info);
+
+ /* Calculate address to fix up */
+ fixup_addr = (u64 *)(base + rela->r_offset);
+
+ switch (reloc_type) {
+ case R_RISCV_NONE:
+ /* No operation */
+ break;
+
+ case R_RISCV_RELATIVE:
+ /* B(P) = B + A */
+ /* For RELA format: A = r_addend, B = base */
+ *fixup_addr = base + rela->r_addend;
+ break;
+
+ case R_RISCV_64:
+ /* B(P) = S + A */
+ /* S is the symbol value, for PIE it's base + addend */
+ *fixup_addr = base + rela->r_addend;
+ break;
+
+ default:
+ pr_err("Unsupported RISC-V relocation type: %u at offset 0x%llx\n",
+ reloc_type, rela->r_offset);
+ return -EINVAL;
+ }
+
+ rela++;
+ relasz -= sizeof(Elf64_Rela);
+ }
+
+ return 0;
+}
+
+#else /* 32-bit RISC-V */
+
+/*
+ * Parse dynamic section and extract relocation info for RISC-V 32-bit
+ */
+static int parse_dynamic_section(struct elf_image *elf, Elf32_Dyn *dyn,
+ Elf32_Rela **rela_out, u64 *relasz_out)
+{
+ Elf32_Rela *rela = NULL;
+ u64 relasz = 0, relaent = 0;
+ int i;
+ phys_addr_t base = (phys_addr_t)elf->reloc_offset;
+
+ /* Iterate through dynamic entries until DT_NULL */
+ for (i = 0; dyn[i].d_tag != DT_NULL; i++) {
+ switch (dyn[i].d_tag) {
+ case DT_RELA:
+ /* RELA table address - needs to be adjusted by load offset */
+ rela = (Elf32_Rela *)(base + dyn[i].d_un.d_ptr);
+ break;
+ case DT_RELASZ:
+ relasz = dyn[i].d_un.d_val;
+ break;
+ case DT_RELAENT:
+ relaent = dyn[i].d_un.d_val;
+ break;
+ case DT_REL:
+ pr_err("RISC-V 32 uses RELA, not REL relocations\n");
+ return -EINVAL;
+ default:
+ break;
+ }
+ }
+
+ if (!rela || !relasz || relaent != sizeof(Elf32_Rela)) {
+ pr_debug("No relocations or invalid relocation info\n");
+ return -EINVAL;
+ }
+
+ *rela_out = rela;
+ *relasz_out = relasz;
+ return 0;
+}
+
+/*
+ * Apply RISC-V 32-bit ELF relocations
+ */
+int elf_apply_relocations(struct elf_image *elf, void *dyn_seg)
+{
+ Elf32_Dyn *dyn = dyn_seg;
+ Elf32_Rela *rela;
+ u64 relasz;
+ phys_addr_t base = (phys_addr_t)elf->reloc_offset;
+ int ret;
+
+ if (elf->class != ELFCLASS32) {
+ pr_err("Wrong ELF class for RISC-V 32 relocation\n");
+ return -EINVAL;
+ }
+
+ ret = parse_dynamic_section(elf, dyn, &rela, &relasz);
+ if (ret)
+ return ret;
+
+ /* Apply each relocation */
+ while (relasz > 0) {
+ u32 *fixup_addr;
+ u32 reloc_type = ELF32_R_TYPE(rela->r_info);
+
+ /* Calculate address to fix up */
+ fixup_addr = (u32 *)(base + rela->r_offset);
+
+ switch (reloc_type) {
+ case R_RISCV_NONE:
+ /* No operation */
+ break;
+
+ case R_RISCV_RELATIVE:
+ /* B(P) = B + A */
+ /* For RELA format: A = r_addend, B = base */
+ *fixup_addr = base + rela->r_addend;
+ break;
+
+ case R_RISCV_32:
+ /* B(P) = S + A */
+ /* S is the symbol value, for PIE it's base + addend */
+ *fixup_addr = base + rela->r_addend;
+ break;
+
+ default:
+ pr_err("Unsupported RISC-V relocation type: %u at offset 0x%x\n",
+ reloc_type, rela->r_offset);
+ return -EINVAL;
+ }
+
+ rela++;
+ relasz -= sizeof(Elf32_Rela);
+ }
+
+ return 0;
+}
+
+#endif
--
2.47.3
^ permalink raw reply [flat|nested] 49+ messages in thread
* [PATCH 06/19] elf: implement elf_load_inplace()
2026-01-05 11:26 [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Sascha Hauer
` (4 preceding siblings ...)
2026-01-05 11:26 ` [PATCH 05/19] riscv: " Sascha Hauer
@ 2026-01-05 11:26 ` Sascha Hauer
2026-01-05 13:37 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 07/19] elf: create elf_open_binary_into() Sascha Hauer
` (13 subsequent siblings)
19 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 11:26 UTC (permalink / raw)
To: BAREBOX; +Cc: Claude Sonnet 4.5
Implement elf_load_inplace() to apply dynamic relocations to an ELF binary
that is already loaded in memory. Unlike elf_load(), this function does not
allocate memory or copy segments - it only modifies the existing image in
place.
This is useful for self-relocating loaders or when the ELF has been loaded
by external means (e.g., firmware or another bootloader).
For ET_DYN (position-independent) binaries, the relocation offset is
calculated relative to the first executable PT_LOAD segment (.text section),
taking into account the difference between the segment's virtual address
and its file offset.
The entry point is also adjusted to point to the relocated image.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
---
common/elf.c | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
include/elf.h | 8 ++++
2 files changed, 160 insertions(+)
diff --git a/common/elf.c b/common/elf.c
index fc2949c285ebb0c0740c68c551926da8d0bb8637..565b283b694773727ef77917cfd8c1d4ee83a8d1 100644
--- a/common/elf.c
+++ b/common/elf.c
@@ -531,3 +531,155 @@ void elf_close(struct elf_image *elf)
free(elf);
}
+
+static void *elf_find_dynamic_inplace(struct elf_image *elf)
+{
+ void *buf = elf->hdr_buf;
+ void *phdr = buf + elf_hdr_e_phoff(elf, buf);
+ int i;
+
+ for (i = 0; i < elf_hdr_e_phnum(elf, buf); i++) {
+ if (elf_phdr_p_type(elf, phdr) == PT_DYNAMIC) {
+ u64 offset = elf_phdr_p_offset(elf, phdr);
+ /* For in-place binary, PT_DYNAMIC is at hdr_buf + offset */
+ return elf->hdr_buf + offset;
+ }
+ phdr += elf_size_of_phdr(elf);
+ }
+
+ return NULL; /* No PT_DYNAMIC segment */
+}
+
+/**
+ * elf_load_inplace() - Apply dynamic relocations to an ELF binary in place
+ * @elf: ELF image previously opened with elf_open_binary()
+ *
+ * This function applies dynamic relocations to an ELF binary that is already
+ * loaded at its target address in memory. Unlike elf_load(), this does not
+ * allocate memory or copy segments - it only modifies the existing image.
+ *
+ * This is useful for self-relocating loaders or when the ELF has been loaded
+ * by external means (e.g., loaded by firmware or another bootloader).
+ *
+ * The ELF image must have been previously opened with elf_open_binary().
+ *
+ * For ET_DYN (position-independent) binaries, the relocation offset is
+ * calculated relative to the first executable PT_LOAD segment (.text section).
+ *
+ * For ET_EXEC binaries, no relocation is applied as they are expected to
+ * be at their link-time addresses.
+ *
+ * Returns: 0 on success, negative error code on failure
+ */
+int elf_load_inplace(struct elf_image *elf)
+{
+ void *dyn_seg;
+ void *buf, *phdr;
+ void *elf_buf;
+ int i, ret;
+
+ buf = elf->hdr_buf;
+ elf_buf = elf->hdr_buf;
+
+ /*
+ * First pass: Clear BSS segments (p_memsz > p_filesz).
+ * This must be done before relocations as uninitialized data
+ * must be zeroed per C standard.
+ */
+ phdr = buf + elf_hdr_e_phoff(elf, buf);
+ for (i = 0; i < elf_hdr_e_phnum(elf, buf); i++) {
+ if (elf_phdr_p_type(elf, phdr) == PT_LOAD) {
+ u64 p_offset = elf_phdr_p_offset(elf, phdr);
+ u64 p_filesz = elf_phdr_p_filesz(elf, phdr);
+ u64 p_memsz = elf_phdr_p_memsz(elf, phdr);
+
+ /* Clear BSS (uninitialized data) */
+ if (p_filesz < p_memsz) {
+ void *bss_start = elf_buf + p_offset + p_filesz;
+ size_t bss_size = p_memsz - p_filesz;
+ memset(bss_start, 0x00, bss_size);
+ }
+ }
+ phdr += elf_size_of_phdr(elf);
+ }
+
+ /*
+ * Calculate relocation offset for the in-place binary.
+ * For ET_DYN, we need to find the first executable PT_LOAD segment
+ * (.text section) and use it as the relocation base.
+ */
+ if (elf->type == ET_DYN) {
+ u64 text_vaddr = 0;
+ u64 text_offset = 0;
+ bool found_text = false;
+
+ /* Find first executable PT_LOAD segment (.text) */
+ phdr = buf + elf_hdr_e_phoff(elf, buf);
+ for (i = 0; i < elf_hdr_e_phnum(elf, buf); i++) {
+ if (elf_phdr_p_type(elf, phdr) == PT_LOAD) {
+ u32 flags = elf_phdr_p_flags(elf, phdr);
+ /* Check if segment is executable (PF_X = 0x1) */
+ if (flags & PF_X) {
+ text_vaddr = elf_phdr_p_vaddr(elf, phdr);
+ text_offset = elf_phdr_p_offset(elf, phdr);
+ found_text = true;
+ break;
+ }
+ }
+ phdr += elf_size_of_phdr(elf);
+ }
+
+ if (!found_text) {
+ pr_err("No executable PT_LOAD segment found\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Calculate relocation offset relative to .text section:
+ * - .text is at file offset text_offset, so in memory at: elf_buf + text_offset
+ * - .text has virtual address text_vaddr
+ * - reloc_offset = (actual .text address) - (virtual .text address)
+ */
+ elf->reloc_offset = ((unsigned long)elf_buf + text_offset) - text_vaddr;
+
+ pr_debug("In-place ELF relocation: text_vaddr=0x%llx, text_offset=0x%llx, "
+ "load_addr=%p, offset=0x%08lx\n",
+ text_vaddr, text_offset, elf_buf, elf->reloc_offset);
+
+ /* Adjust entry point to point to relocated image */
+ elf->entry += elf->reloc_offset;
+ } else {
+ /*
+ * ET_EXEC binaries are at their link-time addresses,
+ * no relocation needed
+ */
+ elf->reloc_offset = 0;
+ }
+
+ /* Find PT_DYNAMIC segment */
+ dyn_seg = elf_find_dynamic_inplace(elf);
+ if (!dyn_seg) {
+ /*
+ * No PT_DYNAMIC segment found.
+ * This is fine for statically-linked binaries or
+ * binaries without relocations.
+ */
+ pr_debug("No PT_DYNAMIC segment found\n");
+ ret = 0;
+ goto out;
+ }
+
+ /* Apply architecture-specific relocations */
+ ret = elf_apply_relocations(elf, dyn_seg);
+ if (ret) {
+ pr_err("In-place relocation failed: %d\n", ret);
+ goto out;
+ }
+
+ pr_debug("In-place ELF relocation completed successfully\n");
+ return 0;
+
+out:
+ return ret;
+}
diff --git a/include/elf.h b/include/elf.h
index 4b19c917639d0d655d9f7530f0311afa599f1a98..5b1d75c14ce198292b052159de310ac14c3bd2d9 100644
--- a/include/elf.h
+++ b/include/elf.h
@@ -422,6 +422,14 @@ int elf_load(struct elf_image *elf);
*/
void elf_set_load_address(struct elf_image *elf, void *addr);
+/*
+ * Apply dynamic relocations to an ELF binary already loaded in memory.
+ * This modifies the ELF image in place without allocating new memory.
+ * Useful for self-relocating loaders or externally loaded binaries.
+ * The elf parameter must have been previously opened with elf_open_binary().
+ */
+int elf_load_inplace(struct elf_image *elf);
+
/*
* Architecture-specific relocation handler.
* Returns 0 on success, -ENOSYS if architecture doesn't support relocations,
--
2.47.3
^ permalink raw reply [flat|nested] 49+ messages in thread
* [PATCH 07/19] elf: create elf_open_binary_into()
2026-01-05 11:26 [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Sascha Hauer
` (5 preceding siblings ...)
2026-01-05 11:26 ` [PATCH 06/19] elf: implement elf_load_inplace() Sascha Hauer
@ 2026-01-05 11:26 ` Sascha Hauer
2026-01-05 11:26 ` [PATCH 08/19] Makefile: add barebox.elf build target Sascha Hauer
` (12 subsequent siblings)
19 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 11:26 UTC (permalink / raw)
To: BAREBOX; +Cc: Claude Sonnet 4.5
elf_open_binary() returns a dynamically allocated struct elf_image *. We
do not have malloc in the PBL, so for better PBL support create
elf_open_binary_into() which takes a struct elf_image * as argument.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
---
common/elf.c | 26 +++++++++++++++++++-------
include/elf.h | 1 +
2 files changed, 20 insertions(+), 7 deletions(-)
diff --git a/common/elf.c b/common/elf.c
index 565b283b694773727ef77917cfd8c1d4ee83a8d1..ee5e31dc58bbb6432d82ff96d9dbeb4c6ce72e39 100644
--- a/common/elf.c
+++ b/common/elf.c
@@ -318,6 +318,23 @@ static void elf_init_struct(struct elf_image *elf)
elf->filename = NULL;
}
+int elf_open_binary_into(struct elf_image *elf, void *buf)
+{
+ int ret;
+
+ memset(elf, 0, sizeof(*elf));
+ elf_init_struct(elf);
+
+ elf->hdr_buf = buf;
+ ret = elf_check_image(elf, buf);
+ if (ret)
+ return ret;
+
+ elf->entry = elf_hdr_e_entry(elf, elf->hdr_buf);
+
+ return 0;
+}
+
struct elf_image *elf_open_binary(void *buf)
{
int ret;
@@ -327,17 +344,12 @@ struct elf_image *elf_open_binary(void *buf)
if (!elf)
return ERR_PTR(-ENOMEM);
- elf_init_struct(elf);
-
- elf->hdr_buf = buf;
- ret = elf_check_image(elf, buf);
+ ret = elf_open_binary_into(elf, buf);
if (ret) {
free(elf);
- return ERR_PTR(-EINVAL);
+ return ERR_PTR(ret);
}
- elf->entry = elf_hdr_e_entry(elf, elf->hdr_buf);
-
return elf;
}
diff --git a/include/elf.h b/include/elf.h
index 5b1d75c14ce198292b052159de310ac14c3bd2d9..ea2c399cca7f70a2c8c6d239be1fa0ff0519a683 100644
--- a/include/elf.h
+++ b/include/elf.h
@@ -410,6 +410,7 @@ static inline size_t elf_get_mem_size(struct elf_image *elf)
return elf->high_addr - elf->low_addr;
}
+int elf_open_binary_into(struct elf_image *elf, void *buf);
struct elf_image *elf_open_binary(void *buf);
struct elf_image *elf_open(const char *filename);
void elf_close(struct elf_image *elf);
--
2.47.3
^ permalink raw reply [flat|nested] 49+ messages in thread
* [PATCH 08/19] Makefile: add barebox.elf build target
2026-01-05 11:26 [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Sascha Hauer
` (6 preceding siblings ...)
2026-01-05 11:26 ` [PATCH 07/19] elf: create elf_open_binary_into() Sascha Hauer
@ 2026-01-05 11:26 ` Sascha Hauer
2026-01-05 12:22 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 09/19] PBL: allow to link ELF image into PBL Sascha Hauer
` (11 subsequent siblings)
19 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 11:26 UTC (permalink / raw)
To: BAREBOX; +Cc: Claude Sonnet 4.5
Add a build target to create barebox.elf, which provides an ELF format
version of barebox that can be used for debugging or alternative boot
scenarios.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
---
Makefile | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
index 75e14383bcdcf987d091442adba9b053af28eae7..c1aa6935abe5c65156b812d4a0b21c6d488038ae 100644
--- a/Makefile
+++ b/Makefile
@@ -850,6 +850,9 @@ all: barebox-flash-images
endif
all: $(symlink-y)
+ifeq ($(CONFIG_PBL_IMAGE)-$(CONFIG_PBL_IMAGE_NO_PIGGY),y-)
+all: barebox.elf
+endif
.SECONDEXPANSION:
$(symlink-y): $$(or $$(SYMLINK_DEP_$$(@F)),$$(SYMLINK_TARGET_$$(@F))) FORCE
@@ -1091,6 +1094,17 @@ barebox.fit: images/barebox-$(CONFIG_ARCH_LINUX_NAME).fit
barebox.srec: barebox
$(OBJCOPY) -O srec $< $@
+OBJCOPYFLAGS_barebox.elf = --strip-debug --strip-unneeded \
+ --remove-section=.comment \
+ --remove-section=.note \
+ --remove-section=.note.gnu.build-id
+
+quiet_cmd_objcopy_elf = OBJCOPY $@
+ cmd_objcopy_elf = $(OBJCOPY) $(OBJCOPYFLAGS_barebox.elf) $< $@
+
+barebox.elf: barebox FORCE
+ $(call if_changed,objcopy_elf)
+
quiet_cmd_barebox_proper__ = CC $@
cmd_barebox_proper__ = $(CC) -r -o $@ -Wl,--whole-archive $(BAREBOX_OBJS)
@@ -1388,7 +1402,7 @@ CLEAN_FILES += barebox System.map include/generated/barebox_default_env.h \
.tmp_version .tmp_barebox* barebox.bin barebox.map \
.tmp_kallsyms* barebox.ldr compile_commands.json \
.tmp_barebox.o barebox.o barebox-flash-image \
- barebox.srec barebox.s5p barebox.ubl \
+ barebox.srec barebox.elf barebox.s5p barebox.ubl \
barebox.uimage \
barebox.efi barebox.canon-a1100.bin
--
2.47.3
^ permalink raw reply [flat|nested] 49+ messages in thread
* [PATCH 09/19] PBL: allow to link ELF image into PBL
2026-01-05 11:26 [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Sascha Hauer
` (7 preceding siblings ...)
2026-01-05 11:26 ` [PATCH 08/19] Makefile: add barebox.elf build target Sascha Hauer
@ 2026-01-05 11:26 ` Sascha Hauer
2026-01-05 12:11 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 10/19] mmu: add MAP_CACHED_RO mapping type Sascha Hauer
` (10 subsequent siblings)
19 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 11:26 UTC (permalink / raw)
To: BAREBOX; +Cc: Claude Sonnet 4.5
Some architectures want to link the barebox proper ELF image into the
PBL. Allow that and provide a Kconfig option to select the ELF image.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
images/Makefile | 18 ++++++++++++++++--
images/piggy.S | 4 ++++
pbl/Kconfig | 8 ++++++++
3 files changed, 28 insertions(+), 2 deletions(-)
diff --git a/images/Makefile b/images/Makefile
index 448479ee8cf355a771ce63314ce64a631544f488..8d4fd1504ab64d4f4d1cc19f2b36cff9939964e3 100644
--- a/images/Makefile
+++ b/images/Makefile
@@ -138,7 +138,13 @@ $(obj)/%.itb: $(obj)/%.its FORCE
$(obj)/%.fit: $(obj)/$$(FILE_$$(@F)) $(dtstree)/dtbs-list FORCE
$(call if_changed,fit)
-$(obj)/piggy.o: $(obj)/barebox.z FORCE
+ifeq ($(CONFIG_PBL_IMAGE_ELF),)
+PIGGY_IMAGE := $(obj)/barebox.z
+else
+PIGGY_IMAGE := $(obj)/barebox.elf.z
+endif
+
+$(obj)/piggy.o: ${PIGGY_IMAGE} FORCE
$(obj)/sha_sum.o: $(obj)/barebox.sha.bin FORCE
@@ -154,6 +160,14 @@ $(obj)/barebox.sum: $(obj)/barebox.z FORCE
$(obj)/barebox.z: $(obj)/../$(BAREBOX_PROPER) FORCE
$(call if_changed,$(suffix_y))
+# barebox.elf.z - compressed barebox ELF binary
+# ----------------------------------------------------------------
+# Prevent the %.elf pattern rule from building ../barebox.elf
+$(obj)/../barebox.elf: ;
+
+$(obj)/barebox.elf.z: $(obj)/../barebox.elf FORCE
+ $(call if_changed,$(suffix_y))
+
# %.img - create a copy from another file
# ----------------------------------------------------------------
@@ -219,7 +233,7 @@ ifneq ($(pblx-y)$(pblx-),)
$(error pblx- has been removed. Please use pblb- instead.)
endif
-targets += $(image-y) pbl.lds barebox.x barebox.z piggy.o sha_sum.o barebox.sha.bin barebox.sum
+targets += $(image-y) pbl.lds barebox.x barebox.z barebox.elf.z piggy.o sha_sum.o barebox.sha.bin barebox.sum
targets += $(patsubst %,%.pblb,$(pblb-y))
targets += $(patsubst %,%.pbl,$(pblb-y))
targets += $(patsubst %,%.s,$(pblb-y))
diff --git a/images/piggy.S b/images/piggy.S
index 654933ea2ffe4aad9bbed44a44971ef67a3fe183..b09fbfd2e215c674548caeab544ef5ada49a40ea 100644
--- a/images/piggy.S
+++ b/images/piggy.S
@@ -3,6 +3,10 @@
.section .piggydata,"a"
.globl input_data
input_data:
+#ifdef CONFIG_PBL_IMAGE_ELF
+ .incbin "images/barebox.elf.z"
+#else
.incbin "images/barebox.z"
+#endif
.globl input_data_end
input_data_end:
diff --git a/pbl/Kconfig b/pbl/Kconfig
index cab9325d16e8625bcca10125b3281062abffedbc..66bba5a4b21a715f3eb55d39c820977bfb3c459f 100644
--- a/pbl/Kconfig
+++ b/pbl/Kconfig
@@ -21,6 +21,14 @@ config PBL_IMAGE_NO_PIGGY
want to use the piggy mechanism to load barebox proper.
It's so far only intended for sandbox.
+config PBL_IMAGE_ELF
+ bool
+ depends on PBL_IMAGE
+ help
+ If yes, link ELF image into the PBL, otherwise a raw binary
+ is linked into the PBL. This must match the loader code in the
+ PBL.
+
config PBL_MULTI_IMAGES
bool
select PBL_IMAGE
--
2.47.3
^ permalink raw reply [flat|nested] 49+ messages in thread
* [PATCH 10/19] mmu: add MAP_CACHED_RO mapping type
2026-01-05 11:26 [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Sascha Hauer
` (8 preceding siblings ...)
2026-01-05 11:26 ` [PATCH 09/19] PBL: allow to link ELF image into PBL Sascha Hauer
@ 2026-01-05 11:26 ` Sascha Hauer
2026-01-05 12:14 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 11/19] mmu: introduce pbl_remap_range() Sascha Hauer
` (9 subsequent siblings)
19 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 11:26 UTC (permalink / raw)
To: BAREBOX; +Cc: Claude Sonnet 4.5
ARM32 and ARM64 have ARCH_MAP_CACHED_RO. We'll move parts of the MMU
initialization to generic code later, so add a new mapping type to
include/mmu.h.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
arch/arm/cpu/mmu-common.c | 4 ++--
arch/arm/cpu/mmu-common.h | 3 +--
arch/arm/cpu/mmu_32.c | 4 ++--
arch/arm/cpu/mmu_64.c | 2 +-
include/mmu.h | 3 ++-
5 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/arch/arm/cpu/mmu-common.c b/arch/arm/cpu/mmu-common.c
index b3d9e9579686c0612068c6281420cb6ccaaf4ee8..3208139fdd24e89cf4c76e27477da23da169f164 100644
--- a/arch/arm/cpu/mmu-common.c
+++ b/arch/arm/cpu/mmu-common.c
@@ -22,7 +22,7 @@ const char *map_type_tostr(maptype_t map_type)
switch (map_type) {
case ARCH_MAP_CACHED_RWX: return "RWX";
- case ARCH_MAP_CACHED_RO: return "RO";
+ case MAP_CACHED_RO: return "RO";
case MAP_CACHED: return "CACHED";
case MAP_UNCACHED: return "UNCACHED";
case MAP_CODE: return "CODE";
@@ -161,7 +161,7 @@ static void mmu_remap_memory_banks(void)
setup_trap_pages();
remap_range((void *)code_start, code_size, MAP_CODE);
- remap_range((void *)rodata_start, rodata_size, ARCH_MAP_CACHED_RO);
+ remap_range((void *)rodata_start, rodata_size, MAP_CACHED_RO);
}
static int mmu_init(void)
diff --git a/arch/arm/cpu/mmu-common.h b/arch/arm/cpu/mmu-common.h
index a111e15a21b479b5ffa2ea8973e2ad189e531925..b42c421ffde8ebba84b17c6311b735f7759dc69b 100644
--- a/arch/arm/cpu/mmu-common.h
+++ b/arch/arm/cpu/mmu-common.h
@@ -12,7 +12,6 @@
#include <linux/bits.h>
#define ARCH_MAP_CACHED_RWX MAP_ARCH(2)
-#define ARCH_MAP_CACHED_RO MAP_ARCH(3)
#define ARCH_MAP_FLAG_PAGEWISE BIT(31)
@@ -32,7 +31,7 @@ static inline maptype_t arm_mmu_maybe_skip_permissions(maptype_t map_type)
switch (map_type & MAP_TYPE_MASK) {
case MAP_CODE:
case MAP_CACHED:
- case ARCH_MAP_CACHED_RO:
+ case MAP_CACHED_RO:
return ARCH_MAP_CACHED_RWX;
default:
return map_type;
diff --git a/arch/arm/cpu/mmu_32.c b/arch/arm/cpu/mmu_32.c
index 63c412873ec8fdb047a3323e773648cb03d5757b..97c7107290ce95ddb21a322a5d0e74f3d324c528 100644
--- a/arch/arm/cpu/mmu_32.c
+++ b/arch/arm/cpu/mmu_32.c
@@ -304,7 +304,7 @@ static uint32_t get_pte_flags(maptype_t map_type)
switch (map_type & MAP_TYPE_MASK) {
case ARCH_MAP_CACHED_RWX:
return PTE_FLAGS_CACHED_V7_RWX;
- case ARCH_MAP_CACHED_RO:
+ case MAP_CACHED_RO:
return PTE_FLAGS_CACHED_RO_V7;
case MAP_CACHED:
return PTE_FLAGS_CACHED_V7;
@@ -320,7 +320,7 @@ static uint32_t get_pte_flags(maptype_t map_type)
}
} else {
switch (map_type & MAP_TYPE_MASK) {
- case ARCH_MAP_CACHED_RO:
+ case MAP_CACHED_RO:
case MAP_CODE:
return PTE_FLAGS_CACHED_RO_V4;
case ARCH_MAP_CACHED_RWX:
diff --git a/arch/arm/cpu/mmu_64.c b/arch/arm/cpu/mmu_64.c
index f22fcb5f8ea4db9843c8447ac5bf4f8cf29bb59c..afb3d2d7efd0bc7ecde1177d1544f54d751b5dc1 100644
--- a/arch/arm/cpu/mmu_64.c
+++ b/arch/arm/cpu/mmu_64.c
@@ -159,7 +159,7 @@ static unsigned long get_pte_attrs(maptype_t map_type)
return attrs_xn() | MEM_ALLOC_WRITECOMBINE;
case MAP_CODE:
return CACHED_MEM | PTE_BLOCK_RO;
- case ARCH_MAP_CACHED_RO:
+ case MAP_CACHED_RO:
return attrs_xn() | CACHED_MEM | PTE_BLOCK_RO;
case ARCH_MAP_CACHED_RWX:
return CACHED_MEM;
diff --git a/include/mmu.h b/include/mmu.h
index 29992ae1d6c644f4eaa6519dae2b57055333bff6..53603b7956c229b4c715c57b19d0398931eb2d6b 100644
--- a/include/mmu.h
+++ b/include/mmu.h
@@ -9,9 +9,10 @@
#define MAP_CACHED 1
#define MAP_FAULT 2
#define MAP_CODE 3
+#define MAP_CACHED_RO 4
#ifdef CONFIG_ARCH_HAS_DMA_WRITE_COMBINE
-#define MAP_WRITECOMBINE 4
+#define MAP_WRITECOMBINE 5
#else
#define MAP_WRITECOMBINE MAP_UNCACHED
#endif
--
2.47.3
^ permalink raw reply [flat|nested] 49+ messages in thread
* [PATCH 11/19] mmu: introduce pbl_remap_range()
2026-01-05 11:26 [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Sascha Hauer
` (9 preceding siblings ...)
2026-01-05 11:26 ` [PATCH 10/19] mmu: add MAP_CACHED_RO mapping type Sascha Hauer
@ 2026-01-05 11:26 ` Sascha Hauer
2026-01-05 12:15 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 12/19] ARM: use relative jumps in exception table Sascha Hauer
` (8 subsequent siblings)
19 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 11:26 UTC (permalink / raw)
To: BAREBOX; +Cc: Claude Sonnet 4.5
Add PBL-specific memory remapping function that always uses page-wise
mapping (ARCH_MAP_FLAG_PAGEWISE) for fine-grained permissions on
adjacent ELF segments with different protection requirements.
Wraps arch-specific __arch_remap_range() for ARMv7 (4KB pages) and
ARMv8 (page tables with BBM). Needed for ELF segment permission setup.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
arch/arm/cpu/mmu_32.c | 7 +++++++
arch/arm/cpu/mmu_64.c | 8 ++++++++
include/mmu.h | 3 +++
3 files changed, 18 insertions(+)
diff --git a/arch/arm/cpu/mmu_32.c b/arch/arm/cpu/mmu_32.c
index 97c7107290ce95ddb21a322a5d0e74f3d324c528..86a55d165ba3cec5154c345a1a3a9cb959f0996f 100644
--- a/arch/arm/cpu/mmu_32.c
+++ b/arch/arm/cpu/mmu_32.c
@@ -435,6 +435,13 @@ static void early_remap_range(u32 addr, size_t size, maptype_t map_type)
__arch_remap_range((void *)addr, addr, size, map_type);
}
+void pbl_remap_range(void *virt_addr, phys_addr_t phys_addr, size_t size,
+ maptype_t map_type)
+{
+ __arch_remap_range(virt_addr, phys_addr, size,
+ map_type | ARCH_MAP_FLAG_PAGEWISE);
+}
+
static bool pte_is_cacheable(uint32_t pte, int level)
{
return (level == 2 && (pte & PTE_CACHEABLE)) ||
diff --git a/arch/arm/cpu/mmu_64.c b/arch/arm/cpu/mmu_64.c
index afb3d2d7efd0bc7ecde1177d1544f54d751b5dc1..63faaa46703697e527eae766392e7ea7ae186b3d 100644
--- a/arch/arm/cpu/mmu_64.c
+++ b/arch/arm/cpu/mmu_64.c
@@ -282,6 +282,14 @@ static void early_remap_range(uint64_t addr, size_t size, maptype_t map_type)
__arch_remap_range(addr, addr, size, map_type, false);
}
+void pbl_remap_range(void *virt_addr, phys_addr_t phys_addr, size_t size,
+ maptype_t map_type)
+{
+ __arch_remap_range((uint64_t)virt_addr, phys_addr,
+ (uint64_t)size, map_type | ARCH_MAP_FLAG_PAGEWISE,
+ true);
+}
+
int arch_remap_range(void *virt_addr, phys_addr_t phys_addr, size_t size, maptype_t map_type)
{
map_type = arm_mmu_maybe_skip_permissions(map_type);
diff --git a/include/mmu.h b/include/mmu.h
index 53603b7956c229b4c715c57b19d0398931eb2d6b..37df7b482e1d83e94e61997db6cf9834d8cf7f3c 100644
--- a/include/mmu.h
+++ b/include/mmu.h
@@ -64,6 +64,9 @@ static inline bool arch_can_remap(void)
}
#endif
+void pbl_remap_range(void *virt_addr, phys_addr_t phys_addr, size_t size,
+ maptype_t map_type);
+
static inline int remap_range(void *start, size_t size, maptype_t map_type)
{
return arch_remap_range(start, virt_to_phys(start), size, map_type);
--
2.47.3
^ permalink raw reply [flat|nested] 49+ messages in thread
* [PATCH 12/19] ARM: use relative jumps in exception table
2026-01-05 11:26 [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Sascha Hauer
` (10 preceding siblings ...)
2026-01-05 11:26 ` [PATCH 11/19] mmu: introduce pbl_remap_range() Sascha Hauer
@ 2026-01-05 11:26 ` Sascha Hauer
2026-01-05 11:44 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 13/19] ARM: exceptions: make in-binary exception table const Sascha Hauer
` (7 subsequent siblings)
19 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 11:26 UTC (permalink / raw)
To: BAREBOX; +Cc: Claude Sonnet 4.5
Create position-independent exception vectors using relative branches
instead of absolute addresses. This works on ARMv7 onwards which
supports setting the address of the exception vectors.
New .text_inplace_exceptions section contains PC-relative branches,
enabling barebox proper to start with MMU already configured using
ELF segment addresses.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
arch/arm/cpu/exceptions_32.S | 20 ++++++++++++++++++++
arch/arm/cpu/interrupts_32.c | 3 +--
arch/arm/cpu/mmu_32.c | 5 +++--
arch/arm/cpu/no-mmu.c | 11 +----------
arch/arm/include/asm/sections.h | 1 +
arch/arm/lib/pbl.lds.S | 6 +++---
arch/arm/lib32/barebox.lds.S | 4 ++++
7 files changed, 33 insertions(+), 17 deletions(-)
diff --git a/arch/arm/cpu/exceptions_32.S b/arch/arm/cpu/exceptions_32.S
index 235996f7ec296b44261637bc98c1c5ee30a3cbe1..302e4d030d6347e8fdb488db73a0a3a4bb1c0ec7 100644
--- a/arch/arm/cpu/exceptions_32.S
+++ b/arch/arm/cpu/exceptions_32.S
@@ -156,6 +156,26 @@ ENTRY(arm_fixup_vectors)
ENDPROC(arm_fixup_vectors)
#endif
+.section .text_inplace_exceptions
+1: b 1b /* barebox_arm_reset_vector */
+#ifdef CONFIG_ARM_EXCEPTIONS
+ b undefined_instruction /* undefined instruction */
+ b software_interrupt /* software interrupt (SWI) */
+ b prefetch_abort /* prefetch abort */
+ b data_abort /* data abort */
+1: b 1b /* (reserved) */
+ b irq /* irq (interrupt) */
+ b fiq /* fiq (fast interrupt) */
+#else
+1: b 1b /* undefined instruction */
+1: b 1b /* software interrupt (SWI) */
+1: b 1b /* prefetch abort */
+1: b 1b /* data abort */
+1: b 1b /* (reserved) */
+1: b 1b /* irq (interrupt) */
+1: b 1b /* fiq (fast interrupt) */
+#endif
+
.section .text_exceptions
.globl extable
extable:
diff --git a/arch/arm/cpu/interrupts_32.c b/arch/arm/cpu/interrupts_32.c
index 185646e38195b2bdc9f0d7e30d53a0506932fa13..6ebcbcd8dc1299c0333da87e496bc57172691fa8 100644
--- a/arch/arm/cpu/interrupts_32.c
+++ b/arch/arm/cpu/interrupts_32.c
@@ -181,7 +181,6 @@ void arm_pbl_init_exceptions(void)
if (cpu_architecture() < CPU_ARCH_ARMv7)
return;
- set_vbar((unsigned long)__exceptions_start);
- arm_fixup_vectors();
+ set_vbar((unsigned long)__inplace_exceptions_start);
}
#endif
diff --git a/arch/arm/cpu/mmu_32.c b/arch/arm/cpu/mmu_32.c
index 86a55d165ba3cec5154c345a1a3a9cb959f0996f..9ce77078d5e35810f2239d82f29f67e2aa2e4d8e 100644
--- a/arch/arm/cpu/mmu_32.c
+++ b/arch/arm/cpu/mmu_32.c
@@ -629,8 +629,9 @@ void setup_trap_pages(void)
* First try to use the vectors where they actually are, works
* on ARMv7 and later.
*/
- if (!set_vector_table((unsigned long)__exceptions_start)) {
- arm_fixup_vectors();
+ if (!set_vector_table((unsigned long)__inplace_exceptions_start)) {
+ pr_debug("Using inplace exception vectors at 0x%08lx\n",
+ (unsigned long)__inplace_exceptions_start);
create_zero_page();
return;
}
diff --git a/arch/arm/cpu/no-mmu.c b/arch/arm/cpu/no-mmu.c
index c4ef5d1f9d55136d606c244309dbeeb8fd988784..68246d71156c7c84b9faff452cebb37132b83573 100644
--- a/arch/arm/cpu/no-mmu.c
+++ b/arch/arm/cpu/no-mmu.c
@@ -21,8 +21,6 @@
#include <asm/sections.h>
#include <asm/cputype.h>
-#define __exceptions_size (__exceptions_stop - __exceptions_start)
-
static bool has_vbar(void)
{
u32 mainid;
@@ -41,7 +39,6 @@ static bool has_vbar(void)
static int nommu_v7_vectors_init(void)
{
- void *vectors;
u32 cr;
if (cpu_architecture() < CPU_ARCH_ARMv7)
@@ -58,13 +55,7 @@ static int nommu_v7_vectors_init(void)
cr &= ~CR_V;
set_cr(cr);
- arm_fixup_vectors();
-
- vectors = xmemalign(PAGE_SIZE, PAGE_SIZE);
- memset(vectors, 0, PAGE_SIZE);
- memcpy(vectors, __exceptions_start, __exceptions_size);
-
- set_vbar((unsigned int)vectors);
+ set_vbar((unsigned int)__inplace_exceptions_start);
return 0;
}
diff --git a/arch/arm/include/asm/sections.h b/arch/arm/include/asm/sections.h
index 15b1a6482a5b148284ab47de2db1c2653909da09..bf4fb7b109a7a22d9a298257af23a11b9efe6861 100644
--- a/arch/arm/include/asm/sections.h
+++ b/arch/arm/include/asm/sections.h
@@ -13,6 +13,7 @@ extern char __dynsym_start[];
extern char __dynsym_end[];
extern char __exceptions_start[];
extern char __exceptions_stop[];
+extern char __inplace_exceptions_start[];
#endif
diff --git a/arch/arm/lib/pbl.lds.S b/arch/arm/lib/pbl.lds.S
index 9c51f5eb3a3d8256752a78e03fed851c84d92edb..53b21084cff2e3d916cd37485281f2f78166c37d 100644
--- a/arch/arm/lib/pbl.lds.S
+++ b/arch/arm/lib/pbl.lds.S
@@ -53,9 +53,9 @@ SECTIONS
*(.text_bare_init*)
__bare_init_end = .;
. = ALIGN(0x20);
- __exceptions_start = .;
- KEEP(*(.text_exceptions*))
- __exceptions_stop = .;
+ __inplace_exceptions_start = .;
+ KEEP(*(.text_inplace_exceptions*))
+ __inplace_exceptions_stop = .;
*(.text*)
}
diff --git a/arch/arm/lib32/barebox.lds.S b/arch/arm/lib32/barebox.lds.S
index ede6889991f16d00f2bad79dd777ae9b5e639ff4..9b2b65e2f17d62d5134fc367621cce733dcea06a 100644
--- a/arch/arm/lib32/barebox.lds.S
+++ b/arch/arm/lib32/barebox.lds.S
@@ -26,6 +26,10 @@ SECTIONS
__exceptions_start = .;
KEEP(*(.text_exceptions*))
__exceptions_stop = .;
+ . = ALIGN(0x20);
+ __inplace_exceptions_start = .;
+ KEEP(*(.text_inplace_exceptions*))
+ __inplace_exceptions_stop = .;
*(.text*)
}
BAREBOX_BARE_INIT_SIZE
--
2.47.3
^ permalink raw reply [flat|nested] 49+ messages in thread
* [PATCH 13/19] ARM: exceptions: make in-binary exception table const
2026-01-05 11:26 [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Sascha Hauer
` (11 preceding siblings ...)
2026-01-05 11:26 ` [PATCH 12/19] ARM: use relative jumps in exception table Sascha Hauer
@ 2026-01-05 11:26 ` Sascha Hauer
2026-01-05 11:26 ` [PATCH 14/19] ARM: linker script: create separate PT_LOAD segments for text, rodata, and data Sascha Hauer
` (6 subsequent siblings)
19 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 11:26 UTC (permalink / raw)
To: BAREBOX; +Cc: Claude Sonnet 4.5
When making the .text section const we can no longer modify the code.
On ARMv5/v6 we finally use a copy of the exception table anyway, so
instead of modifying it in-place and copy afterwards, copy it first and
then modify it.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
arch/arm/cpu/exceptions_32.S | 40 ++++++--------------------------------
arch/arm/cpu/interrupts_32.c | 38 ++++++++++++++++++++++++++++++++++++
arch/arm/cpu/mmu_32.c | 3 +--
arch/arm/include/asm/barebox-arm.h | 4 ++--
4 files changed, 47 insertions(+), 38 deletions(-)
diff --git a/arch/arm/cpu/exceptions_32.S b/arch/arm/cpu/exceptions_32.S
index 302e4d030d6347e8fdb488db73a0a3a4bb1c0ec7..e4a54897bf9fe134fbc48a69a4c5a2efce2acbbc 100644
--- a/arch/arm/cpu/exceptions_32.S
+++ b/arch/arm/cpu/exceptions_32.S
@@ -92,24 +92,28 @@ do_abort_\@:
.arm
.align 5
+.globl undefined_instruction
undefined_instruction:
get_bad_stack
bad_save_user_regs
bl do_undefined_instruction
.align 5
+.globl software_interrupt
software_interrupt:
get_bad_stack
bad_save_user_regs
bl do_software_interrupt
.align 5
+.globl prefetch_abort
prefetch_abort:
get_bad_stack
bad_save_user_regs
bl do_prefetch_abort
.align 5
+.globl data_abort
data_abort:
try_data_abort
get_bad_stack
@@ -117,45 +121,19 @@ data_abort:
bl do_data_abort
.align 5
+.globl irq
irq:
get_bad_stack
bad_save_user_regs
bl do_irq
.align 5
+.globl fiq
fiq:
get_bad_stack
bad_save_user_regs
bl do_fiq
-#ifdef CONFIG_ARM_EXCEPTIONS
-/*
- * With relocatable binary support the runtime exception vectors do not match
- * the addresses in the binary. We have to fix them up during runtime
- */
-ENTRY(arm_fixup_vectors)
- ldr r0, =undefined_instruction
- ldr r1, =_undefined_instruction
- str r0, [r1]
- ldr r0, =software_interrupt
- ldr r1, =_software_interrupt
- str r0, [r1]
- ldr r0, =prefetch_abort
- ldr r1, =_prefetch_abort
- str r0, [r1]
- ldr r0, =data_abort
- ldr r1, =_data_abort
- str r0, [r1]
- ldr r0, =irq
- ldr r1, =_irq
- str r0, [r1]
- ldr r0, =fiq
- ldr r1, =_fiq
- str r0, [r1]
- bx lr
-ENDPROC(arm_fixup_vectors)
-#endif
-
.section .text_inplace_exceptions
1: b 1b /* barebox_arm_reset_vector */
#ifdef CONFIG_ARM_EXCEPTIONS
@@ -188,17 +166,11 @@ extable:
1: b 1b /* (reserved) */
ldr pc, _irq /* irq (interrupt) */
ldr pc, _fiq /* fiq (fast interrupt) */
-.globl _undefined_instruction
_undefined_instruction: .word undefined_instruction
-.globl _software_interrupt
_software_interrupt: .word software_interrupt
-.globl _prefetch_abort
_prefetch_abort: .word prefetch_abort
-.globl _data_abort
_data_abort: .word data_abort
-.globl _irq
_irq: .word irq
-.globl _fiq
_fiq: .word fiq
#else
1: b 1b /* undefined instruction */
diff --git a/arch/arm/cpu/interrupts_32.c b/arch/arm/cpu/interrupts_32.c
index 6ebcbcd8dc1299c0333da87e496bc57172691fa8..99a0bce9244e339f69548868ce6efcaf72194f47 100644
--- a/arch/arm/cpu/interrupts_32.c
+++ b/arch/arm/cpu/interrupts_32.c
@@ -184,3 +184,41 @@ void arm_pbl_init_exceptions(void)
set_vbar((unsigned long)__inplace_exceptions_start);
}
#endif
+
+/* This struct must match the assembly exception table in exceptions_32.S */
+struct extable {
+ uint32_t reset;
+ uint32_t undefined_instruction;
+ uint32_t software_interrupt;
+ uint32_t prefetch_abort;
+ uint32_t data_abort;
+ uint32_t reserved;
+ uint32_t irq;
+ uint32_t fiq;
+ void *undefined_instruction_ptr;
+ void *software_interrupt_ptr;
+ void *prefetch_abort_ptr;
+ void *data_abort_ptr;
+ void *reserved_ptr;
+ void *irq_ptr;
+ void *fiq_ptr;
+};
+
+void undefined_instruction(void);
+void software_interrupt(void);
+void prefetch_abort(void);
+void data_abort(void);
+void irq(void);
+void fiq(void);
+
+void arm_fixup_vectors(void *_table)
+{
+ struct extable *table = _table;
+
+ table->undefined_instruction_ptr = undefined_instruction;
+ table->software_interrupt_ptr = software_interrupt;
+ table->prefetch_abort_ptr = prefetch_abort;
+ table->data_abort_ptr = irq;
+ table->irq_ptr = irq;
+ table->fiq_ptr = fiq;
+}
diff --git a/arch/arm/cpu/mmu_32.c b/arch/arm/cpu/mmu_32.c
index 9ce77078d5e35810f2239d82f29f67e2aa2e4d8e..a1d10db009650c2f9322fa8f7e4a54a0430bb2fa 100644
--- a/arch/arm/cpu/mmu_32.c
+++ b/arch/arm/cpu/mmu_32.c
@@ -538,10 +538,9 @@ static void create_vector_table(unsigned long adr)
get_pte_flags(MAP_CACHED), true);
}
- arm_fixup_vectors();
-
memset(vectors, 0, PAGE_SIZE);
memcpy(vectors, __exceptions_start, __exceptions_stop - __exceptions_start);
+ arm_fixup_vectors(vectors);
}
/**
diff --git a/arch/arm/include/asm/barebox-arm.h b/arch/arm/include/asm/barebox-arm.h
index 11be8b85837ea3d1e56d3433b4b7e05eb9e15a47..6c30d9d22e3d3ef6943dd8bca67d8e1394e051d4 100644
--- a/arch/arm/include/asm/barebox-arm.h
+++ b/arch/arm/include/asm/barebox-arm.h
@@ -45,9 +45,9 @@ unsigned long arm_mem_membase_get(void);
unsigned long arm_mem_endmem_get(void);
#ifdef CONFIG_ARM_EXCEPTIONS
-void arm_fixup_vectors(void);
+void arm_fixup_vectors(void *table);
#else
-static inline void arm_fixup_vectors(void)
+static inline void arm_fixup_vectors(void *table)
{
}
#endif
--
2.47.3
^ permalink raw reply [flat|nested] 49+ messages in thread
* [PATCH 14/19] ARM: linker script: create separate PT_LOAD segments for text, rodata, and data
2026-01-05 11:26 [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Sascha Hauer
` (12 preceding siblings ...)
2026-01-05 11:26 ` [PATCH 13/19] ARM: exceptions: make in-binary exception table const Sascha Hauer
@ 2026-01-05 11:26 ` Sascha Hauer
2026-01-05 13:11 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 15/19] ARM: link ELF image into PBL Sascha Hauer
` (5 subsequent siblings)
19 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 11:26 UTC (permalink / raw)
To: BAREBOX; +Cc: Claude Sonnet 4.5
Fix the linker scripts to generate three distinct PT_LOAD segments with
correct permissions instead of combining .rodata with .data.
Before this fix, the linker auto-generated only two PT_LOAD segments:
1. Text segment (PF_R|PF_X)
2. Data segment (PF_R|PF_W) - containing .rodata, .data, .bss, etc.
This caused .rodata to be mapped with write permissions when
pbl_mmu_setup_from_elf() set up MMU permissions based on ELF segments,
defeating the W^X protection that commit d9ccb0cf14 intended to provide.
With explicit PHDRS directives, we now generate three segments:
1. text segment (PF_R|PF_X): .text and related code sections
2. rodata segment (PF_R): .rodata and unwind tables
3. data segment (PF_R|PF_W): .data, .bss, and related sections
This ensures pbl_mmu_setup_from_elf() correctly maps .rodata as
read-only (MAP_CACHED_RO) instead of read-write (MAP_CACHED).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
arch/arm/include/asm/barebox.lds.h | 14 +++++++-------
arch/arm/lib/pbl.lds.S | 2 +-
arch/arm/lib32/barebox.lds.S | 35 +++++++++++++++++++++++------------
arch/arm/lib64/barebox.lds.S | 31 +++++++++++++++++++++----------
4 files changed, 52 insertions(+), 30 deletions(-)
diff --git a/arch/arm/include/asm/barebox.lds.h b/arch/arm/include/asm/barebox.lds.h
index 72aabe155b5c9e8b9159c7da6c6f0fa1f7b93375..a3ccea91196ccce71c54483c5e1ad5caeb3c1d32 100644
--- a/arch/arm/include/asm/barebox.lds.h
+++ b/arch/arm/include/asm/barebox.lds.h
@@ -14,13 +14,13 @@
#define BAREBOX_RELOCATION_TYPE rela
#endif
-#define BAREBOX_RELOCATION_TABLE \
- .rel_dyn_start : { *(.__rel_dyn_start) } \
- .BAREBOX_RELOCATION_TYPE.dyn : { *(.BAREBOX_RELOCATION_TYPE*) } \
- .rel_dyn_end : { *(.__rel_dyn_end) } \
- .__dynsym_start : { *(.__dynsym_start) } \
- .dynsym : { *(.dynsym) } \
- .__dynsym_end : { *(.__dynsym_end) }
+#define BAREBOX_RELOCATION_TABLE(PHDR) \
+ .rel_dyn_start : { *(.__rel_dyn_start) } PHDR \
+ .BAREBOX_RELOCATION_TYPE.dyn : { *(.BAREBOX_RELOCATION_TYPE*) } PHDR \
+ .rel_dyn_end : { *(.__rel_dyn_end) } PHDR \
+ .__dynsym_start : { *(.__dynsym_start) } PHDR \
+ .dynsym : { *(.dynsym) } PHDR \
+ .__dynsym_end : { *(.__dynsym_end) } PHDR
#include <asm-generic/barebox.lds.h>
diff --git a/arch/arm/lib/pbl.lds.S b/arch/arm/lib/pbl.lds.S
index 53b21084cff2e3d916cd37485281f2f78166c37d..98e25a543525e2bc16b8d10ec24c2ba6b8cbec90 100644
--- a/arch/arm/lib/pbl.lds.S
+++ b/arch/arm/lib/pbl.lds.S
@@ -95,7 +95,7 @@ SECTIONS
}
__shasum_end = .;
- BAREBOX_RELOCATION_TABLE
+ BAREBOX_RELOCATION_TABLE()
pbl_code_size = .;
diff --git a/arch/arm/lib32/barebox.lds.S b/arch/arm/lib32/barebox.lds.S
index 9b2b65e2f17d62d5134fc367621cce733dcea06a..c91c101d6aa5c2a0fcbf79a151a4234d924b3881 100644
--- a/arch/arm/lib32/barebox.lds.S
+++ b/arch/arm/lib32/barebox.lds.S
@@ -7,14 +7,23 @@
OUTPUT_FORMAT(BAREBOX_OUTPUT_FORMAT)
OUTPUT_ARCH(BAREBOX_OUTPUT_ARCH)
ENTRY(start)
+
+PHDRS
+{
+ text PT_LOAD FLAGS(5); /* PF_R | PF_X */
+ rodata PT_LOAD FLAGS(4); /* PF_R */
+ data PT_LOAD FLAGS(6); /* PF_R | PF_W */
+ dynamic PT_DYNAMIC FLAGS(6); /* PF_R | PF_W */
+}
+
SECTIONS
{
. = 0x0;
- .image_start : { *(.__image_start) }
+ .image_start : { *(.__image_start) } :text
. = ALIGN(4);
- ._text : { *(._text) }
+ ._text : { *(._text) } :text
.text :
{
_stext = .;
@@ -31,7 +40,7 @@ SECTIONS
KEEP(*(.text_inplace_exceptions*))
__inplace_exceptions_stop = .;
*(.text*)
- }
+ } :text
BAREBOX_BARE_INIT_SIZE
. = ALIGN(4096);
@@ -39,7 +48,7 @@ SECTIONS
.rodata : {
*(.rodata*)
RO_DATA_SECTION
- }
+ } :rodata
#ifdef CONFIG_ARM_UNWIND
/*
@@ -50,12 +59,12 @@ SECTIONS
__start_unwind_idx = .;
*(.ARM.exidx*)
__stop_unwind_idx = .;
- }
+ } :rodata
.ARM.unwind_tab : {
__start_unwind_tab = .;
*(.ARM.extab*)
__stop_unwind_tab = .;
- }
+ } :rodata
#endif
. = ALIGN(4096);
__end_rodata = .;
@@ -65,19 +74,21 @@ SECTIONS
. = ALIGN(4);
.data : { *(.data*)
CONSTRUCTORS
- }
+ } :data
+
+ .dynamic : { *(.dynamic) } :data :dynamic
. = .;
- BAREBOX_RELOCATION_TABLE
+ BAREBOX_RELOCATION_TABLE(:data)
_edata = .;
- .image_end : { *(.__image_end) }
+ .image_end : { *(.__image_end) } :data
. = ALIGN(4);
- .__bss_start : { *(.__bss_start) }
- .bss : { *(.bss*) }
- .__bss_stop : { *(.__bss_stop) }
+ .__bss_start : { *(.__bss_start) } :data
+ .bss : { *(.bss*) } :data
+ .__bss_stop : { *(.__bss_stop) } :data
#ifdef CONFIG_ARM_SECURE_MONITOR
. = ALIGN(16);
diff --git a/arch/arm/lib64/barebox.lds.S b/arch/arm/lib64/barebox.lds.S
index e2f1164c47f7e5d300028d112a6e90e2fa368c65..603e0ab77490d3221fbfd3341250e6dcae840d91 100644
--- a/arch/arm/lib64/barebox.lds.S
+++ b/arch/arm/lib64/barebox.lds.S
@@ -6,14 +6,23 @@
OUTPUT_FORMAT(BAREBOX_OUTPUT_FORMAT)
OUTPUT_ARCH(BAREBOX_OUTPUT_ARCH)
ENTRY(start)
+
+PHDRS
+{
+ text PT_LOAD FLAGS(5); /* PF_R | PF_X */
+ rodata PT_LOAD FLAGS(4); /* PF_R */
+ data PT_LOAD FLAGS(6); /* PF_R | PF_W */
+ dynamic PT_DYNAMIC FLAGS(6); /* PF_R | PF_W */
+}
+
SECTIONS
{
. = 0x0;
- .image_start : { *(.__image_start) }
+ .image_start : { *(.__image_start) } :text
. = ALIGN(4);
- ._text : { *(._text) }
+ ._text : { *(._text) } :text
.text :
{
_stext = .;
@@ -22,7 +31,7 @@ SECTIONS
*(.text_bare_init*)
__bare_init_end = .;
*(.text*)
- }
+ } :text
BAREBOX_BARE_INIT_SIZE
. = ALIGN(4096);
@@ -30,7 +39,7 @@ SECTIONS
.rodata : {
*(.rodata*)
RO_DATA_SECTION
- }
+ } :rodata
. = ALIGN(4096);
@@ -38,18 +47,20 @@ SECTIONS
_etext = .;
_sdata = .;
- .data : { *(.data*) }
+ .data : { *(.data*) } :data
+
+ .dynamic : { *(.dynamic) } :data :dynamic
- BAREBOX_RELOCATION_TABLE
+ BAREBOX_RELOCATION_TABLE(:data)
_edata = .;
- .image_end : { *(.__image_end) }
+ .image_end : { *(.__image_end) } :data
. = ALIGN(4);
- .__bss_start : { *(.__bss_start) }
- .bss : { *(.bss*) }
- .__bss_stop : { *(.__bss_stop) }
+ .__bss_start : { *(.__bss_start) } :data
+ .bss : { *(.bss*) } :data
+ .__bss_stop : { *(.__bss_stop) } :data
_end = .;
_barebox_image_size = __bss_start;
}
--
2.47.3
^ permalink raw reply [flat|nested] 49+ messages in thread
* [PATCH 15/19] ARM: link ELF image into PBL
2026-01-05 11:26 [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Sascha Hauer
` (13 preceding siblings ...)
2026-01-05 11:26 ` [PATCH 14/19] ARM: linker script: create separate PT_LOAD segments for text, rodata, and data Sascha Hauer
@ 2026-01-05 11:26 ` Sascha Hauer
2026-01-05 12:27 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 16/19] ARM: PBL: setup MMU with proper permissions from ELF segments Sascha Hauer
` (4 subsequent siblings)
19 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 11:26 UTC (permalink / raw)
To: BAREBOX; +Cc: Claude Sonnet 4.5
Instead of linking the raw binary barebox proper image into the PBL link
the ELF image into the PBL. With this barebox proper starts with a properly
linked and fully initialized C environment, so the calls to
relocate_to_adr() and setup_c() can be removed from barebox proper.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
arch/arm/Kconfig | 2 ++
arch/arm/cpu/start.c | 4 ----
arch/arm/cpu/uncompress.c | 32 ++++++++++++++++++++++++++------
3 files changed, 28 insertions(+), 10 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index afa6de3cd0b145e42755e3ca2d7a13c8a69ff1d3..60975083b32f3127f982c526c6cf30ffe3e45924 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -17,6 +17,8 @@ config ARM
select HAVE_ARCH_BOOTM_OFTREE
select HW_HAS_PCI
select ARCH_HAS_DMA_WRITE_COMBINE
+ select ELF
+ select PBL_IMAGE_ELF
default y
config ARCH_LINUX_NAME
diff --git a/arch/arm/cpu/start.c b/arch/arm/cpu/start.c
index f7d4507e71588ba5e241b24b952d55e2a4b0f794..71a12abdd0941889617e6b63063304d0e4522c81 100644
--- a/arch/arm/cpu/start.c
+++ b/arch/arm/cpu/start.c
@@ -139,10 +139,6 @@ __noreturn __prereloc void barebox_non_pbl_start(unsigned long membase,
if (IS_ENABLED(CONFIG_CPU_V7))
armv7_hyp_install();
- relocate_to_adr(barebox_base);
-
- setup_c();
-
barrier();
pbl_barebox_break();
diff --git a/arch/arm/cpu/uncompress.c b/arch/arm/cpu/uncompress.c
index b9fc1d04db96e77c8fcd7fd1930798ea1d9294d7..ccc3c5ae3ba60e990ee73715a49a316e2a14c44e 100644
--- a/arch/arm/cpu/uncompress.c
+++ b/arch/arm/cpu/uncompress.c
@@ -20,6 +20,7 @@
#include <asm/mmu.h>
#include <asm/unaligned.h>
#include <compressed-dtb.h>
+#include <elf.h>
#include <debug_ll.h>
@@ -85,21 +86,40 @@ void __noreturn barebox_pbl_start(unsigned long membase, unsigned long memsize,
else if (IS_ENABLED(CONFIG_ARMV7R_MPU))
set_cr(get_cr() | CR_C);
- pr_debug("uncompressing barebox binary at 0x%p (size 0x%08x) to 0x%08lx (uncompressed size: 0x%08x)\n",
+ pr_debug("uncompressing barebox ELF 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);
- handoff_data_move(handoff_data);
+ pr_debug("relocating ELF in place\n");
- sync_caches_for_execution();
+ struct elf_image elf;
+ int ret;
+
+ ret = elf_open_binary_into(&elf, (void *)barebox_base);
+ if (ret) {
+ pr_err("Failed to open ELF binary: %d\n", ret);
+ hang();
+ }
+
+ ret = elf_load_inplace(&elf);
+ if (ret) {
+ pr_err("Failed to relocate ELF: %d\n", ret);
+ hang();
+ }
+
+ pr_debug("ELF entry point: 0x%llx\n", elf.entry);
if (IS_ENABLED(CONFIG_THUMB2_BAREBOX))
- barebox = (void *)(barebox_base + 1);
+ barebox = (void *)(unsigned long)(elf.entry | 1);
else
- barebox = (void *)barebox_base;
+ barebox = (void *)(unsigned long)elf.entry;
+
+ handoff_data_move(handoff_data);
+
+ sync_caches_for_execution();
- pr_debug("jumping to uncompressed image at 0x%p\n", barebox);
+ pr_debug("jumping to ELF entry point at 0x%p\n", barebox);
if (IS_ENABLED(CONFIG_CPU_V7) && boot_cpu_mode() == HYP_MODE)
armv7_switch_to_hyp();
--
2.47.3
^ permalink raw reply [flat|nested] 49+ messages in thread
* [PATCH 16/19] ARM: PBL: setup MMU with proper permissions from ELF segments
2026-01-05 11:26 [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Sascha Hauer
` (14 preceding siblings ...)
2026-01-05 11:26 ` [PATCH 15/19] ARM: link ELF image into PBL Sascha Hauer
@ 2026-01-05 11:26 ` Sascha Hauer
2026-01-05 12:58 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 17/19] riscv: link ELF image into PBL Sascha Hauer
` (3 subsequent siblings)
19 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 11:26 UTC (permalink / raw)
To: BAREBOX; +Cc: Claude Sonnet 4.5
Move complete MMU setup into PBL by leveraging ELF segment information
to apply correct memory permissions before jumping to barebox proper.
After ELF relocation, parse PT_LOAD segments and map each with
permissions derived from p_flags:
- Text segments (PF_R|PF_X): Read-only + executable (MAP_CODE)
- Data segments (PF_R|PF_W): Read-write (MAP_CACHED)
- RO data segments (PF_R): Read-only (ARCH_MAP_CACHED_RO)
This ensures barebox proper starts with full W^X protection already
in place, eliminating the need for complex remapping in barebox proper.
The mmu_init() function now only sets up trap pages for exception
handling.
The framework is portable - common ELF parsing in pbl/mmu.c uses
architecture-specific early_remap_range() exported from mmu_*.c.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
arch/arm/cpu/mmu-common.c | 64 ++------------------------
arch/arm/cpu/uncompress.c | 14 ++++++
include/pbl/mmu.h | 29 ++++++++++++
pbl/Makefile | 1 +
pbl/mmu.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 158 insertions(+), 61 deletions(-)
diff --git a/arch/arm/cpu/mmu-common.c b/arch/arm/cpu/mmu-common.c
index 3208139fdd24e89cf4c76e27477da23da169f164..3053abd2c7907baccc7f5686dd85de76591ad118 100644
--- a/arch/arm/cpu/mmu-common.c
+++ b/arch/arm/cpu/mmu-common.c
@@ -96,72 +96,14 @@ void zero_page_faulting(void)
remap_range(0x0, PAGE_SIZE, MAP_FAULT);
}
-/**
- * remap_range_end - remap a range identified by [start, end)
- *
- * @start: start of the range
- * @end: end of the first range (exclusive)
- * @map_type: mapping type to apply
- */
-static inline void remap_range_end(unsigned long start, unsigned long end,
- unsigned map_type)
-{
- remap_range((void *)start, end - start, map_type);
-}
-
-static inline void remap_range_end_sans_text(unsigned long start, unsigned long end,
- unsigned map_type)
-{
- unsigned long text_start = (unsigned long)&_stext;
- unsigned long text_end = (unsigned long)&_etext;
-
- if (region_overlap_end_exclusive(start, end, text_start, text_end)) {
- remap_range_end(start, text_start, MAP_CACHED);
- /* skip barebox segments here, will be mapped later */
- start = text_end;
- }
-
- remap_range_end(start, end, MAP_CACHED);
-}
-
static void mmu_remap_memory_banks(void)
{
- struct memory_bank *bank;
- unsigned long code_start = (unsigned long)&_stext;
- unsigned long code_size = (unsigned long)&__start_rodata - (unsigned long)&_stext;
- unsigned long rodata_start = (unsigned long)&__start_rodata;
- unsigned long rodata_size = (unsigned long)&__end_rodata - rodata_start;
-
/*
- * Early mmu init will have mapped everything but the initial memory area
- * (excluding final OPTEE_SIZE bytes) uncached. We have now discovered
- * all memory banks, so let's map all pages, excluding reserved memory areas
- * and barebox text area cacheable.
- *
- * This code will become much less complex once we switch over to using
- * CONFIG_MEMORY_ATTRIBUTES for MMU as well.
+ * PBL has already set up the MMU with proper permissions based on
+ * ELF segment information. We only need to set up trap pages for
+ * exception handling.
*/
- for_each_memory_bank(bank) {
- struct resource *rsv;
- resource_size_t pos;
-
- pos = bank->start;
-
- /* Skip reserved regions */
- for_each_reserved_region(bank, rsv) {
- if (pos != rsv->start)
- remap_range_end_sans_text(pos, rsv->start, MAP_CACHED);
- pos = rsv->end + 1;
- }
-
- remap_range_end_sans_text(pos, bank->start + bank->size, MAP_CACHED);
- }
-
- /* Do this while interrupt vectors are still writable */
setup_trap_pages();
-
- remap_range((void *)code_start, code_size, MAP_CODE);
- remap_range((void *)rodata_start, rodata_size, MAP_CACHED_RO);
}
static int mmu_init(void)
diff --git a/arch/arm/cpu/uncompress.c b/arch/arm/cpu/uncompress.c
index ccc3c5ae3ba60e990ee73715a49a316e2a14c44e..05f2efd48eeca58a820ac7fa4d8c6d8d3b763344 100644
--- a/arch/arm/cpu/uncompress.c
+++ b/arch/arm/cpu/uncompress.c
@@ -21,6 +21,7 @@
#include <asm/unaligned.h>
#include <compressed-dtb.h>
#include <elf.h>
+#include <pbl/mmu.h>
#include <debug_ll.h>
@@ -110,6 +111,19 @@ void __noreturn barebox_pbl_start(unsigned long membase, unsigned long memsize,
pr_debug("ELF entry point: 0x%llx\n", elf.entry);
+ /*
+ * Now that the ELF image is relocated, we know the exact addresses
+ * of all segments. Set up MMU with proper permissions based on
+ * ELF segment flags (PF_R/W/X).
+ */
+ if (IS_ENABLED(CONFIG_MMU)) {
+ ret = pbl_mmu_setup_from_elf(&elf, membase, memsize);
+ if (ret) {
+ pr_err("Failed to setup MMU from ELF: %d\n", ret);
+ hang();
+ }
+ }
+
if (IS_ENABLED(CONFIG_THUMB2_BAREBOX))
barebox = (void *)(unsigned long)(elf.entry | 1);
else
diff --git a/include/pbl/mmu.h b/include/pbl/mmu.h
new file mode 100644
index 0000000000000000000000000000000000000000..4a00d8e528ab5452981347185c9114235f213e2b
--- /dev/null
+++ b/include/pbl/mmu.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __PBL_MMU_H
+#define __PBL_MMU_H
+
+#include <linux/types.h>
+
+struct elf_image;
+
+/**
+ * pbl_mmu_setup_from_elf() - Configure MMU using ELF segment information
+ * @elf: ELF image structure from elf_open_binary_into()
+ * @membase: Base address of RAM
+ * @memsize: Size of RAM
+ *
+ * This function sets up the MMU with proper permissions based on ELF
+ * segment flags. It should be called after elf_load_inplace() has
+ * relocated the barebox image.
+ *
+ * Segment permissions are mapped as follows:
+ * PF_R | PF_X -> Read-only + executable (text)
+ * PF_R | PF_W -> Read-write (data, bss)
+ * PF_R -> Read-only (rodata)
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int pbl_mmu_setup_from_elf(struct elf_image *elf, unsigned long membase,
+ unsigned long memsize);
+
+#endif /* __PBL_MMU_H */
diff --git a/pbl/Makefile b/pbl/Makefile
index f66391be7b2898388425657f54afcd6e4c72e3db..b78124cdcd2a4690be11d5503006723252b4904f 100644
--- a/pbl/Makefile
+++ b/pbl/Makefile
@@ -9,3 +9,4 @@ pbl-$(CONFIG_HAVE_IMAGE_COMPRESSION) += decomp.o
pbl-$(CONFIG_LIBFDT) += fdt.o
pbl-$(CONFIG_PBL_CONSOLE) += console.o
obj-pbl-y += handoff-data.o
+obj-pbl-$(CONFIG_MMU) += mmu.o
diff --git a/pbl/mmu.c b/pbl/mmu.c
new file mode 100644
index 0000000000000000000000000000000000000000..7a8f254a7bd67eccaab715832930c5d4134eb288
--- /dev/null
+++ b/pbl/mmu.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2025 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+
+#define pr_fmt(fmt) "pbl-mmu: " fmt
+
+#include <common.h>
+#include <elf.h>
+#include <mmu.h>
+#include <pbl/mmu.h>
+#include <asm/mmu.h>
+#include <linux/bits.h>
+#include <linux/sizes.h>
+
+/*
+ * Map ELF segment permissions (p_flags) to architecture MMU flags
+ */
+static unsigned int elf_flags_to_mmu_flags(u32 p_flags)
+{
+ bool readable = p_flags & PF_R;
+ bool writable = p_flags & PF_W;
+ bool executable = p_flags & PF_X;
+
+ if (readable && writable) {
+ /* Data, BSS: Read-write, cached, non-executable */
+ return MAP_CACHED;
+ } else if (readable && executable) {
+ /* Text: Read-only, cached, executable */
+ return MAP_CODE;
+ } else if (readable) {
+ /* Read-only data: Read-only, cached, non-executable */
+ return MAP_CACHED_RO;
+ } else {
+ /*
+ * Unusual: segment with no read permission.
+ * Map as uncached, non-executable for safety.
+ */
+ pr_warn("Segment with unusual permissions: flags=0x%x\n", p_flags);
+ return MAP_UNCACHED;
+ }
+}
+
+int pbl_mmu_setup_from_elf(struct elf_image *elf, unsigned long membase,
+ unsigned long memsize)
+{
+ void *phdr;
+ int i;
+ int phnum = elf_hdr_e_phnum(elf, elf->hdr_buf);
+ size_t phoff = elf_hdr_e_phoff(elf, elf->hdr_buf);
+ size_t phentsize = elf_size_of_phdr(elf);
+
+ pr_debug("Setting up MMU from ELF segments\n");
+ pr_debug("ELF entry point: 0x%llx\n", elf->entry);
+ pr_debug("ELF loaded at: 0x%p - 0x%p\n", elf->low_addr, elf->high_addr);
+
+ /*
+ * Iterate through all PT_LOAD segments and set up MMU permissions
+ * based on the segment's p_flags
+ */
+ for (i = 0; i < phnum; i++) {
+ phdr = elf->hdr_buf + phoff + i * phentsize;
+
+ if (elf_phdr_p_type(elf, phdr) != PT_LOAD)
+ continue;
+
+ u64 p_vaddr = elf_phdr_p_vaddr(elf, phdr);
+ u64 p_memsz = elf_phdr_p_memsz(elf, phdr);
+ u32 p_flags = elf_phdr_p_flags(elf, phdr);
+
+ /*
+ * Calculate actual address after relocation.
+ * For ET_EXEC: reloc_offset is 0, use p_vaddr directly
+ * For ET_DYN: reloc_offset adjusts virtual to actual address
+ */
+ unsigned long addr = p_vaddr + elf->reloc_offset;
+ unsigned long size = p_memsz;
+ unsigned long segment_end = addr + size;
+
+ /* Validate segment is within available memory */
+ if (segment_end < addr || /* overflow check */
+ addr < membase ||
+ segment_end > membase + memsize) {
+ pr_err("Segment %d outside memory bounds\n", i);
+ return -EINVAL;
+ }
+
+ /* Validate alignment - warn and round if needed */
+ if (!IS_ALIGNED(addr, SZ_4K) || !IS_ALIGNED(size, SZ_4K)) {
+ pr_warn("Segment %d not page-aligned, rounding\n", i);
+ size = ALIGN(size, SZ_4K);
+ }
+
+ unsigned int mmu_flags = elf_flags_to_mmu_flags(p_flags);
+
+ pr_debug("Segment %d: addr=0x%08lx size=0x%08lx flags=0x%x [%c%c%c] -> mmu_flags=0x%x\n",
+ i, addr, size, p_flags,
+ (p_flags & PF_R) ? 'R' : '-',
+ (p_flags & PF_W) ? 'W' : '-',
+ (p_flags & PF_X) ? 'X' : '-',
+ mmu_flags);
+
+ /*
+ * Remap this segment with proper permissions.
+ * Use page-wise mapping to allow different permissions for
+ * different segments even if they're nearby.
+ */
+ pbl_remap_range((void *)addr, addr, size, mmu_flags);
+ }
+
+ pr_debug("MMU setup from ELF complete\n");
+ return 0;
+}
--
2.47.3
^ permalink raw reply [flat|nested] 49+ messages in thread
* [PATCH 17/19] riscv: link ELF image into PBL
2026-01-05 11:26 [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Sascha Hauer
` (15 preceding siblings ...)
2026-01-05 11:26 ` [PATCH 16/19] ARM: PBL: setup MMU with proper permissions from ELF segments Sascha Hauer
@ 2026-01-05 11:26 ` Sascha Hauer
2026-01-05 13:12 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 18/19] riscv: linker script: create separate PT_LOAD segments for text, rodata, and data Sascha Hauer
` (2 subsequent siblings)
19 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 11:26 UTC (permalink / raw)
To: BAREBOX; +Cc: Claude Sonnet 4.5
Instead of linking the raw binary barebox proper image into the PBL link
the ELF image into the PBL. With this barebox proper starts with a properly
linked and fully initialized C environment, so the calls to
relocate_to_adr() and setup_c() can be removed from barebox proper.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
arch/riscv/Kconfig | 2 ++
arch/riscv/boot/start.c | 4 ----
arch/riscv/boot/uncompress.c | 16 +++++++++++++++-
3 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index 96d013d8514ac12de5c34a426262d85f8cf021b9..f8c8b38ed6d7fdae48669e6d7b737f695f1c4cc9 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -17,6 +17,8 @@ config RISCV
select HAS_KALLSYMS
select RISCV_TIMER if RISCV_SBI
select HW_HAS_PCI
+ select ELF
+ select PBL_IMAGE_ELF
select HAVE_ARCH_BOARD_GENERIC_DT
select HAVE_ARCH_BOOTM_OFTREE
diff --git a/arch/riscv/boot/start.c b/arch/riscv/boot/start.c
index 5091340c8a374fc360ab732ba01ec8516e82a83d..874f4e72ae46fe7435c9defedceaf5b4e9662b55 100644
--- a/arch/riscv/boot/start.c
+++ b/arch/riscv/boot/start.c
@@ -123,10 +123,6 @@ void barebox_non_pbl_start(unsigned long membase, unsigned long memsize,
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();
irq_init_vector(riscv_mode());
diff --git a/arch/riscv/boot/uncompress.c b/arch/riscv/boot/uncompress.c
index 84142acf9c66fe1fcceb6ae63d15ac078ccddee7..dc0d1fd5d41a88acea8717a7f5fcdb8da3298663 100644
--- a/arch/riscv/boot/uncompress.c
+++ b/arch/riscv/boot/uncompress.c
@@ -32,6 +32,8 @@ void __noreturn barebox_pbl_start(unsigned long membase, unsigned long memsize,
unsigned long barebox_base;
void *pg_start, *pg_end;
unsigned long pc = get_pc();
+ struct elf_image elf;
+ int ret;
irq_init_vector(riscv_mode());
@@ -68,7 +70,19 @@ void __noreturn barebox_pbl_start(unsigned long membase, unsigned long memsize,
sync_caches_for_execution();
- barebox = (void *)barebox_base;
+ ret = elf_open_binary_into(&elf, (void *)barebox_base);
+ if (ret) {
+ pr_err("Failed to open ELF binary: %d\n", ret);
+ hang();
+ }
+
+ ret = elf_load_inplace(&elf);
+ if (ret) {
+ pr_err("Failed to relocate ELF: %d\n", ret);
+ hang();
+ }
+
+ barebox = (void *)elf.entry;
pr_debug("jumping to uncompressed image at 0x%p. dtb=0x%p\n", barebox, fdt);
--
2.47.3
^ permalink raw reply [flat|nested] 49+ messages in thread
* [PATCH 18/19] riscv: linker script: create separate PT_LOAD segments for text, rodata, and data
2026-01-05 11:26 [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Sascha Hauer
` (16 preceding siblings ...)
2026-01-05 11:26 ` [PATCH 17/19] riscv: link ELF image into PBL Sascha Hauer
@ 2026-01-05 11:26 ` Sascha Hauer
2026-01-05 13:40 ` Ahmad Fatoum
2026-01-05 11:27 ` [PATCH 19/19] riscv: add ELF segment-based memory protection with MMU Sascha Hauer
2026-01-05 14:08 ` [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Ahmad Fatoum
19 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 11:26 UTC (permalink / raw)
To: BAREBOX; +Cc: Claude Sonnet 4.5
Fix the linker script to generate three distinct PT_LOAD segments with
correct permissions instead of combining .rodata with .data.
Before this fix, the linker auto-generated only two PT_LOAD segments:
1. Text segment (PF_R|PF_X)
2. Data segment (PF_R|PF_W) - containing .rodata, .data, .bss, etc.
This caused .rodata to be mapped with write permissions when
riscv_mmu_setup_from_elf() or riscv_pmp_setup_from_elf() set up memory
permissions based on ELF segments, defeating the W^X protection.
With explicit PHDRS directives, we now generate three segments:
1. text segment (PF_R|PF_X): .text and related code sections
2. rodata segment (PF_R): .rodata and related read-only sections
3. data segment (PF_R|PF_W): .data, .bss, and related sections
This ensures riscv_mmu_setup_from_elf() and riscv_pmp_setup_from_elf()
correctly map .rodata as read-only instead of read-write.
Also update the prelink script to handle binaries without a PT_DYNAMIC
segment, as the new PHDRS layout may result in this case.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
arch/riscv/lib/barebox.lds.S | 38 +++++++++++++++++++++++++-------------
scripts/prelink-riscv.inc | 9 +++++++--
2 files changed, 32 insertions(+), 15 deletions(-)
diff --git a/arch/riscv/lib/barebox.lds.S b/arch/riscv/lib/barebox.lds.S
index 03b3a967193cfee1c67b96632cf972a553e8bec4..1565a6fedef1ade7687740240bc36f407ca880fc 100644
--- a/arch/riscv/lib/barebox.lds.S
+++ b/arch/riscv/lib/barebox.lds.S
@@ -16,14 +16,23 @@
OUTPUT_ARCH(BAREBOX_OUTPUT_ARCH)
ENTRY(start)
OUTPUT_FORMAT(BAREBOX_OUTPUT_FORMAT)
+
+PHDRS
+{
+ text PT_LOAD FLAGS(5); /* PF_R | PF_X */
+ rodata PT_LOAD FLAGS(4); /* PF_R */
+ data PT_LOAD FLAGS(6); /* PF_R | PF_W */
+ dynamic PT_DYNAMIC FLAGS(6); /* PF_R | PF_W */
+}
+
SECTIONS
{
. = 0x0;
- .image_start : { *(.__image_start) }
+ .image_start : { *(.__image_start) } :text
. = ALIGN(4);
- ._text : { *(._text) }
+ ._text : { *(._text) } :text
.text :
{
_stext = .;
@@ -35,44 +44,47 @@ SECTIONS
KEEP(*(.text_exceptions*))
__exceptions_stop = .;
*(.text*)
- }
+ } :text
BAREBOX_BARE_INIT_SIZE
- . = ALIGN(4);
+ . = ALIGN(4096);
__start_rodata = .;
.rodata : {
*(.rodata*)
RO_DATA_SECTION
- }
+ } :rodata
__end_rodata = .;
_etext = .;
_sdata = .;
- . = ALIGN(4);
- .data : { *(.data*) }
+ . = ALIGN(4096);
+
+ .data : { *(.data*) } :data
/DISCARD/ : { *(.rela.plt*) }
.rela.dyn : {
__rel_dyn_start = .;
*(.rel*)
__rel_dyn_end = .;
- }
+ } :data
.dynsym : {
__dynsym_start = .;
*(.dynsym)
__dynsym_end = .;
- }
+ } :data
+
+ .dynamic : { *(.dynamic) } :data :dynamic
_edata = .;
- .image_end : { *(.__image_end) }
+ .image_end : { *(.__image_end) } :data
. = ALIGN(4);
- .__bss_start : { *(.__bss_start) }
- .bss : { *(.bss*) }
- .__bss_stop : { *(.__bss_stop) }
+ .__bss_start : { *(.__bss_start) } :data
+ .bss : { *(.bss*) } :data
+ .__bss_stop : { *(.__bss_stop) } :data
_end = .;
_barebox_image_size = __bss_start;
}
diff --git a/scripts/prelink-riscv.inc b/scripts/prelink-riscv.inc
index f2b5467f5b3c19be285153d3ad7cdb210a24a94c..8a54a9737fe73827ad8cab01a61fbecc68a1140a 100644
--- a/scripts/prelink-riscv.inc
+++ b/scripts/prelink-riscv.inc
@@ -61,8 +61,13 @@ static void prelink_bonn(void *data)
}
}
- if (dyns == NULL)
- die("No dynamic section found");
+ if (dyns == NULL) {
+ /* No PT_DYNAMIC segment found - binary may not need prelinking.
+ * This can happen with statically-linked relocatable binaries
+ * that handle relocations differently. Exit successfully.
+ */
+ return;
+ }
Elf_Rela *rela_dyn = NULL;
size_t rela_count = 0;
--
2.47.3
^ permalink raw reply [flat|nested] 49+ messages in thread
* [PATCH 19/19] riscv: add ELF segment-based memory protection with MMU
2026-01-05 11:26 [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Sascha Hauer
` (17 preceding siblings ...)
2026-01-05 11:26 ` [PATCH 18/19] riscv: linker script: create separate PT_LOAD segments for text, rodata, and data Sascha Hauer
@ 2026-01-05 11:27 ` Sascha Hauer
2026-01-05 13:58 ` Ahmad Fatoum
2026-01-05 14:08 ` [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Ahmad Fatoum
19 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 11:27 UTC (permalink / raw)
To: BAREBOX; +Cc: Claude Sonnet 4.5
Enable hardware-enforced W^X (Write XOR Execute) memory protection through
ELF segment-based permissions using the RISC-V MMU.
This implementation provides memory protection for RISC-V S-mode using
Sv39 (RV64) or Sv32 (RV32) page tables.
Linker Script Changes:
- Add PHDRS directives to pbl.lds.S and barebox.lds.S
- Create three separate PT_LOAD segments with proper permissions:
* text segment (FLAGS(5) = PF_R|PF_X): code sections
* rodata segment (FLAGS(4) = PF_R): read-only data
* data segment (FLAGS(6) = PF_R|PF_W): data and BSS
- Add 4K alignment between segments for page-granular protection
S-mode MMU Implementation (mmu.c):
- Implement page table walking for Sv39/Sv32
- pbl_remap_range(): remap segments with ELF-derived permissions
- mmu_early_enable(): create identity mapping and enable SATP CSR
- Map ELF flags to PTE bits:
* MAP_CODE → PTE_R | PTE_X (read + execute)
* MAP_CACHED_RO → PTE_R (read only)
* MAP_CACHED → PTE_R | PTE_W (read + write)
Integration:
- Update uncompress.c to call mmu_early_enable() before decompression
(enables caching for faster decompression)
- Call pbl_mmu_setup_from_elf() after ELF relocation to apply final
segment-based permissions
- Uses portable pbl/mmu.c infrastructure to parse PT_LOAD segments
Configuration:
- Add CONFIG_MMU option (default y for RISCV_S_MODE)
- Update asm/mmu.h with ARCH_HAS_REMAP and function declarations
Security Benefits:
- Text sections are read-only and executable (cannot be modified)
- Read-only data sections are read-only and non-executable
- Data sections are read-write and non-executable (cannot be executed)
- Hardware-enforced W^X prevents code injection attacks
This matches the ARM implementation philosophy and provides genuine
security improvements on RISC-V S-mode platforms.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
arch/riscv/Kconfig | 16 ++
arch/riscv/Kconfig.socs | 1 -
arch/riscv/boot/uncompress.c | 25 +++
arch/riscv/cpu/Makefile | 1 +
arch/riscv/cpu/mmu.c | 386 +++++++++++++++++++++++++++++++++++++++++++
arch/riscv/cpu/mmu.h | 144 ++++++++++++++++
arch/riscv/include/asm/asm.h | 3 +-
arch/riscv/include/asm/mmu.h | 44 +++++
include/mmu.h | 6 +-
9 files changed, 621 insertions(+), 5 deletions(-)
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index f8c8b38ed6d7fdae48669e6d7b737f695f1c4cc9..1eec3c6c684cfc16f92f612cf45a1511f072948b 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -130,4 +130,20 @@ config RISCV_MULTI_MODE
config RISCV_SBI
def_bool RISCV_S_MODE
+config MMU
+ bool "MMU-based memory protection"
+ help
+ Enable MMU (Memory Management Unit) support for RISC-V S-mode.
+ This provides hardware-enforced W^X (Write XOR Execute) memory
+ protection using page tables (Sv39 for RV64, Sv32 for RV32).
+
+ The PBL sets up page table entries based on ELF segment permissions,
+ ensuring that:
+ - Text sections are read-only and executable
+ - Read-only data sections are read-only and non-executable
+ - Data sections are read-write and non-executable
+
+ Say Y if running in S-mode (supervisor mode) with virtual memory.
+ Say N if running in M-mode or if you don't need memory protection.
+
endmenu
diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs
index 4a3b56b5fff48c86901ed0346be490a6847ac14e..0d9984dd2888e6cab81939e3ee97ef83851362a0 100644
--- a/arch/riscv/Kconfig.socs
+++ b/arch/riscv/Kconfig.socs
@@ -123,7 +123,6 @@ if SOC_ALLWINNER_SUN20I
config BOARD_ALLWINNER_D1
bool "Allwinner D1 Nezha"
select RISCV_S_MODE
- select RISCV_M_MODE
def_bool y
endif
diff --git a/arch/riscv/boot/uncompress.c b/arch/riscv/boot/uncompress.c
index dc0d1fd5d41a88acea8717a7f5fcdb8da3298663..857044e2de32ac9e2c826f131144970332871c3b 100644
--- a/arch/riscv/boot/uncompress.c
+++ b/arch/riscv/boot/uncompress.c
@@ -10,11 +10,14 @@
#include <init.h>
#include <linux/sizes.h>
#include <pbl.h>
+#include <pbl/mmu.h>
#include <asm/barebox-riscv.h>
#include <asm-generic/memory_layout.h>
#include <asm/sections.h>
#include <asm/unaligned.h>
+#include <asm/mmu.h>
#include <asm/irq.h>
+#include <elf.h>
#include <debug_ll.h>
@@ -63,6 +66,15 @@ void __noreturn barebox_pbl_start(unsigned long membase, unsigned long memsize,
free_mem_ptr = riscv_mem_early_malloc(membase, endmem);
free_mem_end_ptr = riscv_mem_early_malloc_end(membase, endmem);
+#ifdef CONFIG_MMU
+ /*
+ * Enable MMU early to enable caching for faster decompression.
+ * This creates an initial identity mapping that will be refined
+ * later based on ELF segments.
+ */
+ mmu_early_enable(membase, memsize, barebox_base);
+#endif
+
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);
@@ -82,6 +94,19 @@ void __noreturn barebox_pbl_start(unsigned long membase, unsigned long memsize,
hang();
}
+ /*
+ * Now that the ELF image is relocated, we know the exact addresses
+ * of all segments. Set up MMU with proper permissions based on
+ * ELF segment flags (PF_R/W/X).
+ */
+#ifdef CONFIG_MMU
+ ret = pbl_mmu_setup_from_elf(&elf, membase, memsize);
+ if (ret) {
+ pr_err("Failed to setup memory protection from ELF: %d\n", ret);
+ hang();
+ }
+#endif
+
barebox = (void *)elf.entry;
pr_debug("jumping to uncompressed image at 0x%p. dtb=0x%p\n", barebox, fdt);
diff --git a/arch/riscv/cpu/Makefile b/arch/riscv/cpu/Makefile
index d79bafc6f142a0060d2a86078f0fb969b298ba98..6bf31b574cd6242df6393fbdc8accc08dceb822a 100644
--- a/arch/riscv/cpu/Makefile
+++ b/arch/riscv/cpu/Makefile
@@ -7,3 +7,4 @@ obj-pbl-$(CONFIG_RISCV_M_MODE) += mtrap.o
obj-pbl-$(CONFIG_RISCV_S_MODE) += strap.o
obj-pbl-y += interrupts.o
endif
+obj-pbl-$(CONFIG_MMU) += mmu.o
diff --git a/arch/riscv/cpu/mmu.c b/arch/riscv/cpu/mmu.c
new file mode 100644
index 0000000000000000000000000000000000000000..6cf4586f364c98dd69105dfa1c558b560755b7d4
--- /dev/null
+++ b/arch/riscv/cpu/mmu.c
@@ -0,0 +1,386 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2026 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+
+#define pr_fmt(fmt) "mmu: " fmt
+
+#include <common.h>
+#include <init.h>
+#include <mmu.h>
+#include <errno.h>
+#include <linux/sizes.h>
+#include <linux/bitops.h>
+#include <asm/sections.h>
+
+#include "mmu.h"
+
+#ifdef __PBL__
+
+/*
+ * Page table storage for early MMU setup in PBL.
+ * Static allocation before BSS is available.
+ */
+static char early_pt_storage[RISCV_EARLY_PAGETABLE_SIZE] __aligned(RISCV_PGSIZE);
+static unsigned int early_pt_idx;
+
+/*
+ * Allocate a page table from the early PBL storage
+ */
+static pte_t *alloc_pte(void)
+{
+ pte_t *pt;
+
+ if ((early_pt_idx + 1) * RISCV_PGSIZE >= RISCV_EARLY_PAGETABLE_SIZE) {
+ pr_err("Out of early page table memory (need more than %d KB)\n",
+ RISCV_EARLY_PAGETABLE_SIZE / 1024);
+ hang();
+ }
+
+ pt = (pte_t *)(early_pt_storage + early_pt_idx * RISCV_PGSIZE);
+ early_pt_idx++;
+
+ /* Clear the page table */
+ memset(pt, 0, RISCV_PGSIZE);
+
+ return pt;
+}
+
+/*
+ * split_pte - Split a megapage/gigapage PTE into a page table
+ * @pte: Pointer to the PTE to split
+ * @level: Current page table level (0-2 for Sv39)
+ *
+ * This function takes a leaf PTE (megapage/gigapage) and converts it into
+ * a page table pointer with 512 entries, each covering 1/512th of the
+ * original range with identical permissions.
+ *
+ * Example: A 2MB megapage at Level 1 becomes a Level 2 page table with
+ * 512 × 4KB pages, all with the same R/W/X attributes.
+ */
+static void split_pte(pte_t *pte, int level)
+{
+ pte_t old_pte = *pte;
+ pte_t *new_table;
+ pte_t phys_base;
+ pte_t attrs;
+ unsigned long granularity;
+ int i;
+
+ /* If already a table pointer (no RWX bits), nothing to do */
+ if (!(*pte & (PTE_R | PTE_W | PTE_X)))
+ return;
+
+ /* Allocate new page table (512 entries × 8 bytes = 4KB) */
+ new_table = alloc_pte();
+
+ /* Extract physical base address from old PTE */
+ phys_base = (old_pte >> PTE_PPN_SHIFT) << RISCV_PGSHIFT;
+
+ /* Extract permission attributes to replicate */
+ attrs = old_pte & (PTE_R | PTE_W | PTE_X | PTE_A | PTE_D | PTE_U | PTE_G);
+
+ /*
+ * Calculate granularity of child level.
+ * Level 0 (1GB) → Level 1 (2MB): granularity = 2MB = 1 << 21
+ * Level 1 (2MB) → Level 2 (4KB): granularity = 4KB = 1 << 12
+ *
+ * Formula: granularity = 1 << (12 + 9 * (Levels - 2 - level))
+ * For Sv39 (3 levels):
+ * level=0: 1 << (12 + 9*1) = 2MB
+ * level=1: 1 << (12 + 9*0) = 4KB
+ */
+ granularity = 1UL << (RISCV_PGSHIFT + RISCV_PGLEVEL_BITS *
+ (RISCV_PGTABLE_LEVELS - 2 - level));
+
+ /* Populate new table: replicate old mapping across 512 entries */
+ for (i = 0; i < RISCV_PTE_ENTRIES; i++) {
+ unsigned long new_phys = phys_base + (i * granularity);
+ pte_t new_pte = ((new_phys >> RISCV_PGSHIFT) << PTE_PPN_SHIFT) |
+ attrs | PTE_V;
+ new_table[i] = new_pte;
+ }
+
+ /*
+ * Replace old leaf PTE with table pointer.
+ * No RWX bits = pointer to next level.
+ */
+ *pte = (((unsigned long)new_table >> RISCV_PGSHIFT) << PTE_PPN_SHIFT) | PTE_V;
+
+ pr_debug("Split level %d PTE at phys=0x%llx (granularity=%lu KB)\n",
+ level, (unsigned long long)phys_base, granularity / 1024);
+}
+
+/*
+ * Get the root page table base
+ */
+static pte_t *get_ttb(void)
+{
+ return (pte_t *)early_pt_storage;
+}
+
+/*
+ * Convert maptype flags to PTE permission bits
+ */
+static unsigned long flags_to_pte(maptype_t flags)
+{
+ unsigned long pte = PTE_V; /* Valid bit always set */
+
+ /*
+ * Map barebox memory types to RISC-V PTE flags:
+ * - ARCH_MAP_CACHED_RWX: read + write + execute (early boot, full RAM access)
+ * - MAP_CODE: read + execute (text sections)
+ * - MAP_CACHED_RO: read only (rodata sections)
+ * - MAP_CACHED: read + write (data/bss sections)
+ * - MAP_UNCACHED: read + write, uncached (device memory)
+ */
+ switch (flags & MAP_TYPE_MASK) {
+ case ARCH_MAP_CACHED_RWX:
+ /* Full access for early boot: R + W + X */
+ pte |= PTE_R | PTE_W | PTE_X;
+ break;
+ case MAP_CACHED_RO:
+ /* Read-only data: R, no W, no X */
+ pte |= PTE_R;
+ break;
+ case MAP_CODE:
+ /* Code: R + X, no W */
+ pte |= PTE_R | PTE_X;
+ break;
+ case MAP_CACHED:
+ case MAP_UNCACHED:
+ default:
+ /* Data or uncached: R + W, no X */
+ pte |= PTE_R | PTE_W;
+ break;
+ }
+
+ /* Set accessed and dirty bits to avoid hardware updates */
+ pte |= PTE_A | PTE_D;
+
+ return pte;
+}
+
+/*
+ * Walk page tables and get/create PTE for given address at specified level
+ */
+static pte_t *walk_pgtable(unsigned long addr, int target_level)
+{
+ pte_t *table = get_ttb();
+ int level;
+
+ for (level = 0; level < target_level; level++) {
+ unsigned int index = VPN(addr, RISCV_PGTABLE_LEVELS - 1 - level);
+ pte_t *pte = &table[index];
+
+ if (!(*pte & PTE_V)) {
+ /* Entry not valid - allocate new page table */
+ pte_t *new_table = alloc_pte();
+ pte_t new_pte = ((unsigned long)new_table >> RISCV_PGSHIFT) << PTE_PPN_SHIFT;
+ new_pte |= PTE_V;
+ *pte = new_pte;
+ table = new_table;
+ } else if (*pte & (PTE_R | PTE_W | PTE_X)) {
+ /* This is a leaf PTE - split it before descending */
+ split_pte(pte, level);
+ /* After split, PTE is now a table pointer - follow it */
+ table = (pte_t *)(((*pte >> PTE_PPN_SHIFT) << RISCV_PGSHIFT));
+ } else {
+ /* Valid non-leaf PTE - follow to next level */
+ table = (pte_t *)(((*pte >> PTE_PPN_SHIFT) << RISCV_PGSHIFT));
+ }
+ }
+
+ return table;
+}
+
+/*
+ * Create a page table entry mapping virt -> phys with given permissions
+ */
+static void create_pte(unsigned long virt, phys_addr_t phys, maptype_t flags)
+{
+ pte_t *table;
+ unsigned int index;
+ pte_t pte;
+
+ /* Walk to leaf level page table */
+ table = walk_pgtable(virt, RISCV_PGTABLE_LEVELS - 1);
+
+ /* Get index for this address at leaf level */
+ index = VPN(virt, 0);
+
+ /* Build PTE: PPN + flags */
+ pte = (phys >> RISCV_PGSHIFT) << PTE_PPN_SHIFT;
+ pte |= flags_to_pte(flags);
+
+ /* Write PTE */
+ table[index] = pte;
+}
+
+/*
+ * create_megapage - Create a 2MB megapage mapping
+ * @virt: Virtual address (should be 2MB-aligned)
+ * @phys: Physical address (should be 2MB-aligned)
+ * @flags: Mapping flags (MAP_CACHED, etc.)
+ *
+ * Creates a leaf PTE at Level 1 covering 2MB. This is identical to a 4KB
+ * PTE except it's placed at Level 1 instead of Level 2, saving page tables.
+ */
+static void create_megapage(unsigned long virt, phys_addr_t phys, maptype_t flags)
+{
+ pte_t *table;
+ unsigned int index;
+ pte_t pte;
+
+ /* Walk to Level 1 (one level above 4KB leaf) */
+ table = walk_pgtable(virt, RISCV_PGTABLE_LEVELS - 2);
+
+ /* Get VPN[1] index for this address at Level 1 */
+ index = VPN(virt, 1);
+
+ /* Build leaf PTE at Level 1: PPN + RWX flags make it a megapage */
+ pte = (phys >> RISCV_PGSHIFT) << PTE_PPN_SHIFT;
+ pte |= flags_to_pte(flags);
+
+ /* Write megapage PTE */
+ table[index] = pte;
+}
+
+/*
+ * pbl_remap_range - Remap a virtual address range with specified permissions
+ *
+ * This is called by the portable pbl/mmu.c code after ELF relocation to set up
+ * proper memory protection based on ELF segment flags.
+ */
+void pbl_remap_range(void *virt, phys_addr_t phys, size_t size, maptype_t flags)
+{
+ unsigned long addr = (unsigned long)virt;
+ unsigned long end = addr + size;
+
+ pr_debug("Remapping 0x%08lx-0x%08lx -> 0x%08llx (flags=0x%x)\n",
+ addr, end, (unsigned long long)phys, flags);
+
+ /* Align to page boundaries */
+ addr &= ~(RISCV_PGSIZE - 1);
+ end = ALIGN(end, RISCV_PGSIZE);
+
+ /* Create page table entries for each page in the range */
+ while (addr < end) {
+ create_pte(addr, phys, flags);
+ addr += RISCV_PGSIZE;
+ phys += RISCV_PGSIZE;
+ }
+
+ /* Flush TLB for the remapped range */
+ sfence_vma();
+}
+
+/*
+ * mmu_early_enable - Set up initial MMU with identity mapping
+ *
+ * Called before barebox decompression to enable caching for faster decompression.
+ * Creates a simple identity map of all RAM with RWX permissions.
+ */
+void mmu_early_enable(unsigned long membase, unsigned long memsize,
+ unsigned long barebox_base)
+{
+ unsigned long addr;
+ unsigned long end = membase + memsize;
+ unsigned long satp;
+
+ pr_debug("Enabling MMU: mem=0x%08lx-0x%08lx barebox=0x%08lx\n",
+ membase, end, barebox_base);
+
+ /* Reset page table allocator */
+ early_pt_idx = 0;
+
+ /* Allocate root page table */
+ (void)alloc_pte();
+
+ pr_debug("Creating flat identity mapping...\n");
+
+ /*
+ * Create a flat identity mapping of the lower address space as uncached.
+ * This ensures I/O devices (UART, etc.) are accessible after MMU is enabled.
+ * RV64: Map lower 4GB using 2MB megapages (2048 entries).
+ * RV32: Map entire 4GB using 4MB superpages (1024 entries in root table).
+ */
+ addr = 0;
+ do {
+ create_megapage(addr, addr, MAP_UNCACHED);
+ addr += RISCV_L1_SIZE;
+ } while (lower_32_bits(addr) != 0); /* Wraps around to 0 after 0xFFFFFFFF */
+
+ /*
+ * Remap RAM as cached with RWX permissions using superpages.
+ * This overwrites the uncached mappings for RAM regions, providing
+ * better performance. Later, pbl_mmu_setup_from_elf() will split
+ * superpages as needed to set fine-grained permissions based on ELF segments.
+ */
+ pr_debug("Remapping RAM 0x%08lx-0x%08lx as cached RWX...\n", membase, end);
+ for (addr = membase; addr < end; addr += RISCV_L1_SIZE)
+ create_megapage(addr, addr, ARCH_MAP_CACHED_RWX);
+
+ pr_debug("Page table setup complete, used %lu KB\n",
+ (early_pt_idx * RISCV_PGSIZE) / 1024);
+
+ /*
+ * Enable MMU by setting SATP CSR:
+ * - MODE field: Sv39 (RV64) or Sv32 (RV32)
+ * - ASID: 0 (no address space ID)
+ * - PPN: physical address of root page table
+ */
+ satp = SATP_MODE | (((unsigned long)get_ttb() >> RISCV_PGSHIFT) & SATP_PPN_MASK);
+
+ pr_debug("Enabling MMU: SATP=0x%08lx\n", satp);
+
+ /* Synchronize before enabling MMU */
+ sfence_vma();
+
+ /* Enable MMU */
+ csr_write(satp, satp);
+
+ /* Synchronize after enabling MMU */
+ sfence_vma();
+
+ pr_debug("MMU enabled with %lu %spages for RAM\n",
+ (memsize / RISCV_L1_SIZE),
+ IS_ENABLED(CONFIG_64BIT) ? "2MB mega" : "4MB super");
+}
+
+#else /* !__PBL__ */
+
+/*
+ * arch_remap_range - Remap a virtual address range (barebox proper)
+ *
+ * This is the non-PBL version used in barebox proper after full relocation.
+ * Currently provides basic remapping support. For full MMU management in
+ * barebox proper, this would need to be extended with:
+ * - Dynamic page table allocation
+ * - Cache flushing for non-cached mappings
+ * - TLB management
+ * - Support for MAP_FAULT (guard pages)
+ */
+int arch_remap_range(void *virt_addr, phys_addr_t phys_addr, size_t size,
+ maptype_t map_type)
+{
+ /*
+ * For now, only allow identity mappings that match the default
+ * cached mapping. This is sufficient for most barebox proper use cases
+ * where the PBL has already set up the basic MMU configuration.
+ *
+ * TODO: Implement full remapping support for:
+ * - Non-identity mappings
+ * - Uncached device memory (MAP_UNCACHED)
+ * - Guard pages (MAP_FAULT)
+ */
+ if (phys_addr == virt_to_phys(virt_addr) &&
+ maptype_is_compatible(map_type, MAP_ARCH_DEFAULT))
+ return 0;
+
+ pr_warn("arch_remap_range: non-identity or non-default mapping not yet supported\n");
+ pr_warn(" virt=0x%p phys=0x%pad size=0x%zx type=0x%x\n",
+ virt_addr, &phys_addr, size, map_type);
+
+ return -ENOSYS;
+}
+
+#endif /* __PBL__ */
diff --git a/arch/riscv/cpu/mmu.h b/arch/riscv/cpu/mmu.h
new file mode 100644
index 0000000000000000000000000000000000000000..dda1e30ad97cd7c5cf99867735c5376c22edf938
--- /dev/null
+++ b/arch/riscv/cpu/mmu.h
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* SPDX-FileCopyrightText: 2026 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix */
+
+#ifndef __RISCV_CPU_MMU_H
+#define __RISCV_CPU_MMU_H
+
+#include <linux/types.h>
+
+/*
+ * RISC-V MMU constants for Sv39 (RV64) and Sv32 (RV32) page tables
+ */
+
+/* Page table configuration */
+#define RISCV_PGSHIFT 12
+#define RISCV_PGSIZE (1UL << RISCV_PGSHIFT) /* 4KB */
+
+#ifdef CONFIG_64BIT
+/* Sv39: 9-bit VPN fields, 512 entries per table */
+#define RISCV_PGLEVEL_BITS 9
+#define RISCV_PGTABLE_ENTRIES 512
+#else
+/* Sv32: 10-bit VPN fields, 1024 entries per table */
+#define RISCV_PGLEVEL_BITS 10
+#define RISCV_PGTABLE_ENTRIES 1024
+#endif
+
+/* Page table entry (PTE) bit definitions */
+#define PTE_V (1UL << 0) /* Valid */
+#define PTE_R (1UL << 1) /* Read */
+#define PTE_W (1UL << 2) /* Write */
+#define PTE_X (1UL << 3) /* Execute */
+#define PTE_U (1UL << 4) /* User accessible */
+#define PTE_G (1UL << 5) /* Global mapping */
+#define PTE_A (1UL << 6) /* Accessed */
+#define PTE_D (1UL << 7) /* Dirty */
+#define PTE_RSW_MASK (3UL << 8) /* Reserved for software */
+
+/* PTE physical page number (PPN) field position */
+#define PTE_PPN_SHIFT 10
+
+#ifdef CONFIG_64BIT
+/*
+ * Sv39: 39-bit virtual addressing, 3-level page tables
+ * Virtual address format: [38:30] VPN[2], [29:21] VPN[1], [20:12] VPN[0], [11:0] offset
+ */
+#define SATP_MODE_SV39 (8UL << 60)
+#define SATP_MODE SATP_MODE_SV39
+#define RISCV_PGTABLE_LEVELS 3
+#define VA_BITS 39
+#else
+/*
+ * Sv32: 32-bit virtual addressing, 2-level page tables
+ * Virtual address format: [31:22] VPN[1], [21:12] VPN[0], [11:0] offset
+ */
+#define SATP_MODE_SV32 (1UL << 31)
+#define SATP_MODE SATP_MODE_SV32
+#define RISCV_PGTABLE_LEVELS 2
+#define VA_BITS 32
+#endif
+
+/* SATP register fields */
+#ifdef CONFIG_64BIT
+#define SATP_PPN_MASK ((1ULL << 44) - 1) /* Physical page number (Sv39) */
+#else
+#define SATP_PPN_MASK ((1UL << 22) - 1) /* Physical page number (Sv32) */
+#endif
+#define SATP_ASID_SHIFT 44
+#define SATP_ASID_MASK (0xFFFFUL << SATP_ASID_SHIFT)
+
+/* Extract VPN (Virtual Page Number) from virtual address */
+#define VPN_MASK ((1UL << RISCV_PGLEVEL_BITS) - 1)
+#define VPN(addr, level) (((addr) >> (RISCV_PGSHIFT + (level) * RISCV_PGLEVEL_BITS)) & VPN_MASK)
+
+/* RISC-V page sizes by level */
+#ifdef CONFIG_64BIT
+/* Sv39: 3-level page tables */
+#define RISCV_L2_SHIFT 30 /* 1GB gigapages (Level 0 in Sv39) */
+#define RISCV_L1_SHIFT 21 /* 2MB megapages (Level 1 in Sv39) */
+#define RISCV_L0_SHIFT 12 /* 4KB pages (Level 2 in Sv39) */
+#else
+/* Sv32: 2-level page tables */
+#define RISCV_L1_SHIFT 22 /* 4MB superpages (Level 0 in Sv32) */
+#define RISCV_L0_SHIFT 12 /* 4KB pages (Level 1 in Sv32) */
+#endif
+
+#ifdef CONFIG_64BIT
+#define RISCV_L2_SIZE (1UL << RISCV_L2_SHIFT) /* 1GB (RV64 only) */
+#endif
+#define RISCV_L1_SIZE (1UL << RISCV_L1_SHIFT) /* 2MB (RV64) or 4MB (RV32) */
+#define RISCV_L0_SIZE (1UL << RISCV_L0_SHIFT) /* 4KB */
+
+/* Number of entries per page table (use RISCV_PGTABLE_ENTRIES instead) */
+#define RISCV_PTE_ENTRIES RISCV_PGTABLE_ENTRIES
+
+/* PTE type - 64-bit on RV64, 32-bit on RV32 */
+#ifdef CONFIG_64BIT
+typedef uint64_t pte_t;
+#else
+typedef uint32_t pte_t;
+#endif
+
+/* Early page table allocation size (PBL) */
+#ifdef CONFIG_64BIT
+/* Sv39: 3 levels, allocate space for root + worst case intermediate tables */
+#define RISCV_EARLY_PAGETABLE_SIZE (64 * 1024) /* 64KB */
+#else
+/* Sv32: 2 levels, smaller allocation */
+#define RISCV_EARLY_PAGETABLE_SIZE (32 * 1024) /* 32KB */
+#endif
+
+#ifndef __ASSEMBLY__
+
+/* CSR access */
+#define csr_read(csr) \
+({ \
+ unsigned long __v; \
+ __asm__ __volatile__ ("csrr %0, " #csr \
+ : "=r" (__v) : \
+ : "memory"); \
+ __v; \
+})
+
+#define csr_write(csr, val) \
+({ \
+ unsigned long __v = (unsigned long)(val); \
+ __asm__ __volatile__ ("csrw " #csr ", %0" \
+ : : "rK" (__v) \
+ : "memory"); \
+})
+
+/* SFENCE.VMA - Synchronize updates to page tables */
+static inline void sfence_vma(void)
+{
+ __asm__ __volatile__ ("sfence.vma" : : : "memory");
+}
+
+static inline void sfence_vma_addr(unsigned long addr)
+{
+ __asm__ __volatile__ ("sfence.vma %0" : : "r" (addr) : "memory");
+}
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __RISCV_CPU_MMU_H */
diff --git a/arch/riscv/include/asm/asm.h b/arch/riscv/include/asm/asm.h
index 9c992a88d858fe6105f16978849f3a564d42b85f..23f60b615ea91e680c57b7b65b868260a761de5e 100644
--- a/arch/riscv/include/asm/asm.h
+++ b/arch/riscv/include/asm/asm.h
@@ -9,7 +9,8 @@
#ifdef __ASSEMBLY__
#define __ASM_STR(x) x
#else
-#define __ASM_STR(x) #x
+#define __ASM_STR_HELPER(x) #x
+#define __ASM_STR(x) __ASM_STR_HELPER(x)
#endif
#if __riscv_xlen == 64
diff --git a/arch/riscv/include/asm/mmu.h b/arch/riscv/include/asm/mmu.h
index 1c2646ebb3393f120ad7208109372fef8bc32e81..63878b4bb4f13287ffe86f86425a167c0beb852b 100644
--- a/arch/riscv/include/asm/mmu.h
+++ b/arch/riscv/include/asm/mmu.h
@@ -3,6 +3,50 @@
#ifndef __ASM_MMU_H
#define __ASM_MMU_H
+#include <linux/types.h>
+
+/*
+ * RISC-V supports memory protection through two mechanisms:
+ * - S-mode: Virtual memory with page tables (MMU)
+ * - M-mode: Physical Memory Protection (PMP) regions
+ */
+
+#if defined(CONFIG_MMU) || defined(CONFIG_RISCV_PMP)
+#define ARCH_HAS_REMAP
+#define MAP_ARCH_DEFAULT MAP_CACHED
+
+/* Architecture-specific memory type flags */
+#define ARCH_MAP_CACHED_RWX MAP_ARCH(2) /* Cached, RWX (early boot) */
+#define ARCH_MAP_FLAG_PAGEWISE (1 << 16) /* Force page-wise mapping */
+
+#ifdef __PBL__
+/*
+ * PBL remap function - used by pbl/mmu.c to apply ELF segment permissions.
+ * Implementation is in arch/riscv/cpu/mmu.c (S-mode) or pmp.c (M-mode).
+ */
+void pbl_remap_range(void *virt, phys_addr_t phys, size_t size, maptype_t flags);
+
+/*
+ * Early MMU/PMP setup - called before decompression for performance.
+ * S-mode: Sets up basic page tables and enables MMU via SATP CSR.
+ * M-mode: Configures initial PMP regions.
+ */
+void mmu_early_enable(unsigned long membase, unsigned long memsize,
+ unsigned long barebox_base);
+#endif /* __PBL__ */
+
+/*
+ * Remap a virtual address range with specified memory type (barebox proper).
+ * Used by the generic remap infrastructure after barebox is fully relocated.
+ * Implementation is in arch/riscv/cpu/mmu.c (S-mode) or pmp.c (M-mode).
+ */
+int arch_remap_range(void *virt_addr, phys_addr_t phys_addr, size_t size,
+ maptype_t map_type);
+
+#else
#define MAP_ARCH_DEFAULT MAP_UNCACHED
+#endif
+
+#include <mmu.h>
#endif /* __ASM_MMU_H */
diff --git a/include/mmu.h b/include/mmu.h
index 37df7b482e1d83e94e61997db6cf9834d8cf7f3c..3c7b5dfa63267e299e12bcbd4f916c974637be0e 100644
--- a/include/mmu.h
+++ b/include/mmu.h
@@ -20,6 +20,8 @@
#define MAP_TYPE_MASK 0xFFFF
#define MAP_ARCH(x) ((u16)~(x))
+#include <asm/mmu.h>
+
/*
* Depending on the architecture the default mapping can be
* cached or uncached. Without ARCH_HAS_REMAP being set this
@@ -27,8 +29,6 @@
*/
#define MAP_DEFAULT MAP_ARCH_DEFAULT
-#include <asm/mmu.h>
-
static inline bool maptype_is_compatible(maptype_t active, maptype_t check)
{
active &= MAP_TYPE_MASK;
@@ -46,7 +46,7 @@ static inline bool maptype_is_compatible(maptype_t active, maptype_t check)
static inline int arch_remap_range(void *virt_addr, phys_addr_t phys_addr,
size_t size, maptype_t map_type)
{
- if (maptype_is_compatible(map_type, MAP_ARCH_DEFAULT) &&
+ if (maptype_is_compatible(map_type, MAP_DEFAULT) &&
phys_addr == virt_to_phys(virt_addr))
return 0;
--
2.47.3
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 12/19] ARM: use relative jumps in exception table
2026-01-05 11:26 ` [PATCH 12/19] ARM: use relative jumps in exception table Sascha Hauer
@ 2026-01-05 11:44 ` Ahmad Fatoum
2026-01-05 12:29 ` Sascha Hauer
0 siblings, 1 reply; 49+ messages in thread
From: Ahmad Fatoum @ 2026-01-05 11:44 UTC (permalink / raw)
To: Sascha Hauer, BAREBOX; +Cc: Claude Sonnet 4.5
Hello Sascha,
On 1/5/26 12:26 PM, Sascha Hauer wrote:
> Create position-independent exception vectors using relative branches
> instead of absolute addresses. This works on ARMv7 onwards which
> supports setting the address of the exception vectors.
>
> New .text_inplace_exceptions section contains PC-relative branches,
> enabling barebox proper to start with MMU already configured using
> ELF segment addresses.
>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Why do we even need arm_fixup_vectors? There should already be
relocation entries emitted for .word symbol lines, so given that we only
install the vector table after relocation, it should be possible to
simply drop arm_fixup_vectors?
Cheers,
Ahmad
> ---
> arch/arm/cpu/exceptions_32.S | 20 ++++++++++++++++++++
> arch/arm/cpu/interrupts_32.c | 3 +--
> arch/arm/cpu/mmu_32.c | 5 +++--
> arch/arm/cpu/no-mmu.c | 11 +----------
> arch/arm/include/asm/sections.h | 1 +
> arch/arm/lib/pbl.lds.S | 6 +++---
> arch/arm/lib32/barebox.lds.S | 4 ++++
> 7 files changed, 33 insertions(+), 17 deletions(-)
>
> diff --git a/arch/arm/cpu/exceptions_32.S b/arch/arm/cpu/exceptions_32.S
> index 235996f7ec296b44261637bc98c1c5ee30a3cbe1..302e4d030d6347e8fdb488db73a0a3a4bb1c0ec7 100644
> --- a/arch/arm/cpu/exceptions_32.S
> +++ b/arch/arm/cpu/exceptions_32.S
> @@ -156,6 +156,26 @@ ENTRY(arm_fixup_vectors)
> ENDPROC(arm_fixup_vectors)
> #endif
>
> +.section .text_inplace_exceptions
> +1: b 1b /* barebox_arm_reset_vector */
> +#ifdef CONFIG_ARM_EXCEPTIONS
> + b undefined_instruction /* undefined instruction */
> + b software_interrupt /* software interrupt (SWI) */
> + b prefetch_abort /* prefetch abort */
> + b data_abort /* data abort */
> +1: b 1b /* (reserved) */
> + b irq /* irq (interrupt) */
> + b fiq /* fiq (fast interrupt) */
> +#else
> +1: b 1b /* undefined instruction */
> +1: b 1b /* software interrupt (SWI) */
> +1: b 1b /* prefetch abort */
> +1: b 1b /* data abort */
> +1: b 1b /* (reserved) */
> +1: b 1b /* irq (interrupt) */
> +1: b 1b /* fiq (fast interrupt) */
> +#endif
> +
> .section .text_exceptions
> .globl extable
> extable:
> diff --git a/arch/arm/cpu/interrupts_32.c b/arch/arm/cpu/interrupts_32.c
> index 185646e38195b2bdc9f0d7e30d53a0506932fa13..6ebcbcd8dc1299c0333da87e496bc57172691fa8 100644
> --- a/arch/arm/cpu/interrupts_32.c
> +++ b/arch/arm/cpu/interrupts_32.c
> @@ -181,7 +181,6 @@ void arm_pbl_init_exceptions(void)
> if (cpu_architecture() < CPU_ARCH_ARMv7)
> return;
>
> - set_vbar((unsigned long)__exceptions_start);
> - arm_fixup_vectors();
> + set_vbar((unsigned long)__inplace_exceptions_start);
> }
> #endif
> diff --git a/arch/arm/cpu/mmu_32.c b/arch/arm/cpu/mmu_32.c
> index 86a55d165ba3cec5154c345a1a3a9cb959f0996f..9ce77078d5e35810f2239d82f29f67e2aa2e4d8e 100644
> --- a/arch/arm/cpu/mmu_32.c
> +++ b/arch/arm/cpu/mmu_32.c
> @@ -629,8 +629,9 @@ void setup_trap_pages(void)
> * First try to use the vectors where they actually are, works
> * on ARMv7 and later.
> */
> - if (!set_vector_table((unsigned long)__exceptions_start)) {
> - arm_fixup_vectors();
> + if (!set_vector_table((unsigned long)__inplace_exceptions_start)) {
> + pr_debug("Using inplace exception vectors at 0x%08lx\n",
> + (unsigned long)__inplace_exceptions_start);
> create_zero_page();
> return;
> }
> diff --git a/arch/arm/cpu/no-mmu.c b/arch/arm/cpu/no-mmu.c
> index c4ef5d1f9d55136d606c244309dbeeb8fd988784..68246d71156c7c84b9faff452cebb37132b83573 100644
> --- a/arch/arm/cpu/no-mmu.c
> +++ b/arch/arm/cpu/no-mmu.c
> @@ -21,8 +21,6 @@
> #include <asm/sections.h>
> #include <asm/cputype.h>
>
> -#define __exceptions_size (__exceptions_stop - __exceptions_start)
> -
> static bool has_vbar(void)
> {
> u32 mainid;
> @@ -41,7 +39,6 @@ static bool has_vbar(void)
>
> static int nommu_v7_vectors_init(void)
> {
> - void *vectors;
> u32 cr;
>
> if (cpu_architecture() < CPU_ARCH_ARMv7)
> @@ -58,13 +55,7 @@ static int nommu_v7_vectors_init(void)
> cr &= ~CR_V;
> set_cr(cr);
>
> - arm_fixup_vectors();
> -
> - vectors = xmemalign(PAGE_SIZE, PAGE_SIZE);
> - memset(vectors, 0, PAGE_SIZE);
> - memcpy(vectors, __exceptions_start, __exceptions_size);
> -
> - set_vbar((unsigned int)vectors);
> + set_vbar((unsigned int)__inplace_exceptions_start);
>
> return 0;
> }
> diff --git a/arch/arm/include/asm/sections.h b/arch/arm/include/asm/sections.h
> index 15b1a6482a5b148284ab47de2db1c2653909da09..bf4fb7b109a7a22d9a298257af23a11b9efe6861 100644
> --- a/arch/arm/include/asm/sections.h
> +++ b/arch/arm/include/asm/sections.h
> @@ -13,6 +13,7 @@ extern char __dynsym_start[];
> extern char __dynsym_end[];
> extern char __exceptions_start[];
> extern char __exceptions_stop[];
> +extern char __inplace_exceptions_start[];
>
> #endif
>
> diff --git a/arch/arm/lib/pbl.lds.S b/arch/arm/lib/pbl.lds.S
> index 9c51f5eb3a3d8256752a78e03fed851c84d92edb..53b21084cff2e3d916cd37485281f2f78166c37d 100644
> --- a/arch/arm/lib/pbl.lds.S
> +++ b/arch/arm/lib/pbl.lds.S
> @@ -53,9 +53,9 @@ SECTIONS
> *(.text_bare_init*)
> __bare_init_end = .;
> . = ALIGN(0x20);
> - __exceptions_start = .;
> - KEEP(*(.text_exceptions*))
> - __exceptions_stop = .;
> + __inplace_exceptions_start = .;
> + KEEP(*(.text_inplace_exceptions*))
> + __inplace_exceptions_stop = .;
> *(.text*)
> }
>
> diff --git a/arch/arm/lib32/barebox.lds.S b/arch/arm/lib32/barebox.lds.S
> index ede6889991f16d00f2bad79dd777ae9b5e639ff4..9b2b65e2f17d62d5134fc367621cce733dcea06a 100644
> --- a/arch/arm/lib32/barebox.lds.S
> +++ b/arch/arm/lib32/barebox.lds.S
> @@ -26,6 +26,10 @@ SECTIONS
> __exceptions_start = .;
> KEEP(*(.text_exceptions*))
> __exceptions_stop = .;
> + . = ALIGN(0x20);
> + __inplace_exceptions_start = .;
> + KEEP(*(.text_inplace_exceptions*))
> + __inplace_exceptions_stop = .;
> *(.text*)
> }
> BAREBOX_BARE_INIT_SIZE
>
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 01/19] elf: Use memcmp to make suitable for PBL
2026-01-05 11:26 ` [PATCH 01/19] elf: Use memcmp to make suitable for PBL Sascha Hauer
@ 2026-01-05 11:46 ` Ahmad Fatoum
0 siblings, 0 replies; 49+ messages in thread
From: Ahmad Fatoum @ 2026-01-05 11:46 UTC (permalink / raw)
To: Sascha Hauer, BAREBOX; +Cc: Claude Sonnet 4.5
On 1/5/26 12:26 PM, Sascha Hauer wrote:
> In preparation of adding ELF support to the PBL use memcmp rather than
> strncmp for checking against the ELF magic. The other possibility would
> be to add strncmp to the PBL, but for the sake of binary size use a
> function that is already there.
>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Reviewed-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
I am still not sure if we want to reuse the ELF code in PBL instead of
duplicating it, but I will read along.
Cheers,
Ahmad
> ---
> common/elf.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/common/elf.c b/common/elf.c
> index c68ea0be3fa668d988b27530644bbb77eb62ff48..692323c6beab2dd8aae9a9e874fb4980152a74e0 100644
> --- a/common/elf.c
> +++ b/common/elf.c
> @@ -201,7 +201,7 @@ static int load_elf_image_segments(struct elf_image *elf)
>
> static int elf_check_image(struct elf_image *elf, void *buf)
> {
> - if (strncmp(buf, ELFMAG, SELFMAG)) {
> + if (memcmp(buf, ELFMAG, SELFMAG)) {
> pr_err("ELF magic not found.\n");
> return -EINVAL;
> }
>
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 04/19] ARM: implement elf_apply_relocations() for ELF relocation support
2026-01-05 11:26 ` [PATCH 04/19] ARM: implement elf_apply_relocations() for ELF " Sascha Hauer
@ 2026-01-05 11:58 ` Ahmad Fatoum
2026-01-05 19:53 ` Sascha Hauer
0 siblings, 1 reply; 49+ messages in thread
From: Ahmad Fatoum @ 2026-01-05 11:58 UTC (permalink / raw)
To: Sascha Hauer, BAREBOX; +Cc: Claude Sonnet 4.5
Hi,
On 1/5/26 12:26 PM, Sascha Hauer wrote:
> Implement architecture-specific ELF relocation handlers for ARM32 and ARM64.
>
> ARM32 implementation (arch/arm/lib32/elf_reloc.c):
> - Handles REL-format relocations (no explicit addend)
> - Supports R_ARM_RELATIVE and R_ARM_ABS32 relocation types
> - Addend is read from the target location
>
> ARM64 implementation (arch/arm/lib64/elf_reloc.c):
> - Handles RELA-format relocations (with explicit addend)
> - Supports R_AARCH64_RELATIVE and R_AARCH64_ABS64 relocation types
> - Addend is provided in relocation entry
>
> Both implementations:
> - Parse PT_DYNAMIC segment to locate relocation tables
> - Validate relocation table format and entry sizes
> - Apply relocations based on the computed load offset
> - Return appropriate errors for unsupported relocation types
>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
> ---
> arch/arm/include/asm/elf.h | 11 +++++
> arch/arm/lib32/Makefile | 1 +
> arch/arm/lib32/elf_reloc.c | 105 +++++++++++++++++++++++++++++++++++++++++++++
> arch/arm/lib64/Makefile | 1 +
> arch/arm/lib64/elf_reloc.c | 105 +++++++++++++++++++++++++++++++++++++++++++++
> 5 files changed, 223 insertions(+)
>
> diff --git a/arch/arm/include/asm/elf.h b/arch/arm/include/asm/elf.h
> index 4043e6fd5b991eb5cccb3fa0ea28d208006ee1fc..cceb92ee1a5f63c37b0e981c263676bd35a261c0 100644
> --- a/arch/arm/include/asm/elf.h
> +++ b/arch/arm/include/asm/elf.h
> @@ -36,6 +36,17 @@ typedef struct user_fp elf_fpregset_t;
> #define R_ARM_THM_CALL 10
> #define R_ARM_THM_JUMP24 30
>
> +/* Additional relocation types for dynamic linking */
> +#define R_ARM_RELATIVE 23
This is already defined in arch/arm/cpu/common.c, you can guess where
this is going ;)
> +#define R_ARM_GLOB_DAT 21
> +#define R_ARM_JUMP_SLOT 22
These two are not used.
> +
> +#define R_AARCH64_NONE 0
> +#define R_AARCH64_ABS64 257
> +#define R_AARCH64_RELATIVE 1027
> +#define R_AARCH64_GLOB_DAT 1025
> +#define R_AARCH64_JUMP_SLOT 1026
Likewise, only R_AARCH64_JUMP_SLOT and R_AARCH64_GLOB_DAT are not used.
> +/*
> + * Parse dynamic section and extract relocation info for ARM32
> + */
> +static int parse_dynamic_section(struct elf_image *elf, Elf32_Dyn *dyn,
> + Elf32_Rel **rel_out, u64 *relsz_out)
> +{
> + Elf32_Rel *rel = NULL;
If we define ELF_CLASS depending on CONFIG_ARM32/CONFIG_ARM64, we can
write a generic parse_dynamic_section() that applies to all architectures.
In this case, we would just collect both REL and RELA in the switch and
just use what's available.
> +int elf_apply_relocations(struct elf_image *elf, void *dyn_seg)
> +{
We already have relocate_to_current_adr(). If we change it to call
a new relocate_image():
relocate_image(get_runtime_offset(),
runtime_addr(__rel_dyn_start),
runtime_addr(__rel_dyn_end),
runtime_addr(__dynsym_start),
runtime_addr(__dynsym_end));
Then we could use relocate_image() in generic code instead of
duplicating it per architecture.
> +/*
> + * Parse dynamic section and extract relocation info for ARM64
> + */
> +static int parse_dynamic_section(struct elf_image *elf, Elf64_Dyn *dyn,
> + Elf64_Rela **rela_out, u64 *relasz_out)
Same comment as for ARM32. I believe this can go into generic code.
> + case R_AARCH64_ABS64:
> + /* B(P) = S + A */
> + *fixup_addr = base + rela->r_addend;
> + break;
What did you do to get R_AARCH64_ABS64 relocations? We don't handle them
in relocate_to_current_adr() and they shouldn't have been generated.
Thanks,
Ahmad
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 09/19] PBL: allow to link ELF image into PBL
2026-01-05 11:26 ` [PATCH 09/19] PBL: allow to link ELF image into PBL Sascha Hauer
@ 2026-01-05 12:11 ` Ahmad Fatoum
0 siblings, 0 replies; 49+ messages in thread
From: Ahmad Fatoum @ 2026-01-05 12:11 UTC (permalink / raw)
To: Sascha Hauer, BAREBOX; +Cc: Claude Sonnet 4.5
Hello Sascha,
On 1/5/26 12:26 PM, Sascha Hauer wrote:
> Some architectures want to link the barebox proper ELF image into the
> PBL. Allow that and provide a Kconfig option to select the ELF image.
>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
> images/Makefile | 18 ++++++++++++++++--
> images/piggy.S | 4 ++++
> pbl/Kconfig | 8 ++++++++
> 3 files changed, 28 insertions(+), 2 deletions(-)
>
> diff --git a/images/Makefile b/images/Makefile
> index 448479ee8cf355a771ce63314ce64a631544f488..8d4fd1504ab64d4f4d1cc19f2b36cff9939964e3 100644
> --- a/images/Makefile
> +++ b/images/Makefile
> @@ -138,7 +138,13 @@ $(obj)/%.itb: $(obj)/%.its FORCE
> $(obj)/%.fit: $(obj)/$$(FILE_$$(@F)) $(dtstree)/dtbs-list FORCE
> $(call if_changed,fit)
>
> -$(obj)/piggy.o: $(obj)/barebox.z FORCE
> +ifeq ($(CONFIG_PBL_IMAGE_ELF),)
> +PIGGY_IMAGE := $(obj)/barebox.z
> +else
> +PIGGY_IMAGE := $(obj)/barebox.elf.z
> +endif
Heh, in my attempt, I had an elfcompress host tool that compressed every
segment separately and then the PBL decompressed every segment on its
own directly to the load address.
Having things laid out properly from the get go is certainly neater.
Why not call it barebox.z still though and change BAREBOX_PROPER to be
vmbarebox? This would
> +
> +$(obj)/piggy.o: ${PIGGY_IMAGE} FORCE
>
> $(obj)/sha_sum.o: $(obj)/barebox.sha.bin FORCE
>
> @@ -154,6 +160,14 @@ $(obj)/barebox.sum: $(obj)/barebox.z FORCE
> $(obj)/barebox.z: $(obj)/../$(BAREBOX_PROPER) FORCE
> $(call if_changed,$(suffix_y))
>
> +# barebox.elf.z - compressed barebox ELF binary
> +# ----------------------------------------------------------------
> +# Prevent the %.elf pattern rule from building ../barebox.elf
> +$(obj)/../barebox.elf: ;
> +
> +$(obj)/barebox.elf.z: $(obj)/../barebox.elf FORCE
> + $(call if_changed,$(suffix_y))
As you noticed youtself, barebox.elf is not a good name, because it's
generated outside this makefile, but still has an %.elf: rule.
How about vmbarebox instead?
> +config PBL_IMAGE_ELF
> + bool
should select ELF.
Cheers,
Ahmad
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 10/19] mmu: add MAP_CACHED_RO mapping type
2026-01-05 11:26 ` [PATCH 10/19] mmu: add MAP_CACHED_RO mapping type Sascha Hauer
@ 2026-01-05 12:14 ` Ahmad Fatoum
0 siblings, 0 replies; 49+ messages in thread
From: Ahmad Fatoum @ 2026-01-05 12:14 UTC (permalink / raw)
To: Sascha Hauer, BAREBOX; +Cc: Claude Sonnet 4.5
On 1/5/26 12:26 PM, Sascha Hauer wrote:
> ARM32 and ARM64 have ARCH_MAP_CACHED_RO. We'll move parts of the MMU
> initialization to generic code later, so add a new mapping type to
> include/mmu.h.
>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Reviewed-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
> ---
> arch/arm/cpu/mmu-common.c | 4 ++--
> arch/arm/cpu/mmu-common.h | 3 +--
> arch/arm/cpu/mmu_32.c | 4 ++--
> arch/arm/cpu/mmu_64.c | 2 +-
> include/mmu.h | 3 ++-
> 5 files changed, 8 insertions(+), 8 deletions(-)
>
> diff --git a/arch/arm/cpu/mmu-common.c b/arch/arm/cpu/mmu-common.c
> index b3d9e9579686c0612068c6281420cb6ccaaf4ee8..3208139fdd24e89cf4c76e27477da23da169f164 100644
> --- a/arch/arm/cpu/mmu-common.c
> +++ b/arch/arm/cpu/mmu-common.c
> @@ -22,7 +22,7 @@ const char *map_type_tostr(maptype_t map_type)
>
> switch (map_type) {
> case ARCH_MAP_CACHED_RWX: return "RWX";
> - case ARCH_MAP_CACHED_RO: return "RO";
> + case MAP_CACHED_RO: return "RO";
> case MAP_CACHED: return "CACHED";
> case MAP_UNCACHED: return "UNCACHED";
> case MAP_CODE: return "CODE";
> @@ -161,7 +161,7 @@ static void mmu_remap_memory_banks(void)
> setup_trap_pages();
>
> remap_range((void *)code_start, code_size, MAP_CODE);
> - remap_range((void *)rodata_start, rodata_size, ARCH_MAP_CACHED_RO);
> + remap_range((void *)rodata_start, rodata_size, MAP_CACHED_RO);
> }
>
> static int mmu_init(void)
> diff --git a/arch/arm/cpu/mmu-common.h b/arch/arm/cpu/mmu-common.h
> index a111e15a21b479b5ffa2ea8973e2ad189e531925..b42c421ffde8ebba84b17c6311b735f7759dc69b 100644
> --- a/arch/arm/cpu/mmu-common.h
> +++ b/arch/arm/cpu/mmu-common.h
> @@ -12,7 +12,6 @@
> #include <linux/bits.h>
>
> #define ARCH_MAP_CACHED_RWX MAP_ARCH(2)
> -#define ARCH_MAP_CACHED_RO MAP_ARCH(3)
>
> #define ARCH_MAP_FLAG_PAGEWISE BIT(31)
>
> @@ -32,7 +31,7 @@ static inline maptype_t arm_mmu_maybe_skip_permissions(maptype_t map_type)
> switch (map_type & MAP_TYPE_MASK) {
> case MAP_CODE:
> case MAP_CACHED:
> - case ARCH_MAP_CACHED_RO:
> + case MAP_CACHED_RO:
> return ARCH_MAP_CACHED_RWX;
> default:
> return map_type;
> diff --git a/arch/arm/cpu/mmu_32.c b/arch/arm/cpu/mmu_32.c
> index 63c412873ec8fdb047a3323e773648cb03d5757b..97c7107290ce95ddb21a322a5d0e74f3d324c528 100644
> --- a/arch/arm/cpu/mmu_32.c
> +++ b/arch/arm/cpu/mmu_32.c
> @@ -304,7 +304,7 @@ static uint32_t get_pte_flags(maptype_t map_type)
> switch (map_type & MAP_TYPE_MASK) {
> case ARCH_MAP_CACHED_RWX:
> return PTE_FLAGS_CACHED_V7_RWX;
> - case ARCH_MAP_CACHED_RO:
> + case MAP_CACHED_RO:
> return PTE_FLAGS_CACHED_RO_V7;
> case MAP_CACHED:
> return PTE_FLAGS_CACHED_V7;
> @@ -320,7 +320,7 @@ static uint32_t get_pte_flags(maptype_t map_type)
> }
> } else {
> switch (map_type & MAP_TYPE_MASK) {
> - case ARCH_MAP_CACHED_RO:
> + case MAP_CACHED_RO:
> case MAP_CODE:
> return PTE_FLAGS_CACHED_RO_V4;
> case ARCH_MAP_CACHED_RWX:
> diff --git a/arch/arm/cpu/mmu_64.c b/arch/arm/cpu/mmu_64.c
> index f22fcb5f8ea4db9843c8447ac5bf4f8cf29bb59c..afb3d2d7efd0bc7ecde1177d1544f54d751b5dc1 100644
> --- a/arch/arm/cpu/mmu_64.c
> +++ b/arch/arm/cpu/mmu_64.c
> @@ -159,7 +159,7 @@ static unsigned long get_pte_attrs(maptype_t map_type)
> return attrs_xn() | MEM_ALLOC_WRITECOMBINE;
> case MAP_CODE:
> return CACHED_MEM | PTE_BLOCK_RO;
> - case ARCH_MAP_CACHED_RO:
> + case MAP_CACHED_RO:
> return attrs_xn() | CACHED_MEM | PTE_BLOCK_RO;
> case ARCH_MAP_CACHED_RWX:
> return CACHED_MEM;
> diff --git a/include/mmu.h b/include/mmu.h
> index 29992ae1d6c644f4eaa6519dae2b57055333bff6..53603b7956c229b4c715c57b19d0398931eb2d6b 100644
> --- a/include/mmu.h
> +++ b/include/mmu.h
> @@ -9,9 +9,10 @@
> #define MAP_CACHED 1
> #define MAP_FAULT 2
> #define MAP_CODE 3
> +#define MAP_CACHED_RO 4
>
> #ifdef CONFIG_ARCH_HAS_DMA_WRITE_COMBINE
> -#define MAP_WRITECOMBINE 4
> +#define MAP_WRITECOMBINE 5
> #else
> #define MAP_WRITECOMBINE MAP_UNCACHED
> #endif
>
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 11/19] mmu: introduce pbl_remap_range()
2026-01-05 11:26 ` [PATCH 11/19] mmu: introduce pbl_remap_range() Sascha Hauer
@ 2026-01-05 12:15 ` Ahmad Fatoum
2026-01-06 8:50 ` Ahmad Fatoum
0 siblings, 1 reply; 49+ messages in thread
From: Ahmad Fatoum @ 2026-01-05 12:15 UTC (permalink / raw)
To: Sascha Hauer, BAREBOX; +Cc: Claude Sonnet 4.5
On 1/5/26 12:26 PM, Sascha Hauer wrote:
> Add PBL-specific memory remapping function that always uses page-wise
> mapping (ARCH_MAP_FLAG_PAGEWISE) for fine-grained permissions on
> adjacent ELF segments with different protection requirements.
>
> Wraps arch-specific __arch_remap_range() for ARMv7 (4KB pages) and
> ARMv8 (page tables with BBM). Needed for ELF segment permission setup.
>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Reviewed-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
> ---
> arch/arm/cpu/mmu_32.c | 7 +++++++
> arch/arm/cpu/mmu_64.c | 8 ++++++++
> include/mmu.h | 3 +++
> 3 files changed, 18 insertions(+)
>
> diff --git a/arch/arm/cpu/mmu_32.c b/arch/arm/cpu/mmu_32.c
> index 97c7107290ce95ddb21a322a5d0e74f3d324c528..86a55d165ba3cec5154c345a1a3a9cb959f0996f 100644
> --- a/arch/arm/cpu/mmu_32.c
> +++ b/arch/arm/cpu/mmu_32.c
> @@ -435,6 +435,13 @@ static void early_remap_range(u32 addr, size_t size, maptype_t map_type)
> __arch_remap_range((void *)addr, addr, size, map_type);
> }
>
> +void pbl_remap_range(void *virt_addr, phys_addr_t phys_addr, size_t size,
> + maptype_t map_type)
> +{
> + __arch_remap_range(virt_addr, phys_addr, size,
> + map_type | ARCH_MAP_FLAG_PAGEWISE);
> +}
> +
> static bool pte_is_cacheable(uint32_t pte, int level)
> {
> return (level == 2 && (pte & PTE_CACHEABLE)) ||
> diff --git a/arch/arm/cpu/mmu_64.c b/arch/arm/cpu/mmu_64.c
> index afb3d2d7efd0bc7ecde1177d1544f54d751b5dc1..63faaa46703697e527eae766392e7ea7ae186b3d 100644
> --- a/arch/arm/cpu/mmu_64.c
> +++ b/arch/arm/cpu/mmu_64.c
> @@ -282,6 +282,14 @@ static void early_remap_range(uint64_t addr, size_t size, maptype_t map_type)
> __arch_remap_range(addr, addr, size, map_type, false);
> }
>
> +void pbl_remap_range(void *virt_addr, phys_addr_t phys_addr, size_t size,
> + maptype_t map_type)
> +{
> + __arch_remap_range((uint64_t)virt_addr, phys_addr,
> + (uint64_t)size, map_type | ARCH_MAP_FLAG_PAGEWISE,
> + true);
> +}
> +
> int arch_remap_range(void *virt_addr, phys_addr_t phys_addr, size_t size, maptype_t map_type)
> {
> map_type = arm_mmu_maybe_skip_permissions(map_type);
> diff --git a/include/mmu.h b/include/mmu.h
> index 53603b7956c229b4c715c57b19d0398931eb2d6b..37df7b482e1d83e94e61997db6cf9834d8cf7f3c 100644
> --- a/include/mmu.h
> +++ b/include/mmu.h
> @@ -64,6 +64,9 @@ static inline bool arch_can_remap(void)
> }
> #endif
>
> +void pbl_remap_range(void *virt_addr, phys_addr_t phys_addr, size_t size,
> + maptype_t map_type);
> +
> static inline int remap_range(void *start, size_t size, maptype_t map_type)
> {
> return arch_remap_range(start, virt_to_phys(start), size, map_type);
>
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 08/19] Makefile: add barebox.elf build target
2026-01-05 11:26 ` [PATCH 08/19] Makefile: add barebox.elf build target Sascha Hauer
@ 2026-01-05 12:22 ` Ahmad Fatoum
2026-01-05 15:43 ` Sascha Hauer
0 siblings, 1 reply; 49+ messages in thread
From: Ahmad Fatoum @ 2026-01-05 12:22 UTC (permalink / raw)
To: Sascha Hauer, BAREBOX; +Cc: Claude Sonnet 4.5
Hi,
On 1/5/26 12:26 PM, Sascha Hauer wrote:
> Add a build target to create barebox.elf, which provides an ELF format
> version of barebox that can be used for debugging or alternative boot
> scenarios.
>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
I think conceptually, the Co-Authored-By should be before your S-o-b.
> ---
> Makefile | 16 +++++++++++++++-
> 1 file changed, 15 insertions(+), 1 deletion(-)
>
> diff --git a/Makefile b/Makefile
> index 75e14383bcdcf987d091442adba9b053af28eae7..c1aa6935abe5c65156b812d4a0b21c6d488038ae 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -850,6 +850,9 @@ all: barebox-flash-images
> endif
>
> all: $(symlink-y)
> +ifeq ($(CONFIG_PBL_IMAGE)-$(CONFIG_PBL_IMAGE_NO_PIGGY),y-)
> +all: barebox.elf
> +endif
Why not:
ifdef CONFIG_PBL_IMAGE_ELF
export BAREBOX_PROPER ?= vmbarebox # or barebox.elf
else
export BAREBOX_PROPER ?= barebox.bin
endif
>
> .SECONDEXPANSION:
> $(symlink-y): $$(or $$(SYMLINK_DEP_$$(@F)),$$(SYMLINK_TARGET_$$(@F))) FORCE
> @@ -1091,6 +1094,17 @@ barebox.fit: images/barebox-$(CONFIG_ARCH_LINUX_NAME).fit
> barebox.srec: barebox
> $(OBJCOPY) -O srec $< $@
>
> +OBJCOPYFLAGS_barebox.elf = --strip-debug --strip-unneeded \
--strip-section-headers goes the farthest I think.
> + --remove-section=.comment \
> + --remove-section=.note \
--remote-section=.note*
> + --remove-section=.note.gnu.build-id
--remove-section=.gnu.hash?
> +
> +quiet_cmd_objcopy_elf = OBJCOPY $@
> + cmd_objcopy_elf = $(OBJCOPY) $(OBJCOPYFLAGS_barebox.elf) $< $@
> +
> +barebox.elf: barebox FORCE
> + $(call if_changed,objcopy_elf)
There is already cmd_objcopy. You should be able to reuse it with
OBJCOPYFLAGS_$@ = $your_options.
Sidenote: We should add --error-rwx-segments depending on
CONFIG_PBL_IMAGE_ELF.
Cheers,
Ahmad
> +
> quiet_cmd_barebox_proper__ = CC $@
> cmd_barebox_proper__ = $(CC) -r -o $@ -Wl,--whole-archive $(BAREBOX_OBJS)
>
> @@ -1388,7 +1402,7 @@ CLEAN_FILES += barebox System.map include/generated/barebox_default_env.h \
> .tmp_version .tmp_barebox* barebox.bin barebox.map \
> .tmp_kallsyms* barebox.ldr compile_commands.json \
> .tmp_barebox.o barebox.o barebox-flash-image \
> - barebox.srec barebox.s5p barebox.ubl \
> + barebox.srec barebox.elf barebox.s5p barebox.ubl \
> barebox.uimage \
> barebox.efi barebox.canon-a1100.bin
>
>
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 15/19] ARM: link ELF image into PBL
2026-01-05 11:26 ` [PATCH 15/19] ARM: link ELF image into PBL Sascha Hauer
@ 2026-01-05 12:27 ` Ahmad Fatoum
0 siblings, 0 replies; 49+ messages in thread
From: Ahmad Fatoum @ 2026-01-05 12:27 UTC (permalink / raw)
To: Sascha Hauer, BAREBOX; +Cc: Claude Sonnet 4.5
Hi,
On 1/5/26 12:26 PM, Sascha Hauer wrote:
> Instead of linking the raw binary barebox proper image into the PBL link
> the ELF image into the PBL. With this barebox proper starts with a properly
> linked and fully initialized C environment, so the calls to
> relocate_to_adr() and setup_c() can be removed from barebox proper.
>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
> arch/arm/Kconfig | 2 ++
> arch/arm/cpu/start.c | 4 ----
> arch/arm/cpu/uncompress.c | 32 ++++++++++++++++++++++++++------
> 3 files changed, 28 insertions(+), 10 deletions(-)
>
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index afa6de3cd0b145e42755e3ca2d7a13c8a69ff1d3..60975083b32f3127f982c526c6cf30ffe3e45924 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -17,6 +17,8 @@ config ARM
> select HAVE_ARCH_BOOTM_OFTREE
> select HW_HAS_PCI
> select ARCH_HAS_DMA_WRITE_COMBINE
> + select ELF
> + select PBL_IMAGE_ELF
> default y
>
> config ARCH_LINUX_NAME
> diff --git a/arch/arm/cpu/start.c b/arch/arm/cpu/start.c
> index f7d4507e71588ba5e241b24b952d55e2a4b0f794..71a12abdd0941889617e6b63063304d0e4522c81 100644
> --- a/arch/arm/cpu/start.c
> +++ b/arch/arm/cpu/start.c
> @@ -139,10 +139,6 @@ __noreturn __prereloc void barebox_non_pbl_start(unsigned long membase,
void NAKED __prereloc __section(.text_entry) start should no longer be
needed as well as the .text_entry section itself.
> if (IS_ENABLED(CONFIG_CPU_V7))
> armv7_hyp_install();
>
> - relocate_to_adr(barebox_base);
> -
> - setup_c();
> -
> barrier();
You can also drop the barrier while at it.
> + struct elf_image elf;
> + int ret;
Move to start of function?
> +
> + ret = elf_open_binary_into(&elf, (void *)barebox_base);
> + if (ret) {
> + pr_err("Failed to open ELF binary: %d\n", ret);
> + hang();
panic?
> + }
> +
> + ret = elf_load_inplace(&elf);
> + if (ret) {
> + pr_err("Failed to relocate ELF: %d\n", ret);
> + hang();
panic?
> + }
> +
> + pr_debug("ELF entry point: 0x%llx\n", elf.entry);
>
> if (IS_ENABLED(CONFIG_THUMB2_BAREBOX))
> - barebox = (void *)(barebox_base + 1);
> + barebox = (void *)(unsigned long)(elf.entry | 1);
In my testing the ELF file already had an odd address. Was it different
for you?
> else
> - barebox = (void *)barebox_base;
> + barebox = (void *)(unsigned long)elf.entry;
I have patches that I can submit afterwards that share some of the ELF
info with barebox proper. This allows us to avoid recomputing addresses
relative to endmem again and just use the info extract from the ELF.
Cheers,
Ahmad
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 12/19] ARM: use relative jumps in exception table
2026-01-05 11:44 ` Ahmad Fatoum
@ 2026-01-05 12:29 ` Sascha Hauer
2026-01-05 12:31 ` Ahmad Fatoum
0 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 12:29 UTC (permalink / raw)
To: Ahmad Fatoum; +Cc: BAREBOX, Claude Sonnet 4.5
On Mon, Jan 05, 2026 at 12:44:13PM +0100, Ahmad Fatoum wrote:
> Hello Sascha,
>
> On 1/5/26 12:26 PM, Sascha Hauer wrote:
> > Create position-independent exception vectors using relative branches
> > instead of absolute addresses. This works on ARMv7 onwards which
> > supports setting the address of the exception vectors.
> >
> > New .text_inplace_exceptions section contains PC-relative branches,
> > enabling barebox proper to start with MMU already configured using
> > ELF segment addresses.
> >
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
>
> Why do we even need arm_fixup_vectors? There should already be
> relocation entries emitted for .word symbol lines, so given that we only
> install the vector table after relocation, it should be possible to
> simply drop arm_fixup_vectors?
No, just verified that there are no relocation entries. I don't know if
there's some other trick to get them relocated.
Sascha
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 12/19] ARM: use relative jumps in exception table
2026-01-05 12:29 ` Sascha Hauer
@ 2026-01-05 12:31 ` Ahmad Fatoum
0 siblings, 0 replies; 49+ messages in thread
From: Ahmad Fatoum @ 2026-01-05 12:31 UTC (permalink / raw)
To: Sascha Hauer; +Cc: BAREBOX, Claude Sonnet 4.5
Hi,
On 1/5/26 1:29 PM, Sascha Hauer wrote:
> On Mon, Jan 05, 2026 at 12:44:13PM +0100, Ahmad Fatoum wrote:
>> Hello Sascha,
>>
>> On 1/5/26 12:26 PM, Sascha Hauer wrote:
>>> Create position-independent exception vectors using relative branches
>>> instead of absolute addresses. This works on ARMv7 onwards which
>>> supports setting the address of the exception vectors.
>>>
>>> New .text_inplace_exceptions section contains PC-relative branches,
>>> enabling barebox proper to start with MMU already configured using
>>> ELF segment addresses.
>>>
>>> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
>>
>> Why do we even need arm_fixup_vectors? There should already be
>> relocation entries emitted for .word symbol lines, so given that we only
>> install the vector table after relocation, it should be possible to
>> simply drop arm_fixup_vectors?
>
> No, just verified that there are no relocation entries. I don't know if
> there's some other trick to get them relocated.
Interesting. I will compare later and see what's different in my patch
stack.
Cheers,
Ahmad
>
> Sascha
>
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 16/19] ARM: PBL: setup MMU with proper permissions from ELF segments
2026-01-05 11:26 ` [PATCH 16/19] ARM: PBL: setup MMU with proper permissions from ELF segments Sascha Hauer
@ 2026-01-05 12:58 ` Ahmad Fatoum
0 siblings, 0 replies; 49+ messages in thread
From: Ahmad Fatoum @ 2026-01-05 12:58 UTC (permalink / raw)
To: Sascha Hauer, BAREBOX; +Cc: Claude Sonnet 4.5
Hi,
On 1/5/26 12:26 PM, Sascha Hauer wrote:
> /*
> - * Early mmu init will have mapped everything but the initial memory area
> - * (excluding final OPTEE_SIZE bytes) uncached. We have now discovered
> - * all memory banks, so let's map all pages, excluding reserved memory areas
> - * and barebox text area cacheable.
> - *
> - * This code will become much less complex once we switch over to using
> - * CONFIG_MEMORY_ATTRIBUTES for MMU as well.
> + * PBL has already set up the MMU with proper permissions based on
> + * ELF segment information. We only need to set up trap pages for
> + * exception handling.
This is incorrect. PBL only know about initial memory and not about
other memory banks and reserved memory regions. We need to keep this code...
> */
> - for_each_memory_bank(bank) {
> - struct resource *rsv;
> - resource_size_t pos;
> -
> - pos = bank->start;
> -
> - /* Skip reserved regions */
> - for_each_reserved_region(bank, rsv) {
> - if (pos != rsv->start)
> - remap_range_end_sans_text(pos, rsv->start, MAP_CACHED);
> - pos = rsv->end + 1;
> - }
> -
> - remap_range_end_sans_text(pos, bank->start + bank->size, MAP_CACHED);
> - }
> -
> - /* Do this while interrupt vectors are still writable */
> setup_trap_pages();
> -
> - remap_range((void *)code_start, code_size, MAP_CODE);
> - remap_range((void *)rodata_start, rodata_size, MAP_CACHED_RO);
These two lines however are fine to remove now.
> + /*
> + * Now that the ELF image is relocated, we know the exact addresses
> + * of all segments. Set up MMU with proper permissions based on
> + * ELF segment flags (PF_R/W/X).
> + */
> + if (IS_ENABLED(CONFIG_MMU)) {
> + ret = pbl_mmu_setup_from_elf(&elf, membase, memsize);
I think it might be cleaner to move the ELF logic to pbl/elf.c given
that it's common for both ARM and RISC-V?
> +/**
> + * pbl_mmu_setup_from_elf() - Configure MMU using ELF segment information
> + * @elf: ELF image structure from elf_open_binary_into()
> + * @membase: Base address of RAM
> + * @memsize: Size of RAM
> + *
> + * This function sets up the MMU with proper permissions based on ELF
> + * segment flags. It should be called after elf_load_inplace() has
> + * relocated the barebox image.
> + *
> + * Segment permissions are mapped as follows:
> + * PF_R | PF_X -> Read-only + executable (text)
> + * PF_R | PF_W -> Read-write (data, bss)
> + * PF_R -> Read-only (rodata)
> + *
> + * Return: 0 on success, negative error code on failure
> + */
> +int pbl_mmu_setup_from_elf(struct elf_image *elf, unsigned long membase,
> + unsigned long memsize);
> +
> +#endif /* __PBL_MMU_H */
> diff --git a/pbl/Makefile b/pbl/Makefile
> index f66391be7b2898388425657f54afcd6e4c72e3db..b78124cdcd2a4690be11d5503006723252b4904f 100644
> --- a/pbl/Makefile
> +++ b/pbl/Makefile
> @@ -9,3 +9,4 @@ pbl-$(CONFIG_HAVE_IMAGE_COMPRESSION) += decomp.o
> pbl-$(CONFIG_LIBFDT) += fdt.o
> pbl-$(CONFIG_PBL_CONSOLE) += console.o
> obj-pbl-y += handoff-data.o
> +obj-pbl-$(CONFIG_MMU) += mmu.o
> diff --git a/pbl/mmu.c b/pbl/mmu.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..7a8f254a7bd67eccaab715832930c5d4134eb288
> --- /dev/null
> +++ b/pbl/mmu.c
> @@ -0,0 +1,111 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +// SPDX-FileCopyrightText: 2025 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
> +
> +#define pr_fmt(fmt) "pbl-mmu: " fmt
> +
> +#include <common.h>
> +#include <elf.h>
> +#include <mmu.h>
> +#include <pbl/mmu.h>
> +#include <asm/mmu.h>
> +#include <linux/bits.h>
> +#include <linux/sizes.h>
> +
> +/*
> + * Map ELF segment permissions (p_flags) to architecture MMU flags
> + */
> +static unsigned int elf_flags_to_mmu_flags(u32 p_flags)
> +{
> + bool readable = p_flags & PF_R;
> + bool writable = p_flags & PF_W;
> + bool executable = p_flags & PF_X;
> +
> + if (readable && writable) {
> + /* Data, BSS: Read-write, cached, non-executable */
> + return MAP_CACHED;
> + } else if (readable && executable) {
> + /* Text: Read-only, cached, executable */
> + return MAP_CODE;
> + } else if (readable) {
> + /* Read-only data: Read-only, cached, non-executable */
> + return MAP_CACHED_RO;
Nitpick: A switch (p_flags & (PF_R | PF_W | PF_X)) might look neater.
> + } else {
> + /*
> + * Unusual: segment with no read permission.
> + * Map as uncached, non-executable for safety.
> + */
> + pr_warn("Segment with unusual permissions: flags=0x%x\n", p_flags);
> + return MAP_UNCACHED;
> + }
> +}
> +
> +int pbl_mmu_setup_from_elf(struct elf_image *elf, unsigned long membase,
> + unsigned long memsize)
> +{
> + void *phdr;
> + int i;
> + int phnum = elf_hdr_e_phnum(elf, elf->hdr_buf);
> + size_t phoff = elf_hdr_e_phoff(elf, elf->hdr_buf);
> + size_t phentsize = elf_size_of_phdr(elf);
> +
> + pr_debug("Setting up MMU from ELF segments\n");
> + pr_debug("ELF entry point: 0x%llx\n", elf->entry);
> + pr_debug("ELF loaded at: 0x%p - 0x%p\n", elf->low_addr, elf->high_addr);
> +
> + /*
> + * Iterate through all PT_LOAD segments and set up MMU permissions
> + * based on the segment's p_flags
> + */
> + for (i = 0; i < phnum; i++) {
> + phdr = elf->hdr_buf + phoff + i * phentsize;
> +
> + if (elf_phdr_p_type(elf, phdr) != PT_LOAD)
> + continue;
> +
> + u64 p_vaddr = elf_phdr_p_vaddr(elf, phdr);
> + u64 p_memsz = elf_phdr_p_memsz(elf, phdr);
> + u32 p_flags = elf_phdr_p_flags(elf, phdr);
> +
> + /*
> + * Calculate actual address after relocation.
> + * For ET_EXEC: reloc_offset is 0, use p_vaddr directly
> + * For ET_DYN: reloc_offset adjusts virtual to actual address
> + */
> + unsigned long addr = p_vaddr + elf->reloc_offset;
> + unsigned long size = p_memsz;
> + unsigned long segment_end = addr + size;
Add a check to skip non-alloc segments? We could use that to include
info that's only used in PBL and discarded after (e.g. dynsym table).
> +
> + /* Validate segment is within available memory */
> + if (segment_end < addr || /* overflow check */
> + addr < membase ||
> + segment_end > membase + memsize) {
> + pr_err("Segment %d outside memory bounds\n", i);
> + return -EINVAL;
> + }
> +
> + /* Validate alignment - warn and round if needed */
> + if (!IS_ALIGNED(addr, SZ_4K) || !IS_ALIGNED(size, SZ_4K)) {
s/SZ_4K/PAGE_SIZE/
> + pr_warn("Segment %d not page-aligned, rounding\n", i);
> + size = ALIGN(size, SZ_4K);
Don't you get a warning every time for PT_DYNAMIC this way?
Cheers,
Ahmad
> + }
> +
> + unsigned int mmu_flags = elf_flags_to_mmu_flags(p_flags);
> +
> + pr_debug("Segment %d: addr=0x%08lx size=0x%08lx flags=0x%x [%c%c%c] -> mmu_flags=0x%x\n",
> + i, addr, size, p_flags,
> + (p_flags & PF_R) ? 'R' : '-',
> + (p_flags & PF_W) ? 'W' : '-',
> + (p_flags & PF_X) ? 'X' : '-',
> + mmu_flags);
> +
> + /*
> + * Remap this segment with proper permissions.
> + * Use page-wise mapping to allow different permissions for
> + * different segments even if they're nearby.
> + */
> + pbl_remap_range((void *)addr, addr, size, mmu_flags);
> + }
> +
> + pr_debug("MMU setup from ELF complete\n");
> + return 0;
> +}
>
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 14/19] ARM: linker script: create separate PT_LOAD segments for text, rodata, and data
2026-01-05 11:26 ` [PATCH 14/19] ARM: linker script: create separate PT_LOAD segments for text, rodata, and data Sascha Hauer
@ 2026-01-05 13:11 ` Ahmad Fatoum
2026-01-05 23:01 ` Sascha Hauer
0 siblings, 1 reply; 49+ messages in thread
From: Ahmad Fatoum @ 2026-01-05 13:11 UTC (permalink / raw)
To: Sascha Hauer, BAREBOX; +Cc: Claude Sonnet 4.5
On 1/5/26 12:26 PM, Sascha Hauer wrote:
> Fix the linker scripts to generate three distinct PT_LOAD segments with
> correct permissions instead of combining .rodata with .data.
>
> Before this fix, the linker auto-generated only two PT_LOAD segments:
> 1. Text segment (PF_R|PF_X)
> 2. Data segment (PF_R|PF_W) - containing .rodata, .data, .bss, etc.
Did it though? Why did we get the RWX linker warnings then?
> This caused .rodata to be mapped with write permissions when
> pbl_mmu_setup_from_elf() set up MMU permissions based on ELF segments,
> defeating the W^X protection that commit d9ccb0cf14 intended to provide.
What commit hash is this?
> With explicit PHDRS directives, we now generate three segments:
> 1. text segment (PF_R|PF_X): .text and related code sections
> 2. rodata segment (PF_R): .rodata and unwind tables
> 3. data segment (PF_R|PF_W): .data, .bss, and related sections
Not directly related, but this is as good a place as any to ask the
question: How is zero-padding implemented? If the file size is shorter
than the memory size, the loader is supposed to zero-fill, which is used
for BSS zeroing for example. Now if you load the ELF in place, we can't
obviously zero-fill. This is fine, but we should have a check somewhere
to make absolutely sure that we don't end up with too short segments.
> -#define BAREBOX_RELOCATION_TABLE \
> - .rel_dyn_start : { *(.__rel_dyn_start) } \
> - .BAREBOX_RELOCATION_TYPE.dyn : { *(.BAREBOX_RELOCATION_TYPE*) } \
> - .rel_dyn_end : { *(.__rel_dyn_end) } \
> - .__dynsym_start : { *(.__dynsym_start) } \
> - .dynsym : { *(.dynsym) } \
> - .__dynsym_end : { *(.__dynsym_end) }
> +#define BAREBOX_RELOCATION_TABLE(PHDR) \
> + .rel_dyn_start : { *(.__rel_dyn_start) } PHDR \
> + .BAREBOX_RELOCATION_TYPE.dyn : { *(.BAREBOX_RELOCATION_TYPE*) } PHDR \
> + .rel_dyn_end : { *(.__rel_dyn_end) } PHDR \
> + .__dynsym_start : { *(.__dynsym_start) } PHDR \
> + .dynsym : { *(.dynsym) } PHDR \
> + .__dynsym_end : { *(.__dynsym_end) } PHDR
Quoting
https://www.sourceware.org/binutils/docs/ld/Output-Section-Phdr.html:
If a section is assigned to one or more segments, then all subsequent
allocated sections will be assigned to those segments as well, unless
they use an explicitly :phdr modifier. You can use :NONE to tell the
linker to not put the section in any segment at all.
This whole hunk is thus unnecessary as it follows the data section.
> +PHDRS
> +{
> + text PT_LOAD FLAGS(5); /* PF_R | PF_X */
> + rodata PT_LOAD FLAGS(4); /* PF_R */
> + data PT_LOAD FLAGS(6); /* PF_R | PF_W */
> + dynamic PT_DYNAMIC FLAGS(6); /* PF_R | PF_W */
I believe we don't need PF_W for PT_DYNAMIC. You could move it one up to
merge with rodata.
> +}
> +
> SECTIONS
> {
> . = 0x0;
> - .image_start : { *(.__image_start) }
> + .image_start : { *(.__image_start) } :text
>
> . = ALIGN(4);
>
> - ._text : { *(._text) }
> + ._text : { *(._text) } :text
> .text :
> {
> _stext = .;
> @@ -31,7 +40,7 @@ SECTIONS
> KEEP(*(.text_inplace_exceptions*))
> __inplace_exceptions_stop = .;
> *(.text*)
> - }
> + } :text
> BAREBOX_BARE_INIT_SIZE
>
> . = ALIGN(4096);
> @@ -39,7 +48,7 @@ SECTIONS
> .rodata : {
> *(.rodata*)
> RO_DATA_SECTION
> - }
> + } :rodata
>
> #ifdef CONFIG_ARM_UNWIND
> /*
> @@ -50,12 +59,12 @@ SECTIONS
> __start_unwind_idx = .;
> *(.ARM.exidx*)
> __stop_unwind_idx = .;
> - }
> + } :rodata
> .ARM.unwind_tab : {
> __start_unwind_tab = .;
> *(.ARM.extab*)
> __stop_unwind_tab = .;
> - }
> + } :rodata
> #endif
> . = ALIGN(4096);
> __end_rodata = .;
> @@ -65,19 +74,21 @@ SECTIONS
> . = ALIGN(4);
> .data : { *(.data*)
> CONSTRUCTORS
> - }
> + } :data
> +
> + .dynamic : { *(.dynamic) } :data :dynamic
As mentioned above. s/:data/:rodata/ and move up?
>
> . = .;
>
> - BAREBOX_RELOCATION_TABLE
> + BAREBOX_RELOCATION_TABLE(:data)
>
> _edata = .;
> - .image_end : { *(.__image_end) }
> + .image_end : { *(.__image_end) } :data
>
> . = ALIGN(4);
> - .__bss_start : { *(.__bss_start) }
> - .bss : { *(.bss*) }
> - .__bss_stop : { *(.__bss_stop) }
> + .__bss_start : { *(.__bss_start) } :data
> + .bss : { *(.bss*) } :data
> + .__bss_stop : { *(.__bss_stop) } :data
Side-effect of having the decompressor take care of zeroing BSS is a big
size increase for CONFIG_IMAGE_COMPRESSION_NONE. I think that's
acceptable, but it's worth a comment here why we don't have a separate
BSS segment (or make bss NOALLOC).
FYI, I have patches to get rid of MAX_BSS_SIZE on top of PBL ELF loader
support, which I will submit once this support is merged.
Cheers,
Ahmad
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 17/19] riscv: link ELF image into PBL
2026-01-05 11:26 ` [PATCH 17/19] riscv: link ELF image into PBL Sascha Hauer
@ 2026-01-05 13:12 ` Ahmad Fatoum
0 siblings, 0 replies; 49+ messages in thread
From: Ahmad Fatoum @ 2026-01-05 13:12 UTC (permalink / raw)
To: Sascha Hauer, BAREBOX; +Cc: Claude Sonnet 4.5
Hi,
On 1/5/26 12:26 PM, Sascha Hauer wrote:
> Instead of linking the raw binary barebox proper image into the PBL link
> the ELF image into the PBL. With this barebox proper starts with a properly
> linked and fully initialized C environment, so the calls to
> relocate_to_adr() and setup_c() can be removed from barebox proper.
>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
> arch/riscv/Kconfig | 2 ++
> arch/riscv/boot/start.c | 4 ----
> arch/riscv/boot/uncompress.c | 16 +++++++++++++++-
> 3 files changed, 17 insertions(+), 5 deletions(-)
>
> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> index 96d013d8514ac12de5c34a426262d85f8cf021b9..f8c8b38ed6d7fdae48669e6d7b737f695f1c4cc9 100644
> --- a/arch/riscv/Kconfig
> +++ b/arch/riscv/Kconfig
> @@ -17,6 +17,8 @@ config RISCV
> select HAS_KALLSYMS
> select RISCV_TIMER if RISCV_SBI
> select HW_HAS_PCI
> + select ELF
IMO should be selected by PBL_IMAGE_ELF instead.
> + select PBL_IMAGE_ELF
> select HAVE_ARCH_BOARD_GENERIC_DT
> select HAVE_ARCH_BOOTM_OFTREE
>
> diff --git a/arch/riscv/boot/start.c b/arch/riscv/boot/start.c
> index 5091340c8a374fc360ab732ba01ec8516e82a83d..874f4e72ae46fe7435c9defedceaf5b4e9662b55 100644
> --- a/arch/riscv/boot/start.c
> +++ b/arch/riscv/boot/start.c
> @@ -123,10 +123,6 @@ void barebox_non_pbl_start(unsigned long membase, unsigned long memsize,
> 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();
barrier() can go away as well.
>
> irq_init_vector(riscv_mode());
> diff --git a/arch/riscv/boot/uncompress.c b/arch/riscv/boot/uncompress.c
> index 84142acf9c66fe1fcceb6ae63d15ac078ccddee7..dc0d1fd5d41a88acea8717a7f5fcdb8da3298663 100644
> --- a/arch/riscv/boot/uncompress.c
> +++ b/arch/riscv/boot/uncompress.c
> @@ -32,6 +32,8 @@ void __noreturn barebox_pbl_start(unsigned long membase, unsigned long memsize,
> unsigned long barebox_base;
> void *pg_start, *pg_end;
> unsigned long pc = get_pc();
> + struct elf_image elf;
> + int ret;
>
> irq_init_vector(riscv_mode());
>
> @@ -68,7 +70,19 @@ void __noreturn barebox_pbl_start(unsigned long membase, unsigned long memsize,
>
> sync_caches_for_execution();
>
> - barebox = (void *)barebox_base;
> + ret = elf_open_binary_into(&elf, (void *)barebox_base);
> + if (ret) {
> + pr_err("Failed to open ELF binary: %d\n", ret);
> + hang();
panic()
> + }
> +
> + ret = elf_load_inplace(&elf);
> + if (ret) {
> + pr_err("Failed to relocate ELF: %d\n", ret);
> + hang();
> + }
panic()
Cheers,
Ahmad
> +
> + barebox = (void *)elf.entry;
>
> pr_debug("jumping to uncompressed image at 0x%p. dtb=0x%p\n", barebox, fdt);
>
>
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 06/19] elf: implement elf_load_inplace()
2026-01-05 11:26 ` [PATCH 06/19] elf: implement elf_load_inplace() Sascha Hauer
@ 2026-01-05 13:37 ` Ahmad Fatoum
2026-01-05 22:42 ` Sascha Hauer
0 siblings, 1 reply; 49+ messages in thread
From: Ahmad Fatoum @ 2026-01-05 13:37 UTC (permalink / raw)
To: Sascha Hauer, BAREBOX; +Cc: Claude Sonnet 4.5
Hi,
On 1/5/26 12:26 PM, Sascha Hauer wrote:
> Implement elf_load_inplace() to apply dynamic relocations to an ELF binary
> that is already loaded in memory. Unlike elf_load(), this function does not
> allocate memory or copy segments - it only modifies the existing image in
> place.
>
> This is useful for self-relocating loaders or when the ELF has been loaded
> by external means (e.g., firmware or another bootloader).
Nice. This is more elegant than what I came up with (compressing every
segment separately). :)
> For ET_DYN (position-independent) binaries, the relocation offset is
> calculated relative to the first executable PT_LOAD segment (.text section),
> taking into account the difference between the segment's virtual address
> and its file offset.
While this may be true for barebox proper, I think in the general case,
we need to iterate over all segments and take the lowest address.
The first executable PT_LOAD shouldn't have a special significance in
this case.
>
> The entry point is also adjusted to point to the relocated image.
>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
> ---
> common/elf.c | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> include/elf.h | 8 ++++
> 2 files changed, 160 insertions(+)
>
> diff --git a/common/elf.c b/common/elf.c
> index fc2949c285ebb0c0740c68c551926da8d0bb8637..565b283b694773727ef77917cfd8c1d4ee83a8d1 100644
> --- a/common/elf.c
> +++ b/common/elf.c
> @@ -531,3 +531,155 @@ void elf_close(struct elf_image *elf)
>
> free(elf);
> }
> +
> +static void *elf_find_dynamic_inplace(struct elf_image *elf)
const void *
> +{
> + void *buf = elf->hdr_buf;
> + void *phdr = buf + elf_hdr_e_phoff(elf, buf);
> + int i;
> +
> + for (i = 0; i < elf_hdr_e_phnum(elf, buf); i++) {
> + if (elf_phdr_p_type(elf, phdr) == PT_DYNAMIC) {
> + u64 offset = elf_phdr_p_offset(elf, phdr);
> + /* For in-place binary, PT_DYNAMIC is at hdr_buf + offset */
> + return elf->hdr_buf + offset;
> + }
> + phdr += elf_size_of_phdr(elf);
> + }
> +
> + return NULL; /* No PT_DYNAMIC segment */
> +}
> +
> +/**
> + * elf_load_inplace() - Apply dynamic relocations to an ELF binary in place
> + * @elf: ELF image previously opened with elf_open_binary()
> + *
> + * This function applies dynamic relocations to an ELF binary that is already
> + * loaded at its target address in memory. Unlike elf_load(), this does not
> + * allocate memory or copy segments - it only modifies the existing image.
> + *
> + * This is useful for self-relocating loaders or when the ELF has been loaded
> + * by external means (e.g., loaded by firmware or another bootloader).
> + *
> + * The ELF image must have been previously opened with elf_open_binary().
> + *
> + * For ET_DYN (position-independent) binaries, the relocation offset is
> + * calculated relative to the first executable PT_LOAD segment (.text section).
> + *
> + * For ET_EXEC binaries, no relocation is applied as they are expected to
> + * be at their link-time addresses.
> + *
> + * Returns: 0 on success, negative error code on failure
> + */
> +int elf_load_inplace(struct elf_image *elf)
> +{
> + void *dyn_seg;
> + void *buf, *phdr;
> + void *elf_buf;
> + int i, ret;
> +
> + buf = elf->hdr_buf;
> + elf_buf = elf->hdr_buf;
> +
> + /*
> + * First pass: Clear BSS segments (p_memsz > p_filesz).
> + * This must be done before relocations as uninitialized data
> + * must be zeroed per C standard.
> + */
> + phdr = buf + elf_hdr_e_phoff(elf, buf);
> + for (i = 0; i < elf_hdr_e_phnum(elf, buf); i++) {
> + if (elf_phdr_p_type(elf, phdr) == PT_LOAD) {
> + u64 p_offset = elf_phdr_p_offset(elf, phdr);
> + u64 p_filesz = elf_phdr_p_filesz(elf, phdr);
> + u64 p_memsz = elf_phdr_p_memsz(elf, phdr);
> +
> + /* Clear BSS (uninitialized data) */
> + if (p_filesz < p_memsz) {
> + void *bss_start = elf_buf + p_offset + p_filesz;
> + size_t bss_size = p_memsz - p_filesz;
> + memset(bss_start, 0x00, bss_size);
How can this be done in-place? If this is padding to get to a page
boundary, I would assume it's already zero. If it goes beyond a page,
this will overwrite follow-up segments wouldn't it?
I also find bss_ an unforunate name here as this is applied to all segments.
> + if (elf->type == ET_DYN) {
> + u64 text_vaddr = 0;
> + u64 text_offset = 0;
> + bool found_text = false;
> +
> + /* Find first executable PT_LOAD segment (.text) */
As mentioned, we should rather get the lowest address across all segments.
> + /* Apply architecture-specific relocations */
> + ret = elf_apply_relocations(elf, dyn_seg);
Should I upstream my relocate_image changes that reuse the
relocate_to_current_adr() code and you rebase on top?
> + if (ret) {
> + pr_err("In-place relocation failed: %d\n", ret);
> + goto out;
return ret; directly here and elsewhere in this function.
Cheers,
Ahmad
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 18/19] riscv: linker script: create separate PT_LOAD segments for text, rodata, and data
2026-01-05 11:26 ` [PATCH 18/19] riscv: linker script: create separate PT_LOAD segments for text, rodata, and data Sascha Hauer
@ 2026-01-05 13:40 ` Ahmad Fatoum
0 siblings, 0 replies; 49+ messages in thread
From: Ahmad Fatoum @ 2026-01-05 13:40 UTC (permalink / raw)
To: Sascha Hauer, BAREBOX; +Cc: Claude Sonnet 4.5
Hi,
On 1/5/26 12:26 PM, Sascha Hauer wrote:
> - if (dyns == NULL)
> - die("No dynamic section found");
> + if (dyns == NULL) {
> + /* No PT_DYNAMIC segment found - binary may not need prelinking.
> + * This can happen with statically-linked relocatable binaries
> + * that handle relocations differently. Exit successfully.
> + */
> + return;
> + }
We use the script only for barebox and we can't do without relocations
there. Is there a particular reason for think hunk or can it be dropped?
Thanks,
Ahmad
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 19/19] riscv: add ELF segment-based memory protection with MMU
2026-01-05 11:27 ` [PATCH 19/19] riscv: add ELF segment-based memory protection with MMU Sascha Hauer
@ 2026-01-05 13:58 ` Ahmad Fatoum
0 siblings, 0 replies; 49+ messages in thread
From: Ahmad Fatoum @ 2026-01-05 13:58 UTC (permalink / raw)
To: Sascha Hauer, BAREBOX, Marco Felsch, Stefan Kerkmann; +Cc: Claude Sonnet 4.5
Hi Sascha,
On 1/5/26 12:27 PM, Sascha Hauer wrote:
> Enable hardware-enforced W^X (Write XOR Execute) memory protection through
> ELF segment-based permissions using the RISC-V MMU.
>
> This implementation provides memory protection for RISC-V S-mode using
> Sv39 (RV64) or Sv32 (RV32) page tables.
>
> Linker Script Changes:
> - Add PHDRS directives to pbl.lds.S and barebox.lds.S
> - Create three separate PT_LOAD segments with proper permissions:
> * text segment (FLAGS(5) = PF_R|PF_X): code sections
> * rodata segment (FLAGS(4) = PF_R): read-only data
> * data segment (FLAGS(6) = PF_R|PF_W): data and BSS
> - Add 4K alignment between segments for page-granular protection
>
> S-mode MMU Implementation (mmu.c):
> - Implement page table walking for Sv39/Sv32
> - pbl_remap_range(): remap segments with ELF-derived permissions
> - mmu_early_enable(): create identity mapping and enable SATP CSR
> - Map ELF flags to PTE bits:
> * MAP_CODE → PTE_R | PTE_X (read + execute)
> * MAP_CACHED_RO → PTE_R (read only)
> * MAP_CACHED → PTE_R | PTE_W (read + write)
>
> Integration:
> - Update uncompress.c to call mmu_early_enable() before decompression
> (enables caching for faster decompression)
> - Call pbl_mmu_setup_from_elf() after ELF relocation to apply final
> segment-based permissions
> - Uses portable pbl/mmu.c infrastructure to parse PT_LOAD segments
>
> Configuration:
> - Add CONFIG_MMU option (default y for RISCV_S_MODE)
> - Update asm/mmu.h with ARCH_HAS_REMAP and function declarations
>
> Security Benefits:
> - Text sections are read-only and executable (cannot be modified)
> - Read-only data sections are read-only and non-executable
> - Data sections are read-write and non-executable (cannot be executed)
> - Hardware-enforced W^X prevents code injection attacks
>
> This matches the ARM implementation philosophy and provides genuine
> security improvements on RISC-V S-mode platforms.
>
> 🤖 Generated with [Claude Code](https://claude.com/claude-code)
>
> Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
> arch/riscv/Kconfig | 16 ++
> arch/riscv/Kconfig.socs | 1 -
> arch/riscv/boot/uncompress.c | 25 +++
> arch/riscv/cpu/Makefile | 1 +
> arch/riscv/cpu/mmu.c | 386 +++++++++++++++++++++++++++++++++++++++++++
> arch/riscv/cpu/mmu.h | 144 ++++++++++++++++
> arch/riscv/include/asm/asm.h | 3 +-
> arch/riscv/include/asm/mmu.h | 44 +++++
> include/mmu.h | 6 +-
> 9 files changed, 621 insertions(+), 5 deletions(-)
>
> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> index f8c8b38ed6d7fdae48669e6d7b737f695f1c4cc9..1eec3c6c684cfc16f92f612cf45a1511f072948b 100644
> --- a/arch/riscv/Kconfig
> +++ b/arch/riscv/Kconfig
> @@ -130,4 +130,20 @@ config RISCV_MULTI_MODE
> config RISCV_SBI
> def_bool RISCV_S_MODE
>
> +config MMU
> + bool "MMU-based memory protection"
> + help
> + Enable MMU (Memory Management Unit) support for RISC-V S-mode.
> + This provides hardware-enforced W^X (Write XOR Execute) memory
> + protection using page tables (Sv39 for RV64, Sv32 for RV32).
> +
> + The PBL sets up page table entries based on ELF segment permissions,
> + ensuring that:
> + - Text sections are read-only and executable
> + - Read-only data sections are read-only and non-executable
> + - Data sections are read-write and non-executable
> +
> + Say Y if running in S-mode (supervisor mode) with virtual memory.
> + Say N if running in M-mode or if you don't need memory protection.
> +
> endmenu
> diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs
> index 4a3b56b5fff48c86901ed0346be490a6847ac14e..0d9984dd2888e6cab81939e3ee97ef83851362a0 100644
> --- a/arch/riscv/Kconfig.socs
> +++ b/arch/riscv/Kconfig.socs
> @@ -123,7 +123,6 @@ if SOC_ALLWINNER_SUN20I
> config BOARD_ALLWINNER_D1
> bool "Allwinner D1 Nezha"
> select RISCV_S_MODE
> - select RISCV_M_MODE
I don't know why this board select both S- and M-Mode, but maybe you can
explain why it drops the latter of them?
> +#ifdef CONFIG_MMU
if (IS_ENABLED()) or stubs in header?
> + /*
> + * Enable MMU early to enable caching for faster decompression.
> + * This creates an initial identity mapping that will be refined
> + * later based on ELF segments.
> + */
> + mmu_early_enable(membase, memsize, barebox_base);
> +#endif
> +
> 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);
>
> @@ -82,6 +94,19 @@ void __noreturn barebox_pbl_start(unsigned long membase, unsigned long memsize,
> hang();
> }
>
> + /*
> + * Now that the ELF image is relocated, we know the exact addresses
> + * of all segments. Set up MMU with proper permissions based on
> + * ELF segment flags (PF_R/W/X).
> + */
> +#ifdef CONFIG_MMU
> + ret = pbl_mmu_setup_from_elf(&elf, membase, memsize);
> + if (ret) {
> + pr_err("Failed to setup memory protection from ELF: %d\n", ret);
> + hang();
> + }
> +#endif
> +
> barebox = (void *)elf.entry;
>
> pr_debug("jumping to uncompressed image at 0x%p. dtb=0x%p\n", barebox, fdt);
> diff --git a/arch/riscv/cpu/Makefile b/arch/riscv/cpu/Makefile
> index d79bafc6f142a0060d2a86078f0fb969b298ba98..6bf31b574cd6242df6393fbdc8accc08dceb822a 100644
> --- a/arch/riscv/cpu/Makefile
> +++ b/arch/riscv/cpu/Makefile
> @@ -7,3 +7,4 @@ obj-pbl-$(CONFIG_RISCV_M_MODE) += mtrap.o
> obj-pbl-$(CONFIG_RISCV_S_MODE) += strap.o
> obj-pbl-y += interrupts.o
> endif
> +obj-pbl-$(CONFIG_MMU) += mmu.o
> diff --git a/arch/riscv/cpu/mmu.c b/arch/riscv/cpu/mmu.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..6cf4586f364c98dd69105dfa1c558b560755b7d4
> --- /dev/null
> +++ b/arch/riscv/cpu/mmu.c
> @@ -0,0 +1,386 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +// SPDX-FileCopyrightText: 2026 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
> +
> +#define pr_fmt(fmt) "mmu: " fmt
> +
> +#include <common.h>
> +#include <init.h>
> +#include <mmu.h>
> +#include <errno.h>
> +#include <linux/sizes.h>
> +#include <linux/bitops.h>
> +#include <asm/sections.h>
> +
> +#include "mmu.h"
> +
> +#ifdef __PBL__
Drop #ifdef and build file only for pbl- and move stub into header?
I won't dig deeper into review here for v1, but you may want to compare
against the RISC-V MMU implementation here:
https://github.com/AndreyLalaev/barebox/tree/riscv-mmu
> +/*
> + * Convert maptype flags to PTE permission bits
> + */
> +static unsigned long flags_to_pte(maptype_t flags)
> +{
> + unsigned long pte = PTE_V; /* Valid bit always set */
> +
> + /*
> + * Map barebox memory types to RISC-V PTE flags:
> + * - ARCH_MAP_CACHED_RWX: read + write + execute (early boot, full RAM access)
> + * - MAP_CODE: read + execute (text sections)
> + * - MAP_CACHED_RO: read only (rodata sections)
> + * - MAP_CACHED: read + write (data/bss sections)
> + * - MAP_UNCACHED: read + write, uncached (device memory)
> + */
> + switch (flags & MAP_TYPE_MASK) {
> + case ARCH_MAP_CACHED_RWX:
> + /* Full access for early boot: R + W + X */
> + pte |= PTE_R | PTE_W | PTE_X;
> + break;
> + case MAP_CACHED_RO:
> + /* Read-only data: R, no W, no X */
> + pte |= PTE_R;
> + break;
> + case MAP_CODE:
> + /* Code: R + X, no W */
> + pte |= PTE_R | PTE_X;
> + break;
> + case MAP_CACHED:
> + case MAP_UNCACHED:
The real world happened and there is a sizable amount of RISC-V silicon
that optimized cost by sacrificing cache coherency, including the
Starfive and Nezha we currently support. Each implements it a different
way and neither supports the Svpbmt extension...
> +#ifndef __ASSEMBLY__
> +
> +/* CSR access */
> +#define csr_read(csr) \
> +({ \
> + unsigned long __v; \
> + __asm__ __volatile__ ("csrr %0, " #csr \
> + : "=r" (__v) : \
> + : "memory"); \
> + __v; \
> +})
> +
> +#define csr_write(csr, val) \
> +({ \
> + unsigned long __v = (unsigned long)(val); \
> + __asm__ __volatile__ ("csrw " #csr ", %0" \
> + : : "rK" (__v) \
> + : "memory"); \
> +})
These duplicate <asm/csr.h> I think.
> - if (maptype_is_compatible(map_type, MAP_ARCH_DEFAULT) &&
> + if (maptype_is_compatible(map_type, MAP_DEFAULT) &&
spurious change?
Cheers,
Ahmad
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 03/19] elf: add dynamic relocation support
2026-01-05 11:26 ` [PATCH 03/19] elf: add dynamic relocation support Sascha Hauer
@ 2026-01-05 14:05 ` Ahmad Fatoum
0 siblings, 0 replies; 49+ messages in thread
From: Ahmad Fatoum @ 2026-01-05 14:05 UTC (permalink / raw)
To: Sascha Hauer, BAREBOX; +Cc: Claude Sonnet 4.5
On 1/5/26 12:26 PM, Sascha Hauer wrote:
> Add support for applying dynamic relocations to ELF binaries. This allows
> loading ET_DYN (position-independent) binaries and ET_EXEC binaries at
> custom load addresses.
>
> Key changes:
> - Add elf_image.reloc_offset to track offset between vaddr and load address
> - Implement elf_compute_load_offset() to calculate relocation offset
> - Add elf_set_load_address() API to specify custom load address
> - Implement elf_find_dynamic_segment() to locate PT_DYNAMIC
> - Add elf_relocate() to apply relocations
> - Provide weak default elf_apply_relocations() stub for unsupported architectures
> - Add ELF dynamic section accessors
>
> The relocation offset type is unsigned long to properly handle pointer
> arithmetic and avoid casting issues.
>
> Architecture-specific implementations should override the weak
> elf_apply_relocations() function to handle their relocation types.
>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
> ---
> + if (elf->reloc_offset) {
> + u64 p_vaddr = elf_phdr_p_vaddr(elf, phdr);
> + dst = (void *)(unsigned long)(elf->reloc_offset + p_vaddr);
> + } else {
> + dst = (void *)(unsigned long)elf_phdr_p_paddr(elf, phdr);
> + }
This stanza repeats multiple times. Maybe factor out into a helper that
takes elf and phdr as arguments?
Cheers,
Ahmad
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations
2026-01-05 11:26 [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Sascha Hauer
` (18 preceding siblings ...)
2026-01-05 11:27 ` [PATCH 19/19] riscv: add ELF segment-based memory protection with MMU Sascha Hauer
@ 2026-01-05 14:08 ` Ahmad Fatoum
2026-01-05 16:47 ` Sascha Hauer
19 siblings, 1 reply; 49+ messages in thread
From: Ahmad Fatoum @ 2026-01-05 14:08 UTC (permalink / raw)
To: Sascha Hauer, BAREBOX; +Cc: Claude Sonnet 4.5
Hi,
On 1/5/26 12:26 PM, Sascha Hauer wrote:
> Until now we linked the raw barebox proper binary into the PBL which
> comes with a number of disadvantages. We rely on self-modifying code
> to in barebox proper (relocate_to_current_adr()) and have no initialized
> bss segment (setup_c()). Also we can only mark the .text and .rodata as
> readonly during runtime of barebox proper.
>
> This series overcomes this by linking a ELF image into the PBL. This
> image is properly layed out, linked and initialized in the PBL. With
> this barebox proper has a proper C environment and text/rodata
> protection from the start.
>
> As a bonus this series also adds initial MMU support for RISCV, also
> based on loading the ELF image and configuring the MMU from the PBL.
>
> This series also marks my start into AI assisted programming as you can
> see in the Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
> and 🤖 Generated with [Claude Code](https://claude.com/claude-code)
> tags. Without it I wouldn't have started this series during my xmas
> break, but with it it was actually quite fun to do; it felt like having
> a programming team which I just had to delegate new tasks to while
> having fun with my family myself ;)
Did you do size comparisons to determine how much this series affects
binary size? I found that compressing every segment separately resulted
in a smaller binary size even after addition of ELF parsing to PBL, but
I used a purpose-built ELF loader unlike to what you did here.
Cheers,
Ahmad
>
> Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
> Sascha Hauer (19):
> elf: Use memcmp to make suitable for PBL
> elf: build for PBL as well
> elf: add dynamic relocation support
> ARM: implement elf_apply_relocations() for ELF relocation support
> riscv: implement elf_apply_relocations() for ELF relocation support
> elf: implement elf_load_inplace()
> elf: create elf_open_binary_into()
> Makefile: add barebox.elf build target
> PBL: allow to link ELF image into PBL
> mmu: add MAP_CACHED_RO mapping type
> mmu: introduce pbl_remap_range()
> ARM: use relative jumps in exception table
> ARM: exceptions: make in-binary exception table const
> ARM: linker script: create separate PT_LOAD segments for text, rodata, and data
> ARM: link ELF image into PBL
> ARM: PBL: setup MMU with proper permissions from ELF segments
> riscv: link ELF image into PBL
> riscv: linker script: create separate PT_LOAD segments for text, rodata, and data
> riscv: add ELF segment-based memory protection with MMU
>
> Makefile | 16 +-
> arch/arm/Kconfig | 2 +
> arch/arm/cpu/exceptions_32.S | 54 +++---
> arch/arm/cpu/interrupts_32.c | 41 +++-
> arch/arm/cpu/mmu-common.c | 66 +------
> arch/arm/cpu/mmu-common.h | 3 +-
> arch/arm/cpu/mmu_32.c | 19 +-
> arch/arm/cpu/mmu_64.c | 10 +-
> arch/arm/cpu/no-mmu.c | 11 +-
> arch/arm/cpu/start.c | 4 -
> arch/arm/cpu/uncompress.c | 46 ++++-
> arch/arm/include/asm/barebox-arm.h | 4 +-
> arch/arm/include/asm/barebox.lds.h | 14 +-
> arch/arm/include/asm/elf.h | 11 ++
> arch/arm/include/asm/sections.h | 1 +
> arch/arm/lib/pbl.lds.S | 8 +-
> arch/arm/lib32/Makefile | 1 +
> arch/arm/lib32/barebox.lds.S | 39 ++--
> arch/arm/lib32/elf_reloc.c | 105 ++++++++++
> arch/arm/lib64/Makefile | 1 +
> arch/arm/lib64/barebox.lds.S | 31 ++-
> arch/arm/lib64/elf_reloc.c | 105 ++++++++++
> arch/riscv/Kconfig | 18 ++
> arch/riscv/Kconfig.socs | 1 -
> arch/riscv/boot/start.c | 4 -
> arch/riscv/boot/uncompress.c | 41 +++-
> arch/riscv/cpu/Makefile | 1 +
> arch/riscv/cpu/mmu.c | 386 +++++++++++++++++++++++++++++++++++++
> arch/riscv/cpu/mmu.h | 144 ++++++++++++++
> arch/riscv/include/asm/asm.h | 3 +-
> arch/riscv/include/asm/mmu.h | 44 +++++
> arch/riscv/lib/Makefile | 1 +
> arch/riscv/lib/barebox.lds.S | 38 ++--
> arch/riscv/lib/elf_reloc.c | 212 ++++++++++++++++++++
> common/Makefile | 2 +-
> common/elf.c | 367 +++++++++++++++++++++++++++++++++--
> images/Makefile | 18 +-
> images/piggy.S | 4 +
> include/elf.h | 62 ++++++
> include/mmu.h | 12 +-
> include/pbl/mmu.h | 29 +++
> lib/Makefile | 1 +
> lib/elf_reloc.c | 15 ++
> pbl/Kconfig | 8 +
> pbl/Makefile | 1 +
> pbl/mmu.c | 111 +++++++++++
> scripts/prelink-riscv.inc | 9 +-
> 47 files changed, 1922 insertions(+), 202 deletions(-)
> ---
> base-commit: e2d7e032281158b54541392b4d8108de204137a1
> change-id: 20251227-pbl-load-elf-cb4cb0ceb7d8
>
> Best regards,
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 08/19] Makefile: add barebox.elf build target
2026-01-05 12:22 ` Ahmad Fatoum
@ 2026-01-05 15:43 ` Sascha Hauer
2026-01-05 17:11 ` Ahmad Fatoum
0 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 15:43 UTC (permalink / raw)
To: Ahmad Fatoum; +Cc: BAREBOX, Claude Sonnet 4.5
On Mon, Jan 05, 2026 at 01:22:40PM +0100, Ahmad Fatoum wrote:
> > .SECONDEXPANSION:
> > $(symlink-y): $$(or $$(SYMLINK_DEP_$$(@F)),$$(SYMLINK_TARGET_$$(@F))) FORCE
> > @@ -1091,6 +1094,17 @@ barebox.fit: images/barebox-$(CONFIG_ARCH_LINUX_NAME).fit
> > barebox.srec: barebox
> > $(OBJCOPY) -O srec $< $@
> >
> > +OBJCOPYFLAGS_barebox.elf = --strip-debug --strip-unneeded \
>
> --strip-section-headers goes the farthest I think.
The objcopy from the toolchain I used (binutils-2.40) doesn't support
this option. A newer one I just upgraded to indeed does.
Sascha
> OBJCOPYFLAGS_$@ = $your_options.
>
>
> Sidenote: We should add --error-rwx-segments depending on
> CONFIG_PBL_IMAGE_ELF.
Is this a objcopy option? My objcopy doesn't seem to support this.
Sascha
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations
2026-01-05 14:08 ` [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Ahmad Fatoum
@ 2026-01-05 16:47 ` Sascha Hauer
2026-01-06 8:35 ` Ahmad Fatoum
0 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 16:47 UTC (permalink / raw)
To: Ahmad Fatoum; +Cc: BAREBOX, Claude Sonnet 4.5
On Mon, Jan 05, 2026 at 03:08:03PM +0100, Ahmad Fatoum wrote:
> Hi,
>
> On 1/5/26 12:26 PM, Sascha Hauer wrote:
> > Until now we linked the raw barebox proper binary into the PBL which
> > comes with a number of disadvantages. We rely on self-modifying code
> > to in barebox proper (relocate_to_current_adr()) and have no initialized
> > bss segment (setup_c()). Also we can only mark the .text and .rodata as
> > readonly during runtime of barebox proper.
> >
> > This series overcomes this by linking a ELF image into the PBL. This
> > image is properly layed out, linked and initialized in the PBL. With
> > this barebox proper has a proper C environment and text/rodata
> > protection from the start.
> >
> > As a bonus this series also adds initial MMU support for RISCV, also
> > based on loading the ELF image and configuring the MMU from the PBL.
> >
> > This series also marks my start into AI assisted programming as you can
> > see in the Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
> > and 🤖 Generated with [Claude Code](https://claude.com/claude-code)
> > tags. Without it I wouldn't have started this series during my xmas
> > break, but with it it was actually quite fun to do; it felt like having
> > a programming team which I just had to delegate new tasks to while
> > having fun with my family myself ;)
>
> Did you do size comparisons to determine how much this series affects
> binary size? I found that compressing every segment separately resulted
> in a smaller binary size even after addition of ELF parsing to PBL, but
> I used a purpose-built ELF loader unlike to what you did here.
I did now.
ARM64 barebox-dt-2nd.img raw: 1246506
ARM64 barebox-dt-2nd.img ELF: 1248892 (+ 2386 bytes)
ARM32 barebox-dt-2nd.img raw: 1217036
ARM32 barebox-dt-2nd.img ELF: 1219159 (+ 2123 bytes)
With --strip-section-headers instead of --strip-debug --strip-unneeded
the last image goes down to 1217940 bytes, so only 904 bytes size
increase.
Sascha
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 08/19] Makefile: add barebox.elf build target
2026-01-05 15:43 ` Sascha Hauer
@ 2026-01-05 17:11 ` Ahmad Fatoum
0 siblings, 0 replies; 49+ messages in thread
From: Ahmad Fatoum @ 2026-01-05 17:11 UTC (permalink / raw)
To: Sascha Hauer; +Cc: BAREBOX, Claude Sonnet 4.5
Hi,
On 1/5/26 16:43, Sascha Hauer wrote:
> On Mon, Jan 05, 2026 at 01:22:40PM +0100, Ahmad Fatoum wrote:
>>> .SECONDEXPANSION:
>>> $(symlink-y): $$(or $$(SYMLINK_DEP_$$(@F)),$$(SYMLINK_TARGET_$$(@F))) FORCE
>>> @@ -1091,6 +1094,17 @@ barebox.fit: images/barebox-$(CONFIG_ARCH_LINUX_NAME).fit
>>> barebox.srec: barebox
>>> $(OBJCOPY) -O srec $< $@
>>>
>>> +OBJCOPYFLAGS_barebox.elf = --strip-debug --strip-unneeded \
>>
>> --strip-section-headers goes the farthest I think.
>
> The objcopy from the toolchain I used (binutils-2.40) doesn't support
> this option. A newer one I just upgraded to indeed does.
Interesting, --strip-all is the next best thing then, I think, so I'd suggest:
$(objcopy-option, --strip-section-headers, --strip-all)
With (untested):
# objcopy-option
# Usage: KBUILD_LDFLAGS += $(call objcopy-option, --strip-section-headers)
objcopy-option = $(call try-run,\
$(CC) -x c /dev/null -c -o "$$TMPO"; $(OBJCOPY) $(1) "$$TMPO" "$$TMP",$(1),$(2))
>> OBJCOPYFLAGS_$@ = $your_options.
>>
>>
>> Sidenote: We should add --error-rwx-segments depending on
>> CONFIG_PBL_IMAGE_ELF.
>
> Is this a objcopy option? My objcopy doesn't seem to support this.
See LDFLAGS_common += $(call ld-option,--no-warn-rwx-segments).
Cheers,
Ahmad
>
> Sascha
>
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 04/19] ARM: implement elf_apply_relocations() for ELF relocation support
2026-01-05 11:58 ` Ahmad Fatoum
@ 2026-01-05 19:53 ` Sascha Hauer
0 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 19:53 UTC (permalink / raw)
To: Ahmad Fatoum; +Cc: BAREBOX, Claude Sonnet 4.5
On Mon, Jan 05, 2026 at 12:58:23PM +0100, Ahmad Fatoum wrote:
> Hi,
>
> On 1/5/26 12:26 PM, Sascha Hauer wrote:
> > Implement architecture-specific ELF relocation handlers for ARM32 and ARM64.
> >
> > ARM32 implementation (arch/arm/lib32/elf_reloc.c):
> > - Handles REL-format relocations (no explicit addend)
> > - Supports R_ARM_RELATIVE and R_ARM_ABS32 relocation types
> > - Addend is read from the target location
> >
> > ARM64 implementation (arch/arm/lib64/elf_reloc.c):
> > - Handles RELA-format relocations (with explicit addend)
> > - Supports R_AARCH64_RELATIVE and R_AARCH64_ABS64 relocation types
> > - Addend is provided in relocation entry
> >
> > Both implementations:
> > - Parse PT_DYNAMIC segment to locate relocation tables
> > - Validate relocation table format and entry sizes
> > - Apply relocations based on the computed load offset
> > - Return appropriate errors for unsupported relocation types
> >
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
> > ---
> > arch/arm/include/asm/elf.h | 11 +++++
> > arch/arm/lib32/Makefile | 1 +
> > arch/arm/lib32/elf_reloc.c | 105 +++++++++++++++++++++++++++++++++++++++++++++
> > arch/arm/lib64/Makefile | 1 +
> > arch/arm/lib64/elf_reloc.c | 105 +++++++++++++++++++++++++++++++++++++++++++++
> > 5 files changed, 223 insertions(+)
> >
> > diff --git a/arch/arm/include/asm/elf.h b/arch/arm/include/asm/elf.h
> > index 4043e6fd5b991eb5cccb3fa0ea28d208006ee1fc..cceb92ee1a5f63c37b0e981c263676bd35a261c0 100644
> > --- a/arch/arm/include/asm/elf.h
> > +++ b/arch/arm/include/asm/elf.h
> > @@ -36,6 +36,17 @@ typedef struct user_fp elf_fpregset_t;
> > #define R_ARM_THM_CALL 10
> > #define R_ARM_THM_JUMP24 30
> >
> > +/* Additional relocation types for dynamic linking */
> > +#define R_ARM_RELATIVE 23
>
> This is already defined in arch/arm/cpu/common.c, you can guess where
> this is going ;)
>
> > +#define R_ARM_GLOB_DAT 21
> > +#define R_ARM_JUMP_SLOT 22
>
> These two are not used.
>
> > +
> > +#define R_AARCH64_NONE 0
> > +#define R_AARCH64_ABS64 257
> > +#define R_AARCH64_RELATIVE 1027
> > +#define R_AARCH64_GLOB_DAT 1025
> > +#define R_AARCH64_JUMP_SLOT 1026
>
> Likewise, only R_AARCH64_JUMP_SLOT and R_AARCH64_GLOB_DAT are not used.
>
> > +/*
> > + * Parse dynamic section and extract relocation info for ARM32
> > + */
> > +static int parse_dynamic_section(struct elf_image *elf, Elf32_Dyn *dyn,
> > + Elf32_Rel **rel_out, u64 *relsz_out)
> > +{
> > + Elf32_Rel *rel = NULL;
>
> If we define ELF_CLASS depending on CONFIG_ARM32/CONFIG_ARM64, we can
> write a generic parse_dynamic_section() that applies to all architectures.
>
> In this case, we would just collect both REL and RELA in the switch and
> just use what's available.
> > +int elf_apply_relocations(struct elf_image *elf, void *dyn_seg)
> > +{
>
> We already have relocate_to_current_adr(). If we change it to call
> a new relocate_image():
>
> relocate_image(get_runtime_offset(),
> runtime_addr(__rel_dyn_start),
> runtime_addr(__rel_dyn_end),
> runtime_addr(__dynsym_start),
> runtime_addr(__dynsym_end));
>
> Then we could use relocate_image() in generic code instead of
> duplicating it per architecture.
>
> > +/*
> > + * Parse dynamic section and extract relocation info for ARM64
> > + */
> > +static int parse_dynamic_section(struct elf_image *elf, Elf64_Dyn *dyn,
> > + Elf64_Rela **rela_out, u64 *relasz_out)
>
> Same comment as for ARM32. I believe this can go into generic code.
>
> > + case R_AARCH64_ABS64:
> > + /* B(P) = S + A */
> > + *fixup_addr = base + rela->r_addend;
> > + break;
>
> What did you do to get R_AARCH64_ABS64 relocations? We don't handle them
> in relocate_to_current_adr() and they shouldn't have been generated.
Likely Claude just generated this for completeness.
Sascha
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 06/19] elf: implement elf_load_inplace()
2026-01-05 13:37 ` Ahmad Fatoum
@ 2026-01-05 22:42 ` Sascha Hauer
2026-01-06 8:18 ` Ahmad Fatoum
0 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 22:42 UTC (permalink / raw)
To: Ahmad Fatoum; +Cc: BAREBOX, Claude Sonnet 4.5
On Mon, Jan 05, 2026 at 02:37:13PM +0100, Ahmad Fatoum wrote:
> Hi,
>
> On 1/5/26 12:26 PM, Sascha Hauer wrote:
> > Implement elf_load_inplace() to apply dynamic relocations to an ELF binary
> > that is already loaded in memory. Unlike elf_load(), this function does not
> > allocate memory or copy segments - it only modifies the existing image in
> > place.
> >
> > This is useful for self-relocating loaders or when the ELF has been loaded
> > by external means (e.g., firmware or another bootloader).
>
> Nice. This is more elegant than what I came up with (compressing every
> segment separately). :)
>
> > For ET_DYN (position-independent) binaries, the relocation offset is
> > calculated relative to the first executable PT_LOAD segment (.text section),
> > taking into account the difference between the segment's virtual address
> > and its file offset.
>
> While this may be true for barebox proper, I think in the general case,
> we need to iterate over all segments and take the lowest address.
> The first executable PT_LOAD shouldn't have a special significance in
> this case.
>
> >
> > The entry point is also adjusted to point to the relocated image.
> >
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
> > ---
> > common/elf.c | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> > include/elf.h | 8 ++++
> > 2 files changed, 160 insertions(+)
> >
> > diff --git a/common/elf.c b/common/elf.c
> > index fc2949c285ebb0c0740c68c551926da8d0bb8637..565b283b694773727ef77917cfd8c1d4ee83a8d1 100644
> > --- a/common/elf.c
> > +++ b/common/elf.c
> > @@ -531,3 +531,155 @@ void elf_close(struct elf_image *elf)
> >
> > free(elf);
> > }
> > +
> > +static void *elf_find_dynamic_inplace(struct elf_image *elf)
>
> const void *
>
> > +{
> > + void *buf = elf->hdr_buf;
> > + void *phdr = buf + elf_hdr_e_phoff(elf, buf);
> > + int i;
> > +
> > + for (i = 0; i < elf_hdr_e_phnum(elf, buf); i++) {
> > + if (elf_phdr_p_type(elf, phdr) == PT_DYNAMIC) {
> > + u64 offset = elf_phdr_p_offset(elf, phdr);
> > + /* For in-place binary, PT_DYNAMIC is at hdr_buf + offset */
> > + return elf->hdr_buf + offset;
> > + }
> > + phdr += elf_size_of_phdr(elf);
> > + }
> > +
> > + return NULL; /* No PT_DYNAMIC segment */
> > +}
> > +
> > +/**
> > + * elf_load_inplace() - Apply dynamic relocations to an ELF binary in place
> > + * @elf: ELF image previously opened with elf_open_binary()
> > + *
> > + * This function applies dynamic relocations to an ELF binary that is already
> > + * loaded at its target address in memory. Unlike elf_load(), this does not
> > + * allocate memory or copy segments - it only modifies the existing image.
> > + *
> > + * This is useful for self-relocating loaders or when the ELF has been loaded
> > + * by external means (e.g., loaded by firmware or another bootloader).
> > + *
> > + * The ELF image must have been previously opened with elf_open_binary().
> > + *
> > + * For ET_DYN (position-independent) binaries, the relocation offset is
> > + * calculated relative to the first executable PT_LOAD segment (.text section).
> > + *
> > + * For ET_EXEC binaries, no relocation is applied as they are expected to
> > + * be at their link-time addresses.
> > + *
> > + * Returns: 0 on success, negative error code on failure
> > + */
> > +int elf_load_inplace(struct elf_image *elf)
> > +{
> > + void *dyn_seg;
> > + void *buf, *phdr;
> > + void *elf_buf;
> > + int i, ret;
> > +
> > + buf = elf->hdr_buf;
> > + elf_buf = elf->hdr_buf;
> > +
> > + /*
> > + * First pass: Clear BSS segments (p_memsz > p_filesz).
> > + * This must be done before relocations as uninitialized data
> > + * must be zeroed per C standard.
> > + */
> > + phdr = buf + elf_hdr_e_phoff(elf, buf);
> > + for (i = 0; i < elf_hdr_e_phnum(elf, buf); i++) {
> > + if (elf_phdr_p_type(elf, phdr) == PT_LOAD) {
> > + u64 p_offset = elf_phdr_p_offset(elf, phdr);
> > + u64 p_filesz = elf_phdr_p_filesz(elf, phdr);
> > + u64 p_memsz = elf_phdr_p_memsz(elf, phdr);
> > +
> > + /* Clear BSS (uninitialized data) */
> > + if (p_filesz < p_memsz) {
> > + void *bss_start = elf_buf + p_offset + p_filesz;
> > + size_t bss_size = p_memsz - p_filesz;
> > + memset(bss_start, 0x00, bss_size);
>
> How can this be done in-place? If this is padding to get to a page
> boundary, I would assume it's already zero. If it goes beyond a page,
> this will overwrite follow-up segments wouldn't it?
>
> I also find bss_ an unforunate name here as this is applied to all segments.
The code basically implements this (from
https://refspecs.linuxbase.org/elf/gabi4+/ch5.pheader.html):
PT_LOAD
The array element specifies a loadable segment, described by
p_filesz and p_memsz. The bytes from the file are mapped to the
beginning of the memory segment. If the segment's memory size
(p_memsz) is larger than the file size (p_filesz), the ``extra''
bytes are defined to hold the value 0 and to follow the segment's
initialized area
You are right, this is done for all segments, but de facto the segment
containing the bss section is the only one where p_filesz < p_memsz
is actually used, so "Clear bss segments" doesn't sound too wrong to me.
I experimented a bit. When I change the linker file to move the bss
section before the data section, the bss section really shows up as
zeroes in the ELF file and p_filesz becomes p_memsz for all segments.
Only when the bss section is at the very end of the binary the bss
section is no longer part of the ELF binary and may hit uninitialized
memory which we really have to memset.
>
>
> > + if (elf->type == ET_DYN) {
> > + u64 text_vaddr = 0;
> > + u64 text_offset = 0;
> > + bool found_text = false;
> > +
> > + /* Find first executable PT_LOAD segment (.text) */
>
> As mentioned, we should rather get the lowest address across all segments.
Not sure if this is true. I didn't manage to generate a binary where the
text section is not the first one. When I try to move for example the
data section before the text section then I end up with no executable
segment at all. Something weird is happening there, I haven't fully
understood how the generation of segments from sections work.
>
> > + /* Apply architecture-specific relocations */
> > + ret = elf_apply_relocations(elf, dyn_seg);
>
> Should I upstream my relocate_image changes that reuse the
> relocate_to_current_adr() code and you rebase on top?
Show the patch and we'll see.
Sascha
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 14/19] ARM: linker script: create separate PT_LOAD segments for text, rodata, and data
2026-01-05 13:11 ` Ahmad Fatoum
@ 2026-01-05 23:01 ` Sascha Hauer
2026-01-06 7:59 ` Ahmad Fatoum
0 siblings, 1 reply; 49+ messages in thread
From: Sascha Hauer @ 2026-01-05 23:01 UTC (permalink / raw)
To: Ahmad Fatoum; +Cc: BAREBOX, Claude Sonnet 4.5
On Mon, Jan 05, 2026 at 02:11:28PM +0100, Ahmad Fatoum wrote:
>
>
> On 1/5/26 12:26 PM, Sascha Hauer wrote:
> > Fix the linker scripts to generate three distinct PT_LOAD segments with
> > correct permissions instead of combining .rodata with .data.
> >
> > Before this fix, the linker auto-generated only two PT_LOAD segments:
> > 1. Text segment (PF_R|PF_X)
> > 2. Data segment (PF_R|PF_W) - containing .rodata, .data, .bss, etc.
>
> Did it though? Why did we get the RWX linker warnings then?
>
> > This caused .rodata to be mapped with write permissions when
> > pbl_mmu_setup_from_elf() set up MMU permissions based on ELF segments,
> > defeating the W^X protection that commit d9ccb0cf14 intended to provide.
>
> What commit hash is this?
It's an earlier variant of "ARM: PBL: setup MMU with proper permissions
from ELF segments". With this I originally had the problem that I could
write to the rodata section. This patch is the solution Claude came up
with, so Claude referenced it by the commit hash. I re-ordered the
patches afterwards.
>
> > With explicit PHDRS directives, we now generate three segments:
> > 1. text segment (PF_R|PF_X): .text and related code sections
> > 2. rodata segment (PF_R): .rodata and unwind tables
> > 3. data segment (PF_R|PF_W): .data, .bss, and related sections
>
> Not directly related, but this is as good a place as any to ask the
> question: How is zero-padding implemented? If the file size is shorter
> than the memory size, the loader is supposed to zero-fill, which is used
> for BSS zeroing for example. Now if you load the ELF in place, we can't
> obviously zero-fill.
Yes we can, see my other mail. The bss section is placed in the ELF binary,
unless it is at the very end of the binary in which case it becomes
smaller by the size of the bss section.
> > +PHDRS
> > +{
> > + text PT_LOAD FLAGS(5); /* PF_R | PF_X */
> > + rodata PT_LOAD FLAGS(4); /* PF_R */
> > + data PT_LOAD FLAGS(6); /* PF_R | PF_W */
> > + dynamic PT_DYNAMIC FLAGS(6); /* PF_R | PF_W */
>
> I believe we don't need PF_W for PT_DYNAMIC. You could move it one up to
> merge with rodata.
See readelf output:
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x001000 0x00000000 0x00000000 0x96190 0x96190 R E 0x1000
LOAD 0x098000 0x00097000 0x00097000 0x4d2f4 0x4d2f4 R 0x1000
LOAD 0x0e6000 0x000e5000 0x000e5000 0x216d4 0x283b0 RW 0x1000
DYNAMIC 0x0f1d74 0x000f0d74 0x000f0d74 0x15960 0x15960 RW 0x4
The DYNAMIC segment is inside the LOAD segment directly above. I don't
know how this segment is really used, but making it readonly means that
it at least would have to be page aligned which it isn't.
> > _edata = .;
> > - .image_end : { *(.__image_end) }
> > + .image_end : { *(.__image_end) } :data
> >
> > . = ALIGN(4);
> > - .__bss_start : { *(.__bss_start) }
> > - .bss : { *(.bss*) }
> > - .__bss_stop : { *(.__bss_stop) }
> > + .__bss_start : { *(.__bss_start) } :data
> > + .bss : { *(.bss*) } :data
> > + .__bss_stop : { *(.__bss_stop) } :data
>
> Side-effect of having the decompressor take care of zeroing BSS is a big
> size increase for CONFIG_IMAGE_COMPRESSION_NONE. I think that's
> acceptable, but it's worth a comment here why we don't have a separate
> BSS segment (or make bss NOALLOC).
The bss section is not part of the binary when it's at its very end,
which it is, so I don't see any size increase.
> FYI, I have patches to get rid of MAX_BSS_SIZE on top of PBL ELF loader
> support, which I will submit once this support is merged.
Great \o/
Sascha
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 14/19] ARM: linker script: create separate PT_LOAD segments for text, rodata, and data
2026-01-05 23:01 ` Sascha Hauer
@ 2026-01-06 7:59 ` Ahmad Fatoum
0 siblings, 0 replies; 49+ messages in thread
From: Ahmad Fatoum @ 2026-01-06 7:59 UTC (permalink / raw)
To: Sascha Hauer; +Cc: BAREBOX, Claude Sonnet 4.5
Hello Sascha,
On 1/6/26 00:01, Sascha Hauer wrote:
> On Mon, Jan 05, 2026 at 02:11:28PM +0100, Ahmad Fatoum wrote:
>>
>>> With explicit PHDRS directives, we now generate three segments:
>>> 1. text segment (PF_R|PF_X): .text and related code sections
>>> 2. rodata segment (PF_R): .rodata and unwind tables
>>> 3. data segment (PF_R|PF_W): .data, .bss, and related sections
>>
>> Not directly related, but this is as good a place as any to ask the
>> question: How is zero-padding implemented? If the file size is shorter
>> than the memory size, the loader is supposed to zero-fill, which is used
>> for BSS zeroing for example. Now if you load the ELF in place, we can't
>> obviously zero-fill.
>
> Yes we can, see my other mail. The bss section is placed in the ELF binary,
> unless it is at the very end of the binary in which case it becomes
> smaller by the size of the bss section.
Ok. Can we have a check to verify that no overlap happened? Can also be at
compile-time.
>>> +PHDRS
>>> +{
>>> + text PT_LOAD FLAGS(5); /* PF_R | PF_X */
>>> + rodata PT_LOAD FLAGS(4); /* PF_R */
>>> + data PT_LOAD FLAGS(6); /* PF_R | PF_W */
>>> + dynamic PT_DYNAMIC FLAGS(6); /* PF_R | PF_W */
>>
>> I believe we don't need PF_W for PT_DYNAMIC. You could move it one up to
>> merge with rodata.
>
> See readelf output:
>
> Program Headers:
> Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
> LOAD 0x001000 0x00000000 0x00000000 0x96190 0x96190 R E 0x1000
> LOAD 0x098000 0x00097000 0x00097000 0x4d2f4 0x4d2f4 R 0x1000
> LOAD 0x0e6000 0x000e5000 0x000e5000 0x216d4 0x283b0 RW 0x1000
> DYNAMIC 0x0f1d74 0x000f0d74 0x000f0d74 0x15960 0x15960 RW 0x4
>
> The DYNAMIC segment is inside the LOAD segment directly above. I don't
> know how this segment is really used, but making it readonly means that
> it at least would have to be page aligned which it isn't.
If you add :rodata instead of :data it should merge with .rodata and not require
alignment itself.
>>> _edata = .;
>>> - .image_end : { *(.__image_end) }
>>> + .image_end : { *(.__image_end) } :data
>>>
>>> . = ALIGN(4);
>>> - .__bss_start : { *(.__bss_start) }
>>> - .bss : { *(.bss*) }
>>> - .__bss_stop : { *(.__bss_stop) }
>>> + .__bss_start : { *(.__bss_start) } :data
>>> + .bss : { *(.bss*) } :data
>>> + .__bss_stop : { *(.__bss_stop) } :data
>>
>> Side-effect of having the decompressor take care of zeroing BSS is a big
>> size increase for CONFIG_IMAGE_COMPRESSION_NONE. I think that's
>> acceptable, but it's worth a comment here why we don't have a separate
>> BSS segment (or make bss NOALLOC).
>
> The bss section is not part of the binary when it's at its very end,
> which it is, so I don't see any size increase.
Very good.
Cheers,
Ahmad
>
>> FYI, I have patches to get rid of MAX_BSS_SIZE on top of PBL ELF loader
>> support, which I will submit once this support is merged.
>
> Great \o/
>
> Sascha
>
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 06/19] elf: implement elf_load_inplace()
2026-01-05 22:42 ` Sascha Hauer
@ 2026-01-06 8:18 ` Ahmad Fatoum
0 siblings, 0 replies; 49+ messages in thread
From: Ahmad Fatoum @ 2026-01-06 8:18 UTC (permalink / raw)
To: Sascha Hauer; +Cc: BAREBOX, Claude Sonnet 4.5
On 1/5/26 23:42, Sascha Hauer wrote:
> On Mon, Jan 05, 2026 at 02:37:13PM +0100, Ahmad Fatoum wrote:
>> Hi,
>>
>> On 1/5/26 12:26 PM, Sascha Hauer wrote:
>>> Implement elf_load_inplace() to apply dynamic relocations to an ELF binary
>>> that is already loaded in memory. Unlike elf_load(), this function does not
>>> allocate memory or copy segments - it only modifies the existing image in
>>> place.
>>>
>>> This is useful for self-relocating loaders or when the ELF has been loaded
>>> by external means (e.g., firmware or another bootloader).
>>
>> Nice. This is more elegant than what I came up with (compressing every
>> segment separately). :)
>>
>>> For ET_DYN (position-independent) binaries, the relocation offset is
>>> calculated relative to the first executable PT_LOAD segment (.text section),
>>> taking into account the difference between the segment's virtual address
>>> and its file offset.
>>
>> While this may be true for barebox proper, I think in the general case,
>> we need to iterate over all segments and take the lowest address.
>> The first executable PT_LOAD shouldn't have a special significance in
>> this case.
>>
>>>
>>> The entry point is also adjusted to point to the relocated image.
>>>
>>> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
>>> Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
>>> ---
>>> common/elf.c | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>> include/elf.h | 8 ++++
>>> 2 files changed, 160 insertions(+)
>>>
>>> diff --git a/common/elf.c b/common/elf.c
>>> index fc2949c285ebb0c0740c68c551926da8d0bb8637..565b283b694773727ef77917cfd8c1d4ee83a8d1 100644
>>> --- a/common/elf.c
>>> +++ b/common/elf.c
>>> @@ -531,3 +531,155 @@ void elf_close(struct elf_image *elf)
>>>
>>> free(elf);
>>> }
>>> +
>>> +static void *elf_find_dynamic_inplace(struct elf_image *elf)
>>
>> const void *
>>
>>> +{
>>> + void *buf = elf->hdr_buf;
>>> + void *phdr = buf + elf_hdr_e_phoff(elf, buf);
>>> + int i;
>>> +
>>> + for (i = 0; i < elf_hdr_e_phnum(elf, buf); i++) {
>>> + if (elf_phdr_p_type(elf, phdr) == PT_DYNAMIC) {
>>> + u64 offset = elf_phdr_p_offset(elf, phdr);
>>> + /* For in-place binary, PT_DYNAMIC is at hdr_buf + offset */
>>> + return elf->hdr_buf + offset;
>>> + }
>>> + phdr += elf_size_of_phdr(elf);
>>> + }
>>> +
>>> + return NULL; /* No PT_DYNAMIC segment */
>>> +}
>>> +
>>> +/**
>>> + * elf_load_inplace() - Apply dynamic relocations to an ELF binary in place
>>> + * @elf: ELF image previously opened with elf_open_binary()
>>> + *
>>> + * This function applies dynamic relocations to an ELF binary that is already
>>> + * loaded at its target address in memory. Unlike elf_load(), this does not
>>> + * allocate memory or copy segments - it only modifies the existing image.
>>> + *
>>> + * This is useful for self-relocating loaders or when the ELF has been loaded
>>> + * by external means (e.g., loaded by firmware or another bootloader).
>>> + *
>>> + * The ELF image must have been previously opened with elf_open_binary().
>>> + *
>>> + * For ET_DYN (position-independent) binaries, the relocation offset is
>>> + * calculated relative to the first executable PT_LOAD segment (.text section).
>>> + *
>>> + * For ET_EXEC binaries, no relocation is applied as they are expected to
>>> + * be at their link-time addresses.
>>> + *
>>> + * Returns: 0 on success, negative error code on failure
>>> + */
>>> +int elf_load_inplace(struct elf_image *elf)
>>> +{
>>> + void *dyn_seg;
>>> + void *buf, *phdr;
>>> + void *elf_buf;
>>> + int i, ret;
>>> +
>>> + buf = elf->hdr_buf;
>>> + elf_buf = elf->hdr_buf;
>>> +
>>> + /*
>>> + * First pass: Clear BSS segments (p_memsz > p_filesz).
>>> + * This must be done before relocations as uninitialized data
>>> + * must be zeroed per C standard.
>>> + */
>>> + phdr = buf + elf_hdr_e_phoff(elf, buf);
>>> + for (i = 0; i < elf_hdr_e_phnum(elf, buf); i++) {
>>> + if (elf_phdr_p_type(elf, phdr) == PT_LOAD) {
>>> + u64 p_offset = elf_phdr_p_offset(elf, phdr);
>>> + u64 p_filesz = elf_phdr_p_filesz(elf, phdr);
>>> + u64 p_memsz = elf_phdr_p_memsz(elf, phdr);
>>> +
>>> + /* Clear BSS (uninitialized data) */
>>> + if (p_filesz < p_memsz) {
>>> + void *bss_start = elf_buf + p_offset + p_filesz;
>>> + size_t bss_size = p_memsz - p_filesz;
>>> + memset(bss_start, 0x00, bss_size);
>>
>> How can this be done in-place? If this is padding to get to a page
>> boundary, I would assume it's already zero. If it goes beyond a page,
>> this will overwrite follow-up segments wouldn't it?
>>
>> I also find bss_ an unforunate name here as this is applied to all segments.
>
> The code basically implements this (from
> https://refspecs.linuxbase.org/elf/gabi4+/ch5.pheader.html):
>
> PT_LOAD
> The array element specifies a loadable segment, described by
> p_filesz and p_memsz. The bytes from the file are mapped to the
> beginning of the memory segment. If the segment's memory size
> (p_memsz) is larger than the file size (p_filesz), the ``extra''
> bytes are defined to hold the value 0 and to follow the segment's
> initialized area
>
> You are right, this is done for all segments, but de facto the segment
> containing the bss section is the only one where p_filesz < p_memsz
> is actually used, so "Clear bss segments" doesn't sound too wrong to me.
>
> I experimented a bit. When I change the linker file to move the bss
> section before the data section, the bss section really shows up as
> zeroes in the ELF file and p_filesz becomes p_memsz for all segments.
> Only when the bss section is at the very end of the binary the bss
> section is no longer part of the ELF binary and may hit uninitialized
> memory which we really have to memset.
Ok.
>>> + if (elf->type == ET_DYN) {
>>> + u64 text_vaddr = 0;
>>> + u64 text_offset = 0;
>>> + bool found_text = false;
>>> +
>>> + /* Find first executable PT_LOAD segment (.text) */
>>
>> As mentioned, we should rather get the lowest address across all segments.
>
> Not sure if this is true. I didn't manage to generate a binary where the
> text section is not the first one. When I try to move for example the
> data section before the text section then I end up with no executable
> segment at all. Something weird is happening there, I haven't fully
> understood how the generation of segments from sections work.
Quoting your link:
An executable or shared object file's base address (on platforms that support
the concept) is calculated during execution from three values:
the virtual memory load address, the maximum page size, and the lowest virtual
address of a program's loadable segment. To compute the base address,
one determines the memory address associated with the lowest p_vaddr value for
a PT_LOAD segment.
This address is truncated to the nearest multiple of the maximum page size.
The corresponding p_vaddr value itself is also truncated to the nearest
multiple of the maximum page size.
The base address is the difference between the truncated memory address and the
truncated p_vaddr value.
>>> + /* Apply architecture-specific relocations */
>>> + ret = elf_apply_relocations(elf, dyn_seg);
>>
>> Should I upstream my relocate_image changes that reuse the
>> relocate_to_current_adr() code and you rebase on top?
>
> Show the patch and we'll see.
Just sent it.
Cheers,
Ahmad
>
> Sascha
>
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations
2026-01-05 16:47 ` Sascha Hauer
@ 2026-01-06 8:35 ` Ahmad Fatoum
0 siblings, 0 replies; 49+ messages in thread
From: Ahmad Fatoum @ 2026-01-06 8:35 UTC (permalink / raw)
To: Sascha Hauer; +Cc: BAREBOX, Claude Sonnet 4.5
Hi,
On 1/5/26 17:47, Sascha Hauer wrote:
> On Mon, Jan 05, 2026 at 03:08:03PM +0100, Ahmad Fatoum wrote:
>> Hi,
>>
>> On 1/5/26 12:26 PM, Sascha Hauer wrote:
>>> Until now we linked the raw barebox proper binary into the PBL which
>>> comes with a number of disadvantages. We rely on self-modifying code
>>> to in barebox proper (relocate_to_current_adr()) and have no initialized
>>> bss segment (setup_c()). Also we can only mark the .text and .rodata as
>>> readonly during runtime of barebox proper.
>>>
>>> This series overcomes this by linking a ELF image into the PBL. This
>>> image is properly layed out, linked and initialized in the PBL. With
>>> this barebox proper has a proper C environment and text/rodata
>>> protection from the start.
>>>
>>> As a bonus this series also adds initial MMU support for RISCV, also
>>> based on loading the ELF image and configuring the MMU from the PBL.
>>>
>>> This series also marks my start into AI assisted programming as you can
>>> see in the Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
>>> and 🤖 Generated with [Claude Code](https://claude.com/claude-code)
>>> tags. Without it I wouldn't have started this series during my xmas
>>> break, but with it it was actually quite fun to do; it felt like having
>>> a programming team which I just had to delegate new tasks to while
>>> having fun with my family myself ;)
>>
>> Did you do size comparisons to determine how much this series affects
>> binary size? I found that compressing every segment separately resulted
>> in a smaller binary size even after addition of ELF parsing to PBL, but
>> I used a purpose-built ELF loader unlike to what you did here.
>
> I did now.
>
> ARM64 barebox-dt-2nd.img raw: 1246506
> ARM64 barebox-dt-2nd.img ELF: 1248892 (+ 2386 bytes)
> ARM32 barebox-dt-2nd.img raw: 1217036
> ARM32 barebox-dt-2nd.img ELF: 1219159 (+ 2123 bytes)
>
> With --strip-section-headers instead of --strip-debug --strip-unneeded
> the last image goes down to 1217940 bytes, so only 904 bytes size
> increase.
Is the raw image from before the series was applied, so we can judge the
total size increase?
I redid tests for my series, but this time in the same build directory and
got values similar to yours:
ARM64 barebox-dt-2nd.img before: 992276
ARM64 barebox-dt-2nd.img after: 993029 (+ 753 bytes)
Previous measurement of ELF compression being smaller must have been
a mistake it seems.
Cheers,
Ahmad
>
> Sascha
>
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 11/19] mmu: introduce pbl_remap_range()
2026-01-05 12:15 ` Ahmad Fatoum
@ 2026-01-06 8:50 ` Ahmad Fatoum
2026-01-06 9:25 ` Sascha Hauer
0 siblings, 1 reply; 49+ messages in thread
From: Ahmad Fatoum @ 2026-01-06 8:50 UTC (permalink / raw)
To: Sascha Hauer, BAREBOX; +Cc: Claude Sonnet 4.5
On 1/5/26 13:15, Ahmad Fatoum wrote:
> On 1/5/26 12:26 PM, Sascha Hauer wrote:
>> Add PBL-specific memory remapping function that always uses page-wise
>> mapping (ARCH_MAP_FLAG_PAGEWISE) for fine-grained permissions on
>> adjacent ELF segments with different protection requirements.
>>
>> Wraps arch-specific __arch_remap_range() for ARMv7 (4KB pages) and
>> ARMv8 (page tables with BBM). Needed for ELF segment permission setup.
>>
>> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
>
> Reviewed-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
Hmm, a question: Why do we need to preemptively break up pages?
Doesn't normal remapping do that already? My understanding was
that we only did it here to allow remapping while executing from
it, but in the PBL ELF loader we are not executing from there,
so there should not be an issue.
Thanks,
Ahmad
>
>> ---
>> arch/arm/cpu/mmu_32.c | 7 +++++++
>> arch/arm/cpu/mmu_64.c | 8 ++++++++
>> include/mmu.h | 3 +++
>> 3 files changed, 18 insertions(+)
>>
>> diff --git a/arch/arm/cpu/mmu_32.c b/arch/arm/cpu/mmu_32.c
>> index 97c7107290ce95ddb21a322a5d0e74f3d324c528..86a55d165ba3cec5154c345a1a3a9cb959f0996f 100644
>> --- a/arch/arm/cpu/mmu_32.c
>> +++ b/arch/arm/cpu/mmu_32.c
>> @@ -435,6 +435,13 @@ static void early_remap_range(u32 addr, size_t size, maptype_t map_type)
>> __arch_remap_range((void *)addr, addr, size, map_type);
>> }
>>
>> +void pbl_remap_range(void *virt_addr, phys_addr_t phys_addr, size_t size,
>> + maptype_t map_type)
>> +{
>> + __arch_remap_range(virt_addr, phys_addr, size,
>> + map_type | ARCH_MAP_FLAG_PAGEWISE);
>> +}
>> +
>> static bool pte_is_cacheable(uint32_t pte, int level)
>> {
>> return (level == 2 && (pte & PTE_CACHEABLE)) ||
>> diff --git a/arch/arm/cpu/mmu_64.c b/arch/arm/cpu/mmu_64.c
>> index afb3d2d7efd0bc7ecde1177d1544f54d751b5dc1..63faaa46703697e527eae766392e7ea7ae186b3d 100644
>> --- a/arch/arm/cpu/mmu_64.c
>> +++ b/arch/arm/cpu/mmu_64.c
>> @@ -282,6 +282,14 @@ static void early_remap_range(uint64_t addr, size_t size, maptype_t map_type)
>> __arch_remap_range(addr, addr, size, map_type, false);
>> }
>>
>> +void pbl_remap_range(void *virt_addr, phys_addr_t phys_addr, size_t size,
>> + maptype_t map_type)
>> +{
>> + __arch_remap_range((uint64_t)virt_addr, phys_addr,
>> + (uint64_t)size, map_type | ARCH_MAP_FLAG_PAGEWISE,
>> + true);
>> +}
>> +
>> int arch_remap_range(void *virt_addr, phys_addr_t phys_addr, size_t size, maptype_t map_type)
>> {
>> map_type = arm_mmu_maybe_skip_permissions(map_type);
>> diff --git a/include/mmu.h b/include/mmu.h
>> index 53603b7956c229b4c715c57b19d0398931eb2d6b..37df7b482e1d83e94e61997db6cf9834d8cf7f3c 100644
>> --- a/include/mmu.h
>> +++ b/include/mmu.h
>> @@ -64,6 +64,9 @@ static inline bool arch_can_remap(void)
>> }
>> #endif
>>
>> +void pbl_remap_range(void *virt_addr, phys_addr_t phys_addr, size_t size,
>> + maptype_t map_type);
>> +
>> static inline int remap_range(void *start, size_t size, maptype_t map_type)
>> {
>> return arch_remap_range(start, virt_to_phys(start), size, map_type);
>>
>
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
* Re: [PATCH 11/19] mmu: introduce pbl_remap_range()
2026-01-06 8:50 ` Ahmad Fatoum
@ 2026-01-06 9:25 ` Sascha Hauer
0 siblings, 0 replies; 49+ messages in thread
From: Sascha Hauer @ 2026-01-06 9:25 UTC (permalink / raw)
To: Ahmad Fatoum; +Cc: BAREBOX, Claude Sonnet 4.5
On Tue, Jan 06, 2026 at 09:50:04AM +0100, Ahmad Fatoum wrote:
> On 1/5/26 13:15, Ahmad Fatoum wrote:
> > On 1/5/26 12:26 PM, Sascha Hauer wrote:
> >> Add PBL-specific memory remapping function that always uses page-wise
> >> mapping (ARCH_MAP_FLAG_PAGEWISE) for fine-grained permissions on
> >> adjacent ELF segments with different protection requirements.
> >>
> >> Wraps arch-specific __arch_remap_range() for ARMv7 (4KB pages) and
> >> ARMv8 (page tables with BBM). Needed for ELF segment permission setup.
> >>
> >> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> >
> > Reviewed-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
>
>
> Hmm, a question: Why do we need to preemptively break up pages?
> Doesn't normal remapping do that already? My understanding was
> that we only did it here to allow remapping while executing from
> it, but in the PBL ELF loader we are not executing from there,
> so there should not be an issue.
Yes, I think the ARCH_MAP_FLAG_PAGEWISE flag can be dropped here.
In the end it seems we do not the flag anymore when loading the
ELF image in PBL.
Sascha
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 49+ messages in thread
end of thread, other threads:[~2026-01-06 9:26 UTC | newest]
Thread overview: 49+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-01-05 11:26 [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Sascha Hauer
2026-01-05 11:26 ` [PATCH 01/19] elf: Use memcmp to make suitable for PBL Sascha Hauer
2026-01-05 11:46 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 02/19] elf: build for PBL as well Sascha Hauer
2026-01-05 11:26 ` [PATCH 03/19] elf: add dynamic relocation support Sascha Hauer
2026-01-05 14:05 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 04/19] ARM: implement elf_apply_relocations() for ELF " Sascha Hauer
2026-01-05 11:58 ` Ahmad Fatoum
2026-01-05 19:53 ` Sascha Hauer
2026-01-05 11:26 ` [PATCH 05/19] riscv: " Sascha Hauer
2026-01-05 11:26 ` [PATCH 06/19] elf: implement elf_load_inplace() Sascha Hauer
2026-01-05 13:37 ` Ahmad Fatoum
2026-01-05 22:42 ` Sascha Hauer
2026-01-06 8:18 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 07/19] elf: create elf_open_binary_into() Sascha Hauer
2026-01-05 11:26 ` [PATCH 08/19] Makefile: add barebox.elf build target Sascha Hauer
2026-01-05 12:22 ` Ahmad Fatoum
2026-01-05 15:43 ` Sascha Hauer
2026-01-05 17:11 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 09/19] PBL: allow to link ELF image into PBL Sascha Hauer
2026-01-05 12:11 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 10/19] mmu: add MAP_CACHED_RO mapping type Sascha Hauer
2026-01-05 12:14 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 11/19] mmu: introduce pbl_remap_range() Sascha Hauer
2026-01-05 12:15 ` Ahmad Fatoum
2026-01-06 8:50 ` Ahmad Fatoum
2026-01-06 9:25 ` Sascha Hauer
2026-01-05 11:26 ` [PATCH 12/19] ARM: use relative jumps in exception table Sascha Hauer
2026-01-05 11:44 ` Ahmad Fatoum
2026-01-05 12:29 ` Sascha Hauer
2026-01-05 12:31 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 13/19] ARM: exceptions: make in-binary exception table const Sascha Hauer
2026-01-05 11:26 ` [PATCH 14/19] ARM: linker script: create separate PT_LOAD segments for text, rodata, and data Sascha Hauer
2026-01-05 13:11 ` Ahmad Fatoum
2026-01-05 23:01 ` Sascha Hauer
2026-01-06 7:59 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 15/19] ARM: link ELF image into PBL Sascha Hauer
2026-01-05 12:27 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 16/19] ARM: PBL: setup MMU with proper permissions from ELF segments Sascha Hauer
2026-01-05 12:58 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 17/19] riscv: link ELF image into PBL Sascha Hauer
2026-01-05 13:12 ` Ahmad Fatoum
2026-01-05 11:26 ` [PATCH 18/19] riscv: linker script: create separate PT_LOAD segments for text, rodata, and data Sascha Hauer
2026-01-05 13:40 ` Ahmad Fatoum
2026-01-05 11:27 ` [PATCH 19/19] riscv: add ELF segment-based memory protection with MMU Sascha Hauer
2026-01-05 13:58 ` Ahmad Fatoum
2026-01-05 14:08 ` [PATCH 00/19] PBL: Add PBL ELF loading support with dynamic relocations Ahmad Fatoum
2026-01-05 16:47 ` Sascha Hauer
2026-01-06 8:35 ` Ahmad Fatoum
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox