mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Ahmad Fatoum <a.fatoum@pengutronix.de>
To: Sascha Hauer <s.hauer@pengutronix.de>,
	BAREBOX <barebox@lists.infradead.org>,
	Marco Felsch <m.felsch@pengutronix.de>,
	Stefan Kerkmann <ske@pengutronix.de>
Cc: "Claude Sonnet 4.5" <noreply@anthropic.com>
Subject: Re: [PATCH 19/19] riscv: add ELF segment-based memory protection with MMU
Date: Mon, 5 Jan 2026 14:58:28 +0100	[thread overview]
Message-ID: <ea147b33-3b1f-40ac-b02b-56276ebed032@pengutronix.de> (raw)
In-Reply-To: <20260105-pbl-load-elf-v1-19-e97853f98232@pengutronix.de>

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 |




  reply	other threads:[~2026-01-05 13:59 UTC|newest]

Thread overview: 49+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ea147b33-3b1f-40ac-b02b-56276ebed032@pengutronix.de \
    --to=a.fatoum@pengutronix.de \
    --cc=barebox@lists.infradead.org \
    --cc=m.felsch@pengutronix.de \
    --cc=noreply@anthropic.com \
    --cc=s.hauer@pengutronix.de \
    --cc=ske@pengutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox