]>
Commit | Line | Data |
---|---|---|
7ecbac74 GH |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (C) 2005-2017 Andes Technology Corporation | |
3 | ||
4 | #include <linux/errno.h> | |
5 | #include <linux/signal.h> | |
6 | #include <linux/ptrace.h> | |
7 | #include <linux/personality.h> | |
8 | #include <linux/freezer.h> | |
9 | #include <linux/tracehook.h> | |
10 | #include <linux/uaccess.h> | |
11 | ||
12 | #include <asm/cacheflush.h> | |
13 | #include <asm/ucontext.h> | |
14 | #include <asm/unistd.h> | |
e46bf83c | 15 | #include <asm/fpu.h> |
7ecbac74 GH |
16 | |
17 | #include <asm/ptrace.h> | |
18 | #include <asm/vdso.h> | |
19 | ||
20 | struct rt_sigframe { | |
21 | struct siginfo info; | |
22 | struct ucontext uc; | |
23 | }; | |
e46bf83c VC |
24 | #if IS_ENABLED(CONFIG_FPU) |
25 | static inline int restore_sigcontext_fpu(struct pt_regs *regs, | |
26 | struct sigcontext __user *sc) | |
27 | { | |
28 | struct task_struct *tsk = current; | |
29 | unsigned long used_math_flag; | |
30 | int ret = 0; | |
31 | ||
32 | clear_used_math(); | |
33 | __get_user_error(used_math_flag, &sc->used_math_flag, ret); | |
34 | ||
35 | if (!used_math_flag) | |
36 | return 0; | |
37 | set_used_math(); | |
38 | ||
39 | #if IS_ENABLED(CONFIG_LAZY_FPU) | |
40 | preempt_disable(); | |
41 | if (current == last_task_used_math) { | |
42 | last_task_used_math = NULL; | |
43 | disable_ptreg_fpu(regs); | |
44 | } | |
45 | preempt_enable(); | |
46 | #else | |
47 | clear_fpu(regs); | |
48 | #endif | |
49 | ||
50 | return __copy_from_user(&tsk->thread.fpu, &sc->fpu, | |
51 | sizeof(struct fpu_struct)); | |
52 | } | |
53 | ||
54 | static inline int setup_sigcontext_fpu(struct pt_regs *regs, | |
55 | struct sigcontext __user *sc) | |
56 | { | |
57 | struct task_struct *tsk = current; | |
58 | int ret = 0; | |
59 | ||
60 | __put_user_error(used_math(), &sc->used_math_flag, ret); | |
61 | ||
62 | if (!used_math()) | |
63 | return ret; | |
64 | ||
65 | preempt_disable(); | |
66 | #if IS_ENABLED(CONFIG_LAZY_FPU) | |
67 | if (last_task_used_math == tsk) | |
68 | save_fpu(last_task_used_math); | |
69 | #else | |
70 | unlazy_fpu(tsk); | |
71 | #endif | |
72 | ret = __copy_to_user(&sc->fpu, &tsk->thread.fpu, | |
73 | sizeof(struct fpu_struct)); | |
74 | preempt_enable(); | |
75 | return ret; | |
76 | } | |
77 | #endif | |
7ecbac74 GH |
78 | |
79 | static int restore_sigframe(struct pt_regs *regs, | |
80 | struct rt_sigframe __user * sf) | |
81 | { | |
82 | sigset_t set; | |
83 | int err; | |
84 | ||
85 | err = __copy_from_user(&set, &sf->uc.uc_sigmask, sizeof(set)); | |
86 | if (err == 0) { | |
87 | set_current_blocked(&set); | |
88 | } | |
89 | ||
90 | __get_user_error(regs->uregs[0], &sf->uc.uc_mcontext.nds32_r0, err); | |
91 | __get_user_error(regs->uregs[1], &sf->uc.uc_mcontext.nds32_r1, err); | |
92 | __get_user_error(regs->uregs[2], &sf->uc.uc_mcontext.nds32_r2, err); | |
93 | __get_user_error(regs->uregs[3], &sf->uc.uc_mcontext.nds32_r3, err); | |
94 | __get_user_error(regs->uregs[4], &sf->uc.uc_mcontext.nds32_r4, err); | |
95 | __get_user_error(regs->uregs[5], &sf->uc.uc_mcontext.nds32_r5, err); | |
96 | __get_user_error(regs->uregs[6], &sf->uc.uc_mcontext.nds32_r6, err); | |
97 | __get_user_error(regs->uregs[7], &sf->uc.uc_mcontext.nds32_r7, err); | |
98 | __get_user_error(regs->uregs[8], &sf->uc.uc_mcontext.nds32_r8, err); | |
99 | __get_user_error(regs->uregs[9], &sf->uc.uc_mcontext.nds32_r9, err); | |
100 | __get_user_error(regs->uregs[10], &sf->uc.uc_mcontext.nds32_r10, err); | |
101 | __get_user_error(regs->uregs[11], &sf->uc.uc_mcontext.nds32_r11, err); | |
102 | __get_user_error(regs->uregs[12], &sf->uc.uc_mcontext.nds32_r12, err); | |
103 | __get_user_error(regs->uregs[13], &sf->uc.uc_mcontext.nds32_r13, err); | |
104 | __get_user_error(regs->uregs[14], &sf->uc.uc_mcontext.nds32_r14, err); | |
105 | __get_user_error(regs->uregs[15], &sf->uc.uc_mcontext.nds32_r15, err); | |
106 | __get_user_error(regs->uregs[16], &sf->uc.uc_mcontext.nds32_r16, err); | |
107 | __get_user_error(regs->uregs[17], &sf->uc.uc_mcontext.nds32_r17, err); | |
108 | __get_user_error(regs->uregs[18], &sf->uc.uc_mcontext.nds32_r18, err); | |
109 | __get_user_error(regs->uregs[19], &sf->uc.uc_mcontext.nds32_r19, err); | |
110 | __get_user_error(regs->uregs[20], &sf->uc.uc_mcontext.nds32_r20, err); | |
111 | __get_user_error(regs->uregs[21], &sf->uc.uc_mcontext.nds32_r21, err); | |
112 | __get_user_error(regs->uregs[22], &sf->uc.uc_mcontext.nds32_r22, err); | |
113 | __get_user_error(regs->uregs[23], &sf->uc.uc_mcontext.nds32_r23, err); | |
114 | __get_user_error(regs->uregs[24], &sf->uc.uc_mcontext.nds32_r24, err); | |
115 | __get_user_error(regs->uregs[25], &sf->uc.uc_mcontext.nds32_r25, err); | |
116 | ||
117 | __get_user_error(regs->fp, &sf->uc.uc_mcontext.nds32_fp, err); | |
118 | __get_user_error(regs->gp, &sf->uc.uc_mcontext.nds32_gp, err); | |
119 | __get_user_error(regs->lp, &sf->uc.uc_mcontext.nds32_lp, err); | |
120 | __get_user_error(regs->sp, &sf->uc.uc_mcontext.nds32_sp, err); | |
121 | __get_user_error(regs->ipc, &sf->uc.uc_mcontext.nds32_ipc, err); | |
122 | #if defined(CONFIG_HWZOL) | |
123 | __get_user_error(regs->lc, &sf->uc.uc_mcontext.zol.nds32_lc, err); | |
124 | __get_user_error(regs->le, &sf->uc.uc_mcontext.zol.nds32_le, err); | |
125 | __get_user_error(regs->lb, &sf->uc.uc_mcontext.zol.nds32_lb, err); | |
126 | #endif | |
e46bf83c VC |
127 | #if IS_ENABLED(CONFIG_FPU) |
128 | err |= restore_sigcontext_fpu(regs, &sf->uc.uc_mcontext); | |
129 | #endif | |
7ecbac74 GH |
130 | /* |
131 | * Avoid sys_rt_sigreturn() restarting. | |
132 | */ | |
133 | forget_syscall(regs); | |
134 | return err; | |
135 | } | |
136 | ||
137 | asmlinkage long sys_rt_sigreturn(struct pt_regs *regs) | |
138 | { | |
139 | struct rt_sigframe __user *frame; | |
140 | ||
141 | /* Always make any pending restarted system calls return -EINTR */ | |
142 | current->restart_block.fn = do_no_restart_syscall; | |
143 | ||
144 | /* | |
145 | * Since we stacked the signal on a 64-bit boundary, | |
146 | * then 'sp' should be two-word aligned here. If it's | |
147 | * not, then the user is trying to mess with us. | |
148 | */ | |
149 | if (regs->sp & 7) | |
150 | goto badframe; | |
151 | ||
152 | frame = (struct rt_sigframe __user *)regs->sp; | |
153 | ||
96d4f267 | 154 | if (!access_ok(frame, sizeof(*frame))) |
7ecbac74 GH |
155 | goto badframe; |
156 | ||
157 | if (restore_sigframe(regs, frame)) | |
158 | goto badframe; | |
159 | ||
160 | if (restore_altstack(&frame->uc.uc_stack)) | |
161 | goto badframe; | |
162 | ||
163 | return regs->uregs[0]; | |
164 | ||
165 | badframe: | |
166 | force_sig(SIGSEGV, current); | |
167 | return 0; | |
168 | } | |
169 | ||
170 | static int | |
171 | setup_sigframe(struct rt_sigframe __user * sf, struct pt_regs *regs, | |
172 | sigset_t * set) | |
173 | { | |
174 | int err = 0; | |
175 | ||
176 | __put_user_error(regs->uregs[0], &sf->uc.uc_mcontext.nds32_r0, err); | |
177 | __put_user_error(regs->uregs[1], &sf->uc.uc_mcontext.nds32_r1, err); | |
178 | __put_user_error(regs->uregs[2], &sf->uc.uc_mcontext.nds32_r2, err); | |
179 | __put_user_error(regs->uregs[3], &sf->uc.uc_mcontext.nds32_r3, err); | |
180 | __put_user_error(regs->uregs[4], &sf->uc.uc_mcontext.nds32_r4, err); | |
181 | __put_user_error(regs->uregs[5], &sf->uc.uc_mcontext.nds32_r5, err); | |
182 | __put_user_error(regs->uregs[6], &sf->uc.uc_mcontext.nds32_r6, err); | |
183 | __put_user_error(regs->uregs[7], &sf->uc.uc_mcontext.nds32_r7, err); | |
184 | __put_user_error(regs->uregs[8], &sf->uc.uc_mcontext.nds32_r8, err); | |
185 | __put_user_error(regs->uregs[9], &sf->uc.uc_mcontext.nds32_r9, err); | |
186 | __put_user_error(regs->uregs[10], &sf->uc.uc_mcontext.nds32_r10, err); | |
187 | __put_user_error(regs->uregs[11], &sf->uc.uc_mcontext.nds32_r11, err); | |
188 | __put_user_error(regs->uregs[12], &sf->uc.uc_mcontext.nds32_r12, err); | |
189 | __put_user_error(regs->uregs[13], &sf->uc.uc_mcontext.nds32_r13, err); | |
190 | __put_user_error(regs->uregs[14], &sf->uc.uc_mcontext.nds32_r14, err); | |
191 | __put_user_error(regs->uregs[15], &sf->uc.uc_mcontext.nds32_r15, err); | |
192 | __put_user_error(regs->uregs[16], &sf->uc.uc_mcontext.nds32_r16, err); | |
193 | __put_user_error(regs->uregs[17], &sf->uc.uc_mcontext.nds32_r17, err); | |
194 | __put_user_error(regs->uregs[18], &sf->uc.uc_mcontext.nds32_r18, err); | |
195 | __put_user_error(regs->uregs[19], &sf->uc.uc_mcontext.nds32_r19, err); | |
196 | __put_user_error(regs->uregs[20], &sf->uc.uc_mcontext.nds32_r20, err); | |
197 | ||
198 | __put_user_error(regs->uregs[21], &sf->uc.uc_mcontext.nds32_r21, err); | |
199 | __put_user_error(regs->uregs[22], &sf->uc.uc_mcontext.nds32_r22, err); | |
200 | __put_user_error(regs->uregs[23], &sf->uc.uc_mcontext.nds32_r23, err); | |
201 | __put_user_error(regs->uregs[24], &sf->uc.uc_mcontext.nds32_r24, err); | |
202 | __put_user_error(regs->uregs[25], &sf->uc.uc_mcontext.nds32_r25, err); | |
203 | __put_user_error(regs->fp, &sf->uc.uc_mcontext.nds32_fp, err); | |
204 | __put_user_error(regs->gp, &sf->uc.uc_mcontext.nds32_gp, err); | |
205 | __put_user_error(regs->lp, &sf->uc.uc_mcontext.nds32_lp, err); | |
206 | __put_user_error(regs->sp, &sf->uc.uc_mcontext.nds32_sp, err); | |
207 | __put_user_error(regs->ipc, &sf->uc.uc_mcontext.nds32_ipc, err); | |
208 | #if defined(CONFIG_HWZOL) | |
209 | __put_user_error(regs->lc, &sf->uc.uc_mcontext.zol.nds32_lc, err); | |
210 | __put_user_error(regs->le, &sf->uc.uc_mcontext.zol.nds32_le, err); | |
211 | __put_user_error(regs->lb, &sf->uc.uc_mcontext.zol.nds32_lb, err); | |
212 | #endif | |
e46bf83c VC |
213 | #if IS_ENABLED(CONFIG_FPU) |
214 | err |= setup_sigcontext_fpu(regs, &sf->uc.uc_mcontext); | |
215 | #endif | |
7ecbac74 GH |
216 | |
217 | __put_user_error(current->thread.trap_no, &sf->uc.uc_mcontext.trap_no, | |
218 | err); | |
219 | __put_user_error(current->thread.error_code, | |
220 | &sf->uc.uc_mcontext.error_code, err); | |
221 | __put_user_error(current->thread.address, | |
222 | &sf->uc.uc_mcontext.fault_address, err); | |
223 | __put_user_error(set->sig[0], &sf->uc.uc_mcontext.oldmask, err); | |
224 | ||
225 | err |= __copy_to_user(&sf->uc.uc_sigmask, set, sizeof(*set)); | |
226 | ||
227 | return err; | |
228 | } | |
229 | ||
230 | static inline void __user *get_sigframe(struct ksignal *ksig, | |
231 | struct pt_regs *regs, int framesize) | |
232 | { | |
233 | unsigned long sp; | |
234 | ||
235 | /* Default to using normal stack */ | |
236 | sp = regs->sp; | |
237 | ||
238 | /* | |
239 | * If we are on the alternate signal stack and would overflow it, don't. | |
240 | * Return an always-bogus address instead so we will die with SIGSEGV. | |
241 | */ | |
242 | if (on_sig_stack(sp) && !likely(on_sig_stack(sp - framesize))) | |
243 | return (void __user __force *)(-1UL); | |
244 | ||
245 | /* This is the X/Open sanctioned signal stack switching. */ | |
246 | sp = (sigsp(sp, ksig) - framesize); | |
247 | ||
248 | /* | |
249 | * nds32 mandates 8-byte alignment | |
250 | */ | |
251 | sp &= ~0x7UL; | |
252 | ||
253 | return (void __user *)sp; | |
254 | } | |
255 | ||
256 | static int | |
257 | setup_return(struct pt_regs *regs, struct ksignal *ksig, void __user * frame) | |
258 | { | |
259 | unsigned long handler = (unsigned long)ksig->ka.sa.sa_handler; | |
260 | unsigned long retcode; | |
261 | ||
262 | retcode = VDSO_SYMBOL(current->mm->context.vdso, rt_sigtramp); | |
263 | regs->uregs[0] = ksig->sig; | |
264 | regs->sp = (unsigned long)frame; | |
265 | regs->lp = retcode; | |
266 | regs->ipc = handler; | |
267 | ||
268 | return 0; | |
269 | } | |
270 | ||
271 | static int | |
272 | setup_rt_frame(struct ksignal *ksig, sigset_t * set, struct pt_regs *regs) | |
273 | { | |
274 | struct rt_sigframe __user *frame = | |
275 | get_sigframe(ksig, regs, sizeof(*frame)); | |
276 | int err = 0; | |
277 | ||
96d4f267 | 278 | if (!access_ok(frame, sizeof(*frame))) |
7ecbac74 GH |
279 | return -EFAULT; |
280 | ||
281 | __put_user_error(0, &frame->uc.uc_flags, err); | |
282 | __put_user_error(NULL, &frame->uc.uc_link, err); | |
283 | ||
284 | err |= __save_altstack(&frame->uc.uc_stack, regs->sp); | |
285 | err |= setup_sigframe(frame, regs, set); | |
286 | if (err == 0) { | |
287 | setup_return(regs, ksig, frame); | |
288 | if (ksig->ka.sa.sa_flags & SA_SIGINFO) { | |
289 | err |= copy_siginfo_to_user(&frame->info, &ksig->info); | |
290 | regs->uregs[1] = (unsigned long)&frame->info; | |
291 | regs->uregs[2] = (unsigned long)&frame->uc; | |
292 | } | |
293 | } | |
294 | return err; | |
295 | } | |
296 | ||
297 | /* | |
298 | * OK, we're invoking a handler | |
299 | */ | |
300 | static void handle_signal(struct ksignal *ksig, struct pt_regs *regs) | |
301 | { | |
302 | int ret; | |
303 | sigset_t *oldset = sigmask_to_save(); | |
304 | ||
305 | if (in_syscall(regs)) { | |
306 | /* Avoid additional syscall restarting via ret_slow_syscall. */ | |
307 | forget_syscall(regs); | |
308 | ||
309 | switch (regs->uregs[0]) { | |
310 | case -ERESTART_RESTARTBLOCK: | |
311 | case -ERESTARTNOHAND: | |
312 | regs->uregs[0] = -EINTR; | |
313 | break; | |
314 | case -ERESTARTSYS: | |
315 | if (!(ksig->ka.sa.sa_flags & SA_RESTART)) { | |
316 | regs->uregs[0] = -EINTR; | |
317 | break; | |
318 | } | |
319 | case -ERESTARTNOINTR: | |
320 | regs->uregs[0] = regs->orig_r0; | |
321 | regs->ipc -= 4; | |
322 | break; | |
323 | } | |
324 | } | |
325 | /* | |
326 | * Set up the stack frame | |
327 | */ | |
328 | ret = setup_rt_frame(ksig, oldset, regs); | |
329 | ||
330 | signal_setup_done(ret, ksig, 0); | |
331 | } | |
332 | ||
333 | /* | |
334 | * Note that 'init' is a special process: it doesn't get signals it doesn't | |
335 | * want to handle. Thus you cannot kill init even with a SIGKILL even by | |
336 | * mistake. | |
337 | * | |
338 | * Note that we go through the signals twice: once to check the signals that | |
339 | * the kernel can handle, and then we build all the user-level signal handling | |
340 | * stack-frames in one go after that. | |
341 | */ | |
342 | static void do_signal(struct pt_regs *regs) | |
343 | { | |
344 | struct ksignal ksig; | |
345 | ||
346 | if (get_signal(&ksig)) { | |
347 | handle_signal(&ksig, regs); | |
348 | return; | |
349 | } | |
350 | ||
351 | /* | |
352 | * If we were from a system call, check for system call restarting... | |
353 | */ | |
354 | if (in_syscall(regs)) { | |
355 | /* Restart the system call - no handlers present */ | |
356 | ||
357 | /* Avoid additional syscall restarting via ret_slow_syscall. */ | |
358 | forget_syscall(regs); | |
359 | ||
360 | switch (regs->uregs[0]) { | |
361 | case -ERESTART_RESTARTBLOCK: | |
362 | regs->uregs[15] = __NR_restart_syscall; | |
363 | case -ERESTARTNOHAND: | |
364 | case -ERESTARTSYS: | |
365 | case -ERESTARTNOINTR: | |
366 | regs->uregs[0] = regs->orig_r0; | |
367 | regs->ipc -= 0x4; | |
368 | break; | |
369 | } | |
370 | } | |
371 | restore_saved_sigmask(); | |
372 | } | |
373 | ||
374 | asmlinkage void | |
375 | do_notify_resume(struct pt_regs *regs, unsigned int thread_flags) | |
376 | { | |
377 | if (thread_flags & _TIF_SIGPENDING) | |
378 | do_signal(regs); | |
379 | ||
380 | if (thread_flags & _TIF_NOTIFY_RESUME) { | |
381 | clear_thread_flag(TIF_NOTIFY_RESUME); | |
382 | tracehook_notify_resume(regs); | |
383 | } | |
384 | } |