Skip to content

Conversation

@fsammoura1980
Copy link
Contributor

@fsammoura1980 fsammoura1980 commented Sep 18, 2025

This commit addresses two aspects of PMP stack guarding on RISC-V:

  1. Defer Locking of Interrupt Stack Guard PMP Entry when multithreading is enabled:

The PMP entry for the interrupt stack guard, initially configured in z_riscv_pmp_init(), was previously locked immediately using the PMP_L flag. This immediate locking causes issues in complex boot scenarios, such as systems that jump from a Read-Only (RO) image to a Read-Write (RW) image. When switching images, the kernel must re-initialize PMP. If the interrupt stack address changes between the RO and RW stages, the already-locked PMP entry for the original address cannot be reconfigured.

This change removes the premature PMP_L flag from the PMP entry setup in z_riscv_pmp_init(). The essential locking is now deferred to z_riscv_pmp_stackguard_enable(), which is executed only when the system is ready and the stack guard is enabled (CONFIG_PMP_STACK_GUARD && CONFIG_MULTITHREADING).

  1. Enforce PMP Stack Guard during IRQ Offload:

Since the PMP entry for the stack guard is no longer locked early in the boot process, we must ensure that the PMP settings are enforced in all critical execution contexts. The do_irq_offload path needs explicit mstatus configuration to activate the PMP protection for the interrupt stack.

This change adds assembly instructions to do_irq_offload to properly configure mstatus before executing the offloaded routine:
- Clear mstatus.MPP (Machine Previous Privilege).
- Set mstatus.MPRV to 1.

Setting mstatus.MPRV makes memory accesses within the offloaded IRQ routine behave as if they are occurring from M-mode, thus subjecting them to the PMP rules, including the stack guard entry. This is essential to ensure the stack guard is active during IRQ offloads, conditionally applied when CONFIG_PMP_STACK_GUARD and CONFIG_MULTITHREADING are enabled.

@zephyrbot zephyrbot added area: RISCV RISCV Architecture (32-bit & 64-bit) size: XS A PR changing only a single line of code labels Sep 18, 2025
Copy link
Member

@fkokosinski fkokosinski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, thanks for the PR!


The PMP entry for the interrupt stack guard is initially configured in z_riscv_pmp_init(). This entry was previously locked immediately using the PMP_L (Lock) flag.

Note that this means that this entry won’t be enforced within the M mode, as per specification:

In addition to locking the PMP entry, the L bit indicates whether the R/W/X permissions are
enforced on M-mode accesses. When the L bit is set, these permissions are enforced for all privilege modes. When the L bit is clear, any M-mode access matching the PMP entry will succeed; the R/W/X permissions apply only to S and U modes.


The essential locking of the interrupt stack guard will now be deferred and correctly applied within z_riscv_pmp_stackguard_enable()

Only when CONFIG_USERSPACE=y. What about other configurations?
https://github.com/zephyrproject-rtos/zephyr/blob/main/arch/riscv/core/isr.S#L361-L383
https://github.com/zephyrproject-rtos/zephyr/blob/main/arch/riscv/core/isr.S#L518-L587

Copy link

@npitre npitre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what the problem is. Please could you elaborate some more?

Also the lock removal is not reflected in the preceding comment.

The statement about z_riscv_pmp_stackguard_enable() in the commit log is
wrong. This only takes care of the process stack not the IRQ stack (the later
never changes unlike the former).

@fsammoura1980 fsammoura1980 force-pushed the make_irq_unlocked branch 2 times, most recently from 0ea47b3 to d807b5e Compare September 24, 2025 23:17
@fsammoura1980
Copy link
Contributor Author

I don't understand what the problem is. Please could you elaborate some more?

Also the lock removal is not reflected in the preceding comment.

The statement about z_riscv_pmp_stackguard_enable() in the commit log is wrong. This only takes care of the process stack not the IRQ stack (the later never changes unlike the former).

