diff options
Diffstat (limited to 'arch/mips/kernel/signal.c')
| -rw-r--r-- | arch/mips/kernel/signal.c | 75 |
1 files changed, 55 insertions, 20 deletions
diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index f091786187a6..8c3c5a5789b0 100644 --- a/arch/mips/kernel/signal.c +++ b/arch/mips/kernel/signal.c | |||
| @@ -82,6 +82,7 @@ int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) | |||
| 82 | { | 82 | { |
| 83 | int err = 0; | 83 | int err = 0; |
| 84 | int i; | 84 | int i; |
| 85 | unsigned int used_math; | ||
| 85 | 86 | ||
| 86 | err |= __put_user(regs->cp0_epc, &sc->sc_pc); | 87 | err |= __put_user(regs->cp0_epc, &sc->sc_pc); |
| 87 | 88 | ||
| @@ -104,26 +105,53 @@ int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) | |||
| 104 | err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp); | 105 | err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp); |
| 105 | } | 106 | } |
| 106 | 107 | ||
| 107 | err |= __put_user(!!used_math(), &sc->sc_used_math); | 108 | used_math = !!used_math(); |
| 109 | err |= __put_user(used_math, &sc->sc_used_math); | ||
| 108 | 110 | ||
| 109 | if (used_math()) { | 111 | if (used_math) { |
| 110 | /* | 112 | /* |
| 111 | * Save FPU state to signal context. Signal handler | 113 | * Save FPU state to signal context. Signal handler |
| 112 | * will "inherit" current FPU state. | 114 | * will "inherit" current FPU state. |
| 113 | */ | 115 | */ |
| 114 | preempt_disable(); | 116 | own_fpu(1); |
| 115 | 117 | enable_fp_in_kernel(); | |
| 116 | if (!is_fpu_owner()) { | ||
| 117 | own_fpu(); | ||
| 118 | restore_fp(current); | ||
| 119 | } | ||
| 120 | err |= save_fp_context(sc); | 118 | err |= save_fp_context(sc); |
| 121 | 119 | disable_fp_in_kernel(); | |
| 122 | preempt_enable(); | ||
| 123 | } | 120 | } |
| 124 | return err; | 121 | return err; |
| 125 | } | 122 | } |
| 126 | 123 | ||
| 124 | int fpcsr_pending(unsigned int __user *fpcsr) | ||
| 125 | { | ||
| 126 | int err, sig = 0; | ||
| 127 | unsigned int csr, enabled; | ||
| 128 | |||
| 129 | err = __get_user(csr, fpcsr); | ||
| 130 | enabled = FPU_CSR_UNI_X | ((csr & FPU_CSR_ALL_E) << 5); | ||
| 131 | /* | ||
| 132 | * If the signal handler set some FPU exceptions, clear it and | ||
| 133 | * send SIGFPE. | ||
| 134 | */ | ||
| 135 | if (csr & enabled) { | ||
| 136 | csr &= ~enabled; | ||
| 137 | err |= __put_user(csr, fpcsr); | ||
| 138 | sig = SIGFPE; | ||
| 139 | } | ||
| 140 | return err ?: sig; | ||
| 141 | } | ||
| 142 | |||
| 143 | static int | ||
| 144 | check_and_restore_fp_context(struct sigcontext __user *sc) | ||
| 145 | { | ||
| 146 | int err, sig; | ||
| 147 | |||
| 148 | err = sig = fpcsr_pending(&sc->sc_fpc_csr); | ||
| 149 | if (err > 0) | ||
| 150 | err = 0; | ||
| 151 | err |= restore_fp_context(sc); | ||
| 152 | return err ?: sig; | ||
| 153 | } | ||
| 154 | |||
| 127 | int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) | 155 | int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) |
| 128 | { | 156 | { |
| 129 | unsigned int used_math; | 157 | unsigned int used_math; |
| @@ -157,19 +185,18 @@ int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) | |||
| 157 | err |= __get_user(used_math, &sc->sc_used_math); | 185 | err |= __get_user(used_math, &sc->sc_used_math); |
| 158 | conditional_used_math(used_math); | 186 | conditional_used_math(used_math); |
| 159 | 187 | ||
| 160 | preempt_disable(); | 188 | if (used_math) { |
| 161 | |||
| 162 | if (used_math()) { | ||
| 163 | /* restore fpu context if we have used it before */ | 189 | /* restore fpu context if we have used it before */ |
| 164 | own_fpu(); | 190 | own_fpu(0); |
| 165 | err |= restore_fp_context(sc); | 191 | enable_fp_in_kernel(); |
| 192 | if (!err) | ||
| 193 | err = check_and_restore_fp_context(sc); | ||
| 194 | disable_fp_in_kernel(); | ||
| 166 | } else { | 195 | } else { |
| 167 | /* signal handler may have used FPU. Give it up. */ | 196 | /* signal handler may have used FPU. Give it up. */ |
| 168 | lose_fpu(); | 197 | lose_fpu(0); |
| 169 | } | 198 | } |
| 170 | 199 | ||
| 171 | preempt_enable(); | ||
| 172 | |||
| 173 | return err; | 200 | return err; |
| 174 | } | 201 | } |
| 175 | 202 | ||
| @@ -332,6 +359,7 @@ asmlinkage void sys_sigreturn(nabi_no_regargs struct pt_regs regs) | |||
| 332 | { | 359 | { |
| 333 | struct sigframe __user *frame; | 360 | struct sigframe __user *frame; |
| 334 | sigset_t blocked; | 361 | sigset_t blocked; |
| 362 | int sig; | ||
| 335 | 363 | ||
| 336 | frame = (struct sigframe __user *) regs.regs[29]; | 364 | frame = (struct sigframe __user *) regs.regs[29]; |
| 337 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | 365 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) |
| @@ -345,8 +373,11 @@ asmlinkage void sys_sigreturn(nabi_no_regargs struct pt_regs regs) | |||
| 345 | recalc_sigpending(); | 373 | recalc_sigpending(); |
| 346 | spin_unlock_irq(¤t->sighand->siglock); | 374 | spin_unlock_irq(¤t->sighand->siglock); |
| 347 | 375 | ||
| 348 | if (restore_sigcontext(®s, &frame->sf_sc)) | 376 | sig = restore_sigcontext(®s, &frame->sf_sc); |
| 377 | if (sig < 0) | ||
| 349 | goto badframe; | 378 | goto badframe; |
| 379 | else if (sig) | ||
| 380 | force_sig(sig, current); | ||
| 350 | 381 | ||
| 351 | /* | 382 | /* |
| 352 | * Don't let your children do this ... | 383 | * Don't let your children do this ... |
| @@ -368,6 +399,7 @@ asmlinkage void sys_rt_sigreturn(nabi_no_regargs struct pt_regs regs) | |||
| 368 | struct rt_sigframe __user *frame; | 399 | struct rt_sigframe __user *frame; |
| 369 | sigset_t set; | 400 | sigset_t set; |
| 370 | stack_t st; | 401 | stack_t st; |
| 402 | int sig; | ||
| 371 | 403 | ||
| 372 | frame = (struct rt_sigframe __user *) regs.regs[29]; | 404 | frame = (struct rt_sigframe __user *) regs.regs[29]; |
| 373 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | 405 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) |
| @@ -381,8 +413,11 @@ asmlinkage void sys_rt_sigreturn(nabi_no_regargs struct pt_regs regs) | |||
| 381 | recalc_sigpending(); | 413 | recalc_sigpending(); |
| 382 | spin_unlock_irq(¤t->sighand->siglock); | 414 | spin_unlock_irq(¤t->sighand->siglock); |
| 383 | 415 | ||
| 384 | if (restore_sigcontext(®s, &frame->rs_uc.uc_mcontext)) | 416 | sig = restore_sigcontext(®s, &frame->rs_uc.uc_mcontext); |
| 417 | if (sig < 0) | ||
| 385 | goto badframe; | 418 | goto badframe; |
| 419 | else if (sig) | ||
| 420 | force_sig(sig, current); | ||
| 386 | 421 | ||
| 387 | if (__copy_from_user(&st, &frame->rs_uc.uc_stack, sizeof(st))) | 422 | if (__copy_from_user(&st, &frame->rs_uc.uc_stack, sizeof(st))) |
| 388 | goto badframe; | 423 | goto badframe; |
