Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * irixsig.c: WHEEE, IRIX signals! YOW, am I compatible or what?!?! | |
3 | * | |
4 | * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) | |
5 | * Copyright (C) 1997 - 2000 Ralf Baechle (ralf@gnu.org) | |
6 | * Copyright (C) 2000 Silicon Graphics, Inc. | |
7 | */ | |
8 | #include <linux/kernel.h> | |
9 | #include <linux/sched.h> | |
10 | #include <linux/mm.h> | |
11 | #include <linux/errno.h> | |
12 | #include <linux/smp.h> | |
13 | #include <linux/smp_lock.h> | |
14 | #include <linux/time.h> | |
15 | #include <linux/ptrace.h> | |
16 | ||
17 | #include <asm/ptrace.h> | |
18 | #include <asm/uaccess.h> | |
19 | ||
20 | #undef DEBUG_SIG | |
21 | ||
22 | #define _S(nr) (1<<((nr)-1)) | |
23 | ||
24 | #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) | |
25 | ||
26 | typedef struct { | |
27 | unsigned long sig[4]; | |
28 | } irix_sigset_t; | |
29 | ||
30 | struct sigctx_irix5 { | |
31 | u32 rmask, cp0_status; | |
32 | u64 pc; | |
33 | u64 regs[32]; | |
34 | u64 fpregs[32]; | |
35 | u32 usedfp, fpcsr, fpeir, sstk_flags; | |
36 | u64 hi, lo; | |
37 | u64 cp0_cause, cp0_badvaddr, _unused0; | |
38 | irix_sigset_t sigset; | |
39 | u64 weird_fpu_thing; | |
40 | u64 _unused1[31]; | |
41 | }; | |
42 | ||
43 | #ifdef DEBUG_SIG | |
44 | /* Debugging */ | |
45 | static inline void dump_irix5_sigctx(struct sigctx_irix5 *c) | |
46 | { | |
47 | int i; | |
48 | ||
49 | printk("misc: rmask[%08lx] status[%08lx] pc[%08lx]\n", | |
50 | (unsigned long) c->rmask, | |
51 | (unsigned long) c->cp0_status, | |
52 | (unsigned long) c->pc); | |
53 | printk("regs: "); | |
54 | for(i = 0; i < 16; i++) | |
55 | printk("[%d]<%08lx> ", i, (unsigned long) c->regs[i]); | |
56 | printk("\nregs: "); | |
57 | for(i = 16; i < 32; i++) | |
58 | printk("[%d]<%08lx> ", i, (unsigned long) c->regs[i]); | |
59 | printk("\nfpregs: "); | |
60 | for(i = 0; i < 16; i++) | |
61 | printk("[%d]<%08lx> ", i, (unsigned long) c->fpregs[i]); | |
62 | printk("\nfpregs: "); | |
63 | for(i = 16; i < 32; i++) | |
64 | printk("[%d]<%08lx> ", i, (unsigned long) c->fpregs[i]); | |
65 | printk("misc: usedfp[%d] fpcsr[%08lx] fpeir[%08lx] stk_flgs[%08lx]\n", | |
66 | (int) c->usedfp, (unsigned long) c->fpcsr, | |
67 | (unsigned long) c->fpeir, (unsigned long) c->sstk_flags); | |
68 | printk("misc: hi[%08lx] lo[%08lx] cause[%08lx] badvaddr[%08lx]\n", | |
69 | (unsigned long) c->hi, (unsigned long) c->lo, | |
70 | (unsigned long) c->cp0_cause, (unsigned long) c->cp0_badvaddr); | |
71 | printk("misc: sigset<0>[%08lx] sigset<1>[%08lx] sigset<2>[%08lx] " | |
72 | "sigset<3>[%08lx]\n", (unsigned long) c->sigset.sig[0], | |
73 | (unsigned long) c->sigset.sig[1], | |
74 | (unsigned long) c->sigset.sig[2], | |
75 | (unsigned long) c->sigset.sig[3]); | |
76 | } | |
77 | #endif | |
78 | ||
fe00f943 RB |
79 | static int setup_irix_frame(struct k_sigaction *ka, struct pt_regs *regs, |
80 | int signr, sigset_t *oldmask) | |
1da177e4 | 81 | { |
fe00f943 | 82 | struct sigctx_irix5 __user *ctx; |
1da177e4 | 83 | unsigned long sp; |
fe00f943 | 84 | int error, i; |
1da177e4 LT |
85 | |
86 | sp = regs->regs[29]; | |
87 | sp -= sizeof(struct sigctx_irix5); | |
88 | sp &= ~(0xf); | |
fe00f943 | 89 | ctx = (struct sigctx_irix5 __user *) sp; |
1da177e4 LT |
90 | if (!access_ok(VERIFY_WRITE, ctx, sizeof(*ctx))) |
91 | goto segv_and_exit; | |
92 | ||
fe00f943 RB |
93 | error = __put_user(0, &ctx->weird_fpu_thing); |
94 | error |= __put_user(~(0x00000001), &ctx->rmask); | |
95 | error |= __put_user(0, &ctx->regs[0]); | |
1da177e4 | 96 | for(i = 1; i < 32; i++) |
fe00f943 RB |
97 | error |= __put_user((u64) regs->regs[i], &ctx->regs[i]); |
98 | ||
99 | error |= __put_user((u64) regs->hi, &ctx->hi); | |
100 | error |= __put_user((u64) regs->lo, &ctx->lo); | |
101 | error |= __put_user((u64) regs->cp0_epc, &ctx->pc); | |
102 | error |= __put_user(!!used_math(), &ctx->usedfp); | |
103 | error |= __put_user((u64) regs->cp0_cause, &ctx->cp0_cause); | |
104 | error |= __put_user((u64) regs->cp0_badvaddr, &ctx->cp0_badvaddr); | |
1da177e4 | 105 | |
fe00f943 | 106 | error |= __put_user(0, &ctx->sstk_flags); /* XXX sigstack unimp... todo... */ |
1da177e4 | 107 | |
fe00f943 | 108 | error |= __copy_to_user(&ctx->sigset, oldmask, sizeof(irix_sigset_t)) ? -EFAULT : 0; |
1da177e4 | 109 | |
fe00f943 RB |
110 | if (error) |
111 | goto segv_and_exit; | |
1da177e4 LT |
112 | |
113 | #ifdef DEBUG_SIG | |
114 | dump_irix5_sigctx(ctx); | |
115 | #endif | |
116 | ||
117 | regs->regs[4] = (unsigned long) signr; | |
118 | regs->regs[5] = 0; /* XXX sigcode XXX */ | |
119 | regs->regs[6] = regs->regs[29] = sp; | |
120 | regs->regs[7] = (unsigned long) ka->sa.sa_handler; | |
121 | regs->regs[25] = regs->cp0_epc = (unsigned long) ka->sa_restorer; | |
122 | ||
fe00f943 | 123 | return 1; |
1da177e4 LT |
124 | |
125 | segv_and_exit: | |
126 | force_sigsegv(signr, current); | |
fe00f943 | 127 | return 0; |
1da177e4 LT |
128 | } |
129 | ||
fe00f943 | 130 | static int inline |
1da177e4 LT |
131 | setup_irix_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, |
132 | int signr, sigset_t *oldmask, siginfo_t *info) | |
133 | { | |
134 | printk("Aiee: setup_tr_frame wants to be written"); | |
135 | do_exit(SIGSEGV); | |
136 | } | |
137 | ||
fe00f943 | 138 | static inline int handle_signal(unsigned long sig, siginfo_t *info, |
1da177e4 LT |
139 | struct k_sigaction *ka, sigset_t *oldset, struct pt_regs * regs) |
140 | { | |
fe00f943 RB |
141 | int ret; |
142 | ||
1da177e4 LT |
143 | switch(regs->regs[0]) { |
144 | case ERESTARTNOHAND: | |
145 | regs->regs[2] = EINTR; | |
146 | break; | |
147 | case ERESTARTSYS: | |
148 | if(!(ka->sa.sa_flags & SA_RESTART)) { | |
149 | regs->regs[2] = EINTR; | |
150 | break; | |
151 | } | |
152 | /* fallthrough */ | |
153 | case ERESTARTNOINTR: /* Userland will reload $v0. */ | |
154 | regs->cp0_epc -= 8; | |
155 | } | |
156 | ||
157 | regs->regs[0] = 0; /* Don't deal with this again. */ | |
158 | ||
159 | if (ka->sa.sa_flags & SA_SIGINFO) | |
fe00f943 | 160 | ret = setup_irix_rt_frame(ka, regs, sig, oldset, info); |
1da177e4 | 161 | else |
fe00f943 | 162 | ret = setup_irix_frame(ka, regs, sig, oldset); |
1da177e4 | 163 | |
69be8f18 SR |
164 | spin_lock_irq(¤t->sighand->siglock); |
165 | sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); | |
166 | if (!(ka->sa.sa_flags & SA_NODEFER)) | |
1da177e4 | 167 | sigaddset(¤t->blocked,sig); |
69be8f18 SR |
168 | recalc_sigpending(); |
169 | spin_unlock_irq(¤t->sighand->siglock); | |
fe00f943 RB |
170 | |
171 | return ret; | |
1da177e4 LT |
172 | } |
173 | ||
174 | asmlinkage int do_irix_signal(sigset_t *oldset, struct pt_regs *regs) | |
175 | { | |
176 | struct k_sigaction ka; | |
177 | siginfo_t info; | |
178 | int signr; | |
179 | ||
180 | /* | |
181 | * We want the common case to go fast, which is why we may in certain | |
182 | * cases get here from kernel mode. Just return without doing anything | |
183 | * if so. | |
184 | */ | |
185 | if (!user_mode(regs)) | |
186 | return 1; | |
187 | ||
0e6c1f5f | 188 | if (try_to_freeze()) |
1da177e4 LT |
189 | goto no_signal; |
190 | ||
191 | if (!oldset) | |
192 | oldset = ¤t->blocked; | |
193 | ||
194 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); | |
fe00f943 RB |
195 | if (signr > 0) |
196 | return handle_signal(signr, &info, &ka, oldset, regs); | |
1da177e4 LT |
197 | |
198 | no_signal: | |
199 | /* | |
200 | * Who's code doesn't conform to the restartable syscall convention | |
201 | * dies here!!! The li instruction, a single machine instruction, | |
202 | * must directly be followed by the syscall instruction. | |
203 | */ | |
204 | if (regs->regs[0]) { | |
205 | if (regs->regs[2] == ERESTARTNOHAND || | |
206 | regs->regs[2] == ERESTARTSYS || | |
207 | regs->regs[2] == ERESTARTNOINTR) { | |
208 | regs->cp0_epc -= 8; | |
209 | } | |
210 | } | |
211 | return 0; | |
212 | } | |
213 | ||
214 | asmlinkage void | |
215 | irix_sigreturn(struct pt_regs *regs) | |
216 | { | |
fe00f943 | 217 | struct sigctx_irix5 __user *context, *magic; |
1da177e4 LT |
218 | unsigned long umask, mask; |
219 | u64 *fregs; | |
fe00f943 RB |
220 | u32 usedfp; |
221 | int error, sig, i, base = 0; | |
1da177e4 LT |
222 | sigset_t blocked; |
223 | ||
224 | /* Always make any pending restarted system calls return -EINTR */ | |
225 | current_thread_info()->restart_block.fn = do_no_restart_syscall; | |
226 | ||
227 | if (regs->regs[2] == 1000) | |
228 | base = 1; | |
229 | ||
fe00f943 RB |
230 | context = (struct sigctx_irix5 __user *) regs->regs[base + 4]; |
231 | magic = (struct sigctx_irix5 __user *) regs->regs[base + 5]; | |
1da177e4 LT |
232 | sig = (int) regs->regs[base + 6]; |
233 | #ifdef DEBUG_SIG | |
234 | printk("[%s:%d] IRIX sigreturn(scp[%p],ucp[%p],sig[%d])\n", | |
235 | current->comm, current->pid, context, magic, sig); | |
236 | #endif | |
237 | if (!context) | |
238 | context = magic; | |
239 | if (!access_ok(VERIFY_READ, context, sizeof(struct sigctx_irix5))) | |
240 | goto badframe; | |
241 | ||
242 | #ifdef DEBUG_SIG | |
243 | dump_irix5_sigctx(context); | |
244 | #endif | |
245 | ||
fe00f943 RB |
246 | error = __get_user(regs->cp0_epc, &context->pc); |
247 | error |= __get_user(umask, &context->rmask); | |
248 | ||
249 | mask = 2; | |
1da177e4 | 250 | for (i = 1; i < 32; i++, mask <<= 1) { |
fe00f943 RB |
251 | if (umask & mask) |
252 | error |= __get_user(regs->regs[i], &context->regs[i]); | |
1da177e4 | 253 | } |
fe00f943 RB |
254 | error |= __get_user(regs->hi, &context->hi); |
255 | error |= __get_user(regs->lo, &context->lo); | |
1da177e4 | 256 | |
fe00f943 RB |
257 | error |= __get_user(usedfp, &context->usedfp); |
258 | if ((umask & 1) && usedfp) { | |
1da177e4 | 259 | fregs = (u64 *) ¤t->thread.fpu; |
fe00f943 | 260 | |
1da177e4 | 261 | for(i = 0; i < 32; i++) |
fe00f943 | 262 | error |= __get_user(fregs[i], &context->fpregs[i]); |
eae89076 | 263 | error |= __get_user(current->thread.fpu.fcr31, &context->fpcsr); |
1da177e4 LT |
264 | } |
265 | ||
266 | /* XXX do sigstack crapola here... XXX */ | |
267 | ||
fe00f943 RB |
268 | error |= __copy_from_user(&blocked, &context->sigset, sizeof(blocked)) ? -EFAULT : 0; |
269 | ||
270 | if (error) | |
1da177e4 LT |
271 | goto badframe; |
272 | ||
273 | sigdelsetmask(&blocked, ~_BLOCKABLE); | |
274 | spin_lock_irq(¤t->sighand->siglock); | |
275 | current->blocked = blocked; | |
276 | recalc_sigpending(); | |
277 | spin_unlock_irq(¤t->sighand->siglock); | |
278 | ||
279 | /* | |
280 | * Don't let your children do this ... | |
281 | */ | |
1da177e4 LT |
282 | __asm__ __volatile__( |
283 | "move\t$29,%0\n\t" | |
284 | "j\tsyscall_exit" | |
285 | :/* no outputs */ | |
286 | :"r" (®s)); | |
287 | /* Unreached */ | |
288 | ||
289 | badframe: | |
290 | force_sig(SIGSEGV, current); | |
291 | } | |
292 | ||
293 | struct sigact_irix5 { | |
294 | int flags; | |
295 | void (*handler)(int); | |
296 | u32 sigset[4]; | |
297 | int _unused0[2]; | |
298 | }; | |
299 | ||
300 | #ifdef DEBUG_SIG | |
301 | static inline void dump_sigact_irix5(struct sigact_irix5 *p) | |
302 | { | |
303 | printk("<f[%d] hndlr[%08lx] msk[%08lx]>", p->flags, | |
304 | (unsigned long) p->handler, | |
305 | (unsigned long) p->sigset[0]); | |
306 | } | |
307 | #endif | |
308 | ||
309 | asmlinkage int | |
fe00f943 RB |
310 | irix_sigaction(int sig, const struct sigaction __user *act, |
311 | struct sigaction __user *oact, void __user *trampoline) | |
1da177e4 LT |
312 | { |
313 | struct k_sigaction new_ka, old_ka; | |
314 | int ret; | |
315 | ||
316 | #ifdef DEBUG_SIG | |
317 | printk(" (%d,%s,%s,%08lx) ", sig, (!new ? "0" : "NEW"), | |
318 | (!old ? "0" : "OLD"), trampoline); | |
319 | if(new) { | |
320 | dump_sigact_irix5(new); printk(" "); | |
321 | } | |
322 | #endif | |
323 | if (act) { | |
324 | sigset_t mask; | |
fe00f943 RB |
325 | int err; |
326 | ||
327 | if (!access_ok(VERIFY_READ, act, sizeof(*act))) | |
1da177e4 | 328 | return -EFAULT; |
fe00f943 RB |
329 | err = __get_user(new_ka.sa.sa_handler, &act->sa_handler); |
330 | err |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); | |
1da177e4 | 331 | |
fe00f943 RB |
332 | err |= __copy_from_user(&mask, &act->sa_mask, sizeof(sigset_t)) ? -EFAULT : 0; |
333 | if (err) | |
334 | return err; | |
1da177e4 LT |
335 | |
336 | /* | |
337 | * Hmmm... methinks IRIX libc always passes a valid trampoline | |
338 | * value for all invocations of sigaction. Will have to | |
339 | * investigate. POSIX POSIX, die die die... | |
340 | */ | |
341 | new_ka.sa_restorer = trampoline; | |
342 | } | |
343 | ||
344 | /* XXX Implement SIG_SETMASK32 for IRIX compatibility */ | |
345 | ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); | |
346 | ||
347 | if (!ret && oact) { | |
fe00f943 RB |
348 | int err; |
349 | ||
350 | if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact))) | |
351 | return -EFAULT; | |
352 | ||
353 | err = __put_user(old_ka.sa.sa_handler, &oact->sa_handler); | |
354 | err |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); | |
355 | err |= __copy_to_user(&oact->sa_mask, &old_ka.sa.sa_mask, | |
356 | sizeof(sigset_t)) ? -EFAULT : 0; | |
357 | if (err) | |
1da177e4 | 358 | return -EFAULT; |
1da177e4 LT |
359 | } |
360 | ||
361 | return ret; | |
362 | } | |
363 | ||
fe00f943 | 364 | asmlinkage int irix_sigpending(irix_sigset_t __user *set) |
1da177e4 LT |
365 | { |
366 | return do_sigpending(set, sizeof(*set)); | |
367 | } | |
368 | ||
fe00f943 RB |
369 | asmlinkage int irix_sigprocmask(int how, irix_sigset_t __user *new, |
370 | irix_sigset_t __user *old) | |
1da177e4 LT |
371 | { |
372 | sigset_t oldbits, newbits; | |
373 | ||
374 | if (new) { | |
375 | if (!access_ok(VERIFY_READ, new, sizeof(*new))) | |
376 | return -EFAULT; | |
fe00f943 RB |
377 | if (__copy_from_user(&newbits, new, sizeof(unsigned long)*4)) |
378 | return -EFAULT; | |
1da177e4 LT |
379 | sigdelsetmask(&newbits, ~_BLOCKABLE); |
380 | ||
381 | spin_lock_irq(¤t->sighand->siglock); | |
382 | oldbits = current->blocked; | |
383 | ||
384 | switch(how) { | |
385 | case 1: | |
386 | sigorsets(&newbits, &oldbits, &newbits); | |
387 | break; | |
388 | ||
389 | case 2: | |
390 | sigandsets(&newbits, &oldbits, &newbits); | |
391 | break; | |
392 | ||
393 | case 3: | |
394 | break; | |
395 | ||
396 | case 256: | |
397 | siginitset(&newbits, newbits.sig[0]); | |
398 | break; | |
399 | ||
400 | default: | |
401 | return -EINVAL; | |
402 | } | |
403 | recalc_sigpending(); | |
404 | spin_unlock_irq(¤t->sighand->siglock); | |
405 | } | |
fe00f943 RB |
406 | if (old) |
407 | return copy_to_user(old, ¤t->blocked, | |
408 | sizeof(unsigned long)*4) ? -EFAULT : 0; | |
1da177e4 LT |
409 | |
410 | return 0; | |
411 | } | |
412 | ||
413 | asmlinkage int irix_sigsuspend(struct pt_regs *regs) | |
414 | { | |
fe00f943 RB |
415 | sigset_t saveset, newset; |
416 | sigset_t __user *uset; | |
1da177e4 | 417 | |
fe00f943 | 418 | uset = (sigset_t __user *) regs->regs[4]; |
1da177e4 LT |
419 | if (copy_from_user(&newset, uset, sizeof(sigset_t))) |
420 | return -EFAULT; | |
421 | sigdelsetmask(&newset, ~_BLOCKABLE); | |
422 | ||
423 | spin_lock_irq(¤t->sighand->siglock); | |
424 | saveset = current->blocked; | |
425 | current->blocked = newset; | |
426 | recalc_sigpending(); | |
427 | spin_unlock_irq(¤t->sighand->siglock); | |
428 | ||
429 | regs->regs[2] = -EINTR; | |
430 | while (1) { | |
431 | current->state = TASK_INTERRUPTIBLE; | |
432 | schedule(); | |
433 | if (do_irix_signal(&saveset, regs)) | |
434 | return -EINTR; | |
435 | } | |
436 | } | |
437 | ||
438 | /* hate hate hate... */ | |
439 | struct irix5_siginfo { | |
440 | int sig, code, error; | |
441 | union { | |
442 | char unused[128 - (3 * 4)]; /* Safety net. */ | |
443 | struct { | |
444 | int pid; | |
445 | union { | |
446 | int uid; | |
447 | struct { | |
448 | int utime, status, stime; | |
449 | } child; | |
450 | } procdata; | |
451 | } procinfo; | |
452 | ||
453 | unsigned long fault_addr; | |
454 | ||
455 | struct { | |
456 | int fd; | |
457 | long band; | |
458 | } fileinfo; | |
459 | ||
460 | unsigned long sigval; | |
461 | } stuff; | |
462 | }; | |
463 | ||
fe00f943 RB |
464 | asmlinkage int irix_sigpoll_sys(unsigned long __user *set, |
465 | struct irix5_siginfo __user *info, struct timespec __user *tp) | |
1da177e4 LT |
466 | { |
467 | long expire = MAX_SCHEDULE_TIMEOUT; | |
468 | sigset_t kset; | |
469 | int i, sig, error, timeo = 0; | |
fe00f943 | 470 | struct timespec ktp; |
1da177e4 LT |
471 | |
472 | #ifdef DEBUG_SIG | |
473 | printk("[%s:%d] irix_sigpoll_sys(%p,%p,%p)\n", | |
474 | current->comm, current->pid, set, info, tp); | |
475 | #endif | |
476 | ||
477 | /* Must always specify the signal set. */ | |
478 | if (!set) | |
479 | return -EINVAL; | |
480 | ||
fe00f943 RB |
481 | if (copy_from_user(&kset, set, sizeof(set))) |
482 | return -EFAULT; | |
1da177e4 LT |
483 | |
484 | if (info && clear_user(info, sizeof(*info))) { | |
485 | error = -EFAULT; | |
486 | goto out; | |
487 | } | |
488 | ||
489 | if (tp) { | |
fe00f943 | 490 | if (copy_from_user(&ktp, tp, sizeof(*tp))) |
1da177e4 | 491 | return -EFAULT; |
fe00f943 RB |
492 | |
493 | if (!ktp.tv_sec && !ktp.tv_nsec) | |
494 | return -EINVAL; | |
495 | ||
496 | expire = timespec_to_jiffies(&ktp) + | |
497 | (ktp.tv_sec || ktp.tv_nsec); | |
1da177e4 LT |
498 | } |
499 | ||
500 | while(1) { | |
501 | long tmp = 0; | |
502 | ||
0d959c26 | 503 | expire = schedule_timeout_interruptible(expire); |
1da177e4 LT |
504 | |
505 | for (i=0; i<=4; i++) | |
506 | tmp |= (current->pending.signal.sig[i] & kset.sig[i]); | |
507 | ||
508 | if (tmp) | |
509 | break; | |
510 | if (!expire) { | |
511 | timeo = 1; | |
512 | break; | |
513 | } | |
514 | if (signal_pending(current)) | |
515 | return -EINTR; | |
516 | } | |
517 | if (timeo) | |
518 | return -EAGAIN; | |
519 | ||
fe00f943 | 520 | for (sig = 1; i <= 65 /* IRIX_NSIG */; sig++) { |
1da177e4 LT |
521 | if (sigismember (&kset, sig)) |
522 | continue; | |
523 | if (sigismember (¤t->pending.signal, sig)) { | |
524 | /* XXX need more than this... */ | |
525 | if (info) | |
fe00f943 RB |
526 | return copy_to_user(&info->sig, &sig, sizeof(sig)); |
527 | return 0; | |
1da177e4 LT |
528 | } |
529 | } | |
530 | ||
531 | /* Should not get here, but do something sane if we do. */ | |
532 | error = -EINTR; | |
533 | ||
534 | out: | |
535 | return error; | |
536 | } | |
537 | ||
538 | /* This is here because of irix5_siginfo definition. */ | |
539 | #define IRIX_P_PID 0 | |
540 | #define IRIX_P_PGID 2 | |
541 | #define IRIX_P_ALL 7 | |
542 | ||
543 | extern int getrusage(struct task_struct *, int, struct rusage __user *); | |
544 | ||
545 | #define W_EXITED 1 | |
546 | #define W_TRAPPED 2 | |
547 | #define W_STOPPED 4 | |
548 | #define W_CONT 8 | |
549 | #define W_NOHANG 64 | |
550 | ||
551 | #define W_MASK (W_EXITED | W_TRAPPED | W_STOPPED | W_CONT | W_NOHANG) | |
552 | ||
fe00f943 RB |
553 | asmlinkage int irix_waitsys(int type, int pid, |
554 | struct irix5_siginfo __user *info, int options, | |
555 | struct rusage __user *ru) | |
1da177e4 LT |
556 | { |
557 | int flag, retval; | |
558 | DECLARE_WAITQUEUE(wait, current); | |
559 | struct task_struct *tsk; | |
560 | struct task_struct *p; | |
561 | struct list_head *_p; | |
562 | ||
fe00f943 RB |
563 | if (!info) |
564 | return -EINVAL; | |
565 | ||
566 | if (!access_ok(VERIFY_WRITE, info, sizeof(*info))) | |
567 | return -EFAULT; | |
568 | ||
569 | if (ru) | |
570 | if (!access_ok(VERIFY_WRITE, ru, sizeof(*ru))) | |
571 | return -EFAULT; | |
572 | ||
573 | if (options & ~W_MASK) | |
574 | return -EINVAL; | |
575 | ||
576 | if (type != IRIX_P_PID && type != IRIX_P_PGID && type != IRIX_P_ALL) | |
577 | return -EINVAL; | |
578 | ||
1da177e4 LT |
579 | add_wait_queue(¤t->signal->wait_chldexit, &wait); |
580 | repeat: | |
581 | flag = 0; | |
582 | current->state = TASK_INTERRUPTIBLE; | |
583 | read_lock(&tasklist_lock); | |
584 | tsk = current; | |
585 | list_for_each(_p,&tsk->children) { | |
586 | p = list_entry(_p,struct task_struct,sibling); | |
587 | if ((type == IRIX_P_PID) && p->pid != pid) | |
588 | continue; | |
589 | if ((type == IRIX_P_PGID) && process_group(p) != pid) | |
590 | continue; | |
591 | if ((p->exit_signal != SIGCHLD)) | |
592 | continue; | |
593 | flag = 1; | |
594 | switch (p->state) { | |
595 | case TASK_STOPPED: | |
596 | if (!p->exit_code) | |
597 | continue; | |
598 | if (!(options & (W_TRAPPED|W_STOPPED)) && | |
599 | !(p->ptrace & PT_PTRACED)) | |
600 | continue; | |
601 | read_unlock(&tasklist_lock); | |
602 | ||
603 | /* move to end of parent's list to avoid starvation */ | |
604 | write_lock_irq(&tasklist_lock); | |
605 | remove_parent(p); | |
8fafabd8 | 606 | add_parent(p); |
1da177e4 LT |
607 | write_unlock_irq(&tasklist_lock); |
608 | retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0; | |
fe00f943 RB |
609 | if (retval) |
610 | goto end_waitsys; | |
611 | ||
612 | retval = __put_user(SIGCHLD, &info->sig); | |
613 | retval |= __put_user(0, &info->code); | |
614 | retval |= __put_user(p->pid, &info->stuff.procinfo.pid); | |
615 | retval |= __put_user((p->exit_code >> 8) & 0xff, | |
616 | &info->stuff.procinfo.procdata.child.status); | |
617 | retval |= __put_user(p->utime, &info->stuff.procinfo.procdata.child.utime); | |
618 | retval |= __put_user(p->stime, &info->stuff.procinfo.procdata.child.stime); | |
619 | if (retval) | |
620 | goto end_waitsys; | |
621 | ||
622 | p->exit_code = 0; | |
1da177e4 LT |
623 | goto end_waitsys; |
624 | ||
625 | case EXIT_ZOMBIE: | |
626 | current->signal->cutime += p->utime + p->signal->cutime; | |
627 | current->signal->cstime += p->stime + p->signal->cstime; | |
628 | if (ru != NULL) | |
629 | getrusage(p, RUSAGE_BOTH, ru); | |
fe00f943 RB |
630 | retval = __put_user(SIGCHLD, &info->sig); |
631 | retval |= __put_user(1, &info->code); /* CLD_EXITED */ | |
632 | retval |= __put_user(p->pid, &info->stuff.procinfo.pid); | |
633 | retval |= __put_user((p->exit_code >> 8) & 0xff, | |
1da177e4 | 634 | &info->stuff.procinfo.procdata.child.status); |
fe00f943 | 635 | retval |= __put_user(p->utime, |
1da177e4 | 636 | &info->stuff.procinfo.procdata.child.utime); |
fe00f943 | 637 | retval |= __put_user(p->stime, |
1da177e4 | 638 | &info->stuff.procinfo.procdata.child.stime); |
fe00f943 RB |
639 | if (retval) |
640 | return retval; | |
641 | ||
1da177e4 LT |
642 | if (p->real_parent != p->parent) { |
643 | write_lock_irq(&tasklist_lock); | |
644 | remove_parent(p); | |
645 | p->parent = p->real_parent; | |
8fafabd8 | 646 | add_parent(p); |
1da177e4 LT |
647 | do_notify_parent(p, SIGCHLD); |
648 | write_unlock_irq(&tasklist_lock); | |
649 | } else | |
650 | release_task(p); | |
651 | goto end_waitsys; | |
652 | default: | |
653 | continue; | |
654 | } | |
655 | tsk = next_thread(tsk); | |
656 | } | |
657 | read_unlock(&tasklist_lock); | |
658 | if (flag) { | |
659 | retval = 0; | |
660 | if (options & W_NOHANG) | |
661 | goto end_waitsys; | |
662 | retval = -ERESTARTSYS; | |
663 | if (signal_pending(current)) | |
664 | goto end_waitsys; | |
665 | current->state = TASK_INTERRUPTIBLE; | |
666 | schedule(); | |
667 | goto repeat; | |
668 | } | |
669 | retval = -ECHILD; | |
670 | end_waitsys: | |
671 | current->state = TASK_RUNNING; | |
672 | remove_wait_queue(¤t->signal->wait_chldexit, &wait); | |
673 | ||
1da177e4 LT |
674 | return retval; |
675 | } | |
676 | ||
677 | struct irix5_context { | |
678 | u32 flags; | |
679 | u32 link; | |
680 | u32 sigmask[4]; | |
681 | struct { u32 sp, size, flags; } stack; | |
682 | int regs[36]; | |
683 | u32 fpregs[32]; | |
684 | u32 fpcsr; | |
685 | u32 _unused0; | |
686 | u32 _unused1[47]; | |
687 | u32 weird_graphics_thing; | |
688 | }; | |
689 | ||
690 | asmlinkage int irix_getcontext(struct pt_regs *regs) | |
691 | { | |
fe00f943 RB |
692 | int error, i, base = 0; |
693 | struct irix5_context __user *ctx; | |
1da177e4 LT |
694 | unsigned long flags; |
695 | ||
696 | if (regs->regs[2] == 1000) | |
697 | base = 1; | |
fe00f943 | 698 | ctx = (struct irix5_context __user *) regs->regs[base + 4]; |
1da177e4 LT |
699 | |
700 | #ifdef DEBUG_SIG | |
701 | printk("[%s:%d] irix_getcontext(%p)\n", | |
702 | current->comm, current->pid, ctx); | |
703 | #endif | |
704 | ||
fe00f943 | 705 | if (!access_ok(VERIFY_WRITE, ctx, sizeof(*ctx))); |
1da177e4 LT |
706 | return -EFAULT; |
707 | ||
fe00f943 | 708 | error = __put_user(current->thread.irix_oldctx, &ctx->link); |
1da177e4 | 709 | |
fe00f943 | 710 | error |= __copy_to_user(&ctx->sigmask, ¤t->blocked, sizeof(irix_sigset_t)) ? -EFAULT : 0; |
1da177e4 LT |
711 | |
712 | /* XXX Do sigstack stuff someday... */ | |
fe00f943 RB |
713 | error |= __put_user(0, &ctx->stack.sp); |
714 | error |= __put_user(0, &ctx->stack.size); | |
715 | error |= __put_user(0, &ctx->stack.flags); | |
1da177e4 | 716 | |
fe00f943 RB |
717 | error |= __put_user(0, &ctx->weird_graphics_thing); |
718 | error |= __put_user(0, &ctx->regs[0]); | |
1da177e4 | 719 | for (i = 1; i < 32; i++) |
fe00f943 RB |
720 | error |= __put_user(regs->regs[i], &ctx->regs[i]); |
721 | error |= __put_user(regs->lo, &ctx->regs[32]); | |
722 | error |= __put_user(regs->hi, &ctx->regs[33]); | |
723 | error |= __put_user(regs->cp0_cause, &ctx->regs[34]); | |
724 | error |= __put_user(regs->cp0_epc, &ctx->regs[35]); | |
1da177e4 LT |
725 | |
726 | flags = 0x0f; | |
727 | if (!used_math()) { | |
728 | flags &= ~(0x08); | |
729 | } else { | |
730 | /* XXX wheee... */ | |
731 | printk("Wheee, no code for saving IRIX FPU context yet.\n"); | |
732 | } | |
fe00f943 | 733 | error |= __put_user(flags, &ctx->flags); |
1da177e4 | 734 | |
fe00f943 | 735 | return error; |
1da177e4 LT |
736 | } |
737 | ||
fe00f943 | 738 | asmlinkage void irix_setcontext(struct pt_regs *regs) |
1da177e4 | 739 | { |
fe00f943 RB |
740 | struct irix5_context __user *ctx; |
741 | int err, base = 0; | |
742 | u32 flags; | |
1da177e4 | 743 | |
fe00f943 | 744 | if (regs->regs[2] == 1000) |
1da177e4 | 745 | base = 1; |
fe00f943 | 746 | ctx = (struct irix5_context __user *) regs->regs[base + 4]; |
1da177e4 LT |
747 | |
748 | #ifdef DEBUG_SIG | |
749 | printk("[%s:%d] irix_setcontext(%p)\n", | |
750 | current->comm, current->pid, ctx); | |
751 | #endif | |
752 | ||
fe00f943 RB |
753 | if (!access_ok(VERIFY_READ, ctx, sizeof(*ctx))) |
754 | goto segv_and_exit; | |
1da177e4 | 755 | |
fe00f943 RB |
756 | err = __get_user(flags, &ctx->flags); |
757 | if (flags & 0x02) { | |
1da177e4 LT |
758 | /* XXX sigstack garbage, todo... */ |
759 | printk("Wheee, cannot do sigstack stuff in setcontext\n"); | |
760 | } | |
761 | ||
fe00f943 | 762 | if (flags & 0x04) { |
1da177e4 LT |
763 | int i; |
764 | ||
765 | /* XXX extra control block stuff... todo... */ | |
fe00f943 RB |
766 | for (i = 1; i < 32; i++) |
767 | err |= __get_user(regs->regs[i], &ctx->regs[i]); | |
768 | err |= __get_user(regs->lo, &ctx->regs[32]); | |
769 | err |= __get_user(regs->hi, &ctx->regs[33]); | |
770 | err |= __get_user(regs->cp0_epc, &ctx->regs[35]); | |
1da177e4 LT |
771 | } |
772 | ||
fe00f943 | 773 | if (flags & 0x08) |
1da177e4 | 774 | /* XXX fpu context, blah... */ |
fe00f943 | 775 | printk(KERN_ERR "Wheee, cannot restore FPU context yet...\n"); |
1da177e4 | 776 | |
fe00f943 RB |
777 | err |= __get_user(current->thread.irix_oldctx, &ctx->link); |
778 | if (err) | |
779 | goto segv_and_exit; | |
780 | ||
781 | /* | |
782 | * Don't let your children do this ... | |
783 | */ | |
fe00f943 RB |
784 | __asm__ __volatile__( |
785 | "move\t$29,%0\n\t" | |
786 | "j\tsyscall_exit" | |
787 | :/* no outputs */ | |
788 | :"r" (®s)); | |
789 | /* Unreached */ | |
790 | ||
791 | segv_and_exit: | |
792 | force_sigsegv(SIGSEGV, current); | |
1da177e4 LT |
793 | } |
794 | ||
fe00f943 RB |
795 | struct irix_sigstack { |
796 | unsigned long sp; | |
797 | int status; | |
798 | }; | |
1da177e4 | 799 | |
fe00f943 RB |
800 | asmlinkage int irix_sigstack(struct irix_sigstack __user *new, |
801 | struct irix_sigstack __user *old) | |
1da177e4 | 802 | { |
1da177e4 LT |
803 | #ifdef DEBUG_SIG |
804 | printk("[%s:%d] irix_sigstack(%p,%p)\n", | |
805 | current->comm, current->pid, new, old); | |
806 | #endif | |
fe00f943 | 807 | if (new) { |
1da177e4 | 808 | if (!access_ok(VERIFY_READ, new, sizeof(*new))) |
fe00f943 | 809 | return -EFAULT; |
1da177e4 LT |
810 | } |
811 | ||
fe00f943 | 812 | if (old) { |
1da177e4 | 813 | if (!access_ok(VERIFY_WRITE, old, sizeof(*old))) |
fe00f943 | 814 | return -EFAULT; |
1da177e4 | 815 | } |
1da177e4 | 816 | |
fe00f943 | 817 | return 0; |
1da177e4 LT |
818 | } |
819 | ||
820 | struct irix_sigaltstack { unsigned long sp; int size; int status; }; | |
821 | ||
fe00f943 RB |
822 | asmlinkage int irix_sigaltstack(struct irix_sigaltstack __user *new, |
823 | struct irix_sigaltstack __user *old) | |
1da177e4 | 824 | { |
1da177e4 LT |
825 | #ifdef DEBUG_SIG |
826 | printk("[%s:%d] irix_sigaltstack(%p,%p)\n", | |
827 | current->comm, current->pid, new, old); | |
828 | #endif | |
fe00f943 | 829 | if (new) |
1da177e4 | 830 | if (!access_ok(VERIFY_READ, new, sizeof(*new))) |
fe00f943 | 831 | return -EFAULT; |
1da177e4 LT |
832 | |
833 | if (old) { | |
834 | if (!access_ok(VERIFY_WRITE, old, sizeof(*old))) | |
fe00f943 | 835 | return -EFAULT; |
1da177e4 | 836 | } |
1da177e4 | 837 | |
fe00f943 | 838 | return 0; |
1da177e4 LT |
839 | } |
840 | ||
841 | struct irix_procset { | |
842 | int cmd, ltype, lid, rtype, rid; | |
843 | }; | |
844 | ||
fe00f943 | 845 | asmlinkage int irix_sigsendset(struct irix_procset __user *pset, int sig) |
1da177e4 LT |
846 | { |
847 | if (!access_ok(VERIFY_READ, pset, sizeof(*pset))) | |
848 | return -EFAULT; | |
1da177e4 LT |
849 | #ifdef DEBUG_SIG |
850 | printk("[%s:%d] irix_sigsendset([%d,%d,%d,%d,%d],%d)\n", | |
851 | current->comm, current->pid, | |
852 | pset->cmd, pset->ltype, pset->lid, pset->rtype, pset->rid, | |
853 | sig); | |
854 | #endif | |
855 | return -EINVAL; | |
856 | } |