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