riscv: new TLS-based arch_is_user_context() implementation
This reverts the bulk of commit c8bfc2afda
("riscv: make
arch_is_user_context() SMP compatible") and replaces it with a flag
stored in the thread local storage (TLS) area, therefore making TLS
mandatory for userspace support on RISC-V.
This has many advantages:
- The tp (x4) register is already dedicated by the standard for this
purpose, making TLS support almost free.
- This is very efficient, requiring only a single instruction to clear
and 2 instructions to set.
- This makes the SMP case much more efficient. No need for funky
exception code any longer.
- SMP and non-SMP now use the same implementation making maintenance
easier.
- The is_user_mode variable no longer requires a dedicated PMP mapping
and therefore freeing one PMP slot for other purposes.
Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
5f65dbcc9dab3d39473b05397e05.
This commit is contained in:
parent
3f8e326d1a
commit
00a9634c05
7 changed files with 34 additions and 104 deletions
|
@ -128,6 +128,7 @@ config RISCV_PMP
|
|||
select SRAM_REGION_PERMISSIONS
|
||||
select ARCH_MEM_DOMAIN_SYNCHRONOUS_API if USERSPACE
|
||||
select ARCH_MEM_DOMAIN_DATA if USERSPACE
|
||||
select THREAD_LOCAL_STORAGE if USERSPACE
|
||||
help
|
||||
MCU implements Physical Memory Protection.
|
||||
|
||||
|
|
|
@ -41,11 +41,9 @@
|
|||
op fa6, __z_arch_esf_t_fa6_OFFSET(reg) ;\
|
||||
op fa7, __z_arch_esf_t_fa7_OFFSET(reg) ;
|
||||
|
||||
#define DO_CALLER_SAVED_T0T1(op) \
|
||||
#define DO_CALLER_SAVED(op) \
|
||||
RV_E( op t0, __z_arch_esf_t_t0_OFFSET(sp) );\
|
||||
RV_E( op t1, __z_arch_esf_t_t1_OFFSET(sp) )
|
||||
|
||||
#define DO_CALLER_SAVED_REST(op) \
|
||||
RV_E( op t1, __z_arch_esf_t_t1_OFFSET(sp) );\
|
||||
RV_E( op t2, __z_arch_esf_t_t2_OFFSET(sp) );\
|
||||
RV_I( op t3, __z_arch_esf_t_t3_OFFSET(sp) );\
|
||||
RV_I( op t4, __z_arch_esf_t_t4_OFFSET(sp) );\
|
||||
|
@ -139,46 +137,12 @@ SECTION_FUNC(exception.entry, _isr_wrapper)
|
|||
|
||||
/* restore privileged stack pointer and zero the scratch reg */
|
||||
csrrw sp, mscratch, sp
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
j 2f
|
||||
|
||||
1: /*
|
||||
* We were in user space. Determine if it attempted to execute an
|
||||
* arch_is_user_context() based on mscratch access. We want to return
|
||||
* to u-mode with t0!=0 as quickly as possible if so.
|
||||
*/
|
||||
addi sp, sp, -__z_arch_esf_t_SIZEOF
|
||||
DO_CALLER_SAVED_T0T1(sr) ;
|
||||
/* First, determine if we had an illegal instruction exception. */
|
||||
csrr t0, mcause
|
||||
li t1, SOC_MCAUSE_EXP_MASK
|
||||
and t0, t0, t1
|
||||
addi t0, t0, -2 /* = 2 = illegal instruction */
|
||||
bnez t0, 3f
|
||||
/* Make sure it was actually a "csrr t0, mscratch" */
|
||||
csrr t0, mepc
|
||||
lw t0, 0(t0)
|
||||
li t1, 0x340022f3
|
||||
bne t0, t1, 3f
|
||||
/* So it was: skip over it and return leaving t0 clobbered. */
|
||||
csrr t0, mepc
|
||||
addi t0, t0, 4
|
||||
csrw mepc, t0
|
||||
lr t1, __z_arch_esf_t_t1_OFFSET(sp)
|
||||
addi sp, sp, __z_arch_esf_t_SIZEOF
|
||||
/* restore user stack pointer and leave */
|
||||
csrrw sp, mscratch, sp
|
||||
mret
|
||||
2:
|
||||
#endif /* CONFIG_SMP */
|
||||
1:
|
||||
#endif /* CONFIG_USERSPACE */
|
||||
#endif
|
||||
|
||||
/* Save caller-saved registers on current thread stack. */
|
||||
addi sp, sp, -__z_arch_esf_t_SIZEOF
|
||||
DO_CALLER_SAVED_T0T1(sr) ;
|
||||
3: DO_CALLER_SAVED_REST(sr) ;
|
||||
DO_CALLER_SAVED(sr) ;
|
||||
|
||||
/* Save s0 in the esf and load it with &_current_cpu. */
|
||||
sr s0, __z_arch_esf_t_s0_OFFSET(sp)
|
||||
|
@ -199,17 +163,14 @@ SECTION_FUNC(exception.entry, _isr_wrapper)
|
|||
/* save stack value to be restored later */
|
||||
sr t0, __z_arch_esf_t_sp_OFFSET(sp)
|
||||
|
||||
#if defined(CONFIG_THREAD_LOCAL_STORAGE)
|
||||
/* Make sure tls pointer is sane */
|
||||
lr t0, ___cpu_t_current_OFFSET(s0)
|
||||
lr tp, _thread_offset_to_tls(t0)
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_SMP)
|
||||
/* Clear user mode variable */
|
||||
la t0, is_user_mode
|
||||
sw zero, 0(t0)
|
||||
#endif
|
||||
/* Clear our per-thread usermode flag */
|
||||
lui t0, %tprel_hi(is_user_mode)
|
||||
add t0, t0, tp, %tprel_add(is_user_mode)
|
||||
sb zero, %tprel_lo(is_user_mode)(t0)
|
||||
#endif
|
||||
|
||||
/* Save MEPC register */
|
||||
|
@ -608,12 +569,11 @@ no_fp: /* make sure this is reflected in the restored mstatus */
|
|||
call z_riscv_pmp_usermode_enable
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_SMP)
|
||||
/* Set user mode variable */
|
||||
li t0, 1
|
||||
la t1, is_user_mode
|
||||
sw t0, 0(t1)
|
||||
#endif
|
||||
/* Set our per-thread usermode flag */
|
||||
li t1, 1
|
||||
lui t0, %tprel_hi(is_user_mode)
|
||||
add t0, t0, tp, %tprel_add(is_user_mode)
|
||||
sb t1, %tprel_lo(is_user_mode)(t0)
|
||||
|
||||
/* load scratch reg with stack pointer for next exception entry */
|
||||
add t0, sp, __z_arch_esf_t_SIZEOF
|
||||
|
@ -625,8 +585,7 @@ no_fp: /* make sure this is reflected in the restored mstatus */
|
|||
lr s0, __z_arch_esf_t_s0_OFFSET(sp)
|
||||
|
||||
/* Restore caller-saved registers from thread stack */
|
||||
DO_CALLER_SAVED_T0T1(lr)
|
||||
DO_CALLER_SAVED_REST(lr)
|
||||
DO_CALLER_SAVED(lr)
|
||||
|
||||
#ifdef CONFIG_USERSPACE
|
||||
/* retrieve saved stack pointer */
|
||||
|
|
|
@ -434,15 +434,6 @@ void z_riscv_pmp_usermode_prepare(struct k_thread *thread)
|
|||
/* Retrieve pmpcfg0 partial content from global entries */
|
||||
thread->arch.u_mode_pmpcfg_regs[0] = global_pmp_cfg[0];
|
||||
|
||||
#if !defined(CONFIG_SMP)
|
||||
/* Map the is_user_mode variable */
|
||||
extern uint32_t is_user_mode;
|
||||
|
||||
set_pmp_entry(&index, PMP_R,
|
||||
(uintptr_t) &is_user_mode, sizeof(is_user_mode),
|
||||
PMP_U_MODE(thread));
|
||||
#endif
|
||||
|
||||
/* Map the usermode stack */
|
||||
set_pmp_entry(&index, PMP_R | PMP_W,
|
||||
thread->stack_info.start, thread->stack_info.size,
|
||||
|
@ -542,11 +533,6 @@ int arch_mem_domain_max_partitions_get(void)
|
|||
/* remove those slots dedicated to global entries */
|
||||
available_pmp_slots -= global_pmp_end_index;
|
||||
|
||||
#if !defined(CONFIG_SMP)
|
||||
/* One slot needed to map the is_user_mode variable */
|
||||
available_pmp_slots -= 1;
|
||||
#endif
|
||||
|
||||
/* At least one slot to map the user thread's stack */
|
||||
available_pmp_slots -= 1;
|
||||
|
||||
|
|
|
@ -32,6 +32,9 @@ void arch_start_cpu(int cpu_num, k_thread_stack_t *stack, int sz,
|
|||
|
||||
void z_riscv_secondary_cpu_init(int cpu_num)
|
||||
{
|
||||
#ifdef CONFIG_THREAD_LOCAL_STORAGE
|
||||
__asm__("mv tp, %0" : : "r" (z_idle_threads[cpu_num].tls));
|
||||
#endif
|
||||
#if defined(CONFIG_RISCV_SOC_INTERRUPT_INIT)
|
||||
soc_interrupt_init();
|
||||
#endif
|
||||
|
@ -40,9 +43,6 @@ void z_riscv_secondary_cpu_init(int cpu_num)
|
|||
#endif
|
||||
#ifdef CONFIG_SMP
|
||||
irq_enable(RISCV_MACHINE_SOFT_IRQ);
|
||||
#endif
|
||||
#ifdef CONFIG_THREAD_LOCAL_STORAGE
|
||||
__asm__("mv tp, %0" : : "r" (z_idle_threads[cpu_num].tls));
|
||||
#endif
|
||||
riscv_cpu_init[cpu_num].fn(riscv_cpu_init[cpu_num].arg);
|
||||
}
|
||||
|
|
|
@ -11,12 +11,11 @@
|
|||
#include <stdio.h>
|
||||
#include <pmp.h>
|
||||
|
||||
#if defined(CONFIG_USERSPACE) && !defined(CONFIG_SMP)
|
||||
#ifdef CONFIG_USERSPACE
|
||||
/*
|
||||
* Glogal variable used to know the current mode running.
|
||||
* Is not boolean because it must match the PMP granularity of the arch.
|
||||
* Per-thread (TLS) variable indicating whether execution is in user mode.
|
||||
*/
|
||||
uint32_t is_user_mode;
|
||||
__thread uint8_t is_user_mode;
|
||||
#endif
|
||||
|
||||
void arch_new_thread(struct k_thread *thread, k_thread_stack_t *stack,
|
||||
|
@ -246,9 +245,7 @@ FUNC_NORETURN void arch_user_mode_enter(k_thread_entry_t user_entry,
|
|||
/* exception stack has to be in mscratch */
|
||||
csr_write(mscratch, top_of_priv_stack);
|
||||
|
||||
#if !defined(CONFIG_SMP)
|
||||
is_user_mode = true;
|
||||
#endif
|
||||
|
||||
register void *a0 __asm__("a0") = user_entry;
|
||||
register void *a1 __asm__("a1") = p1;
|
||||
|
|
|
@ -26,6 +26,9 @@ extern "C" {
|
|||
|
||||
static ALWAYS_INLINE void arch_kernel_init(void)
|
||||
{
|
||||
#ifdef CONFIG_THREAD_LOCAL_STORAGE
|
||||
__asm__ volatile ("li tp, 0");
|
||||
#endif
|
||||
#ifdef CONFIG_USERSPACE
|
||||
csr_write(mscratch, 0);
|
||||
#endif
|
||||
|
|
|
@ -147,35 +147,19 @@ static inline uintptr_t arch_syscall_invoke0(uintptr_t call_id)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_USERSPACE
|
||||
register ulong_t riscv_tp_reg __asm__ ("tp");
|
||||
|
||||
static inline bool arch_is_user_context(void)
|
||||
{
|
||||
#ifdef CONFIG_SMP
|
||||
/*
|
||||
* This is painful. There is no way for u-mode code to know if we're
|
||||
* currently executing in u-mode without generating a fault, besides
|
||||
* stealing a general purpose register away from the standard ABI
|
||||
* that is. And a global variable doesn't work on SMP as this must be
|
||||
* per-CPU and we could be migrated to another CPU just at the right
|
||||
* moment to peek at the wrong CPU variable (and u-mode can't disable
|
||||
* preemption either).
|
||||
*
|
||||
* So, given that we'll have to pay the price of an exception entry
|
||||
* anyway, let's at least make it free to privileged threads by using
|
||||
* the mscratch register as the non-user context indicator (it must
|
||||
* be zero in m-mode for exception entry to work properly). In the
|
||||
* case of u-mode we'll simulate a proper return value in the
|
||||
* exception trap code. Let's settle on the return value in t0
|
||||
* and omit the volatile to give the compiler a chance to cache
|
||||
* the result.
|
||||
*/
|
||||
register ulong_t is_user __asm__ ("t1");
|
||||
__asm__ ("csrr %0, mscratch" : "=r" (is_user));
|
||||
return is_user != 0;
|
||||
#else
|
||||
/* don't try accessing TLS variables if tp is not initialized */
|
||||
if (riscv_tp_reg == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Defined in arch/riscv/core/thread.c */
|
||||
extern uint32_t is_user_mode;
|
||||
return is_user_mode;
|
||||
#endif
|
||||
extern __thread uint8_t is_user_mode;
|
||||
|
||||
return is_user_mode != 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue