162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
462306a36Sopenharmony_ci *  Chen Liqin <liqin.chen@sunplusct.com>
562306a36Sopenharmony_ci *  Lennox Wu <lennox.wu@sunplusct.com>
662306a36Sopenharmony_ci * Copyright (C) 2012 Regents of the University of California
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/compat.h>
1062306a36Sopenharmony_ci#include <linux/signal.h>
1162306a36Sopenharmony_ci#include <linux/uaccess.h>
1262306a36Sopenharmony_ci#include <linux/syscalls.h>
1362306a36Sopenharmony_ci#include <linux/resume_user_mode.h>
1462306a36Sopenharmony_ci#include <linux/linkage.h>
1562306a36Sopenharmony_ci#include <linux/entry-common.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <asm/ucontext.h>
1862306a36Sopenharmony_ci#include <asm/vdso.h>
1962306a36Sopenharmony_ci#include <asm/signal.h>
2062306a36Sopenharmony_ci#include <asm/signal32.h>
2162306a36Sopenharmony_ci#include <asm/switch_to.h>
2262306a36Sopenharmony_ci#include <asm/vector.h>
2362306a36Sopenharmony_ci#include <asm/csr.h>
2462306a36Sopenharmony_ci#include <asm/cacheflush.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ciunsigned long signal_minsigstksz __ro_after_init;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ciextern u32 __user_rt_sigreturn[2];
2962306a36Sopenharmony_cistatic size_t riscv_v_sc_size __ro_after_init;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define DEBUG_SIG 0
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistruct rt_sigframe {
3462306a36Sopenharmony_ci	struct siginfo info;
3562306a36Sopenharmony_ci	struct ucontext uc;
3662306a36Sopenharmony_ci#ifndef CONFIG_MMU
3762306a36Sopenharmony_ci	u32 sigreturn_code[2];
3862306a36Sopenharmony_ci#endif
3962306a36Sopenharmony_ci};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#ifdef CONFIG_FPU
4262306a36Sopenharmony_cistatic long restore_fp_state(struct pt_regs *regs,
4362306a36Sopenharmony_ci			     union __riscv_fp_state __user *sc_fpregs)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	long err;
4662306a36Sopenharmony_ci	struct __riscv_d_ext_state __user *state = &sc_fpregs->d;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	err = __copy_from_user(&current->thread.fstate, state, sizeof(*state));
4962306a36Sopenharmony_ci	if (unlikely(err))
5062306a36Sopenharmony_ci		return err;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	fstate_restore(current, regs);
5362306a36Sopenharmony_ci	return 0;
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic long save_fp_state(struct pt_regs *regs,
5762306a36Sopenharmony_ci			  union __riscv_fp_state __user *sc_fpregs)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	long err;
6062306a36Sopenharmony_ci	struct __riscv_d_ext_state __user *state = &sc_fpregs->d;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	fstate_save(current, regs);
6362306a36Sopenharmony_ci	err = __copy_to_user(state, &current->thread.fstate, sizeof(*state));
6462306a36Sopenharmony_ci	return err;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci#else
6762306a36Sopenharmony_ci#define save_fp_state(task, regs) (0)
6862306a36Sopenharmony_ci#define restore_fp_state(task, regs) (0)
6962306a36Sopenharmony_ci#endif
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci#ifdef CONFIG_RISCV_ISA_V
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic long save_v_state(struct pt_regs *regs, void __user **sc_vec)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct __riscv_ctx_hdr __user *hdr;
7662306a36Sopenharmony_ci	struct __sc_riscv_v_state __user *state;
7762306a36Sopenharmony_ci	void __user *datap;
7862306a36Sopenharmony_ci	long err;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	hdr = *sc_vec;
8162306a36Sopenharmony_ci	/* Place state to the user's signal context space after the hdr */
8262306a36Sopenharmony_ci	state = (struct __sc_riscv_v_state __user *)(hdr + 1);
8362306a36Sopenharmony_ci	/* Point datap right after the end of __sc_riscv_v_state */
8462306a36Sopenharmony_ci	datap = state + 1;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	/* datap is designed to be 16 byte aligned for better performance */
8762306a36Sopenharmony_ci	WARN_ON(unlikely(!IS_ALIGNED((unsigned long)datap, 16)));
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	riscv_v_vstate_save(current, regs);
9062306a36Sopenharmony_ci	/* Copy everything of vstate but datap. */
9162306a36Sopenharmony_ci	err = __copy_to_user(&state->v_state, &current->thread.vstate,
9262306a36Sopenharmony_ci			     offsetof(struct __riscv_v_ext_state, datap));
9362306a36Sopenharmony_ci	/* Copy the pointer datap itself. */
9462306a36Sopenharmony_ci	err |= __put_user(datap, &state->v_state.datap);
9562306a36Sopenharmony_ci	/* Copy the whole vector content to user space datap. */
9662306a36Sopenharmony_ci	err |= __copy_to_user(datap, current->thread.vstate.datap, riscv_v_vsize);
9762306a36Sopenharmony_ci	/* Copy magic to the user space after saving  all vector conetext */
9862306a36Sopenharmony_ci	err |= __put_user(RISCV_V_MAGIC, &hdr->magic);
9962306a36Sopenharmony_ci	err |= __put_user(riscv_v_sc_size, &hdr->size);
10062306a36Sopenharmony_ci	if (unlikely(err))
10162306a36Sopenharmony_ci		return err;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	/* Only progress the sv_vec if everything has done successfully  */
10462306a36Sopenharmony_ci	*sc_vec += riscv_v_sc_size;
10562306a36Sopenharmony_ci	return 0;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/*
10962306a36Sopenharmony_ci * Restore Vector extension context from the user's signal frame. This function
11062306a36Sopenharmony_ci * assumes a valid extension header. So magic and size checking must be done by
11162306a36Sopenharmony_ci * the caller.
11262306a36Sopenharmony_ci */
11362306a36Sopenharmony_cistatic long __restore_v_state(struct pt_regs *regs, void __user *sc_vec)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	long err;
11662306a36Sopenharmony_ci	struct __sc_riscv_v_state __user *state = sc_vec;
11762306a36Sopenharmony_ci	void __user *datap;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	/* Copy everything of __sc_riscv_v_state except datap. */
12062306a36Sopenharmony_ci	err = __copy_from_user(&current->thread.vstate, &state->v_state,
12162306a36Sopenharmony_ci			       offsetof(struct __riscv_v_ext_state, datap));
12262306a36Sopenharmony_ci	if (unlikely(err))
12362306a36Sopenharmony_ci		return err;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	/* Copy the pointer datap itself. */
12662306a36Sopenharmony_ci	err = __get_user(datap, &state->v_state.datap);
12762306a36Sopenharmony_ci	if (unlikely(err))
12862306a36Sopenharmony_ci		return err;
12962306a36Sopenharmony_ci	/*
13062306a36Sopenharmony_ci	 * Copy the whole vector content from user space datap. Use
13162306a36Sopenharmony_ci	 * copy_from_user to prevent information leak.
13262306a36Sopenharmony_ci	 */
13362306a36Sopenharmony_ci	err = copy_from_user(current->thread.vstate.datap, datap, riscv_v_vsize);
13462306a36Sopenharmony_ci	if (unlikely(err))
13562306a36Sopenharmony_ci		return err;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	riscv_v_vstate_restore(current, regs);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	return err;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci#else
14262306a36Sopenharmony_ci#define save_v_state(task, regs) (0)
14362306a36Sopenharmony_ci#define __restore_v_state(task, regs) (0)
14462306a36Sopenharmony_ci#endif
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic long restore_sigcontext(struct pt_regs *regs,
14762306a36Sopenharmony_ci	struct sigcontext __user *sc)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	void __user *sc_ext_ptr = &sc->sc_extdesc.hdr;
15062306a36Sopenharmony_ci	__u32 rsvd;
15162306a36Sopenharmony_ci	long err;
15262306a36Sopenharmony_ci	/* sc_regs is structured the same as the start of pt_regs */
15362306a36Sopenharmony_ci	err = __copy_from_user(regs, &sc->sc_regs, sizeof(sc->sc_regs));
15462306a36Sopenharmony_ci	if (unlikely(err))
15562306a36Sopenharmony_ci		return err;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	/* Restore the floating-point state. */
15862306a36Sopenharmony_ci	if (has_fpu()) {
15962306a36Sopenharmony_ci		err = restore_fp_state(regs, &sc->sc_fpregs);
16062306a36Sopenharmony_ci		if (unlikely(err))
16162306a36Sopenharmony_ci			return err;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* Check the reserved word before extensions parsing */
16562306a36Sopenharmony_ci	err = __get_user(rsvd, &sc->sc_extdesc.reserved);
16662306a36Sopenharmony_ci	if (unlikely(err))
16762306a36Sopenharmony_ci		return err;
16862306a36Sopenharmony_ci	if (unlikely(rsvd))
16962306a36Sopenharmony_ci		return -EINVAL;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	while (!err) {
17262306a36Sopenharmony_ci		__u32 magic, size;
17362306a36Sopenharmony_ci		struct __riscv_ctx_hdr __user *head = sc_ext_ptr;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci		err |= __get_user(magic, &head->magic);
17662306a36Sopenharmony_ci		err |= __get_user(size, &head->size);
17762306a36Sopenharmony_ci		if (unlikely(err))
17862306a36Sopenharmony_ci			return err;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci		sc_ext_ptr += sizeof(*head);
18162306a36Sopenharmony_ci		switch (magic) {
18262306a36Sopenharmony_ci		case END_MAGIC:
18362306a36Sopenharmony_ci			if (size != END_HDR_SIZE)
18462306a36Sopenharmony_ci				return -EINVAL;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci			return 0;
18762306a36Sopenharmony_ci		case RISCV_V_MAGIC:
18862306a36Sopenharmony_ci			if (!has_vector() || !riscv_v_vstate_query(regs) ||
18962306a36Sopenharmony_ci			    size != riscv_v_sc_size)
19062306a36Sopenharmony_ci				return -EINVAL;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci			err = __restore_v_state(regs, sc_ext_ptr);
19362306a36Sopenharmony_ci			break;
19462306a36Sopenharmony_ci		default:
19562306a36Sopenharmony_ci			return -EINVAL;
19662306a36Sopenharmony_ci		}
19762306a36Sopenharmony_ci		sc_ext_ptr = (void __user *)head + size;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci	return err;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic size_t get_rt_frame_size(bool cal_all)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct rt_sigframe __user *frame;
20562306a36Sopenharmony_ci	size_t frame_size;
20662306a36Sopenharmony_ci	size_t total_context_size = 0;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	frame_size = sizeof(*frame);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (has_vector()) {
21162306a36Sopenharmony_ci		if (cal_all || riscv_v_vstate_query(task_pt_regs(current)))
21262306a36Sopenharmony_ci			total_context_size += riscv_v_sc_size;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci	/*
21562306a36Sopenharmony_ci	 * Preserved a __riscv_ctx_hdr for END signal context header if an
21662306a36Sopenharmony_ci	 * extension uses __riscv_extra_ext_header
21762306a36Sopenharmony_ci	 */
21862306a36Sopenharmony_ci	if (total_context_size)
21962306a36Sopenharmony_ci		total_context_size += sizeof(struct __riscv_ctx_hdr);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	frame_size += total_context_size;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	frame_size = round_up(frame_size, 16);
22462306a36Sopenharmony_ci	return frame_size;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ciSYSCALL_DEFINE0(rt_sigreturn)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	struct pt_regs *regs = current_pt_regs();
23062306a36Sopenharmony_ci	struct rt_sigframe __user *frame;
23162306a36Sopenharmony_ci	struct task_struct *task;
23262306a36Sopenharmony_ci	sigset_t set;
23362306a36Sopenharmony_ci	size_t frame_size = get_rt_frame_size(false);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	/* Always make any pending restarted system calls return -EINTR */
23662306a36Sopenharmony_ci	current->restart_block.fn = do_no_restart_syscall;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	frame = (struct rt_sigframe __user *)regs->sp;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (!access_ok(frame, frame_size))
24162306a36Sopenharmony_ci		goto badframe;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
24462306a36Sopenharmony_ci		goto badframe;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	set_current_blocked(&set);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
24962306a36Sopenharmony_ci		goto badframe;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (restore_altstack(&frame->uc.uc_stack))
25262306a36Sopenharmony_ci		goto badframe;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	regs->cause = -1UL;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	return regs->a0;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cibadframe:
25962306a36Sopenharmony_ci	task = current;
26062306a36Sopenharmony_ci	if (show_unhandled_signals) {
26162306a36Sopenharmony_ci		pr_info_ratelimited(
26262306a36Sopenharmony_ci			"%s[%d]: bad frame in %s: frame=%p pc=%p sp=%p\n",
26362306a36Sopenharmony_ci			task->comm, task_pid_nr(task), __func__,
26462306a36Sopenharmony_ci			frame, (void *)regs->epc, (void *)regs->sp);
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci	force_sig(SIGSEGV);
26762306a36Sopenharmony_ci	return 0;
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic long setup_sigcontext(struct rt_sigframe __user *frame,
27162306a36Sopenharmony_ci	struct pt_regs *regs)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct sigcontext __user *sc = &frame->uc.uc_mcontext;
27462306a36Sopenharmony_ci	struct __riscv_ctx_hdr __user *sc_ext_ptr = &sc->sc_extdesc.hdr;
27562306a36Sopenharmony_ci	long err;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	/* sc_regs is structured the same as the start of pt_regs */
27862306a36Sopenharmony_ci	err = __copy_to_user(&sc->sc_regs, regs, sizeof(sc->sc_regs));
27962306a36Sopenharmony_ci	/* Save the floating-point state. */
28062306a36Sopenharmony_ci	if (has_fpu())
28162306a36Sopenharmony_ci		err |= save_fp_state(regs, &sc->sc_fpregs);
28262306a36Sopenharmony_ci	/* Save the vector state. */
28362306a36Sopenharmony_ci	if (has_vector() && riscv_v_vstate_query(regs))
28462306a36Sopenharmony_ci		err |= save_v_state(regs, (void __user **)&sc_ext_ptr);
28562306a36Sopenharmony_ci	/* Write zero to fp-reserved space and check it on restore_sigcontext */
28662306a36Sopenharmony_ci	err |= __put_user(0, &sc->sc_extdesc.reserved);
28762306a36Sopenharmony_ci	/* And put END __riscv_ctx_hdr at the end. */
28862306a36Sopenharmony_ci	err |= __put_user(END_MAGIC, &sc_ext_ptr->magic);
28962306a36Sopenharmony_ci	err |= __put_user(END_HDR_SIZE, &sc_ext_ptr->size);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	return err;
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic inline void __user *get_sigframe(struct ksignal *ksig,
29562306a36Sopenharmony_ci	struct pt_regs *regs, size_t framesize)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	unsigned long sp;
29862306a36Sopenharmony_ci	/* Default to using normal stack */
29962306a36Sopenharmony_ci	sp = regs->sp;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	/*
30262306a36Sopenharmony_ci	 * If we are on the alternate signal stack and would overflow it, don't.
30362306a36Sopenharmony_ci	 * Return an always-bogus address instead so we will die with SIGSEGV.
30462306a36Sopenharmony_ci	 */
30562306a36Sopenharmony_ci	if (on_sig_stack(sp) && !likely(on_sig_stack(sp - framesize)))
30662306a36Sopenharmony_ci		return (void __user __force *)(-1UL);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	/* This is the X/Open sanctioned signal stack switching. */
30962306a36Sopenharmony_ci	sp = sigsp(sp, ksig) - framesize;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	/* Align the stack frame. */
31262306a36Sopenharmony_ci	sp &= ~0xfUL;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	return (void __user *)sp;
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic int setup_rt_frame(struct ksignal *ksig, sigset_t *set,
31862306a36Sopenharmony_ci	struct pt_regs *regs)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	struct rt_sigframe __user *frame;
32162306a36Sopenharmony_ci	long err = 0;
32262306a36Sopenharmony_ci	unsigned long __maybe_unused addr;
32362306a36Sopenharmony_ci	size_t frame_size = get_rt_frame_size(false);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	frame = get_sigframe(ksig, regs, frame_size);
32662306a36Sopenharmony_ci	if (!access_ok(frame, frame_size))
32762306a36Sopenharmony_ci		return -EFAULT;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	err |= copy_siginfo_to_user(&frame->info, &ksig->info);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	/* Create the ucontext. */
33262306a36Sopenharmony_ci	err |= __put_user(0, &frame->uc.uc_flags);
33362306a36Sopenharmony_ci	err |= __put_user(NULL, &frame->uc.uc_link);
33462306a36Sopenharmony_ci	err |= __save_altstack(&frame->uc.uc_stack, regs->sp);
33562306a36Sopenharmony_ci	err |= setup_sigcontext(frame, regs);
33662306a36Sopenharmony_ci	err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
33762306a36Sopenharmony_ci	if (err)
33862306a36Sopenharmony_ci		return -EFAULT;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	/* Set up to return from userspace. */
34162306a36Sopenharmony_ci#ifdef CONFIG_MMU
34262306a36Sopenharmony_ci	regs->ra = (unsigned long)VDSO_SYMBOL(
34362306a36Sopenharmony_ci		current->mm->context.vdso, rt_sigreturn);
34462306a36Sopenharmony_ci#else
34562306a36Sopenharmony_ci	/*
34662306a36Sopenharmony_ci	 * For the nommu case we don't have a VDSO.  Instead we push two
34762306a36Sopenharmony_ci	 * instructions to call the rt_sigreturn syscall onto the user stack.
34862306a36Sopenharmony_ci	 */
34962306a36Sopenharmony_ci	if (copy_to_user(&frame->sigreturn_code, __user_rt_sigreturn,
35062306a36Sopenharmony_ci			 sizeof(frame->sigreturn_code)))
35162306a36Sopenharmony_ci		return -EFAULT;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	addr = (unsigned long)&frame->sigreturn_code;
35462306a36Sopenharmony_ci	/* Make sure the two instructions are pushed to icache. */
35562306a36Sopenharmony_ci	flush_icache_range(addr, addr + sizeof(frame->sigreturn_code));
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	regs->ra = addr;
35862306a36Sopenharmony_ci#endif /* CONFIG_MMU */
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	/*
36162306a36Sopenharmony_ci	 * Set up registers for signal handler.
36262306a36Sopenharmony_ci	 * Registers that we don't modify keep the value they had from
36362306a36Sopenharmony_ci	 * user-space at the time we took the signal.
36462306a36Sopenharmony_ci	 * We always pass siginfo and mcontext, regardless of SA_SIGINFO,
36562306a36Sopenharmony_ci	 * since some things rely on this (e.g. glibc's debug/segfault.c).
36662306a36Sopenharmony_ci	 */
36762306a36Sopenharmony_ci	regs->epc = (unsigned long)ksig->ka.sa.sa_handler;
36862306a36Sopenharmony_ci	regs->sp = (unsigned long)frame;
36962306a36Sopenharmony_ci	regs->a0 = ksig->sig;                     /* a0: signal number */
37062306a36Sopenharmony_ci	regs->a1 = (unsigned long)(&frame->info); /* a1: siginfo pointer */
37162306a36Sopenharmony_ci	regs->a2 = (unsigned long)(&frame->uc);   /* a2: ucontext pointer */
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci#if DEBUG_SIG
37462306a36Sopenharmony_ci	pr_info("SIG deliver (%s:%d): sig=%d pc=%p ra=%p sp=%p\n",
37562306a36Sopenharmony_ci		current->comm, task_pid_nr(current), ksig->sig,
37662306a36Sopenharmony_ci		(void *)regs->epc, (void *)regs->ra, frame);
37762306a36Sopenharmony_ci#endif
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	return 0;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic void handle_signal(struct ksignal *ksig, struct pt_regs *regs)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	sigset_t *oldset = sigmask_to_save();
38562306a36Sopenharmony_ci	int ret;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	/* Are we from a system call? */
38862306a36Sopenharmony_ci	if (regs->cause == EXC_SYSCALL) {
38962306a36Sopenharmony_ci		/* Avoid additional syscall restarting via ret_from_exception */
39062306a36Sopenharmony_ci		regs->cause = -1UL;
39162306a36Sopenharmony_ci		/* If so, check system call restarting.. */
39262306a36Sopenharmony_ci		switch (regs->a0) {
39362306a36Sopenharmony_ci		case -ERESTART_RESTARTBLOCK:
39462306a36Sopenharmony_ci		case -ERESTARTNOHAND:
39562306a36Sopenharmony_ci			regs->a0 = -EINTR;
39662306a36Sopenharmony_ci			break;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci		case -ERESTARTSYS:
39962306a36Sopenharmony_ci			if (!(ksig->ka.sa.sa_flags & SA_RESTART)) {
40062306a36Sopenharmony_ci				regs->a0 = -EINTR;
40162306a36Sopenharmony_ci				break;
40262306a36Sopenharmony_ci			}
40362306a36Sopenharmony_ci			fallthrough;
40462306a36Sopenharmony_ci		case -ERESTARTNOINTR:
40562306a36Sopenharmony_ci                        regs->a0 = regs->orig_a0;
40662306a36Sopenharmony_ci			regs->epc -= 0x4;
40762306a36Sopenharmony_ci			break;
40862306a36Sopenharmony_ci		}
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	rseq_signal_deliver(ksig, regs);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	/* Set up the stack frame */
41462306a36Sopenharmony_ci	if (is_compat_task())
41562306a36Sopenharmony_ci		ret = compat_setup_rt_frame(ksig, oldset, regs);
41662306a36Sopenharmony_ci	else
41762306a36Sopenharmony_ci		ret = setup_rt_frame(ksig, oldset, regs);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	signal_setup_done(ret, ksig, 0);
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_civoid arch_do_signal_or_restart(struct pt_regs *regs)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct ksignal ksig;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	if (get_signal(&ksig)) {
42762306a36Sopenharmony_ci		/* Actually deliver the signal */
42862306a36Sopenharmony_ci		handle_signal(&ksig, regs);
42962306a36Sopenharmony_ci		return;
43062306a36Sopenharmony_ci	}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	/* Did we come from a system call? */
43362306a36Sopenharmony_ci	if (regs->cause == EXC_SYSCALL) {
43462306a36Sopenharmony_ci		/* Avoid additional syscall restarting via ret_from_exception */
43562306a36Sopenharmony_ci		regs->cause = -1UL;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci		/* Restart the system call - no handlers present */
43862306a36Sopenharmony_ci		switch (regs->a0) {
43962306a36Sopenharmony_ci		case -ERESTARTNOHAND:
44062306a36Sopenharmony_ci		case -ERESTARTSYS:
44162306a36Sopenharmony_ci		case -ERESTARTNOINTR:
44262306a36Sopenharmony_ci                        regs->a0 = regs->orig_a0;
44362306a36Sopenharmony_ci			regs->epc -= 0x4;
44462306a36Sopenharmony_ci			break;
44562306a36Sopenharmony_ci		case -ERESTART_RESTARTBLOCK:
44662306a36Sopenharmony_ci                        regs->a0 = regs->orig_a0;
44762306a36Sopenharmony_ci			regs->a7 = __NR_restart_syscall;
44862306a36Sopenharmony_ci			regs->epc -= 0x4;
44962306a36Sopenharmony_ci			break;
45062306a36Sopenharmony_ci		}
45162306a36Sopenharmony_ci	}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	/*
45462306a36Sopenharmony_ci	 * If there is no signal to deliver, we just put the saved
45562306a36Sopenharmony_ci	 * sigmask back.
45662306a36Sopenharmony_ci	 */
45762306a36Sopenharmony_ci	restore_saved_sigmask();
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_civoid init_rt_signal_env(void);
46162306a36Sopenharmony_civoid __init init_rt_signal_env(void)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	riscv_v_sc_size = sizeof(struct __riscv_ctx_hdr) +
46462306a36Sopenharmony_ci			  sizeof(struct __sc_riscv_v_state) + riscv_v_vsize;
46562306a36Sopenharmony_ci	/*
46662306a36Sopenharmony_ci	 * Determine the stack space required for guaranteed signal delivery.
46762306a36Sopenharmony_ci	 * The signal_minsigstksz will be populated into the AT_MINSIGSTKSZ entry
46862306a36Sopenharmony_ci	 * in the auxiliary array at process startup.
46962306a36Sopenharmony_ci	 */
47062306a36Sopenharmony_ci	signal_minsigstksz = get_rt_frame_size(true);
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci#ifdef CONFIG_DYNAMIC_SIGFRAME
47462306a36Sopenharmony_cibool sigaltstack_size_valid(size_t ss_size)
47562306a36Sopenharmony_ci{
47662306a36Sopenharmony_ci	return ss_size > get_rt_frame_size(false);
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci#endif /* CONFIG_DYNAMIC_SIGFRAME */
479