diff options
Diffstat (limited to 'arch/mips/kernel/branch.c')
-rw-r--r-- | arch/mips/kernel/branch.c | 29 |
1 files changed, 25 insertions, 4 deletions
diff --git a/arch/mips/kernel/branch.c b/arch/mips/kernel/branch.c index 01117e977a7..374de839558 100644 --- a/arch/mips/kernel/branch.c +++ b/arch/mips/kernel/branch.c @@ -12,6 +12,7 @@ #include <asm/branch.h> #include <asm/cpu.h> #include <asm/cpu-features.h> +#include <asm/fpu.h> #include <asm/inst.h> #include <asm/ptrace.h> #include <asm/uaccess.h> @@ -21,7 +22,7 @@ */ int __compute_return_epc(struct pt_regs *regs) { - unsigned int *addr, bit, fcr31; + unsigned int *addr, bit, fcr31, dspcontrol; long epc; union mips_instruction insn; @@ -98,6 +99,18 @@ int __compute_return_epc(struct pt_regs *regs) epc += 8; regs->cp0_epc = epc; break; + case bposge32_op: + if (!cpu_has_dsp) + goto sigill; + + dspcontrol = rddsp(0x01); + + if (dspcontrol >= 32) { + epc = epc + 4 + (insn.i_format.simmediate << 2); + } else + epc += 8; + regs->cp0_epc = epc; + break; } break; @@ -161,10 +174,13 @@ int __compute_return_epc(struct pt_regs *regs) * And now the FPA/cp1 branch instructions. */ case cop1_op: - if (!cpu_has_fpu) - fcr31 = current->thread.fpu.soft.fcr31; - else + preempt_disable(); + if (is_fpu_owner()) asm volatile("cfc1\t%0,$31" : "=r" (fcr31)); + else + fcr31 = current->thread.fpu.hard.fcr31; + preempt_enable(); + bit = (insn.i_format.rt >> 2); bit += (bit != 0); bit += 23; @@ -196,4 +212,9 @@ unaligned: printk("%s: unaligned epc - sending SIGBUS.\n", current->comm); force_sig(SIGBUS, current); return -EFAULT; + +sigill: + printk("%s: DSP branch but not DSP ASE - sending SIGBUS.\n", current->comm); + force_sig(SIGBUS, current); + return -EFAULT; } |