Skip to content

Commit 04419c9

Browse files
committed
riscv: pmp: Add support for custom user-defined PMP entry
This commit introduces configuration and infrastructure to reserve a dedicated Physical Memory Protection (PMP) slot for a custom memory region. This feature is designed for applications requiring **runtime control of memory permissions** for critical areas, such as firmware rollback segments or sensitive configuration data. This control is established early in the boot process and allows the system to **temporarily modify the privileges granted to the ROM flash** or other protected regions as necessary during execution. The new Kconfig options allow the user to define: - **CONFIG_CUSTOM_PMP_ENTRY_START**: Base address of the protected region. - **CONFIG_CUSTOM_PMP_ENTRY_SIZE**: Size of the protected region. - **CONFIG_CUSTOM_PMP_ENTRY_PERMISSIONS**: R/W/X access permissions for the slot. The custom PMP entry is configured in `z_riscv_pmp_init()` and purposefully placed before the locked read-only ROM protection entry to ensure early enforcement. The necessary context switch logic (`z_riscv_custom_pmp_entry_enable`) is added to `switch.S` and `pmp.c` to handle the required MPRV/MPP register configuration when the custom entry is enabled and the PMP stack guard is disabled. Signed-off-by: Firas Sammoura <[email protected]>
1 parent 803dc90 commit 04419c9

File tree

4 files changed

+78
-1
lines changed

4 files changed

+78
-1
lines changed

arch/riscv/Kconfig

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,44 @@ config PMP_STACK_GUARD_MIN_SIZE
432432
wiggle room to accommodate the eventual overflow exception
433433
stack usage.
434434

435+
config CUSTOM_PMP_ENTRY
436+
bool "Use PMP for custom protection region"
437+
depends on RISCV_PMP
438+
help
439+
Enable a custom Physical Memory Protection (PMP) entry to protect
440+
a user-defined memory region. This is typically used for critical
441+
data or firmware rollback protection.
442+
443+
if CUSTOM_PMP_ENTRY
444+
445+
config CUSTOM_PMP_ENTRY_START
446+
hex "Custom PMP entry start address"
447+
default 0x80000000
448+
help
449+
The base address of the memory region to be protected by the custom
450+
PMP entry.
451+
452+
config CUSTOM_PMP_ENTRY_SIZE
453+
hex "Custom PMP entry size in bytes"
454+
default 0x4000
455+
help
456+
The size, in bytes, of the memory region to be protected. The region
457+
will extend from CUSTOM_PMP_ENTRY_START for this many bytes.
458+
459+
config CUSTOM_PMP_ENTRY_PERMISSIONS
460+
hex "Custom PMP entry permissions (PMP_R, PMP_W, PMP_X)"
461+
default 0x3 # PMP_R | PMP_W (Read and Write)
462+
help
463+
The permissions for the PMP region, represented as a bitmask of
464+
PMP attributes:
465+
0x1: PMP_R (Read)
466+
0x2: PMP_W (Write)
467+
0x4: PMP_X (Execute)
468+
469+
Example: 0x3 is (Read | Write).
470+
471+
endif # CUSTOM_PMP_ENTRY
472+
435473
# Implement the null pointer detection using the Physical Memory Protection
436474
# (PMP) Unit.
437475
config NULL_POINTER_EXCEPTION_DETECTION_PMP

arch/riscv/core/pmp.c

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ static bool set_pmp_entry(unsigned int *index_p, uint8_t perm,
204204
return ok;
205205
}
206206

207-
#ifdef CONFIG_PMP_STACK_GUARD
207+
#if defined(CONFIG_PMP_STACK_GUARD) || defined(CONFIG_CUSTOM_PMP_ENTRY)
208208
static inline bool set_pmp_mprv_catchall(unsigned int *index_p,
209209
unsigned long *pmp_addr, unsigned long *pmp_cfg,
210210
unsigned int index_limit)
@@ -354,6 +354,13 @@ void z_riscv_pmp_init(void)
354354
unsigned long pmp_cfg[CONFIG_PMP_SLOTS / PMPCFG_STRIDE];
355355
unsigned int index = 0;
356356

357+
#ifdef CONFIG_CUSTOM_PMP_ENTRY
358+
set_pmp_entry(&index, CONFIG_CUSTOM_PMP_ENTRY_PERMISSIONS,
359+
(uintptr_t)(CONFIG_CUSTOM_PMP_ENTRY_START),
360+
(size_t)(CONFIG_CUSTOM_PMP_ENTRY_SIZE), pmp_addr, pmp_cfg,
361+
ARRAY_SIZE(pmp_addr));
362+
#endif
363+
357364
/* The read-only area is always there for every mode */
358365
set_pmp_entry(&index, PMP_R | PMP_X | PMP_L,
359366
(uintptr_t)__rom_region_start,
@@ -371,6 +378,17 @@ void z_riscv_pmp_init(void)
371378
pmp_addr, pmp_cfg, ARRAY_SIZE(pmp_addr));
372379
#endif
373380

381+
#ifdef CONFIG_CUSTOM_PMP_ENTRY
382+
#ifndef CONFIG_PMP_STACK_GUARD
383+
/*
384+
* This early, the kernel init code uses the custom entry and we want to
385+
* safeguard it as soon as possible. But we need a temporary default
386+
* "catch all" PMP entry for MPRV to work.
387+
*/
388+
set_pmp_mprv_catchall(&index, pmp_addr, pmp_cfg, ARRAY_SIZE(pmp_addr));
389+
#endif
390+
#endif
391+
374392
#ifdef CONFIG_PMP_STACK_GUARD
375393
#ifdef CONFIG_MULTITHREADING
376394
/*
@@ -446,6 +464,20 @@ void z_riscv_pmp_init(void)
446464
}
447465
}
448466

467+
#if defined(CONFIG_CUSTOM_PMP_ENTRY)
468+
/**
469+
* @brief Prepare M-mode for custom PMP entry handling.
470+
*
471+
* Configures the Machine Status Register (mstatus) by clearing MPP and setting MPRV to control
472+
* the memory privilege context for PMP access or configuration.
473+
*/
474+
void z_riscv_custom_pmp_entry_enable(void)
475+
{
476+
csr_clear(mstatus, MSTATUS_MPRV | MSTATUS_MPP);
477+
csr_set(mstatus, MSTATUS_MPRV);
478+
}
479+
#endif
480+
449481
/**
450482
* @Brief Initialize the per-thread PMP register copy with global values.
451483
*/

arch/riscv/core/switch.S

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ SECTION_FUNC(TEXT, z_riscv_switch)
6161
mv a0, s0
6262
#endif
6363

64+
#if defined(CONFIG_CUSTOM_PMP_ENTRY) && !defined(CONFIG_PMP_STACK_GUARD)
65+
mv s0, a0
66+
call z_riscv_custom_pmp_entry_enable
67+
mv a0, s0
68+
#endif
69+
6470
#if defined(CONFIG_PMP_STACK_GUARD)
6571
/* Stack guard has priority over user space for PMP usage. */
6672
mv s0, a0

arch/riscv/include/pmp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ void z_riscv_pmp_stackguard_disable(void);
1414
void z_riscv_pmp_usermode_init(struct k_thread *thread);
1515
void z_riscv_pmp_usermode_prepare(struct k_thread *thread);
1616
void z_riscv_pmp_usermode_enable(struct k_thread *thread);
17+
void z_riscv_custom_pmp_entry_enable(void);
1718

1819
#endif /* PMP_H_ */

0 commit comments

Comments
 (0)