Commit | Line | Data |
---|---|---|
e9564df7 | 1 | // SPDX-License-Identifier: GPL-2.0 |
e9564df7 | 2 | |
e9564df7 | 3 | #include <linux/signal.h> |
bf241682 | 4 | #include <linux/uaccess.h> |
e9564df7 | 5 | #include <linux/syscalls.h> |
e9564df7 | 6 | #include <linux/tracehook.h> |
e9564df7 | 7 | |
e9564df7 GR |
8 | #include <asm/traps.h> |
9 | #include <asm/ucontext.h> | |
10 | #include <asm/vdso.h> | |
11 | ||
12 | #include <abi/regdef.h> | |
13 | ||
14 | #ifdef CONFIG_CPU_HAS_FPU | |
15 | #include <abi/fpu.h> | |
bf241682 | 16 | static int restore_fpu_state(struct sigcontext __user *sc) |
e9564df7 GR |
17 | { |
18 | int err = 0; | |
19 | struct user_fp user_fp; | |
20 | ||
bf241682 | 21 | err = __copy_from_user(&user_fp, &sc->sc_user_fp, sizeof(user_fp)); |
e9564df7 GR |
22 | |
23 | restore_from_user_fp(&user_fp); | |
24 | ||
25 | return err; | |
26 | } | |
27 | ||
bf241682 | 28 | static int save_fpu_state(struct sigcontext __user *sc) |
e9564df7 GR |
29 | { |
30 | struct user_fp user_fp; | |
31 | ||
32 | save_to_user_fp(&user_fp); | |
33 | ||
bf241682 | 34 | return __copy_to_user(&sc->sc_user_fp, &user_fp, sizeof(user_fp)); |
e9564df7 GR |
35 | } |
36 | #else | |
bf241682 GR |
37 | #define restore_fpu_state(sigcontext) (0) |
38 | #define save_fpu_state(sigcontext) (0) | |
e9564df7 GR |
39 | #endif |
40 | ||
41 | struct rt_sigframe { | |
19e5e2ae GR |
42 | /* |
43 | * pad[3] is compatible with the same struct defined in | |
44 | * gcc/libgcc/config/csky/linux-unwind.h | |
45 | */ | |
46 | int pad[3]; | |
e9564df7 GR |
47 | struct siginfo info; |
48 | struct ucontext uc; | |
49 | }; | |
50 | ||
bf241682 GR |
51 | static long restore_sigcontext(struct pt_regs *regs, |
52 | struct sigcontext __user *sc) | |
e9564df7 GR |
53 | { |
54 | int err = 0; | |
55 | ||
bf241682 GR |
56 | /* sc_pt_regs is structured the same as the start of pt_regs */ |
57 | err |= __copy_from_user(regs, &sc->sc_pt_regs, sizeof(struct pt_regs)); | |
e9564df7 | 58 | |
bf241682 | 59 | /* Restore the floating-point state. */ |
e9564df7 GR |
60 | err |= restore_fpu_state(sc); |
61 | ||
e9564df7 GR |
62 | return err; |
63 | } | |
64 | ||
bf241682 | 65 | SYSCALL_DEFINE0(rt_sigreturn) |
e9564df7 | 66 | { |
e9564df7 | 67 | struct pt_regs *regs = current_pt_regs(); |
bf241682 | 68 | struct rt_sigframe __user *frame; |
bf241682 GR |
69 | sigset_t set; |
70 | ||
71 | /* Always make any pending restarted system calls return -EINTR */ | |
72 | current->restart_block.fn = do_no_restart_syscall; | |
73 | ||
74 | frame = (struct rt_sigframe __user *)regs->usp; | |
e9564df7 | 75 | |
96d4f267 | 76 | if (!access_ok(frame, sizeof(*frame))) |
e9564df7 | 77 | goto badframe; |
bf241682 | 78 | |
e9564df7 GR |
79 | if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) |
80 | goto badframe; | |
81 | ||
bf241682 GR |
82 | set_current_blocked(&set); |
83 | ||
84 | if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) | |
85 | goto badframe; | |
e9564df7 | 86 | |
bf241682 | 87 | if (restore_altstack(&frame->uc.uc_stack)) |
e9564df7 GR |
88 | goto badframe; |
89 | ||
bf241682 | 90 | return regs->a0; |
e9564df7 GR |
91 | |
92 | badframe: | |
3cf5d076 | 93 | force_sig(SIGSEGV); |
e9564df7 GR |
94 | return 0; |
95 | } | |
96 | ||
bf241682 GR |
97 | static int setup_sigcontext(struct rt_sigframe __user *frame, |
98 | struct pt_regs *regs) | |
e9564df7 | 99 | { |
bf241682 | 100 | struct sigcontext __user *sc = &frame->uc.uc_mcontext; |
e9564df7 GR |
101 | int err = 0; |
102 | ||
bf241682 | 103 | err |= __copy_to_user(&sc->sc_pt_regs, regs, sizeof(struct pt_regs)); |
e9564df7 GR |
104 | err |= save_fpu_state(sc); |
105 | ||
106 | return err; | |
107 | } | |
108 | ||
bf241682 GR |
109 | static inline void __user *get_sigframe(struct ksignal *ksig, |
110 | struct pt_regs *regs, size_t framesize) | |
e9564df7 | 111 | { |
bf241682 GR |
112 | unsigned long sp; |
113 | /* Default to using normal stack */ | |
114 | sp = regs->usp; | |
e9564df7 | 115 | |
bf241682 GR |
116 | /* |
117 | * If we are on the alternate signal stack and would overflow it, don't. | |
118 | * Return an always-bogus address instead so we will die with SIGSEGV. | |
119 | */ | |
120 | if (on_sig_stack(sp) && !likely(on_sig_stack(sp - framesize))) | |
121 | return (void __user __force *)(-1UL); | |
e9564df7 | 122 | |
bf241682 GR |
123 | /* This is the X/Open sanctioned signal stack switching. */ |
124 | sp = sigsp(sp, ksig) - framesize; | |
125 | ||
126 | /* Align the stack frame. */ | |
127 | sp &= -8UL; | |
128 | ||
129 | return (void __user *)sp; | |
e9564df7 GR |
130 | } |
131 | ||
132 | static int | |
133 | setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) | |
134 | { | |
135 | struct rt_sigframe *frame; | |
136 | int err = 0; | |
e9564df7 GR |
137 | struct csky_vdso *vdso = current->mm->context.vdso; |
138 | ||
bf241682 GR |
139 | frame = get_sigframe(ksig, regs, sizeof(*frame)); |
140 | if (!access_ok(frame, sizeof(*frame))) | |
141 | return -EFAULT; | |
e9564df7 | 142 | |
e9564df7 GR |
143 | err |= copy_siginfo_to_user(&frame->info, &ksig->info); |
144 | ||
bf241682 | 145 | /* Create the ucontext. */ |
e9564df7 | 146 | err |= __put_user(0, &frame->uc.uc_flags); |
bf241682 GR |
147 | err |= __put_user(NULL, &frame->uc.uc_link); |
148 | err |= __save_altstack(&frame->uc.uc_stack, regs->usp); | |
149 | err |= setup_sigcontext(frame, regs); | |
150 | err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); | |
e9564df7 | 151 | if (err) |
bf241682 | 152 | return -EFAULT; |
e9564df7 | 153 | |
bf241682 GR |
154 | /* Set up to return from userspace. */ |
155 | regs->lr = (unsigned long)(vdso->rt_signal_retcode); | |
e9564df7 | 156 | |
bf241682 GR |
157 | /* |
158 | * Set up registers for signal handler. | |
159 | * Registers that we don't modify keep the value they had from | |
160 | * user-space at the time we took the signal. | |
161 | * We always pass siginfo and mcontext, regardless of SA_SIGINFO, | |
162 | * since some things rely on this (e.g. glibc's debug/segfault.c). | |
163 | */ | |
164 | regs->pc = (unsigned long)ksig->ka.sa.sa_handler; | |
165 | regs->usp = (unsigned long)frame; | |
166 | regs->a0 = ksig->sig; /* a0: signal number */ | |
167 | regs->a1 = (unsigned long)(&(frame->info)); /* a1: siginfo pointer */ | |
168 | regs->a2 = (unsigned long)(&(frame->uc)); /* a2: ucontext pointer */ | |
e9564df7 | 169 | |
bf241682 | 170 | return 0; |
e9564df7 GR |
171 | } |
172 | ||
bf241682 | 173 | static void handle_signal(struct ksignal *ksig, struct pt_regs *regs) |
e9564df7 | 174 | { |
e9564df7 | 175 | sigset_t *oldset = sigmask_to_save(); |
bf241682 | 176 | int ret; |
e9564df7 | 177 | |
9866d141 GR |
178 | rseq_signal_deliver(ksig, regs); |
179 | ||
bf241682 GR |
180 | /* Are we from a system call? */ |
181 | if (in_syscall(regs)) { | |
182 | /* Avoid additional syscall restarting via ret_from_exception */ | |
183 | forget_syscall(regs); | |
184 | ||
185 | /* If so, check system call restarting.. */ | |
186 | switch (regs->a0) { | |
187 | case -ERESTART_RESTARTBLOCK: | |
188 | case -ERESTARTNOHAND: | |
189 | regs->a0 = -EINTR; | |
190 | break; | |
e9564df7 | 191 | |
bf241682 GR |
192 | case -ERESTARTSYS: |
193 | if (!(ksig->ka.sa.sa_flags & SA_RESTART)) { | |
194 | regs->a0 = -EINTR; | |
195 | break; | |
196 | } | |
df561f66 | 197 | fallthrough; |
bf241682 GR |
198 | case -ERESTARTNOINTR: |
199 | regs->a0 = regs->orig_a0; | |
200 | regs->pc -= TRAP0_SIZE; | |
201 | break; | |
202 | } | |
e9564df7 GR |
203 | } |
204 | ||
bf241682 GR |
205 | /* Set up the stack frame */ |
206 | ret = setup_rt_frame(ksig, oldset, regs); | |
e9564df7 | 207 | |
bf241682 | 208 | signal_setup_done(ret, ksig, 0); |
e9564df7 GR |
209 | } |
210 | ||
f4625ee0 | 211 | static void do_signal(struct pt_regs *regs) |
e9564df7 | 212 | { |
e9564df7 GR |
213 | struct ksignal ksig; |
214 | ||
bf241682 GR |
215 | if (get_signal(&ksig)) { |
216 | /* Actually deliver the signal */ | |
217 | handle_signal(&ksig, regs); | |
e9564df7 | 218 | return; |
bf241682 | 219 | } |
e9564df7 | 220 | |
bf241682 | 221 | /* Did we come from a system call? */ |
f4625ee0 | 222 | if (in_syscall(regs)) { |
bf241682 | 223 | /* Avoid additional syscall restarting via ret_from_exception */ |
f4625ee0 GR |
224 | forget_syscall(regs); |
225 | ||
bf241682 GR |
226 | /* Restart the system call - no handlers present */ |
227 | switch (regs->a0) { | |
e9564df7 GR |
228 | case -ERESTARTNOHAND: |
229 | case -ERESTARTSYS: | |
230 | case -ERESTARTNOINTR: | |
231 | regs->a0 = regs->orig_a0; | |
bf241682 | 232 | regs->pc -= TRAP0_SIZE; |
e9564df7 GR |
233 | break; |
234 | case -ERESTART_RESTARTBLOCK: | |
bf241682 GR |
235 | regs->a0 = regs->orig_a0; |
236 | regs_syscallid(regs) = __NR_restart_syscall; | |
237 | regs->pc -= TRAP0_SIZE; | |
e9564df7 GR |
238 | break; |
239 | } | |
240 | } | |
241 | ||
e9564df7 | 242 | /* |
bf241682 GR |
243 | * If there is no signal to deliver, we just put the saved |
244 | * sigmask back. | |
e9564df7 | 245 | */ |
bf241682 | 246 | restore_saved_sigmask(); |
e9564df7 GR |
247 | } |
248 | ||
bf241682 GR |
249 | /* |
250 | * notification of userspace execution resumption | |
251 | * - triggered by the _TIF_WORK_MASK flags | |
252 | */ | |
253 | asmlinkage void do_notify_resume(struct pt_regs *regs, | |
254 | unsigned long thread_info_flags) | |
e9564df7 | 255 | { |
8f6bb793 GR |
256 | if (thread_info_flags & _TIF_UPROBE) |
257 | uprobe_notify_resume(regs); | |
258 | ||
bf241682 GR |
259 | /* Handle pending signal delivery */ |
260 | if (thread_info_flags & _TIF_SIGPENDING) | |
f4625ee0 | 261 | do_signal(regs); |
e9564df7 | 262 | |
bf241682 | 263 | if (thread_info_flags & _TIF_NOTIFY_RESUME) { |
e9564df7 GR |
264 | clear_thread_flag(TIF_NOTIFY_RESUME); |
265 | tracehook_notify_resume(regs); | |
9866d141 | 266 | rseq_handle_notify_resume(NULL, regs); |
e9564df7 GR |
267 | } |
268 | } |