From 169eea1ef072c8403277a66313b00258080ac92c Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Wed, 21 Sep 2022 14:43:52 -0700 Subject: [PATCH] riscv: Add sigcontext save/restore for vector V state needs to be preserved across signal handling on user stack. To avoid glibc ABI break, this is not added to struct sigcontext (just as for int/fp regs) but to struct rt_sigframe. Also this is all done dynamically (vs. some static allocation) to cleanly handle implementation defined VLEN wide V-regs. We also borrow arm64 style of "context header" to tag the extension state to allow for easy integration of future extensions. Co-developed-by: Vincent Chen Co-developed-by: Greentime Hu Signed-off-by: Vincent Chen Signed-off-by: Greentime Hu Signed-off-by: Vineet Gupta [vineetg: reworked to not change struct sigcontext, wireup init_rt_signal_env] --- arch/riscv/include/asm/processor.h | 1 + arch/riscv/include/uapi/asm/sigcontext.h | 18 +++ arch/riscv/kernel/asm-offsets.c | 2 + arch/riscv/kernel/setup.c | 2 + arch/riscv/kernel/signal.c | 171 +++++++++++++++++++++-- 5 files changed, 186 insertions(+), 8 deletions(-) diff --git a/arch/riscv/include/asm/processor.h b/arch/riscv/include/asm/processor.h index 95917a2b24f9..854854b377b2 100644 --- a/arch/riscv/include/asm/processor.h +++ b/arch/riscv/include/asm/processor.h @@ -85,6 +85,7 @@ int riscv_of_parent_hartid(struct device_node *node, unsigned long *hartid); extern void riscv_fill_hwcap(void); extern int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src); +void init_rt_signal_env(void); #endif /* __ASSEMBLY__ */ diff --git a/arch/riscv/include/uapi/asm/sigcontext.h b/arch/riscv/include/uapi/asm/sigcontext.h index 84f2dfcfdbce..411bf6985784 100644 --- a/arch/riscv/include/uapi/asm/sigcontext.h +++ b/arch/riscv/include/uapi/asm/sigcontext.h @@ -8,6 +8,24 @@ #include +/* The Magic number for signal context frame header. */ +#define RVV_MAGIC 0x53465457 +#define END_MAGIC 0x0 + +/* The size of END signal context header. */ +#define END_HDR_SIZE 0x0 + +/* Every optional extension state needs to have the hdr. */ +struct __riscv_ctx_hdr { + __u32 magic; + __u32 size; +}; + +struct __sc_riscv_v_state { + struct __riscv_ctx_hdr head; + struct __riscv_v_state v_state; +} __attribute__((aligned(16))); + /* * Signal context structure * diff --git a/arch/riscv/kernel/asm-offsets.c b/arch/riscv/kernel/asm-offsets.c index 37e3e6a8d877..80316ef7bb78 100644 --- a/arch/riscv/kernel/asm-offsets.c +++ b/arch/riscv/kernel/asm-offsets.c @@ -75,6 +75,8 @@ void asm_offsets(void) OFFSET(TSK_STACK_CANARY, task_struct, stack_canary); #endif + OFFSET(RISCV_V_STATE_MAGIC, __riscv_ctx_hdr, magic); + OFFSET(RISCV_V_STATE_SIZE, __riscv_ctx_hdr, size); OFFSET(RISCV_V_STATE_VSTART, __riscv_v_state, vstart); OFFSET(RISCV_V_STATE_VL, __riscv_v_state, vl); OFFSET(RISCV_V_STATE_VTYPE, __riscv_v_state, vtype); diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c index 2dfc463b86bb..aa0eedd3b890 100644 --- a/arch/riscv/kernel/setup.c +++ b/arch/riscv/kernel/setup.c @@ -299,6 +299,8 @@ void __init setup_arch(char **cmdline_p) riscv_init_cbom_blocksize(); riscv_fill_hwcap(); apply_boot_alternatives(); + /* needs to be after riscv_fill_hwcap */ + init_rt_signal_env(); } static int __init topology_init(void) diff --git a/arch/riscv/kernel/signal.c b/arch/riscv/kernel/signal.c index 5c591123c440..ee234c319e5b 100644 --- a/arch/riscv/kernel/signal.c +++ b/arch/riscv/kernel/signal.c @@ -21,15 +21,27 @@ #include extern u32 __user_rt_sigreturn[2]; +static size_t rvv_sc_size; #define DEBUG_SIG 0 struct rt_sigframe { struct siginfo info; - struct ucontext uc; #ifndef CONFIG_MMU u32 sigreturn_code[2]; #endif + struct ucontext uc; + /* + * Placeholder for additional state for V ext (and others in future). + * - Not added to struct sigcontext (unlike int/fp regs) to remain + * compatible with existing glibc struct sigcontext + * - Not added here explicitly either to allow for + * - Implementation defined VLEN wide V reg + * - Ability to do this per process + * The actual V state struct is defined in uapi header. + * Note: The alignment of 16 is ABI mandated for stack entries. + */ + __u8 sc_extn[] __attribute__((__aligned__(16))); }; #ifdef CONFIG_FPU @@ -86,16 +98,142 @@ static long save_fp_state(struct pt_regs *regs, #define restore_fp_state(task, regs) (0) #endif -static long restore_sigcontext(struct pt_regs *regs, - struct sigcontext __user *sc) +#ifdef CONFIG_RISCV_ISA_V + +static long save_v_state(struct pt_regs *regs, void **sc_vec) +{ + /* + * Put __sc_riscv_v_state to the user's signal context space pointed + * by sc_vec and the datap point the address right + * after __sc_riscv_v_state. + */ + struct __sc_riscv_v_state __user *state = (struct __sc_riscv_v_state *) (*sc_vec); + void __user *datap = state + 1; + long err; + + err = __put_user(RVV_MAGIC, &state->head.magic); + err = __put_user(rvv_sc_size, &state->head.size); + + vstate_save(current, regs); + /* Copy additional vstate (except V regfile). */ + err = __copy_to_user(&state->v_state, ¤t->thread.vstate, + RISCV_V_STATE_DATAP); + if (unlikely(err)) + return err; + + /* Copy the pointer datap itself. */ + err = __put_user(datap, &state->v_state.datap); + if (unlikely(err)) + return err; + + /* Copy the V regfile to user space datap. */ + err = __copy_to_user(datap, current->thread.vstate.datap, riscv_vsize); + + *sc_vec += rvv_sc_size; + + return err; +} + +static long restore_v_state(struct pt_regs *regs, void **sc_vec) +{ + long err; + struct __sc_riscv_v_state __user *state = (struct __sc_riscv_v_state *)(*sc_vec); + void __user *datap; + + /* ctx_hdr check for RVV_MAGIC already done in caller. */ + + /* Copy everything of __sc_riscv_v_state except datap. */ + err = __copy_from_user(¤t->thread.vstate, &state->v_state, + RISCV_V_STATE_DATAP); + if (unlikely(err)) + return err; + + /* Copy the pointer datap itself. */ + err = __get_user(datap, &state->v_state.datap); + if (unlikely(err)) + return err; + + /* Copy the whole vector content from user space datap. */ + err = __copy_from_user(current->thread.vstate.datap, datap, riscv_vsize); + if (unlikely(err)) + return err; + + vstate_restore(current, regs); + + *sc_vec += rvv_sc_size; + + return err; +} + +#else +#define save_v_state(task, regs) (0) +#define restore_v_state(task, regs) (0) +#endif + +static long restore_sigcontext(struct rt_sigframe __user *frame, + struct pt_regs *regs) { + struct sigcontext __user *sc = &frame->uc.uc_mcontext; + void *sc_extn = &frame->sc_extn; long err; + /* sc_regs is structured the same as the start of pt_regs */ err = __copy_from_user(regs, &sc->sc_regs, sizeof(sc->sc_regs)); /* Restore the floating-point state. */ if (has_fpu()) err |= restore_fp_state(regs, &sc->sc_fpregs); + + while (1 && !err) { + struct __riscv_ctx_hdr *head = (struct __riscv_ctx_hdr *)sc_extn; + __u32 magic, size; + + err |= __get_user(magic, &head->magic); + err |= __get_user(size, &head->size); + if (err) + goto done; + + switch (magic) { + case END_MAGIC: + if (size != END_HDR_SIZE) + goto invalid; + goto done; + case RVV_MAGIC: + if (!has_vector() || (size != rvv_sc_size)) + goto invalid; + err |= restore_v_state(regs, &sc_extn); + break; + default: + goto invalid; + } + } +done: return err; + +invalid: + return -EINVAL; +} + +static size_t cal_rt_frame_size(void) +{ + struct rt_sigframe __user *frame; + static size_t frame_size; + size_t total_context_size = 0; + + if (frame_size) + goto done; + + total_context_size = sizeof(*frame); + + if (has_vector()) + total_context_size += rvv_sc_size; + + /* Add a __riscv_ctx_hdr for END signal context header. */ + total_context_size += sizeof(struct __riscv_ctx_hdr); + + frame_size = round_up(total_context_size, 16); +done: + return frame_size; + } SYSCALL_DEFINE0(rt_sigreturn) @@ -104,13 +242,14 @@ SYSCALL_DEFINE0(rt_sigreturn) struct rt_sigframe __user *frame; struct task_struct *task; sigset_t set; + size_t frame_size = cal_rt_frame_size(); /* Always make any pending restarted system calls return -EINTR */ current->restart_block.fn = do_no_restart_syscall; frame = (struct rt_sigframe __user *)regs->sp; - if (!access_ok(frame, sizeof(*frame))) + if (!access_ok(frame, frame_size)) goto badframe; if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) @@ -118,7 +257,7 @@ SYSCALL_DEFINE0(rt_sigreturn) set_current_blocked(&set); - if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) + if (restore_sigcontext(frame, regs)) goto badframe; if (restore_altstack(&frame->uc.uc_stack)) @@ -141,15 +280,24 @@ SYSCALL_DEFINE0(rt_sigreturn) } static long setup_sigcontext(struct rt_sigframe __user *frame, - struct pt_regs *regs) + struct pt_regs *regs) { struct sigcontext __user *sc = &frame->uc.uc_mcontext; + void *sc_extn = &frame->sc_extn; long err; + /* sc_regs is structured the same as the start of pt_regs */ err = __copy_to_user(&sc->sc_regs, regs, sizeof(sc->sc_regs)); /* Save the floating-point state. */ if (has_fpu()) err |= save_fp_state(regs, &sc->sc_fpregs); + /* Save the vector state. */ + if (has_vector()) + err |= save_v_state(regs, &sc_extn); + + /* Put END __riscv_ctx_hdr at the end. */ + err = __put_user(END_MAGIC, &((struct __riscv_ctx_hdr *)sc_extn)->magic); + err = __put_user(END_HDR_SIZE, &((struct __riscv_ctx_hdr *)sc_extn)->size); return err; } @@ -180,10 +328,11 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { struct rt_sigframe __user *frame; + size_t frame_size = cal_rt_frame_size(); long err = 0; - frame = get_sigframe(ksig, regs, sizeof(*frame)); - if (!access_ok(frame, sizeof(*frame))) + frame = get_sigframe(ksig, regs, frame_size); + if (!access_ok(frame, frame_size)) return -EFAULT; err |= copy_siginfo_to_user(&frame->info, &ksig->info); @@ -329,3 +478,9 @@ asmlinkage __visible void do_notify_resume(struct pt_regs *regs, if (thread_info_flags & _TIF_NOTIFY_RESUME) resume_user_mode_work(regs); } + +void __init init_rt_signal_env(void) +{ + /* Vector regfile + control regs. */ + rvv_sc_size = sizeof(struct __sc_riscv_v_state) + riscv_vsize; +} -- 2.34.1