xref: /linux/arch/loongarch/kernel/signal.c (revision 61650023)
1b74baf4aSHuacai Chen // SPDX-License-Identifier: GPL-2.0+
2b74baf4aSHuacai Chen /*
3b74baf4aSHuacai Chen  * Author: Hanlu Li <lihanlu@loongson.cn>
4b74baf4aSHuacai Chen  *         Huacai Chen <chenhuacai@loongson.cn>
5b74baf4aSHuacai Chen  * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
6b74baf4aSHuacai Chen  *
7b74baf4aSHuacai Chen  * Derived from MIPS:
8b74baf4aSHuacai Chen  * Copyright (C) 1991, 1992  Linus Torvalds
9b74baf4aSHuacai Chen  * Copyright (C) 1994 - 2000  Ralf Baechle
10b74baf4aSHuacai Chen  * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
11b74baf4aSHuacai Chen  * Copyright (C) 2014, Imagination Technologies Ltd.
12b74baf4aSHuacai Chen  */
13b74baf4aSHuacai Chen #include <linux/audit.h>
14b74baf4aSHuacai Chen #include <linux/cache.h>
15b74baf4aSHuacai Chen #include <linux/context_tracking.h>
16b74baf4aSHuacai Chen #include <linux/irqflags.h>
17b74baf4aSHuacai Chen #include <linux/sched.h>
18b74baf4aSHuacai Chen #include <linux/mm.h>
19b74baf4aSHuacai Chen #include <linux/personality.h>
20b74baf4aSHuacai Chen #include <linux/smp.h>
21b74baf4aSHuacai Chen #include <linux/kernel.h>
22b74baf4aSHuacai Chen #include <linux/signal.h>
23b74baf4aSHuacai Chen #include <linux/errno.h>
24b74baf4aSHuacai Chen #include <linux/wait.h>
25b74baf4aSHuacai Chen #include <linux/ptrace.h>
26b74baf4aSHuacai Chen #include <linux/unistd.h>
27b74baf4aSHuacai Chen #include <linux/compiler.h>
28b74baf4aSHuacai Chen #include <linux/syscalls.h>
29b74baf4aSHuacai Chen #include <linux/uaccess.h>
30b74baf4aSHuacai Chen 
31b74baf4aSHuacai Chen #include <asm/asm.h>
32b74baf4aSHuacai Chen #include <asm/cacheflush.h>
33b74baf4aSHuacai Chen #include <asm/cpu-features.h>
34b74baf4aSHuacai Chen #include <asm/fpu.h>
35b74baf4aSHuacai Chen #include <asm/ucontext.h>
36b74baf4aSHuacai Chen #include <asm/vdso.h>
37b74baf4aSHuacai Chen 
38b74baf4aSHuacai Chen #ifdef DEBUG_SIG
39b74baf4aSHuacai Chen #  define DEBUGP(fmt, args...) printk("%s: " fmt, __func__, ##args)
40b74baf4aSHuacai Chen #else
41b74baf4aSHuacai Chen #  define DEBUGP(fmt, args...)
42b74baf4aSHuacai Chen #endif
43b74baf4aSHuacai Chen 
44b74baf4aSHuacai Chen /* Make sure we will not lose FPU ownership */
45b74baf4aSHuacai Chen #define lock_fpu_owner()	({ preempt_disable(); pagefault_disable(); })
46b74baf4aSHuacai Chen #define unlock_fpu_owner()	({ pagefault_enable(); preempt_enable(); })
47b74baf4aSHuacai Chen 
48b74baf4aSHuacai Chen /* Assembly functions to move context to/from the FPU */
49b74baf4aSHuacai Chen extern asmlinkage int
50b74baf4aSHuacai Chen _save_fp_context(void __user *fpregs, void __user *fcc, void __user *csr);
51b74baf4aSHuacai Chen extern asmlinkage int
52b74baf4aSHuacai Chen _restore_fp_context(void __user *fpregs, void __user *fcc, void __user *csr);
53*61650023SHuacai Chen extern asmlinkage int
54*61650023SHuacai Chen _save_lsx_context(void __user *fpregs, void __user *fcc, void __user *fcsr);
55*61650023SHuacai Chen extern asmlinkage int
56*61650023SHuacai Chen _restore_lsx_context(void __user *fpregs, void __user *fcc, void __user *fcsr);
57*61650023SHuacai Chen extern asmlinkage int
58*61650023SHuacai Chen _save_lasx_context(void __user *fpregs, void __user *fcc, void __user *fcsr);
59*61650023SHuacai Chen extern asmlinkage int
60*61650023SHuacai Chen _restore_lasx_context(void __user *fpregs, void __user *fcc, void __user *fcsr);
61b74baf4aSHuacai Chen 
62b74baf4aSHuacai Chen struct rt_sigframe {
63b74baf4aSHuacai Chen 	struct siginfo rs_info;
64b74baf4aSHuacai Chen 	struct ucontext rs_uctx;
65b74baf4aSHuacai Chen };
66b74baf4aSHuacai Chen 
67b74baf4aSHuacai Chen struct _ctx_layout {
68b74baf4aSHuacai Chen 	struct sctx_info *addr;
69b74baf4aSHuacai Chen 	unsigned int size;
70b74baf4aSHuacai Chen };
71b74baf4aSHuacai Chen 
72b74baf4aSHuacai Chen struct extctx_layout {
73b74baf4aSHuacai Chen 	unsigned long size;
74b74baf4aSHuacai Chen 	unsigned int flags;
75b74baf4aSHuacai Chen 	struct _ctx_layout fpu;
76*61650023SHuacai Chen 	struct _ctx_layout lsx;
77*61650023SHuacai Chen 	struct _ctx_layout lasx;
78b74baf4aSHuacai Chen 	struct _ctx_layout end;
79b74baf4aSHuacai Chen };
80b74baf4aSHuacai Chen 
81b74baf4aSHuacai Chen static void __user *get_ctx_through_ctxinfo(struct sctx_info *info)
82b74baf4aSHuacai Chen {
83b74baf4aSHuacai Chen 	return (void __user *)((char *)info + sizeof(struct sctx_info));
84b74baf4aSHuacai Chen }
85b74baf4aSHuacai Chen 
86b74baf4aSHuacai Chen /*
87b74baf4aSHuacai Chen  * Thread saved context copy to/from a signal context presumed to be on the
88b74baf4aSHuacai Chen  * user stack, and therefore accessed with appropriate macros from uaccess.h.
89b74baf4aSHuacai Chen  */
90b74baf4aSHuacai Chen static int copy_fpu_to_sigcontext(struct fpu_context __user *ctx)
91b74baf4aSHuacai Chen {
92b74baf4aSHuacai Chen 	int i;
93b74baf4aSHuacai Chen 	int err = 0;
94b74baf4aSHuacai Chen 	uint64_t __user *regs	= (uint64_t *)&ctx->regs;
95b74baf4aSHuacai Chen 	uint64_t __user *fcc	= &ctx->fcc;
96b74baf4aSHuacai Chen 	uint32_t __user *fcsr	= &ctx->fcsr;
97b74baf4aSHuacai Chen 
98b74baf4aSHuacai Chen 	for (i = 0; i < NUM_FPU_REGS; i++) {
99b74baf4aSHuacai Chen 		err |=
100b74baf4aSHuacai Chen 		    __put_user(get_fpr64(&current->thread.fpu.fpr[i], 0),
101b74baf4aSHuacai Chen 			       &regs[i]);
102b74baf4aSHuacai Chen 	}
103b74baf4aSHuacai Chen 	err |= __put_user(current->thread.fpu.fcc, fcc);
104b74baf4aSHuacai Chen 	err |= __put_user(current->thread.fpu.fcsr, fcsr);
105b74baf4aSHuacai Chen 
106b74baf4aSHuacai Chen 	return err;
107b74baf4aSHuacai Chen }
108b74baf4aSHuacai Chen 
109b74baf4aSHuacai Chen static int copy_fpu_from_sigcontext(struct fpu_context __user *ctx)
110b74baf4aSHuacai Chen {
111b74baf4aSHuacai Chen 	int i;
112b74baf4aSHuacai Chen 	int err = 0;
113b74baf4aSHuacai Chen 	u64 fpr_val;
114b74baf4aSHuacai Chen 	uint64_t __user *regs	= (uint64_t *)&ctx->regs;
115b74baf4aSHuacai Chen 	uint64_t __user *fcc	= &ctx->fcc;
116b74baf4aSHuacai Chen 	uint32_t __user *fcsr	= &ctx->fcsr;
117b74baf4aSHuacai Chen 
118b74baf4aSHuacai Chen 	for (i = 0; i < NUM_FPU_REGS; i++) {
119b74baf4aSHuacai Chen 		err |= __get_user(fpr_val, &regs[i]);
120b74baf4aSHuacai Chen 		set_fpr64(&current->thread.fpu.fpr[i], 0, fpr_val);
121b74baf4aSHuacai Chen 	}
122b74baf4aSHuacai Chen 	err |= __get_user(current->thread.fpu.fcc, fcc);
123b74baf4aSHuacai Chen 	err |= __get_user(current->thread.fpu.fcsr, fcsr);
124b74baf4aSHuacai Chen 
125b74baf4aSHuacai Chen 	return err;
126b74baf4aSHuacai Chen }
127b74baf4aSHuacai Chen 
128*61650023SHuacai Chen static int copy_lsx_to_sigcontext(struct lsx_context __user *ctx)
129*61650023SHuacai Chen {
130*61650023SHuacai Chen 	int i;
131*61650023SHuacai Chen 	int err = 0;
132*61650023SHuacai Chen 	uint64_t __user *regs	= (uint64_t *)&ctx->regs;
133*61650023SHuacai Chen 	uint64_t __user *fcc	= &ctx->fcc;
134*61650023SHuacai Chen 	uint32_t __user *fcsr	= &ctx->fcsr;
135*61650023SHuacai Chen 
136*61650023SHuacai Chen 	for (i = 0; i < NUM_FPU_REGS; i++) {
137*61650023SHuacai Chen 		err |= __put_user(get_fpr64(&current->thread.fpu.fpr[i], 0),
138*61650023SHuacai Chen 				  &regs[2*i]);
139*61650023SHuacai Chen 		err |= __put_user(get_fpr64(&current->thread.fpu.fpr[i], 1),
140*61650023SHuacai Chen 				  &regs[2*i+1]);
141*61650023SHuacai Chen 	}
142*61650023SHuacai Chen 	err |= __put_user(current->thread.fpu.fcc, fcc);
143*61650023SHuacai Chen 	err |= __put_user(current->thread.fpu.fcsr, fcsr);
144*61650023SHuacai Chen 
145*61650023SHuacai Chen 	return err;
146*61650023SHuacai Chen }
147*61650023SHuacai Chen 
148*61650023SHuacai Chen static int copy_lsx_from_sigcontext(struct lsx_context __user *ctx)
149*61650023SHuacai Chen {
150*61650023SHuacai Chen 	int i;
151*61650023SHuacai Chen 	int err = 0;
152*61650023SHuacai Chen 	u64 fpr_val;
153*61650023SHuacai Chen 	uint64_t __user *regs	= (uint64_t *)&ctx->regs;
154*61650023SHuacai Chen 	uint64_t __user *fcc	= &ctx->fcc;
155*61650023SHuacai Chen 	uint32_t __user *fcsr	= &ctx->fcsr;
156*61650023SHuacai Chen 
157*61650023SHuacai Chen 	for (i = 0; i < NUM_FPU_REGS; i++) {
158*61650023SHuacai Chen 		err |= __get_user(fpr_val, &regs[2*i]);
159*61650023SHuacai Chen 		set_fpr64(&current->thread.fpu.fpr[i], 0, fpr_val);
160*61650023SHuacai Chen 		err |= __get_user(fpr_val, &regs[2*i+1]);
161*61650023SHuacai Chen 		set_fpr64(&current->thread.fpu.fpr[i], 1, fpr_val);
162*61650023SHuacai Chen 	}
163*61650023SHuacai Chen 	err |= __get_user(current->thread.fpu.fcc, fcc);
164*61650023SHuacai Chen 	err |= __get_user(current->thread.fpu.fcsr, fcsr);
165*61650023SHuacai Chen 
166*61650023SHuacai Chen 	return err;
167*61650023SHuacai Chen }
168*61650023SHuacai Chen 
169*61650023SHuacai Chen static int copy_lasx_to_sigcontext(struct lasx_context __user *ctx)
170*61650023SHuacai Chen {
171*61650023SHuacai Chen 	int i;
172*61650023SHuacai Chen 	int err = 0;
173*61650023SHuacai Chen 	uint64_t __user *regs	= (uint64_t *)&ctx->regs;
174*61650023SHuacai Chen 	uint64_t __user *fcc	= &ctx->fcc;
175*61650023SHuacai Chen 	uint32_t __user *fcsr	= &ctx->fcsr;
176*61650023SHuacai Chen 
177*61650023SHuacai Chen 	for (i = 0; i < NUM_FPU_REGS; i++) {
178*61650023SHuacai Chen 		err |= __put_user(get_fpr64(&current->thread.fpu.fpr[i], 0),
179*61650023SHuacai Chen 				  &regs[4*i]);
180*61650023SHuacai Chen 		err |= __put_user(get_fpr64(&current->thread.fpu.fpr[i], 1),
181*61650023SHuacai Chen 				  &regs[4*i+1]);
182*61650023SHuacai Chen 		err |= __put_user(get_fpr64(&current->thread.fpu.fpr[i], 2),
183*61650023SHuacai Chen 				  &regs[4*i+2]);
184*61650023SHuacai Chen 		err |= __put_user(get_fpr64(&current->thread.fpu.fpr[i], 3),
185*61650023SHuacai Chen 				  &regs[4*i+3]);
186*61650023SHuacai Chen 	}
187*61650023SHuacai Chen 	err |= __put_user(current->thread.fpu.fcc, fcc);
188*61650023SHuacai Chen 	err |= __put_user(current->thread.fpu.fcsr, fcsr);
189*61650023SHuacai Chen 
190*61650023SHuacai Chen 	return err;
191*61650023SHuacai Chen }
192*61650023SHuacai Chen 
193*61650023SHuacai Chen static int copy_lasx_from_sigcontext(struct lasx_context __user *ctx)
194*61650023SHuacai Chen {
195*61650023SHuacai Chen 	int i;
196*61650023SHuacai Chen 	int err = 0;
197*61650023SHuacai Chen 	u64 fpr_val;
198*61650023SHuacai Chen 	uint64_t __user *regs	= (uint64_t *)&ctx->regs;
199*61650023SHuacai Chen 	uint64_t __user *fcc	= &ctx->fcc;
200*61650023SHuacai Chen 	uint32_t __user *fcsr	= &ctx->fcsr;
201*61650023SHuacai Chen 
202*61650023SHuacai Chen 	for (i = 0; i < NUM_FPU_REGS; i++) {
203*61650023SHuacai Chen 		err |= __get_user(fpr_val, &regs[4*i]);
204*61650023SHuacai Chen 		set_fpr64(&current->thread.fpu.fpr[i], 0, fpr_val);
205*61650023SHuacai Chen 		err |= __get_user(fpr_val, &regs[4*i+1]);
206*61650023SHuacai Chen 		set_fpr64(&current->thread.fpu.fpr[i], 1, fpr_val);
207*61650023SHuacai Chen 		err |= __get_user(fpr_val, &regs[4*i+2]);
208*61650023SHuacai Chen 		set_fpr64(&current->thread.fpu.fpr[i], 2, fpr_val);
209*61650023SHuacai Chen 		err |= __get_user(fpr_val, &regs[4*i+3]);
210*61650023SHuacai Chen 		set_fpr64(&current->thread.fpu.fpr[i], 3, fpr_val);
211*61650023SHuacai Chen 	}
212*61650023SHuacai Chen 	err |= __get_user(current->thread.fpu.fcc, fcc);
213*61650023SHuacai Chen 	err |= __get_user(current->thread.fpu.fcsr, fcsr);
214*61650023SHuacai Chen 
215*61650023SHuacai Chen 	return err;
216*61650023SHuacai Chen }
217*61650023SHuacai Chen 
218b74baf4aSHuacai Chen /*
219b74baf4aSHuacai Chen  * Wrappers for the assembly _{save,restore}_fp_context functions.
220b74baf4aSHuacai Chen  */
221b74baf4aSHuacai Chen static int save_hw_fpu_context(struct fpu_context __user *ctx)
222b74baf4aSHuacai Chen {
223b74baf4aSHuacai Chen 	uint64_t __user *regs	= (uint64_t *)&ctx->regs;
224b74baf4aSHuacai Chen 	uint64_t __user *fcc	= &ctx->fcc;
225b74baf4aSHuacai Chen 	uint32_t __user *fcsr	= &ctx->fcsr;
226b74baf4aSHuacai Chen 
227b74baf4aSHuacai Chen 	return _save_fp_context(regs, fcc, fcsr);
228b74baf4aSHuacai Chen }
229b74baf4aSHuacai Chen 
230b74baf4aSHuacai Chen static int restore_hw_fpu_context(struct fpu_context __user *ctx)
231b74baf4aSHuacai Chen {
232b74baf4aSHuacai Chen 	uint64_t __user *regs	= (uint64_t *)&ctx->regs;
233b74baf4aSHuacai Chen 	uint64_t __user *fcc	= &ctx->fcc;
234b74baf4aSHuacai Chen 	uint32_t __user *fcsr	= &ctx->fcsr;
235b74baf4aSHuacai Chen 
236b74baf4aSHuacai Chen 	return _restore_fp_context(regs, fcc, fcsr);
237b74baf4aSHuacai Chen }
238b74baf4aSHuacai Chen 
239*61650023SHuacai Chen static int save_hw_lsx_context(struct lsx_context __user *ctx)
240*61650023SHuacai Chen {
241*61650023SHuacai Chen 	uint64_t __user *regs	= (uint64_t *)&ctx->regs;
242*61650023SHuacai Chen 	uint64_t __user *fcc	= &ctx->fcc;
243*61650023SHuacai Chen 	uint32_t __user *fcsr	= &ctx->fcsr;
244*61650023SHuacai Chen 
245*61650023SHuacai Chen 	return _save_lsx_context(regs, fcc, fcsr);
246*61650023SHuacai Chen }
247*61650023SHuacai Chen 
248*61650023SHuacai Chen static int restore_hw_lsx_context(struct lsx_context __user *ctx)
249*61650023SHuacai Chen {
250*61650023SHuacai Chen 	uint64_t __user *regs	= (uint64_t *)&ctx->regs;
251*61650023SHuacai Chen 	uint64_t __user *fcc	= &ctx->fcc;
252*61650023SHuacai Chen 	uint32_t __user *fcsr	= &ctx->fcsr;
253*61650023SHuacai Chen 
254*61650023SHuacai Chen 	return _restore_lsx_context(regs, fcc, fcsr);
255*61650023SHuacai Chen }
256*61650023SHuacai Chen 
257*61650023SHuacai Chen static int save_hw_lasx_context(struct lasx_context __user *ctx)
258*61650023SHuacai Chen {
259*61650023SHuacai Chen 	uint64_t __user *regs	= (uint64_t *)&ctx->regs;
260*61650023SHuacai Chen 	uint64_t __user *fcc	= &ctx->fcc;
261*61650023SHuacai Chen 	uint32_t __user *fcsr	= &ctx->fcsr;
262*61650023SHuacai Chen 
263*61650023SHuacai Chen 	return _save_lasx_context(regs, fcc, fcsr);
264*61650023SHuacai Chen }
265*61650023SHuacai Chen 
266*61650023SHuacai Chen static int restore_hw_lasx_context(struct lasx_context __user *ctx)
267*61650023SHuacai Chen {
268*61650023SHuacai Chen 	uint64_t __user *regs	= (uint64_t *)&ctx->regs;
269*61650023SHuacai Chen 	uint64_t __user *fcc	= &ctx->fcc;
270*61650023SHuacai Chen 	uint32_t __user *fcsr	= &ctx->fcsr;
271*61650023SHuacai Chen 
272*61650023SHuacai Chen 	return _restore_lasx_context(regs, fcc, fcsr);
273*61650023SHuacai Chen }
274*61650023SHuacai Chen 
275b74baf4aSHuacai Chen static int fcsr_pending(unsigned int __user *fcsr)
276b74baf4aSHuacai Chen {
277b74baf4aSHuacai Chen 	int err, sig = 0;
278b74baf4aSHuacai Chen 	unsigned int csr, enabled;
279b74baf4aSHuacai Chen 
280b74baf4aSHuacai Chen 	err = __get_user(csr, fcsr);
281b74baf4aSHuacai Chen 	enabled = ((csr & FPU_CSR_ALL_E) << 24);
282b74baf4aSHuacai Chen 	/*
283b74baf4aSHuacai Chen 	 * If the signal handler set some FPU exceptions, clear it and
284b74baf4aSHuacai Chen 	 * send SIGFPE.
285b74baf4aSHuacai Chen 	 */
286b74baf4aSHuacai Chen 	if (csr & enabled) {
287b74baf4aSHuacai Chen 		csr &= ~enabled;
288b74baf4aSHuacai Chen 		err |= __put_user(csr, fcsr);
289b74baf4aSHuacai Chen 		sig = SIGFPE;
290b74baf4aSHuacai Chen 	}
291b74baf4aSHuacai Chen 	return err ?: sig;
292b74baf4aSHuacai Chen }
293b74baf4aSHuacai Chen 
294b74baf4aSHuacai Chen /*
295b74baf4aSHuacai Chen  * Helper routines
296b74baf4aSHuacai Chen  */
297b74baf4aSHuacai Chen static int protected_save_fpu_context(struct extctx_layout *extctx)
298b74baf4aSHuacai Chen {
299b74baf4aSHuacai Chen 	int err = 0;
300b74baf4aSHuacai Chen 	struct sctx_info __user *info = extctx->fpu.addr;
301b74baf4aSHuacai Chen 	struct fpu_context __user *fpu_ctx = (struct fpu_context *)get_ctx_through_ctxinfo(info);
302b74baf4aSHuacai Chen 	uint64_t __user *regs	= (uint64_t *)&fpu_ctx->regs;
303b74baf4aSHuacai Chen 	uint64_t __user *fcc	= &fpu_ctx->fcc;
304b74baf4aSHuacai Chen 	uint32_t __user *fcsr	= &fpu_ctx->fcsr;
305b74baf4aSHuacai Chen 
306b74baf4aSHuacai Chen 	while (1) {
307b74baf4aSHuacai Chen 		lock_fpu_owner();
308b74baf4aSHuacai Chen 		if (is_fpu_owner())
309b74baf4aSHuacai Chen 			err = save_hw_fpu_context(fpu_ctx);
310b74baf4aSHuacai Chen 		else
311b74baf4aSHuacai Chen 			err = copy_fpu_to_sigcontext(fpu_ctx);
312b74baf4aSHuacai Chen 		unlock_fpu_owner();
313b74baf4aSHuacai Chen 
314b74baf4aSHuacai Chen 		err |= __put_user(FPU_CTX_MAGIC, &info->magic);
315b74baf4aSHuacai Chen 		err |= __put_user(extctx->fpu.size, &info->size);
316b74baf4aSHuacai Chen 
317b74baf4aSHuacai Chen 		if (likely(!err))
318b74baf4aSHuacai Chen 			break;
319b74baf4aSHuacai Chen 		/* Touch the FPU context and try again */
320b74baf4aSHuacai Chen 		err = __put_user(0, &regs[0]) |
321b74baf4aSHuacai Chen 			__put_user(0, &regs[31]) |
322b74baf4aSHuacai Chen 			__put_user(0, fcc) |
323b74baf4aSHuacai Chen 			__put_user(0, fcsr);
324b74baf4aSHuacai Chen 		if (err)
325b74baf4aSHuacai Chen 			return err;	/* really bad sigcontext */
326b74baf4aSHuacai Chen 	}
327b74baf4aSHuacai Chen 
328b74baf4aSHuacai Chen 	return err;
329b74baf4aSHuacai Chen }
330b74baf4aSHuacai Chen 
331b74baf4aSHuacai Chen static int protected_restore_fpu_context(struct extctx_layout *extctx)
332b74baf4aSHuacai Chen {
333b74baf4aSHuacai Chen 	int err = 0, sig = 0, tmp __maybe_unused;
334b74baf4aSHuacai Chen 	struct sctx_info __user *info = extctx->fpu.addr;
335b74baf4aSHuacai Chen 	struct fpu_context __user *fpu_ctx = (struct fpu_context *)get_ctx_through_ctxinfo(info);
336b74baf4aSHuacai Chen 	uint64_t __user *regs	= (uint64_t *)&fpu_ctx->regs;
337b74baf4aSHuacai Chen 	uint64_t __user *fcc	= &fpu_ctx->fcc;
338b74baf4aSHuacai Chen 	uint32_t __user *fcsr	= &fpu_ctx->fcsr;
339b74baf4aSHuacai Chen 
340b74baf4aSHuacai Chen 	err = sig = fcsr_pending(fcsr);
341b74baf4aSHuacai Chen 	if (err < 0)
342b74baf4aSHuacai Chen 		return err;
343b74baf4aSHuacai Chen 
344b74baf4aSHuacai Chen 	while (1) {
345b74baf4aSHuacai Chen 		lock_fpu_owner();
346b74baf4aSHuacai Chen 		if (is_fpu_owner())
347b74baf4aSHuacai Chen 			err = restore_hw_fpu_context(fpu_ctx);
348b74baf4aSHuacai Chen 		else
349b74baf4aSHuacai Chen 			err = copy_fpu_from_sigcontext(fpu_ctx);
350b74baf4aSHuacai Chen 		unlock_fpu_owner();
351b74baf4aSHuacai Chen 
352b74baf4aSHuacai Chen 		if (likely(!err))
353b74baf4aSHuacai Chen 			break;
354b74baf4aSHuacai Chen 		/* Touch the FPU context and try again */
355b74baf4aSHuacai Chen 		err = __get_user(tmp, &regs[0]) |
356b74baf4aSHuacai Chen 			__get_user(tmp, &regs[31]) |
357b74baf4aSHuacai Chen 			__get_user(tmp, fcc) |
358b74baf4aSHuacai Chen 			__get_user(tmp, fcsr);
359b74baf4aSHuacai Chen 		if (err)
360b74baf4aSHuacai Chen 			break;	/* really bad sigcontext */
361b74baf4aSHuacai Chen 	}
362b74baf4aSHuacai Chen 
363b74baf4aSHuacai Chen 	return err ?: sig;
364b74baf4aSHuacai Chen }
365b74baf4aSHuacai Chen 
366*61650023SHuacai Chen static int protected_save_lsx_context(struct extctx_layout *extctx)
367*61650023SHuacai Chen {
368*61650023SHuacai Chen 	int err = 0;
369*61650023SHuacai Chen 	struct sctx_info __user *info = extctx->lsx.addr;
370*61650023SHuacai Chen 	struct lsx_context __user *lsx_ctx = (struct lsx_context *)get_ctx_through_ctxinfo(info);
371*61650023SHuacai Chen 	uint64_t __user *regs	= (uint64_t *)&lsx_ctx->regs;
372*61650023SHuacai Chen 	uint64_t __user *fcc	= &lsx_ctx->fcc;
373*61650023SHuacai Chen 	uint32_t __user *fcsr	= &lsx_ctx->fcsr;
374*61650023SHuacai Chen 
375*61650023SHuacai Chen 	while (1) {
376*61650023SHuacai Chen 		lock_fpu_owner();
377*61650023SHuacai Chen 		if (is_lsx_enabled())
378*61650023SHuacai Chen 			err = save_hw_lsx_context(lsx_ctx);
379*61650023SHuacai Chen 		else {
380*61650023SHuacai Chen 			if (is_fpu_owner())
381*61650023SHuacai Chen 				save_fp(current);
382*61650023SHuacai Chen 			err = copy_lsx_to_sigcontext(lsx_ctx);
383*61650023SHuacai Chen 		}
384*61650023SHuacai Chen 		unlock_fpu_owner();
385*61650023SHuacai Chen 
386*61650023SHuacai Chen 		err |= __put_user(LSX_CTX_MAGIC, &info->magic);
387*61650023SHuacai Chen 		err |= __put_user(extctx->lsx.size, &info->size);
388*61650023SHuacai Chen 
389*61650023SHuacai Chen 		if (likely(!err))
390*61650023SHuacai Chen 			break;
391*61650023SHuacai Chen 		/* Touch the LSX context and try again */
392*61650023SHuacai Chen 		err = __put_user(0, &regs[0]) |
393*61650023SHuacai Chen 			__put_user(0, &regs[32*2-1]) |
394*61650023SHuacai Chen 			__put_user(0, fcc) |
395*61650023SHuacai Chen 			__put_user(0, fcsr);
396*61650023SHuacai Chen 		if (err)
397*61650023SHuacai Chen 			return err;	/* really bad sigcontext */
398*61650023SHuacai Chen 	}
399*61650023SHuacai Chen 
400*61650023SHuacai Chen 	return err;
401*61650023SHuacai Chen }
402*61650023SHuacai Chen 
403*61650023SHuacai Chen static int protected_restore_lsx_context(struct extctx_layout *extctx)
404*61650023SHuacai Chen {
405*61650023SHuacai Chen 	int err = 0, sig = 0, tmp __maybe_unused;
406*61650023SHuacai Chen 	struct sctx_info __user *info = extctx->lsx.addr;
407*61650023SHuacai Chen 	struct lsx_context __user *lsx_ctx = (struct lsx_context *)get_ctx_through_ctxinfo(info);
408*61650023SHuacai Chen 	uint64_t __user *regs	= (uint64_t *)&lsx_ctx->regs;
409*61650023SHuacai Chen 	uint64_t __user *fcc	= &lsx_ctx->fcc;
410*61650023SHuacai Chen 	uint32_t __user *fcsr	= &lsx_ctx->fcsr;
411*61650023SHuacai Chen 
412*61650023SHuacai Chen 	err = sig = fcsr_pending(fcsr);
413*61650023SHuacai Chen 	if (err < 0)
414*61650023SHuacai Chen 		return err;
415*61650023SHuacai Chen 
416*61650023SHuacai Chen 	while (1) {
417*61650023SHuacai Chen 		lock_fpu_owner();
418*61650023SHuacai Chen 		if (is_lsx_enabled())
419*61650023SHuacai Chen 			err = restore_hw_lsx_context(lsx_ctx);
420*61650023SHuacai Chen 		else {
421*61650023SHuacai Chen 			err = copy_lsx_from_sigcontext(lsx_ctx);
422*61650023SHuacai Chen 			if (is_fpu_owner())
423*61650023SHuacai Chen 				restore_fp(current);
424*61650023SHuacai Chen 		}
425*61650023SHuacai Chen 		unlock_fpu_owner();
426*61650023SHuacai Chen 
427*61650023SHuacai Chen 		if (likely(!err))
428*61650023SHuacai Chen 			break;
429*61650023SHuacai Chen 		/* Touch the LSX context and try again */
430*61650023SHuacai Chen 		err = __get_user(tmp, &regs[0]) |
431*61650023SHuacai Chen 			__get_user(tmp, &regs[32*2-1]) |
432*61650023SHuacai Chen 			__get_user(tmp, fcc) |
433*61650023SHuacai Chen 			__get_user(tmp, fcsr);
434*61650023SHuacai Chen 		if (err)
435*61650023SHuacai Chen 			break;	/* really bad sigcontext */
436*61650023SHuacai Chen 	}
437*61650023SHuacai Chen 
438*61650023SHuacai Chen 	return err ?: sig;
439*61650023SHuacai Chen }
440*61650023SHuacai Chen 
441*61650023SHuacai Chen static int protected_save_lasx_context(struct extctx_layout *extctx)
442*61650023SHuacai Chen {
443*61650023SHuacai Chen 	int err = 0;
444*61650023SHuacai Chen 	struct sctx_info __user *info = extctx->lasx.addr;
445*61650023SHuacai Chen 	struct lasx_context __user *lasx_ctx =
446*61650023SHuacai Chen 		(struct lasx_context *)get_ctx_through_ctxinfo(info);
447*61650023SHuacai Chen 	uint64_t __user *regs	= (uint64_t *)&lasx_ctx->regs;
448*61650023SHuacai Chen 	uint64_t __user *fcc	= &lasx_ctx->fcc;
449*61650023SHuacai Chen 	uint32_t __user *fcsr	= &lasx_ctx->fcsr;
450*61650023SHuacai Chen 
451*61650023SHuacai Chen 	while (1) {
452*61650023SHuacai Chen 		lock_fpu_owner();
453*61650023SHuacai Chen 		if (is_lasx_enabled())
454*61650023SHuacai Chen 			err = save_hw_lasx_context(lasx_ctx);
455*61650023SHuacai Chen 		else {
456*61650023SHuacai Chen 			if (is_lsx_enabled())
457*61650023SHuacai Chen 				save_lsx(current);
458*61650023SHuacai Chen 			else if (is_fpu_owner())
459*61650023SHuacai Chen 				save_fp(current);
460*61650023SHuacai Chen 			err = copy_lasx_to_sigcontext(lasx_ctx);
461*61650023SHuacai Chen 		}
462*61650023SHuacai Chen 		unlock_fpu_owner();
463*61650023SHuacai Chen 
464*61650023SHuacai Chen 		err |= __put_user(LASX_CTX_MAGIC, &info->magic);
465*61650023SHuacai Chen 		err |= __put_user(extctx->lasx.size, &info->size);
466*61650023SHuacai Chen 
467*61650023SHuacai Chen 		if (likely(!err))
468*61650023SHuacai Chen 			break;
469*61650023SHuacai Chen 		/* Touch the LASX context and try again */
470*61650023SHuacai Chen 		err = __put_user(0, &regs[0]) |
471*61650023SHuacai Chen 			__put_user(0, &regs[32*4-1]) |
472*61650023SHuacai Chen 			__put_user(0, fcc) |
473*61650023SHuacai Chen 			__put_user(0, fcsr);
474*61650023SHuacai Chen 		if (err)
475*61650023SHuacai Chen 			return err;	/* really bad sigcontext */
476*61650023SHuacai Chen 	}
477*61650023SHuacai Chen 
478*61650023SHuacai Chen 	return err;
479*61650023SHuacai Chen }
480*61650023SHuacai Chen 
481*61650023SHuacai Chen static int protected_restore_lasx_context(struct extctx_layout *extctx)
482*61650023SHuacai Chen {
483*61650023SHuacai Chen 	int err = 0, sig = 0, tmp __maybe_unused;
484*61650023SHuacai Chen 	struct sctx_info __user *info = extctx->lasx.addr;
485*61650023SHuacai Chen 	struct lasx_context __user *lasx_ctx =
486*61650023SHuacai Chen 		(struct lasx_context *)get_ctx_through_ctxinfo(info);
487*61650023SHuacai Chen 	uint64_t __user *regs	= (uint64_t *)&lasx_ctx->regs;
488*61650023SHuacai Chen 	uint64_t __user *fcc	= &lasx_ctx->fcc;
489*61650023SHuacai Chen 	uint32_t __user *fcsr	= &lasx_ctx->fcsr;
490*61650023SHuacai Chen 
491*61650023SHuacai Chen 	err = sig = fcsr_pending(fcsr);
492*61650023SHuacai Chen 	if (err < 0)
493*61650023SHuacai Chen 		return err;
494*61650023SHuacai Chen 
495*61650023SHuacai Chen 	while (1) {
496*61650023SHuacai Chen 		lock_fpu_owner();
497*61650023SHuacai Chen 		if (is_lasx_enabled())
498*61650023SHuacai Chen 			err = restore_hw_lasx_context(lasx_ctx);
499*61650023SHuacai Chen 		else {
500*61650023SHuacai Chen 			err = copy_lasx_from_sigcontext(lasx_ctx);
501*61650023SHuacai Chen 			if (is_lsx_enabled())
502*61650023SHuacai Chen 				restore_lsx(current);
503*61650023SHuacai Chen 			else if (is_fpu_owner())
504*61650023SHuacai Chen 				restore_fp(current);
505*61650023SHuacai Chen 		}
506*61650023SHuacai Chen 		unlock_fpu_owner();
507*61650023SHuacai Chen 
508*61650023SHuacai Chen 		if (likely(!err))
509*61650023SHuacai Chen 			break;
510*61650023SHuacai Chen 		/* Touch the LASX context and try again */
511*61650023SHuacai Chen 		err = __get_user(tmp, &regs[0]) |
512*61650023SHuacai Chen 			__get_user(tmp, &regs[32*4-1]) |
513*61650023SHuacai Chen 			__get_user(tmp, fcc) |
514*61650023SHuacai Chen 			__get_user(tmp, fcsr);
515*61650023SHuacai Chen 		if (err)
516*61650023SHuacai Chen 			break;	/* really bad sigcontext */
517*61650023SHuacai Chen 	}
518*61650023SHuacai Chen 
519*61650023SHuacai Chen 	return err ?: sig;
520*61650023SHuacai Chen }
521*61650023SHuacai Chen 
522b74baf4aSHuacai Chen static int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc,
523b74baf4aSHuacai Chen 			    struct extctx_layout *extctx)
524b74baf4aSHuacai Chen {
525b74baf4aSHuacai Chen 	int i, err = 0;
526b74baf4aSHuacai Chen 	struct sctx_info __user *info;
527b74baf4aSHuacai Chen 
528b74baf4aSHuacai Chen 	err |= __put_user(regs->csr_era, &sc->sc_pc);
529b74baf4aSHuacai Chen 	err |= __put_user(extctx->flags, &sc->sc_flags);
530b74baf4aSHuacai Chen 
531b74baf4aSHuacai Chen 	err |= __put_user(0, &sc->sc_regs[0]);
532b74baf4aSHuacai Chen 	for (i = 1; i < 32; i++)
533b74baf4aSHuacai Chen 		err |= __put_user(regs->regs[i], &sc->sc_regs[i]);
534b74baf4aSHuacai Chen 
535*61650023SHuacai Chen 	if (extctx->lasx.addr)
536*61650023SHuacai Chen 		err |= protected_save_lasx_context(extctx);
537*61650023SHuacai Chen 	else if (extctx->lsx.addr)
538*61650023SHuacai Chen 		err |= protected_save_lsx_context(extctx);
539*61650023SHuacai Chen 	else if (extctx->fpu.addr)
540b74baf4aSHuacai Chen 		err |= protected_save_fpu_context(extctx);
541b74baf4aSHuacai Chen 
542b74baf4aSHuacai Chen 	/* Set the "end" magic */
543b74baf4aSHuacai Chen 	info = (struct sctx_info *)extctx->end.addr;
544b74baf4aSHuacai Chen 	err |= __put_user(0, &info->magic);
545b74baf4aSHuacai Chen 	err |= __put_user(0, &info->size);
546b74baf4aSHuacai Chen 
547b74baf4aSHuacai Chen 	return err;
548b74baf4aSHuacai Chen }
549b74baf4aSHuacai Chen 
550b74baf4aSHuacai Chen static int parse_extcontext(struct sigcontext __user *sc, struct extctx_layout *extctx)
551b74baf4aSHuacai Chen {
552b74baf4aSHuacai Chen 	int err = 0;
553b74baf4aSHuacai Chen 	unsigned int magic, size;
554b74baf4aSHuacai Chen 	struct sctx_info __user *info = (struct sctx_info __user *)&sc->sc_extcontext;
555b74baf4aSHuacai Chen 
556b74baf4aSHuacai Chen 	while(1) {
557b74baf4aSHuacai Chen 		err |= __get_user(magic, &info->magic);
558b74baf4aSHuacai Chen 		err |= __get_user(size, &info->size);
559b74baf4aSHuacai Chen 		if (err)
560b74baf4aSHuacai Chen 			return err;
561b74baf4aSHuacai Chen 
562b74baf4aSHuacai Chen 		switch (magic) {
563b74baf4aSHuacai Chen 		case 0: /* END */
564b74baf4aSHuacai Chen 			goto done;
565b74baf4aSHuacai Chen 
566b74baf4aSHuacai Chen 		case FPU_CTX_MAGIC:
567b74baf4aSHuacai Chen 			if (size < (sizeof(struct sctx_info) +
568b74baf4aSHuacai Chen 				    sizeof(struct fpu_context)))
569b74baf4aSHuacai Chen 				goto invalid;
570b74baf4aSHuacai Chen 			extctx->fpu.addr = info;
571b74baf4aSHuacai Chen 			break;
572b74baf4aSHuacai Chen 
573*61650023SHuacai Chen 		case LSX_CTX_MAGIC:
574*61650023SHuacai Chen 			if (size < (sizeof(struct sctx_info) +
575*61650023SHuacai Chen 				    sizeof(struct lsx_context)))
576*61650023SHuacai Chen 				goto invalid;
577*61650023SHuacai Chen 			extctx->lsx.addr = info;
578*61650023SHuacai Chen 			break;
579*61650023SHuacai Chen 
580*61650023SHuacai Chen 		case LASX_CTX_MAGIC:
581*61650023SHuacai Chen 			if (size < (sizeof(struct sctx_info) +
582*61650023SHuacai Chen 				    sizeof(struct lasx_context)))
583*61650023SHuacai Chen 				goto invalid;
584*61650023SHuacai Chen 			extctx->lasx.addr = info;
585*61650023SHuacai Chen 			break;
586*61650023SHuacai Chen 
587b74baf4aSHuacai Chen 		default:
588b74baf4aSHuacai Chen 			goto invalid;
589b74baf4aSHuacai Chen 		}
590b74baf4aSHuacai Chen 
591b74baf4aSHuacai Chen 		info = (struct sctx_info *)((char *)info + size);
592b74baf4aSHuacai Chen 	}
593b74baf4aSHuacai Chen 
594b74baf4aSHuacai Chen done:
595b74baf4aSHuacai Chen 	return 0;
596b74baf4aSHuacai Chen 
597b74baf4aSHuacai Chen invalid:
598b74baf4aSHuacai Chen 	return -EINVAL;
599b74baf4aSHuacai Chen }
600b74baf4aSHuacai Chen 
601b74baf4aSHuacai Chen static int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
602b74baf4aSHuacai Chen {
603b74baf4aSHuacai Chen 	int i, err = 0;
604b74baf4aSHuacai Chen 	struct extctx_layout extctx;
605b74baf4aSHuacai Chen 
606b74baf4aSHuacai Chen 	memset(&extctx, 0, sizeof(struct extctx_layout));
607b74baf4aSHuacai Chen 
608b74baf4aSHuacai Chen 	err = __get_user(extctx.flags, &sc->sc_flags);
609b74baf4aSHuacai Chen 	if (err)
610b74baf4aSHuacai Chen 		goto bad;
611b74baf4aSHuacai Chen 
612b74baf4aSHuacai Chen 	err = parse_extcontext(sc, &extctx);
613b74baf4aSHuacai Chen 	if (err)
614b74baf4aSHuacai Chen 		goto bad;
615b74baf4aSHuacai Chen 
616b74baf4aSHuacai Chen 	conditional_used_math(extctx.flags & SC_USED_FP);
617b74baf4aSHuacai Chen 
618b74baf4aSHuacai Chen 	/*
619b74baf4aSHuacai Chen 	 * The signal handler may have used FPU; give it up if the program
620b74baf4aSHuacai Chen 	 * doesn't want it following sigreturn.
621b74baf4aSHuacai Chen 	 */
622b74baf4aSHuacai Chen 	if (!(extctx.flags & SC_USED_FP))
623b74baf4aSHuacai Chen 		lose_fpu(0);
624b74baf4aSHuacai Chen 
625b74baf4aSHuacai Chen 	/* Always make any pending restarted system calls return -EINTR */
626b74baf4aSHuacai Chen 	current->restart_block.fn = do_no_restart_syscall;
627b74baf4aSHuacai Chen 
628b74baf4aSHuacai Chen 	err |= __get_user(regs->csr_era, &sc->sc_pc);
629b74baf4aSHuacai Chen 	for (i = 1; i < 32; i++)
630b74baf4aSHuacai Chen 		err |= __get_user(regs->regs[i], &sc->sc_regs[i]);
631b74baf4aSHuacai Chen 
632*61650023SHuacai Chen 	if (extctx.lasx.addr)
633*61650023SHuacai Chen 		err |= protected_restore_lasx_context(&extctx);
634*61650023SHuacai Chen 	else if (extctx.lsx.addr)
635*61650023SHuacai Chen 		err |= protected_restore_lsx_context(&extctx);
636*61650023SHuacai Chen 	else if (extctx.fpu.addr)
637b74baf4aSHuacai Chen 		err |= protected_restore_fpu_context(&extctx);
638b74baf4aSHuacai Chen 
639b74baf4aSHuacai Chen bad:
640b74baf4aSHuacai Chen 	return err;
641b74baf4aSHuacai Chen }
642b74baf4aSHuacai Chen 
643b74baf4aSHuacai Chen static unsigned int handle_flags(void)
644b74baf4aSHuacai Chen {
645b74baf4aSHuacai Chen 	unsigned int flags = 0;
646b74baf4aSHuacai Chen 
647b74baf4aSHuacai Chen 	flags = used_math() ? SC_USED_FP : 0;
648b74baf4aSHuacai Chen 
649b74baf4aSHuacai Chen 	switch (current->thread.error_code) {
650b74baf4aSHuacai Chen 	case 1:
651b74baf4aSHuacai Chen 		flags |= SC_ADDRERR_RD;
652b74baf4aSHuacai Chen 		break;
653b74baf4aSHuacai Chen 	case 2:
654b74baf4aSHuacai Chen 		flags |= SC_ADDRERR_WR;
655b74baf4aSHuacai Chen 		break;
656b74baf4aSHuacai Chen 	}
657b74baf4aSHuacai Chen 
658b74baf4aSHuacai Chen 	return flags;
659b74baf4aSHuacai Chen }
660b74baf4aSHuacai Chen 
661b74baf4aSHuacai Chen static unsigned long extframe_alloc(struct extctx_layout *extctx,
662b74baf4aSHuacai Chen 				    struct _ctx_layout *layout,
663b74baf4aSHuacai Chen 				    size_t size, unsigned int align, unsigned long base)
664b74baf4aSHuacai Chen {
665b74baf4aSHuacai Chen 	unsigned long new_base = base - size;
666b74baf4aSHuacai Chen 
667b74baf4aSHuacai Chen 	new_base = round_down(new_base, (align < 16 ? 16 : align));
668b74baf4aSHuacai Chen 	new_base -= sizeof(struct sctx_info);
669b74baf4aSHuacai Chen 
670b74baf4aSHuacai Chen 	layout->addr = (void *)new_base;
671b74baf4aSHuacai Chen 	layout->size = (unsigned int)(base - new_base);
672b74baf4aSHuacai Chen 	extctx->size += layout->size;
673b74baf4aSHuacai Chen 
674b74baf4aSHuacai Chen 	return new_base;
675b74baf4aSHuacai Chen }
676b74baf4aSHuacai Chen 
677b74baf4aSHuacai Chen static unsigned long setup_extcontext(struct extctx_layout *extctx, unsigned long sp)
678b74baf4aSHuacai Chen {
679b74baf4aSHuacai Chen 	unsigned long new_sp = sp;
680b74baf4aSHuacai Chen 
681b74baf4aSHuacai Chen 	memset(extctx, 0, sizeof(struct extctx_layout));
682b74baf4aSHuacai Chen 
683b74baf4aSHuacai Chen 	extctx->flags = handle_flags();
684b74baf4aSHuacai Chen 
685b74baf4aSHuacai Chen 	/* Grow down, alloc "end" context info first. */
686b74baf4aSHuacai Chen 	new_sp -= sizeof(struct sctx_info);
687b74baf4aSHuacai Chen 	extctx->end.addr = (void *)new_sp;
688b74baf4aSHuacai Chen 	extctx->end.size = (unsigned int)sizeof(struct sctx_info);
689b74baf4aSHuacai Chen 	extctx->size += extctx->end.size;
690b74baf4aSHuacai Chen 
691b74baf4aSHuacai Chen 	if (extctx->flags & SC_USED_FP) {
692*61650023SHuacai Chen 		if (cpu_has_lasx && thread_lasx_context_live())
693*61650023SHuacai Chen 			new_sp = extframe_alloc(extctx, &extctx->lasx,
694*61650023SHuacai Chen 			  sizeof(struct lasx_context), LASX_CTX_ALIGN, new_sp);
695*61650023SHuacai Chen 		else if (cpu_has_lsx && thread_lsx_context_live())
696*61650023SHuacai Chen 			new_sp = extframe_alloc(extctx, &extctx->lsx,
697*61650023SHuacai Chen 			  sizeof(struct lsx_context), LSX_CTX_ALIGN, new_sp);
698*61650023SHuacai Chen 		else if (cpu_has_fpu)
699b74baf4aSHuacai Chen 			new_sp = extframe_alloc(extctx, &extctx->fpu,
700b74baf4aSHuacai Chen 			  sizeof(struct fpu_context), FPU_CTX_ALIGN, new_sp);
701b74baf4aSHuacai Chen 	}
702b74baf4aSHuacai Chen 
703b74baf4aSHuacai Chen 	return new_sp;
704b74baf4aSHuacai Chen }
705b74baf4aSHuacai Chen 
706b74baf4aSHuacai Chen void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs,
707b74baf4aSHuacai Chen 			  struct extctx_layout *extctx)
708b74baf4aSHuacai Chen {
709b74baf4aSHuacai Chen 	unsigned long sp;
710b74baf4aSHuacai Chen 
711b74baf4aSHuacai Chen 	/* Default to using normal stack */
712b74baf4aSHuacai Chen 	sp = regs->regs[3];
713b74baf4aSHuacai Chen 
714b74baf4aSHuacai Chen 	/*
715b74baf4aSHuacai Chen 	 * If we are on the alternate signal stack and would overflow it, don't.
716b74baf4aSHuacai Chen 	 * Return an always-bogus address instead so we will die with SIGSEGV.
717b74baf4aSHuacai Chen 	 */
718b74baf4aSHuacai Chen 	if (on_sig_stack(sp) &&
719b74baf4aSHuacai Chen 	    !likely(on_sig_stack(sp - sizeof(struct rt_sigframe))))
720b74baf4aSHuacai Chen 		return (void __user __force *)(-1UL);
721b74baf4aSHuacai Chen 
722b74baf4aSHuacai Chen 	sp = sigsp(sp, ksig);
723b74baf4aSHuacai Chen 	sp = round_down(sp, 16);
724b74baf4aSHuacai Chen 	sp = setup_extcontext(extctx, sp);
725b74baf4aSHuacai Chen 	sp -= sizeof(struct rt_sigframe);
726b74baf4aSHuacai Chen 
727b74baf4aSHuacai Chen 	if (!IS_ALIGNED(sp, 16))
728b74baf4aSHuacai Chen 		BUG();
729b74baf4aSHuacai Chen 
730b74baf4aSHuacai Chen 	return (void __user *)sp;
731b74baf4aSHuacai Chen }
732b74baf4aSHuacai Chen 
733b74baf4aSHuacai Chen /*
734b74baf4aSHuacai Chen  * Atomically swap in the new signal mask, and wait for a signal.
735b74baf4aSHuacai Chen  */
736b74baf4aSHuacai Chen 
737b74baf4aSHuacai Chen asmlinkage long sys_rt_sigreturn(void)
738b74baf4aSHuacai Chen {
739b74baf4aSHuacai Chen 	int sig;
740b74baf4aSHuacai Chen 	sigset_t set;
741b74baf4aSHuacai Chen 	struct pt_regs *regs;
742b74baf4aSHuacai Chen 	struct rt_sigframe __user *frame;
743b74baf4aSHuacai Chen 
744b74baf4aSHuacai Chen 	regs = current_pt_regs();
745b74baf4aSHuacai Chen 	frame = (struct rt_sigframe __user *)regs->regs[3];
746b74baf4aSHuacai Chen 	if (!access_ok(frame, sizeof(*frame)))
747b74baf4aSHuacai Chen 		goto badframe;
748b74baf4aSHuacai Chen 	if (__copy_from_user(&set, &frame->rs_uctx.uc_sigmask, sizeof(set)))
749b74baf4aSHuacai Chen 		goto badframe;
750b74baf4aSHuacai Chen 
751b74baf4aSHuacai Chen 	set_current_blocked(&set);
752b74baf4aSHuacai Chen 
753b74baf4aSHuacai Chen 	sig = restore_sigcontext(regs, &frame->rs_uctx.uc_mcontext);
754b74baf4aSHuacai Chen 	if (sig < 0)
755b74baf4aSHuacai Chen 		goto badframe;
756b74baf4aSHuacai Chen 	else if (sig)
757b74baf4aSHuacai Chen 		force_sig(sig);
758b74baf4aSHuacai Chen 
759b74baf4aSHuacai Chen 	regs->regs[0] = 0; /* No syscall restarting */
760b74baf4aSHuacai Chen 	if (restore_altstack(&frame->rs_uctx.uc_stack))
761b74baf4aSHuacai Chen 		goto badframe;
762b74baf4aSHuacai Chen 
763b74baf4aSHuacai Chen 	return regs->regs[4];
764b74baf4aSHuacai Chen 
765b74baf4aSHuacai Chen badframe:
766b74baf4aSHuacai Chen 	force_sig(SIGSEGV);
767b74baf4aSHuacai Chen 	return 0;
768b74baf4aSHuacai Chen }
769b74baf4aSHuacai Chen 
770b74baf4aSHuacai Chen static int setup_rt_frame(void *sig_return, struct ksignal *ksig,
771b74baf4aSHuacai Chen 			  struct pt_regs *regs, sigset_t *set)
772b74baf4aSHuacai Chen {
773b74baf4aSHuacai Chen 	int err = 0;
774b74baf4aSHuacai Chen 	struct extctx_layout extctx;
775b74baf4aSHuacai Chen 	struct rt_sigframe __user *frame;
776b74baf4aSHuacai Chen 
777b74baf4aSHuacai Chen 	frame = get_sigframe(ksig, regs, &extctx);
778b74baf4aSHuacai Chen 	if (!access_ok(frame, sizeof(*frame) + extctx.size))
779b74baf4aSHuacai Chen 		return -EFAULT;
780b74baf4aSHuacai Chen 
781b74baf4aSHuacai Chen 	/* Create siginfo.  */
782b74baf4aSHuacai Chen 	err |= copy_siginfo_to_user(&frame->rs_info, &ksig->info);
783b74baf4aSHuacai Chen 
784b74baf4aSHuacai Chen 	/* Create the ucontext.	 */
785b74baf4aSHuacai Chen 	err |= __put_user(0, &frame->rs_uctx.uc_flags);
786b74baf4aSHuacai Chen 	err |= __put_user(NULL, &frame->rs_uctx.uc_link);
787b74baf4aSHuacai Chen 	err |= __save_altstack(&frame->rs_uctx.uc_stack, regs->regs[3]);
788b74baf4aSHuacai Chen 	err |= setup_sigcontext(regs, &frame->rs_uctx.uc_mcontext, &extctx);
789b74baf4aSHuacai Chen 	err |= __copy_to_user(&frame->rs_uctx.uc_sigmask, set, sizeof(*set));
790b74baf4aSHuacai Chen 
791b74baf4aSHuacai Chen 	if (err)
792b74baf4aSHuacai Chen 		return -EFAULT;
793b74baf4aSHuacai Chen 
794b74baf4aSHuacai Chen 	/*
795b74baf4aSHuacai Chen 	 * Arguments to signal handler:
796b74baf4aSHuacai Chen 	 *
797b74baf4aSHuacai Chen 	 *   a0 = signal number
798b74baf4aSHuacai Chen 	 *   a1 = pointer to siginfo
799b74baf4aSHuacai Chen 	 *   a2 = pointer to ucontext
800b74baf4aSHuacai Chen 	 *
801b74baf4aSHuacai Chen 	 * c0_era point to the signal handler, $r3 (sp) points to
802b74baf4aSHuacai Chen 	 * the struct rt_sigframe.
803b74baf4aSHuacai Chen 	 */
804b74baf4aSHuacai Chen 	regs->regs[4] = ksig->sig;
805b74baf4aSHuacai Chen 	regs->regs[5] = (unsigned long) &frame->rs_info;
806b74baf4aSHuacai Chen 	regs->regs[6] = (unsigned long) &frame->rs_uctx;
807b74baf4aSHuacai Chen 	regs->regs[3] = (unsigned long) frame;
808b74baf4aSHuacai Chen 	regs->regs[1] = (unsigned long) sig_return;
809b74baf4aSHuacai Chen 	regs->csr_era = (unsigned long) ksig->ka.sa.sa_handler;
810b74baf4aSHuacai Chen 
811b74baf4aSHuacai Chen 	DEBUGP("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%lx\n",
812b74baf4aSHuacai Chen 	       current->comm, current->pid,
813b74baf4aSHuacai Chen 	       frame, regs->csr_era, regs->regs[1]);
814b74baf4aSHuacai Chen 
815b74baf4aSHuacai Chen 	return 0;
816b74baf4aSHuacai Chen }
817b74baf4aSHuacai Chen 
818b74baf4aSHuacai Chen static void handle_signal(struct ksignal *ksig, struct pt_regs *regs)
819b74baf4aSHuacai Chen {
820b74baf4aSHuacai Chen 	int ret;
821b74baf4aSHuacai Chen 	sigset_t *oldset = sigmask_to_save();
822b74baf4aSHuacai Chen 	void *vdso = current->mm->context.vdso;
823b74baf4aSHuacai Chen 
824b74baf4aSHuacai Chen 	/* Are we from a system call? */
825b74baf4aSHuacai Chen 	if (regs->regs[0]) {
826b74baf4aSHuacai Chen 		switch (regs->regs[4]) {
827b74baf4aSHuacai Chen 		case -ERESTART_RESTARTBLOCK:
828b74baf4aSHuacai Chen 		case -ERESTARTNOHAND:
829b74baf4aSHuacai Chen 			regs->regs[4] = -EINTR;
830b74baf4aSHuacai Chen 			break;
831b74baf4aSHuacai Chen 		case -ERESTARTSYS:
832b74baf4aSHuacai Chen 			if (!(ksig->ka.sa.sa_flags & SA_RESTART)) {
833b74baf4aSHuacai Chen 				regs->regs[4] = -EINTR;
834b74baf4aSHuacai Chen 				break;
835b74baf4aSHuacai Chen 			}
836b74baf4aSHuacai Chen 			fallthrough;
837b74baf4aSHuacai Chen 		case -ERESTARTNOINTR:
838b74baf4aSHuacai Chen 			regs->regs[4] = regs->orig_a0;
839b74baf4aSHuacai Chen 			regs->csr_era -= 4;
840b74baf4aSHuacai Chen 		}
841b74baf4aSHuacai Chen 
842b74baf4aSHuacai Chen 		regs->regs[0] = 0;	/* Don't deal with this again.	*/
843b74baf4aSHuacai Chen 	}
844b74baf4aSHuacai Chen 
845b74baf4aSHuacai Chen 	rseq_signal_deliver(ksig, regs);
846b74baf4aSHuacai Chen 
847b74baf4aSHuacai Chen 	ret = setup_rt_frame(vdso + current->thread.vdso->offset_sigreturn, ksig, regs, oldset);
848b74baf4aSHuacai Chen 
849b74baf4aSHuacai Chen 	signal_setup_done(ret, ksig, 0);
850b74baf4aSHuacai Chen }
851b74baf4aSHuacai Chen 
85201630053SHuacai Chen void arch_do_signal_or_restart(struct pt_regs *regs)
853b74baf4aSHuacai Chen {
854b74baf4aSHuacai Chen 	struct ksignal ksig;
855b74baf4aSHuacai Chen 
85601630053SHuacai Chen 	if (get_signal(&ksig)) {
857b74baf4aSHuacai Chen 		/* Whee!  Actually deliver the signal.	*/
858b74baf4aSHuacai Chen 		handle_signal(&ksig, regs);
859b74baf4aSHuacai Chen 		return;
860b74baf4aSHuacai Chen 	}
861b74baf4aSHuacai Chen 
862b74baf4aSHuacai Chen 	/* Are we from a system call? */
863b74baf4aSHuacai Chen 	if (regs->regs[0]) {
864b74baf4aSHuacai Chen 		switch (regs->regs[4]) {
865b74baf4aSHuacai Chen 		case -ERESTARTNOHAND:
866b74baf4aSHuacai Chen 		case -ERESTARTSYS:
867b74baf4aSHuacai Chen 		case -ERESTARTNOINTR:
868b74baf4aSHuacai Chen 			regs->regs[4] = regs->orig_a0;
869b74baf4aSHuacai Chen 			regs->csr_era -= 4;
870b74baf4aSHuacai Chen 			break;
871b74baf4aSHuacai Chen 
872b74baf4aSHuacai Chen 		case -ERESTART_RESTARTBLOCK:
873b74baf4aSHuacai Chen 			regs->regs[4] = regs->orig_a0;
874b74baf4aSHuacai Chen 			regs->regs[11] = __NR_restart_syscall;
875b74baf4aSHuacai Chen 			regs->csr_era -= 4;
876b74baf4aSHuacai Chen 			break;
877b74baf4aSHuacai Chen 		}
878b74baf4aSHuacai Chen 		regs->regs[0] = 0;	/* Don't deal with this again.	*/
879b74baf4aSHuacai Chen 	}
880b74baf4aSHuacai Chen 
881b74baf4aSHuacai Chen 	/*
882b74baf4aSHuacai Chen 	 * If there's no signal to deliver, we just put the saved sigmask
883b74baf4aSHuacai Chen 	 * back
884b74baf4aSHuacai Chen 	 */
885b74baf4aSHuacai Chen 	restore_saved_sigmask();
886b74baf4aSHuacai Chen }
887