diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 575c9871c1304..d3d1076b367c5 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -311,7 +311,6 @@ endif #RISCV_PMP config PMP_STACK_GUARD def_bool y - depends on MULTITHREADING depends on HW_STACK_PROTECTION config PMP_STACK_GUARD_MIN_SIZE diff --git a/arch/riscv/core/fatal.c b/arch/riscv/core/fatal.c index d5cbd2f4dc144..879ffab9a8095 100644 --- a/arch/riscv/core/fatal.c +++ b/arch/riscv/core/fatal.c @@ -172,11 +172,21 @@ static bool bad_stack_pointer(struct arch_esf *esf) } #endif /* CONFIG_USERSPACE */ +#if CONFIG_MULTITHREADING if (sp >= _current->stack_info.start - K_KERNEL_STACK_RESERVED && sp < _current->stack_info.start - K_KERNEL_STACK_RESERVED + Z_RISCV_STACK_GUARD_SIZE) { return true; } +#else + uintptr_t isr_stack = (uintptr_t)z_interrupt_stacks; + uintptr_t main_stack = (uintptr_t)z_main_stack; + + if ((sp >= isr_stack && sp < isr_stack + Z_RISCV_STACK_GUARD_SIZE) || + (sp >= main_stack && sp < main_stack + Z_RISCV_STACK_GUARD_SIZE)) { + return true; + } +#endif /* CONFIG_MULTITHREADING */ #endif /* CONFIG_PMP_STACK_GUARD */ #ifdef CONFIG_USERSPACE diff --git a/arch/riscv/core/pmp.c b/arch/riscv/core/pmp.c index e41eb8d4bb080..e29c8abd76d61 100644 --- a/arch/riscv/core/pmp.c +++ b/arch/riscv/core/pmp.c @@ -348,8 +348,8 @@ static unsigned int global_pmp_end_index; */ void z_riscv_pmp_init(void) { - unsigned long pmp_addr[5]; - unsigned long pmp_cfg[2]; + unsigned long pmp_addr[CONFIG_PMP_SLOTS]; + unsigned long pmp_cfg[CONFIG_PMP_SLOTS / PMPCFG_STRIDE]; unsigned int index = 0; /* The read-only area is always there for every mode */ @@ -370,6 +370,7 @@ void z_riscv_pmp_init(void) #endif #ifdef CONFIG_PMP_STACK_GUARD +#ifdef CONFIG_MULTITHREADING /* * Set the stack guard for this CPU's IRQ stack by making the bottom * addresses inaccessible. This will never change so we do it here @@ -396,6 +397,21 @@ void z_riscv_pmp_init(void) /* And forget about that last entry as we won't need it later */ index--; +#else + /* Without multithreading setup stack guards for IRQ and main stacks */ + set_pmp_entry(&index, PMP_NONE | PMP_L, + (uintptr_t)z_interrupt_stacks, + Z_RISCV_STACK_GUARD_SIZE, + pmp_addr, pmp_cfg, ARRAY_SIZE(pmp_addr)); + + set_pmp_entry(&index, PMP_NONE | PMP_L, + (uintptr_t)z_main_stack, + Z_RISCV_STACK_GUARD_SIZE, + pmp_addr, pmp_cfg, ARRAY_SIZE(pmp_addr)); + + /* Write those entries to PMP regs. */ + write_pmp_entries(0, index, true, pmp_addr, pmp_cfg, ARRAY_SIZE(pmp_addr)); +#endif /* CONFIG_MULTITHREADING */ #else /* Write those entries to PMP regs. */ write_pmp_entries(0, index, true, pmp_addr, pmp_cfg, ARRAY_SIZE(pmp_addr)); @@ -419,7 +435,6 @@ void z_riscv_pmp_init(void) } #endif - __ASSERT(index <= PMPCFG_STRIDE, "provision for one global word only"); global_pmp_cfg[0] = pmp_cfg[0]; global_pmp_last_addr = pmp_addr[index - 1]; global_pmp_end_index = index; @@ -454,6 +469,7 @@ static inline unsigned int z_riscv_pmp_thread_init(unsigned long *pmp_addr, #ifdef CONFIG_PMP_STACK_GUARD +#ifdef CONFIG_MULTITHREADING /** * @brief Prepare the PMP stackguard content for given thread. * @@ -511,6 +527,8 @@ void z_riscv_pmp_stackguard_enable(struct k_thread *thread) csr_set(mstatus, MSTATUS_MPRV); } +#endif /* CONFIG_MULTITHREADING */ + /** * @brief Remove PMP stackguard content to actual PMP registers */ diff --git a/kernel/include/kernel_internal.h b/kernel/include/kernel_internal.h index bb9ee4b01f1e6..94f90ce94624c 100644 --- a/kernel/include/kernel_internal.h +++ b/kernel/include/kernel_internal.h @@ -152,6 +152,7 @@ extern struct k_thread z_idle_threads[CONFIG_MP_MAX_NUM_CPUS]; #endif /* CONFIG_MULTITHREADING */ K_KERNEL_PINNED_STACK_ARRAY_DECLARE(z_interrupt_stacks, CONFIG_MP_MAX_NUM_CPUS, CONFIG_ISR_STACK_SIZE); +K_THREAD_STACK_DECLARE(z_main_stack, CONFIG_MAIN_STACK_SIZE); #ifdef CONFIG_GEN_PRIV_STACKS extern uint8_t *z_priv_stack_find(k_thread_stack_t *stack); diff --git a/tests/arch/riscv/pmp/no-multithreading/CMakeLists.txt b/tests/arch/riscv/pmp/no-multithreading/CMakeLists.txt new file mode 100644 index 0000000000000..e0f392177d6f3 --- /dev/null +++ b/tests/arch/riscv/pmp/no-multithreading/CMakeLists.txt @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(riscv_pmp) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) + +target_include_directories(app PRIVATE + ${ZEPHYR_BASE}/kernel/include + ${ZEPHYR_BASE}/arch/${ARCH}/include + ) diff --git a/tests/arch/riscv/pmp/no-multithreading/prj.conf b/tests/arch/riscv/pmp/no-multithreading/prj.conf new file mode 100644 index 0000000000000..7414c11237a8a --- /dev/null +++ b/tests/arch/riscv/pmp/no-multithreading/prj.conf @@ -0,0 +1,2 @@ +CONFIG_ZTEST=y +CONFIG_MULTITHREADING=n diff --git a/tests/arch/riscv/pmp/no-multithreading/src/main.c b/tests/arch/riscv/pmp/no-multithreading/src/main.c new file mode 100644 index 0000000000000..26ded61165f7a --- /dev/null +++ b/tests/arch/riscv/pmp/no-multithreading/src/main.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024 Marvell. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +static volatile ZTEST_BMEM bool valid_fault; + +void k_sys_fatal_error_handler(unsigned int reason, const struct arch_esf *pEsf) +{ + int rv = TC_PASS; + + TC_PRINT("Caught system error -- reason %d %d\n", reason, valid_fault); + if (!valid_fault) { + TC_PRINT("Fatal error was unexpected, aborting...\n"); + rv = TC_FAIL; + } + TC_END_RESULT_CUSTOM(rv, "test_pmp"); + TC_END_REPORT(rv); + arch_system_halt(reason); +} + +#ifdef CONFIG_PMP_STACK_GUARD +static void check_isr_stack_guard(void) +{ + char *isr_stack = (char *)z_interrupt_stacks; + + valid_fault = true; + *isr_stack = 42; +} + +static void check_main_stack_guard(void) +{ + char *main_stack = (char *)z_main_stack; + + valid_fault = true; + *main_stack = 42; +} + +#else + +static void check_isr_stack_guard(void) +{ + ztest_test_skip(); +} + +static void check_main_stack_guard(void) +{ + ztest_test_skip(); +} + +#endif /* CONFIG_PMP_STACK_GUARD */ + +typedef void (*pmp_test_func_t)(void); + +static const pmp_test_func_t pmp_test_func[] = { + check_isr_stack_guard, + check_main_stack_guard, +}; + +/** + * @brief Verify RISC-V specific PMP stack guard regions. + * @details Manually write to the protected stack region to trigger fatal error. + */ +ZTEST(riscv_pmp_no_mt, test_pmp) +{ +#ifndef PMP_TEST_FUNC_IDX +#define PMP_TEST_FUNC_IDX 0 +#endif + pmp_test_func[PMP_TEST_FUNC_IDX](); + + zassert_unreachable("Write to stack guard did not fault"); + TC_END_REPORT(TC_FAIL); +} + +ZTEST_SUITE(riscv_pmp_no_mt, NULL, NULL, NULL, NULL, NULL); diff --git a/tests/arch/riscv/pmp/no-multithreading/testcase.yaml b/tests/arch/riscv/pmp/no-multithreading/testcase.yaml new file mode 100644 index 0000000000000..d8f887fa34121 --- /dev/null +++ b/tests/arch/riscv/pmp/no-multithreading/testcase.yaml @@ -0,0 +1,13 @@ +common: + platform_allow: + - qemu_riscv32 + - qemu_riscv32e + - qemu_riscv64 + filter: CONFIG_RISCV_PMP + ignore_faults: true + +tests: + arch.riscv.pmp.no-mt.isr-stack-guard: + extra_args: EXTRA_CFLAGS=-DPMP_TEST_FUNC_IDX=0 + arch.riscv.pmp.no-mt.main-stack-guard: + extra_args: EXTRA_CFLAGS=-DPMP_TEST_FUNC_IDX=1