diff --git a/arch/riscv/core/fatal.c b/arch/riscv/core/fatal.c index 779d23905cab0..3eeb169725079 100644 --- a/arch/riscv/core/fatal.c +++ b/arch/riscv/core/fatal.c @@ -226,6 +226,13 @@ void _Fault(struct arch_esf *esf) unsigned int reason = K_ERR_CPU_EXCEPTION; if (bad_stack_pointer(esf)) { +#ifdef CONFIG_PMP_STACK_GUARD + /* + * Remove the thread's PMP setting to prevent triggering a stack + * overflow error again due to the previous configuration. + */ + z_riscv_pmp_stackguard_disable(); +#endif /* CONFIG_PMP_STACK_GUARD */ reason = K_ERR_STACK_CHK_FAIL; } diff --git a/arch/riscv/core/isr.S b/arch/riscv/core/isr.S index da2068ed43dcd..bd21fed6b5587 100644 --- a/arch/riscv/core/isr.S +++ b/arch/riscv/core/isr.S @@ -347,6 +347,21 @@ no_fp: /* increment _current->arch.exception_depth */ */ li t1, RISCV_EXC_ECALLU beq t0, t1, is_user_syscall + +#ifdef CONFIG_PMP_STACK_GUARD + /* + * Determine if we come from user space. If so, reconfigure the PMP for + * kernel mode stack guard. + */ + csrr t0, mstatus + li t1, MSTATUS_MPP + and t0, t0, t1 + bnez t0, 1f + lr a0, ___cpu_t_current_OFFSET(s0) + call z_riscv_pmp_stackguard_enable +1: +#endif /* CONFIG_PMP_STACK_GUARD */ + #endif /* CONFIG_USERSPACE */ /* diff --git a/arch/riscv/core/pmp.c b/arch/riscv/core/pmp.c index 1df51f056314a..e41eb8d4bb080 100644 --- a/arch/riscv/core/pmp.c +++ b/arch/riscv/core/pmp.c @@ -511,6 +511,37 @@ void z_riscv_pmp_stackguard_enable(struct k_thread *thread) csr_set(mstatus, MSTATUS_MPRV); } +/** + * @brief Remove PMP stackguard content to actual PMP registers + */ +void z_riscv_pmp_stackguard_disable(void) +{ + + unsigned long pmp_addr[PMP_M_MODE_SLOTS]; + unsigned long pmp_cfg[PMP_M_MODE_SLOTS / sizeof(unsigned long)]; + unsigned int index = global_pmp_end_index; + + /* Retrieve the pmpaddr value matching the last global PMP slot. */ + pmp_addr[global_pmp_end_index - 1] = global_pmp_last_addr; + + /* Disable (non-locked) PMP entries for m-mode while we update them. */ + csr_clear(mstatus, MSTATUS_MPRV); + + /* + * Set a temporary default "catch all" PMP entry for MPRV to work, + * except for the global locked entries. + */ + set_pmp_mprv_catchall(&index, pmp_addr, pmp_cfg, ARRAY_SIZE(pmp_addr)); + + /* Write "catch all" entry and clear unlocked entries to PMP regs. */ + write_pmp_entries(global_pmp_end_index, index, + true, pmp_addr, pmp_cfg, ARRAY_SIZE(pmp_addr)); + + if (PMP_DEBUG_DUMP) { + dump_pmp_regs("catch all register dump"); + } +} + #endif /* CONFIG_PMP_STACK_GUARD */ #ifdef CONFIG_USERSPACE diff --git a/arch/riscv/include/pmp.h b/arch/riscv/include/pmp.h index 20877f14fd26b..ca4f37f3a2aba 100644 --- a/arch/riscv/include/pmp.h +++ b/arch/riscv/include/pmp.h @@ -10,6 +10,7 @@ void z_riscv_pmp_init(void); void z_riscv_pmp_stackguard_prepare(struct k_thread *thread); void z_riscv_pmp_stackguard_enable(struct k_thread *thread); +void z_riscv_pmp_stackguard_disable(void); void z_riscv_pmp_usermode_init(struct k_thread *thread); void z_riscv_pmp_usermode_prepare(struct k_thread *thread); void z_riscv_pmp_usermode_enable(struct k_thread *thread);