diff options
Diffstat (limited to 'arch/mips/kernel/signal.c')
| -rw-r--r-- | arch/mips/kernel/signal.c | 111 |
1 files changed, 90 insertions, 21 deletions
diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index f091786187a6..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 | ||
| @@ -104,24 +145,48 @@ int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) | |||
| 104 | err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp); | 145 | err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp); |
| 105 | } | 146 | } |
| 106 | 147 | ||
| 107 | err |= __put_user(!!used_math(), &sc->sc_used_math); | 148 | used_math = !!used_math(); |
| 149 | err |= __put_user(used_math, &sc->sc_used_math); | ||
| 108 | 150 | ||
| 109 | if (used_math()) { | 151 | if (used_math) { |
| 110 | /* | 152 | /* |
| 111 | * Save FPU state to signal context. Signal handler | 153 | * Save FPU state to signal context. Signal handler |
| 112 | * will "inherit" current FPU state. | 154 | * will "inherit" current FPU state. |
| 113 | */ | 155 | */ |
| 114 | preempt_disable(); | 156 | err |= protected_save_fp_context(sc); |
| 157 | } | ||
| 158 | return err; | ||
| 159 | } | ||
| 115 | 160 | ||
| 116 | if (!is_fpu_owner()) { | 161 | int fpcsr_pending(unsigned int __user *fpcsr) |
| 117 | own_fpu(); | 162 | { |
| 118 | restore_fp(current); | 163 | int err, sig = 0; |
| 119 | } | 164 | unsigned int csr, enabled; |
| 120 | err |= save_fp_context(sc); | ||
| 121 | 165 | ||
| 122 | 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; | ||
| 123 | } | 176 | } |
| 124 | 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; | ||
| 125 | } | 190 | } |
| 126 | 191 | ||
| 127 | int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) | 192 | int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) |
| @@ -157,19 +222,15 @@ int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) | |||
| 157 | err |= __get_user(used_math, &sc->sc_used_math); | 222 | err |= __get_user(used_math, &sc->sc_used_math); |
| 158 | conditional_used_math(used_math); | 223 | conditional_used_math(used_math); |
| 159 | 224 | ||
| 160 | preempt_disable(); | 225 | if (used_math) { |
| 161 | |||
| 162 | if (used_math()) { | ||
| 163 | /* restore fpu context if we have used it before */ | 226 | /* restore fpu context if we have used it before */ |
| 164 | own_fpu(); | 227 | if (!err) |
| 165 | err |= restore_fp_context(sc); | 228 | err = check_and_restore_fp_context(sc); |
| 166 | } else { | 229 | } else { |
| 167 | /* signal handler may have used FPU. Give it up. */ | 230 | /* signal handler may have used FPU. Give it up. */ |
| 168 | lose_fpu(); | 231 | lose_fpu(0); |
| 169 | } | 232 | } |
| 170 | 233 | ||
| 171 | preempt_enable(); | ||
| 172 | |||
| 173 | return err; | 234 | return err; |
| 174 | } | 235 | } |
| 175 | 236 | ||
| @@ -332,6 +393,7 @@ asmlinkage void sys_sigreturn(nabi_no_regargs struct pt_regs regs) | |||
| 332 | { | 393 | { |
| 333 | struct sigframe __user *frame; | 394 | struct sigframe __user *frame; |
| 334 | sigset_t blocked; | 395 | sigset_t blocked; |
| 396 | int sig; | ||
| 335 | 397 | ||
| 336 | frame = (struct sigframe __user *) regs.regs[29]; | 398 | frame = (struct sigframe __user *) regs.regs[29]; |
| 337 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | 399 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) |
| @@ -345,8 +407,11 @@ asmlinkage void sys_sigreturn(nabi_no_regargs struct pt_regs regs) | |||
| 345 | recalc_sigpending(); | 407 | recalc_sigpending(); |
| 346 | spin_unlock_irq(¤t->sighand->siglock); | 408 | spin_unlock_irq(¤t->sighand->siglock); |
| 347 | 409 | ||
| 348 | if (restore_sigcontext(®s, &frame->sf_sc)) | 410 | sig = restore_sigcontext(®s, &frame->sf_sc); |
| 411 | if (sig < 0) | ||
| 349 | goto badframe; | 412 | goto badframe; |
| 413 | else if (sig) | ||
| 414 | force_sig(sig, current); | ||
| 350 | 415 | ||
| 351 | /* | 416 | /* |
| 352 | * Don't let your children do this ... | 417 | * Don't let your children do this ... |
| @@ -368,6 +433,7 @@ asmlinkage void sys_rt_sigreturn(nabi_no_regargs struct pt_regs regs) | |||
| 368 | struct rt_sigframe __user *frame; | 433 | struct rt_sigframe __user *frame; |
| 369 | sigset_t set; | 434 | sigset_t set; |
| 370 | stack_t st; | 435 | stack_t st; |
| 436 | int sig; | ||
| 371 | 437 | ||
| 372 | frame = (struct rt_sigframe __user *) regs.regs[29]; | 438 | frame = (struct rt_sigframe __user *) regs.regs[29]; |
| 373 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | 439 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) |
| @@ -381,8 +447,11 @@ asmlinkage void sys_rt_sigreturn(nabi_no_regargs struct pt_regs regs) | |||
| 381 | recalc_sigpending(); | 447 | recalc_sigpending(); |
| 382 | spin_unlock_irq(¤t->sighand->siglock); | 448 | spin_unlock_irq(¤t->sighand->siglock); |
| 383 | 449 | ||
| 384 | if (restore_sigcontext(®s, &frame->rs_uc.uc_mcontext)) | 450 | sig = restore_sigcontext(®s, &frame->rs_uc.uc_mcontext); |
| 451 | if (sig < 0) | ||
| 385 | goto badframe; | 452 | goto badframe; |
| 453 | else if (sig) | ||
| 454 | force_sig(sig, current); | ||
| 386 | 455 | ||
| 387 | 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))) |
| 388 | goto badframe; | 457 | goto badframe; |
