arch: riscv: support CONFIG_USERSPACE in CONFIG_RISCV_CURRENT_VIA_GP

Reset the the `gp` register to `_kernel->cpus[i].current` when
`CONFIG_USERSPACE` is enabled on exception to keep it sane.

Updated the testcase to test both `CONFIG_RISCV_GP` and
`CONFIG_RISCV_CURRENT_VIA_GP`.

Signed-off-by: Yong Cong Sin <ycsin@meta.com>
Signed-off-by: Yong Cong Sin <yongcong.sin@gmail.com>
This commit is contained in:
Yong Cong Sin 2024-11-25 12:31:13 +08:00 committed by Benjamin Cabé
commit 033804e266
5 changed files with 55 additions and 7 deletions

View file

@ -1,3 +1,2 @@
CONFIG_ZTEST=y
CONFIG_RISCV_GP=y
CONFIG_TEST_USERSPACE=y

View file

@ -10,6 +10,10 @@
#include <zephyr/kernel.h>
#include <zephyr/ztest.h>
#if !defined(CONFIG_RISCV_GP) && !defined(CONFIG_RISCV_CURRENT_VIA_GP)
#error "CONFIG_RISCV_GP or CONFIG_RISCV_CURRENT_VIA_GP must be enabled for this test"
#endif
#define ROGUE_USER_STACK_SZ 2048
static struct k_thread rogue_user_thread;
@ -18,22 +22,58 @@ static K_THREAD_STACK_DEFINE(rogue_user_stack, ROGUE_USER_STACK_SZ);
static void rogue_user_fn(void *p1, void *p2, void *p3)
{
zassert_true(k_is_user_context());
uintptr_t gp_val = reg_read(gp);
uintptr_t gp_test_val;
/* Make sure that `gp` is as expected */
if (IS_ENABLED(CONFIG_RISCV_GP)) {
__asm__ volatile("la %0, __global_pointer$" : "=r" (gp_test_val));
} else { /* CONFIG_RISCV_CURRENT_VIA_GP */
gp_test_val = (uintptr_t)k_current_get();
}
/* Corrupt `gp` reg */
reg_write(gp, 0xbad);
zassert_equal(reg_read(gp), 0xbad);
/* Make sure that `gp` is corrupted */
if (IS_ENABLED(CONFIG_RISCV_GP)) {
zassert_equal(reg_read(gp), 0xbad);
} else { /* CONFIG_RISCV_CURRENT_VIA_GP */
zassert_equal((uintptr_t)arch_current_thread(), 0xbad);
}
/* Sleep to force a context switch, which will sanitize `gp` */
k_msleep(50);
/* Make sure that `gp` is sane again */
if (IS_ENABLED(CONFIG_RISCV_GP)) {
__asm__ volatile("la %0, __global_pointer$" : "=r" (gp_test_val));
} else { /* CONFIG_RISCV_CURRENT_VIA_GP */
gp_test_val = (uintptr_t)k_current_get();
}
zassert_equal(gp_val, gp_test_val);
}
ZTEST_USER(riscv_gp, test_gp_value)
{
uintptr_t gp_val = reg_read(gp);
uintptr_t gp_test_val;
k_tid_t th;
zassert_not_equal(gp_val, 0);
if (IS_ENABLED(CONFIG_RISCV_GP)) {
__asm__ volatile("la %0, __global_pointer$" : "=r" (gp_test_val));
} else { /* CONFIG_RISCV_CURRENT_VIA_GP */
gp_test_val = (uintptr_t)k_current_get();
}
zassert_equal(gp_val, gp_test_val);
/* Create and run a rogue thread to corrupt the `gp` */
th = k_thread_create(&rogue_user_thread, rogue_user_stack, ROGUE_USER_STACK_SZ,
rogue_user_fn, NULL, NULL, NULL, -1, K_USER, K_NO_WAIT);
zassert_ok(k_thread_join(th, K_FOREVER));
/* Make sure that `gp` is the same as before a rogue thread was executed */
zassert_equal(reg_read(gp), gp_val, "`gp` corrupted by user thread");
}

View file

@ -3,6 +3,13 @@ common:
ignore_qemu_crash: true
tags: kernel riscv
platform_allow:
- qemu_riscv64
- qemu_riscv64/qemu_virt_riscv64/smp
tests:
arch.riscv64.riscv_gp: {}
arch.riscv64.riscv_gp.relative_addressing:
extra_configs:
- CONFIG_RISCV_GP=y
- CONFIG_RISCV_CURRENT_VIA_GP=n
arch.riscv64.riscv_gp.thread_pointer:
extra_configs:
- CONFIG_RISCV_CURRENT_VIA_GP=y
- CONFIG_RISCV_GP=n