We have multi-stage firmware where we jump from RO firmware to RW firmware. The IRQ stack address is different for the 2 firmwares. With this entry being locked, the jumping between the firmware stages is blocked. If I make this entry unlocked, I can jump with no issues. I see that this entry is enabled with the other stackguard entries. I do not see why we cannot remove the locked permission and make it unlocked. It is going to be enforced.

@fsammoura1980
Copy link
Contributor Author

Hi, thanks for the PR!

The PMP entry for the interrupt stack guard is initially configured in z_riscv_pmp_init(). This entry was previously locked immediately using the PMP_L (Lock) flag.

Note that this means that this entry won’t be enforced within the M mode, as per specification:

In addition to locking the PMP entry, the L bit indicates whether the R/W/X permissions are
enforced on M-mode accesses. When the L bit is set, these permissions are enforced for all privilege modes. When the L bit is clear, any M-mode access matching the PMP entry will succeed; the R/W/X permissions apply only to S and U modes.

The essential locking of the interrupt stack guard will now be deferred and correctly applied within z_riscv_pmp_stackguard_enable()

Only when CONFIG_USERSPACE=y. What about other configurations? https://github.com/zephyrproject-rtos/zephyr/blob/main/arch/riscv/core/isr.S#L361-L383 https://github.com/zephyrproject-rtos/zephyr/blob/main/arch/riscv/core/isr.S#L518-L587

This specific PMP entry is conditionally defined and enforced. It is only active when both CONFIG_PMP_STACK_GUARD and CONFIG_MULTITHREADING are enabled. When these configurations are present, the function z_riscv_pmp_stackguard_enable is defined and executes to enforce the PMP protection for the IRQ stack.

This commit addresses two aspects of PMP stack guarding on RISC-V:

1.  Defer Locking of Interrupt Stack Guard PMP Entry:

    The PMP entry for the interrupt stack guard, initially configured in
    `z_riscv_pmp_init()`, was previously locked immediately using the
    `PMP_L` flag. This immediate locking causes issues in complex boot
    scenarios, such as systems that jump from a Read-Only (RO) image to a
    Read-Write (RW) image. When switching images, the kernel must
    re-initialize PMP. If the interrupt stack address changes between the
    RO and RW stages, the already-locked PMP entry for the original
    address cannot be reconfigured.

    This change removes the premature `PMP_L` flag from the PMP entry setup
    in `z_riscv_pmp_init()`. The essential locking is now deferred to
    `z_riscv_pmp_stackguard_enable()`, which is executed only when the
    system is ready and the stack guard is enabled
    (`CONFIG_PMP_STACK_GUARD` && `CONFIG_MULTITHREADING`).

2.  Enforce PMP Stack Guard during IRQ Offload:

    Since the PMP entry for the stack guard is no longer locked early in
    the boot process, we must ensure that the PMP settings are enforced
    in all critical execution contexts. The `do_irq_offload` path needs
    explicit `mstatus` configuration to activate the PMP protection for
    the interrupt stack.

    This change adds assembly instructions to `do_irq_offload` to
    properly configure `mstatus` before executing the offloaded routine:
      - Clear `mstatus.MPP` (Machine Previous Privilege).
      - Set `mstatus.MPRV` to 1.

    Setting `mstatus.MPRV` makes memory accesses within the offloaded IRQ
    routine behave as if they are occurring from M-mode, thus subjecting
    them to the PMP rules, including the stack guard entry. This is
    essential to ensure the stack guard is active during IRQ offloads,
    conditionally applied when `CONFIG_PMP_STACK_GUARD` and
    `CONFIG_MULTITHREADING` are enabled.

Signed-off-by: Firas Sammoura <[email protected]>
@sonarqubecloud
Copy link

sonarqubecloud bot commented Oct 9, 2025

@fsammoura1980
Copy link
Contributor Author

should be generalized in this PR: #99520
closing this PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: RISCV RISCV Architecture (32-bit & 64-bit) size: XS A PR changing only a single line of code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants