-
Couldn't load subscription status.
- Fork 8.1k
riscv: pmp: Add support for custom user-defined PMP entry #96241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
be6bbb4 to
edfc116
Compare
|
Hi, thanks for the PR! I see that we're limiting the user to just one custom protected memory region. Is this due to limitations imposed by Kconfig? Could we use devicetree for defining those regions instead? That would allow the user to specify more than one protected memory region. |
I am just defining only a new one because this is something that our application requires. users of the entry can get the config values from the device tree. Do you have any specific suggestion of how to generalize extracting things from a device tree early in the abstraction phase? |
We could start using |
61fc494 to
7219bdd
Compare
ok, I implemented it using |
4e71970 to
7bf83a8
Compare
7567ba0 to
278fa15
Compare
| /* Read the current PMP configuration from the control registers */ | ||
| z_riscv_pmp_read_config(current_pmpcfg_regs, num_pmpcfg_regs); | ||
| z_riscv_pmp_read_addr(current_pmpaddr_regs, num_pmpaddr_regs); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IF_DISABLED(CONFIG_ZTEST, (static inline)) void z_riscv_pmp_read_config in pmp.c is odd to me.
I think we can read PMP CSR directly in test case.
#define PMPADDR_READ(x) current_pmpaddr_regs[x] = csr_read(pmpaddr##x)
FOR_EACH(PMPADDR_READ, (;), 0, 1, 2, 3, 4, 5, 6, 7);
#if CONFIG_PMP_SLOTS > 8
FOR_EACH(PMPADDR_READ, (;), 8, 9, 10, 11, 12, 13, 14, 15);
#endif
#undef PMPADDR_READ
#ifdef CONFIG_64BIT
/* RV64: pmpcfg0 holds entries 0-7; pmpcfg2 holds entries 8-15. */
current_pmpcfg_regs[0] = csr_read(pmpcfg0);
#if CONFIG_PMP_SLOTS > 8
current_pmpcfg_regs[1] = csr_read(pmpcfg2);
#endif
#else
/* RV32: Each pmpcfg register holds 4 entries. */
current_pmpcfg_regs[0] = csr_read(pmpcfg0);
current_pmpcfg_regs[1] = csr_read(pmpcfg1);
#if CONFIG_PMP_SLOTS > 8
current_pmpcfg_regs[2] = csr_read(pmpcfg2);
current_pmpcfg_regs[3] = csr_read(pmpcfg3);
#endif
#endif
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we usually remove the static for testing purposes in or codebase. I am open to suggestions. It is better than duplicating the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, although this is my first time to see this usage, I am ok with this.
Could we move the declaration to arch/riscv/include/pmp.h and guard it with CONFIG_ZTEST? So that test cases can simply #include <kernel_internal.h> without declaring these again. This would be useful for other test cases that may need these PMP debug API in the future.
arch/riscv/include/pmp.h:
#ifdef CONFIG_ZTEST
void z_riscv_pmp_read_config(...);
void z_riscv_pmp_read_addr(...);
void pmp_decode_region(...);
#endif /* CONFIG_ZTEST */
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
007c437 to
63bc418
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The added functionality is sound but the way this PR is organized needs
improvements.
You have only 3 commits but you are making at least 4 independent changes
deserving 5 independent commits, the second commit carries changes that
belong to the first one, stack guard for the init code without
CONFIG_MEM_ATTR is now broken, code is added to resync_pmp_domain()
without explanation, etc.
Suggestion:
Commit 1: Perform the function rename, and only the rename, but all the
rename, all comments included.
Commit 2: Introduce a CONFIG_RISCV_PMP_KERNEL_MODE_DYNAMIC symbol (or any
better name). Have it be selected by the stack guard config option. Then
propagate it in the code. The custom PMP functionality could select it too
later which would be cleaner than adding ... defined(CONFIG_MEM_ATTR)
everywhere.
Commit 3: Introduce set_pmp_mem_attr() functionality. Document all tricky
parts.
Commit 4: Add the test. It would be a good idea for the test not to presume
any specific PMP slot encoding as those may vary. In other words, we want to
make sure the test regions from DT do match the PMP after those PMP entries
are decoded. There is code in print_pmp_entries() to decode PMP ranges and
permissions, so commit #4 could factor that out and commit #5 could be the
actual test.
f59b598 to
b629bd6
Compare
I believe I responded to all of your comments |
715ce91 to
31b6b88
Compare
Rename the `z_riscv_pmp_stackguard_*` functions to `z_riscv_pmp_kernelmode_*`. This change better reflects that these functions are used for general kernel mode PMP configuration, not strictly limited to stack guard purposes. Call sites in fatal.c, isr.S, and switch.S have been updated accordingly. Signed-off-by: Firas Sammoura <[email protected]>
Introduce `CONFIG_PMP_KERNEL_MODE_DYNAMIC` to enable dynamic configuration and activation of Machine mode PMP entries. This allows PMP settings to be managed efficiently during transitions between kernel and thread contexts. Signed-off-by: Firas Sammoura <[email protected]>
…utes The Physical Memory Protection (PMP) initialization is updated to support custom entries defined in the Device Tree (DT) using the `zephyr,memattr` property, contingent on `CONFIG_MEM_ATTR` being enabled. A new function, `set_pmp_mem_attr()`, iterates over DT-defined regions and programs PMP entries in `z_riscv_pmp_init()`, allowing for early, flexible, and hardware-specific R/W/X protection for critical memory areas. DT-based entries are also installed in `z_riscv_pmp_kernelmode_prepare()` for thread-specific configuration. The logic for the temporary PMP "catch-all" entry is adjusted to account for new DT entries. Furthermore, the PMP domain resync logic now masks user partition permissions against DT-defined region permissions, preventing privilege escalation. `CONFIG_RISCV_PMP` is updated to select `PMP_KERNEL_MODE_DYNAMIC` if `MEM_ATTR`. Finally, the `pmp_cfg` array in `z_riscv_pmp_init()` is initialized to zero to prevent writing uninitialized stack data to unused PMP entries. Signed-off-by: Firas Sammoura <[email protected]>
The logic to decode PMP addressing modes (**TOR**, **NA4**, **NAPOT**) into physical start and end addresses was previously embedded in `print_pmp_entries()`. Extract this calculation into a new static helper function, `pmp_decode_region()`, to significantly improve the readability and modularity of the PMP debug printing code. The new helper function is fully self-contained and exposes a defined API for the PMP address decoding logic. This enables **direct reuse** in **unit tests** (e.g., using **Ztest**) to verify the core address calculation accuracy for all PMP modes and boundary conditions, independent of the main PMP initialization or logging path. Signed-off-by: Firas Sammoura <[email protected]>
8aa24d3 to
945ee0b
Compare
…state This commit implements a new unit test suite to validate the integration of Device Tree memory attributes (`zephyr,memory-attr`) with the RISC-V Physical Memory Protection (PMP) hardware. The test suite includes: 1. **`test_pmp_devicetree_memattr_config`**: Verifies that the PMP Control and Status Registers (CSRs) are programmed correctly based on the memory regions defined with `zephyr,memory-attr` in the Device Tree. It iterates through the active PMP entries and asserts a match against the expected DT-defined regions. 2. **`test_riscv_mprv_mpp_config`**: Checks the initial state of the Machine Privilege Register Virtualization (MPRV) bit and Machine Previous Privilege (MPP) field in the `mstatus` CSR to ensure PMP is configured for correct privilege level switching during boot. 3. **`test_dt_pmp_perm_conversion`**: Validates the `DT_MEM_RISCV_TO_PMP_PERM` macro to ensure the conversion from Device Tree memory attribute flags to RISC-V PMP permission bits (R/W/X) is correct. Signed-off-by: Firas Sammoura <[email protected]>
945ee0b to
53fba00
Compare
|



This series significantly upgrades the RISC-V Physical Memory Protection (PMP) implementation by introducing dynamic configuration and Device Tree integration.
Key changes:
zephyr,memattrDevice Tree property. The PMP domain resync logic is updated to respect these DT-defined regions.CONFIG_PMP_KERNEL_MODE_DYNAMICto allow dynamic configuration and activation of Machine mode PMP entries for better context switching support.stackguardtokernelmode) to reflect their general kernel configuration role.