diff options
Diffstat (limited to 'arch/mips/kernel/signal.c')
-rw-r--r-- | arch/mips/kernel/signal.c | 136 |
1 files changed, 110 insertions, 26 deletions
diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index b2e9ab1bb101..07d67309451a 100644 --- a/arch/mips/kernel/signal.c +++ b/arch/mips/kernel/signal.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/ptrace.h> | 20 | #include <linux/ptrace.h> |
21 | #include <linux/unistd.h> | 21 | #include <linux/unistd.h> |
22 | #include <linux/compiler.h> | 22 | #include <linux/compiler.h> |
23 | #include <linux/uaccess.h> | ||
23 | 24 | ||
24 | #include <asm/abi.h> | 25 | #include <asm/abi.h> |
25 | #include <asm/asm.h> | 26 | #include <asm/asm.h> |
@@ -27,7 +28,6 @@ | |||
27 | #include <asm/cacheflush.h> | 28 | #include <asm/cacheflush.h> |
28 | #include <asm/fpu.h> | 29 | #include <asm/fpu.h> |
29 | #include <asm/sim.h> | 30 | #include <asm/sim.h> |
30 | #include <asm/uaccess.h> | ||
31 | #include <asm/ucontext.h> | 31 | #include <asm/ucontext.h> |
32 | #include <asm/cpu-features.h> | 32 | #include <asm/cpu-features.h> |
33 | #include <asm/war.h> | 33 | #include <asm/war.h> |
@@ -78,10 +78,51 @@ struct rt_sigframe { | |||
78 | /* | 78 | /* |
79 | * Helper routines | 79 | * Helper routines |
80 | */ | 80 | */ |
81 | static int protected_save_fp_context(struct sigcontext __user *sc) | ||
82 | { | ||
83 | int err; | ||
84 | while (1) { | ||
85 | lock_fpu_owner(); | ||
86 | own_fpu_inatomic(1); | ||
87 | err = save_fp_context(sc); /* this might fail */ | ||
88 | unlock_fpu_owner(); | ||
89 | if (likely(!err)) | ||
90 | break; | ||
91 | /* touch the sigcontext and try again */ | ||
92 | err = __put_user(0, &sc->sc_fpregs[0]) | | ||
93 | __put_user(0, &sc->sc_fpregs[31]) | | ||
94 | __put_user(0, &sc->sc_fpc_csr); | ||
95 | if (err) | ||
96 | break; /* really bad sigcontext */ | ||
97 | } | ||
98 | return err; | ||
99 | } | ||
100 | |||
101 | static int protected_restore_fp_context(struct sigcontext __user *sc) | ||
102 | { | ||
103 | int err, tmp; | ||
104 | while (1) { | ||
105 | lock_fpu_owner(); | ||
106 | own_fpu_inatomic(0); | ||
107 | err = restore_fp_context(sc); /* this might fail */ | ||
108 | unlock_fpu_owner(); | ||
109 | if (likely(!err)) | ||
110 | break; | ||
111 | /* touch the sigcontext and try again */ | ||
112 | err = __get_user(tmp, &sc->sc_fpregs[0]) | | ||
113 | __get_user(tmp, &sc->sc_fpregs[31]) | | ||
114 | __get_user(tmp, &sc->sc_fpc_csr); | ||
115 | if (err) | ||
116 | break; /* really bad sigcontext */ | ||
117 | } | ||
118 | return err; | ||
119 | } | ||
120 | |||
81 | int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) | 121 | int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) |
82 | { | 122 | { |
83 | int err = 0; | 123 | int err = 0; |
84 | int i; | 124 | int i; |
125 | unsigned int used_math; | ||
85 | 126 | ||
86 | err |= __put_user(regs->cp0_epc, &sc->sc_pc); | 127 | err |= __put_user(regs->cp0_epc, &sc->sc_pc); |
87 | 128 | ||
@@ -89,6 +130,9 @@ int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) | |||
89 | for (i = 1; i < 32; i++) | 130 | for (i = 1; i < 32; i++) |
90 | err |= __put_user(regs->regs[i], &sc->sc_regs[i]); | 131 | err |= __put_user(regs->regs[i], &sc->sc_regs[i]); |
91 | 132 | ||
133 | #ifdef CONFIG_CPU_HAS_SMARTMIPS | ||
134 | err |= __put_user(regs->acx, &sc->sc_acx); | ||
135 | #endif | ||
92 | err |= __put_user(regs->hi, &sc->sc_mdhi); | 136 | err |= __put_user(regs->hi, &sc->sc_mdhi); |
93 | err |= __put_user(regs->lo, &sc->sc_mdlo); | 137 | err |= __put_user(regs->lo, &sc->sc_mdlo); |
94 | if (cpu_has_dsp) { | 138 | if (cpu_has_dsp) { |
@@ -101,24 +145,48 @@ int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) | |||
101 | err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp); | 145 | err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp); |
102 | } | 146 | } |
103 | 147 | ||
104 | err |= __put_user(!!used_math(), &sc->sc_used_math); | 148 | used_math = !!used_math(); |
149 | err |= __put_user(used_math, &sc->sc_used_math); | ||
105 | 150 | ||
106 | if (used_math()) { | 151 | if (used_math) { |
107 | /* | 152 | /* |
108 | * Save FPU state to signal context. Signal handler | 153 | * Save FPU state to signal context. Signal handler |
109 | * will "inherit" current FPU state. | 154 | * will "inherit" current FPU state. |
110 | */ | 155 | */ |
111 | preempt_disable(); | 156 | err |= protected_save_fp_context(sc); |
157 | } | ||
158 | return err; | ||
159 | } | ||
112 | 160 | ||
113 | if (!is_fpu_owner()) { | 161 | int fpcsr_pending(unsigned int __user *fpcsr) |
114 | own_fpu(); | 162 | { |
115 | restore_fp(current); | 163 | int err, sig = 0; |
116 | } | 164 | unsigned int csr, enabled; |
117 | err |= save_fp_context(sc); | ||
118 | 165 | ||
119 | preempt_enable(); | 166 | err = __get_user(csr, fpcsr); |
167 | enabled = FPU_CSR_UNI_X | ((csr & FPU_CSR_ALL_E) << 5); | ||
168 | /* | ||
169 | * If the signal handler set some FPU exceptions, clear it and | ||
170 | * send SIGFPE. | ||
171 | */ | ||
172 | if (csr & enabled) { | ||
173 | csr &= ~enabled; | ||
174 | err |= __put_user(csr, fpcsr); | ||
175 | sig = SIGFPE; | ||
120 | } | 176 | } |
121 | return err; | 177 | return err ?: sig; |
178 | } | ||
179 | |||
180 | static int | ||
181 | check_and_restore_fp_context(struct sigcontext __user *sc) | ||
182 | { | ||
183 | int err, sig; | ||
184 | |||
185 | err = sig = fpcsr_pending(&sc->sc_fpc_csr); | ||
186 | if (err > 0) | ||
187 | err = 0; | ||
188 | err |= protected_restore_fp_context(sc); | ||
189 | return err ?: sig; | ||
122 | } | 190 | } |
123 | 191 | ||
124 | int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) | 192 | int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) |
@@ -132,6 +200,10 @@ int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) | |||
132 | current_thread_info()->restart_block.fn = do_no_restart_syscall; | 200 | current_thread_info()->restart_block.fn = do_no_restart_syscall; |
133 | 201 | ||
134 | err |= __get_user(regs->cp0_epc, &sc->sc_pc); | 202 | err |= __get_user(regs->cp0_epc, &sc->sc_pc); |
203 | |||
204 | #ifdef CONFIG_CPU_HAS_SMARTMIPS | ||
205 | err |= __get_user(regs->acx, &sc->sc_acx); | ||
206 | #endif | ||
135 | err |= __get_user(regs->hi, &sc->sc_mdhi); | 207 | err |= __get_user(regs->hi, &sc->sc_mdhi); |
136 | err |= __get_user(regs->lo, &sc->sc_mdlo); | 208 | err |= __get_user(regs->lo, &sc->sc_mdlo); |
137 | if (cpu_has_dsp) { | 209 | if (cpu_has_dsp) { |
@@ -150,19 +222,15 @@ int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) | |||
150 | err |= __get_user(used_math, &sc->sc_used_math); | 222 | err |= __get_user(used_math, &sc->sc_used_math); |
151 | conditional_used_math(used_math); | 223 | conditional_used_math(used_math); |
152 | 224 | ||
153 | preempt_disable(); | 225 | if (used_math) { |
154 | |||
155 | if (used_math()) { | ||
156 | /* restore fpu context if we have used it before */ | 226 | /* restore fpu context if we have used it before */ |
157 | own_fpu(); | 227 | if (!err) |
158 | err |= restore_fp_context(sc); | 228 | err = check_and_restore_fp_context(sc); |
159 | } else { | 229 | } else { |
160 | /* signal handler may have used FPU. Give it up. */ | 230 | /* signal handler may have used FPU. Give it up. */ |
161 | lose_fpu(); | 231 | lose_fpu(0); |
162 | } | 232 | } |
163 | 233 | ||
164 | preempt_enable(); | ||
165 | |||
166 | return err; | 234 | return err; |
167 | } | 235 | } |
168 | 236 | ||
@@ -325,6 +393,7 @@ asmlinkage void sys_sigreturn(nabi_no_regargs struct pt_regs regs) | |||
325 | { | 393 | { |
326 | struct sigframe __user *frame; | 394 | struct sigframe __user *frame; |
327 | sigset_t blocked; | 395 | sigset_t blocked; |
396 | int sig; | ||
328 | 397 | ||
329 | frame = (struct sigframe __user *) regs.regs[29]; | 398 | frame = (struct sigframe __user *) regs.regs[29]; |
330 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | 399 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) |
@@ -338,8 +407,11 @@ asmlinkage void sys_sigreturn(nabi_no_regargs struct pt_regs regs) | |||
338 | recalc_sigpending(); | 407 | recalc_sigpending(); |
339 | spin_unlock_irq(¤t->sighand->siglock); | 408 | spin_unlock_irq(¤t->sighand->siglock); |
340 | 409 | ||
341 | if (restore_sigcontext(®s, &frame->sf_sc)) | 410 | sig = restore_sigcontext(®s, &frame->sf_sc); |
411 | if (sig < 0) | ||
342 | goto badframe; | 412 | goto badframe; |
413 | else if (sig) | ||
414 | force_sig(sig, current); | ||
343 | 415 | ||
344 | /* | 416 | /* |
345 | * Don't let your children do this ... | 417 | * Don't let your children do this ... |
@@ -361,6 +433,7 @@ asmlinkage void sys_rt_sigreturn(nabi_no_regargs struct pt_regs regs) | |||
361 | struct rt_sigframe __user *frame; | 433 | struct rt_sigframe __user *frame; |
362 | sigset_t set; | 434 | sigset_t set; |
363 | stack_t st; | 435 | stack_t st; |
436 | int sig; | ||
364 | 437 | ||
365 | frame = (struct rt_sigframe __user *) regs.regs[29]; | 438 | frame = (struct rt_sigframe __user *) regs.regs[29]; |
366 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | 439 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) |
@@ -374,8 +447,11 @@ asmlinkage void sys_rt_sigreturn(nabi_no_regargs struct pt_regs regs) | |||
374 | recalc_sigpending(); | 447 | recalc_sigpending(); |
375 | spin_unlock_irq(¤t->sighand->siglock); | 448 | spin_unlock_irq(¤t->sighand->siglock); |
376 | 449 | ||
377 | if (restore_sigcontext(®s, &frame->rs_uc.uc_mcontext)) | 450 | sig = restore_sigcontext(®s, &frame->rs_uc.uc_mcontext); |
451 | if (sig < 0) | ||
378 | goto badframe; | 452 | goto badframe; |
453 | else if (sig) | ||
454 | force_sig(sig, current); | ||
379 | 455 | ||
380 | if (__copy_from_user(&st, &frame->rs_uc.uc_stack, sizeof(st))) | 456 | if (__copy_from_user(&st, &frame->rs_uc.uc_stack, sizeof(st))) |
381 | goto badframe; | 457 | goto badframe; |
@@ -398,7 +474,7 @@ badframe: | |||
398 | } | 474 | } |
399 | 475 | ||
400 | #ifdef CONFIG_TRAD_SIGNALS | 476 | #ifdef CONFIG_TRAD_SIGNALS |
401 | int setup_frame(struct k_sigaction * ka, struct pt_regs *regs, | 477 | static int setup_frame(struct k_sigaction * ka, struct pt_regs *regs, |
402 | int signr, sigset_t *set) | 478 | int signr, sigset_t *set) |
403 | { | 479 | { |
404 | struct sigframe __user *frame; | 480 | struct sigframe __user *frame; |
@@ -443,7 +519,7 @@ give_sigsegv: | |||
443 | } | 519 | } |
444 | #endif | 520 | #endif |
445 | 521 | ||
446 | int setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, | 522 | static int setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, |
447 | int signr, sigset_t *set, siginfo_t *info) | 523 | int signr, sigset_t *set, siginfo_t *info) |
448 | { | 524 | { |
449 | struct rt_sigframe __user *frame; | 525 | struct rt_sigframe __user *frame; |
@@ -501,6 +577,14 @@ give_sigsegv: | |||
501 | return -EFAULT; | 577 | return -EFAULT; |
502 | } | 578 | } |
503 | 579 | ||
580 | struct mips_abi mips_abi = { | ||
581 | #ifdef CONFIG_TRAD_SIGNALS | ||
582 | .setup_frame = setup_frame, | ||
583 | #endif | ||
584 | .setup_rt_frame = setup_rt_frame, | ||
585 | .restart = __NR_restart_syscall | ||
586 | }; | ||
587 | |||
504 | static int handle_signal(unsigned long sig, siginfo_t *info, | 588 | static int handle_signal(unsigned long sig, siginfo_t *info, |
505 | struct k_sigaction *ka, sigset_t *oldset, struct pt_regs *regs) | 589 | struct k_sigaction *ka, sigset_t *oldset, struct pt_regs *regs) |
506 | { | 590 | { |
@@ -539,7 +623,7 @@ static int handle_signal(unsigned long sig, siginfo_t *info, | |||
539 | return ret; | 623 | return ret; |
540 | } | 624 | } |
541 | 625 | ||
542 | void do_signal(struct pt_regs *regs) | 626 | static void do_signal(struct pt_regs *regs) |
543 | { | 627 | { |
544 | struct k_sigaction ka; | 628 | struct k_sigaction ka; |
545 | sigset_t *oldset; | 629 | sigset_t *oldset; |
@@ -589,7 +673,7 @@ void do_signal(struct pt_regs *regs) | |||
589 | regs->cp0_epc -= 8; | 673 | regs->cp0_epc -= 8; |
590 | } | 674 | } |
591 | if (regs->regs[2] == ERESTART_RESTARTBLOCK) { | 675 | if (regs->regs[2] == ERESTART_RESTARTBLOCK) { |
592 | regs->regs[2] = __NR_restart_syscall; | 676 | regs->regs[2] = current->thread.abi->restart; |
593 | regs->regs[7] = regs->regs[26]; | 677 | regs->regs[7] = regs->regs[26]; |
594 | regs->cp0_epc -= 4; | 678 | regs->cp0_epc -= 4; |
595 | } | 679 | } |
@@ -615,5 +699,5 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused, | |||
615 | { | 699 | { |
616 | /* deal with pending signal delivery */ | 700 | /* deal with pending signal delivery */ |
617 | if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK)) | 701 | if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK)) |
618 | current->thread.abi->do_signal(regs); | 702 | do_signal(regs); |
619 | } | 703 | } |