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:
parent
1eeee010bd
commit
033804e266
5 changed files with 55 additions and 7 deletions
|
@ -30,7 +30,7 @@ config RISCV_GP
|
||||||
|
|
||||||
config RISCV_CURRENT_VIA_GP
|
config RISCV_CURRENT_VIA_GP
|
||||||
bool "Store current thread into the global pointer (GP) register"
|
bool "Store current thread into the global pointer (GP) register"
|
||||||
depends on !RISCV_GP && !USERSPACE
|
depends on !RISCV_GP
|
||||||
depends on MP_MAX_NUM_CPUS > 1
|
depends on MP_MAX_NUM_CPUS > 1
|
||||||
select ARCH_HAS_CUSTOM_CURRENT_IMPL
|
select ARCH_HAS_CUSTOM_CURRENT_IMPL
|
||||||
help
|
help
|
||||||
|
|
|
@ -169,7 +169,9 @@ SECTION_FUNC(exception.entry, _isr_wrapper)
|
||||||
.option norelax
|
.option norelax
|
||||||
la gp, __global_pointer$
|
la gp, __global_pointer$
|
||||||
.option pop
|
.option pop
|
||||||
#endif /* CONFIG_RISCV_GP */
|
#elif defined(CONFIG_RISCV_CURRENT_VIA_GP)
|
||||||
|
lr gp, ___cpu_t_current_OFFSET(s0)
|
||||||
|
#endif /* CONFIG_RISCV_GP / CONFIG_RISCV_CURRENT_VIA_GP */
|
||||||
|
|
||||||
/* Clear our per-thread usermode flag */
|
/* Clear our per-thread usermode flag */
|
||||||
lui t0, %tprel_hi(is_user_mode)
|
lui t0, %tprel_hi(is_user_mode)
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
CONFIG_ZTEST=y
|
CONFIG_ZTEST=y
|
||||||
CONFIG_RISCV_GP=y
|
|
||||||
CONFIG_TEST_USERSPACE=y
|
CONFIG_TEST_USERSPACE=y
|
||||||
|
|
|
@ -10,6 +10,10 @@
|
||||||
#include <zephyr/kernel.h>
|
#include <zephyr/kernel.h>
|
||||||
#include <zephyr/ztest.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
|
#define ROGUE_USER_STACK_SZ 2048
|
||||||
|
|
||||||
static struct k_thread rogue_user_thread;
|
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)
|
static void rogue_user_fn(void *p1, void *p2, void *p3)
|
||||||
{
|
{
|
||||||
zassert_true(k_is_user_context());
|
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);
|
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)
|
ZTEST_USER(riscv_gp, test_gp_value)
|
||||||
{
|
{
|
||||||
uintptr_t gp_val = reg_read(gp);
|
uintptr_t gp_val = reg_read(gp);
|
||||||
|
uintptr_t gp_test_val;
|
||||||
k_tid_t th;
|
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,
|
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);
|
rogue_user_fn, NULL, NULL, NULL, -1, K_USER, K_NO_WAIT);
|
||||||
zassert_ok(k_thread_join(th, K_FOREVER));
|
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");
|
zassert_equal(reg_read(gp), gp_val, "`gp` corrupted by user thread");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,13 @@ common:
|
||||||
ignore_qemu_crash: true
|
ignore_qemu_crash: true
|
||||||
tags: kernel riscv
|
tags: kernel riscv
|
||||||
platform_allow:
|
platform_allow:
|
||||||
- qemu_riscv64
|
- qemu_riscv64/qemu_virt_riscv64/smp
|
||||||
tests:
|
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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue