/* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 1996, 97, 2000, 2001 by Ralf Baechle * Copyright (C) 2001 MIPS Technologies, Inc. */ #include #include #include #include #include #include #include #include #include #include #include /* * Calculate and return exception epc in case of * branch delay slot for microMIPS/MIPS16e * It doesn't clear ISA mode bit. */ int __isa_exception_epc(struct pt_regs *regs) { long epc; union mips16e_instruction inst; /* calc exception pc in branch delay slot */ epc = regs->cp0_epc; if (__get_user(inst.full, (u16 __user *) (epc & ~MIPS_ISA_MODE))) { /* it should never happens... because delay slot was checked */ force_sig(SIGSEGV, current); return epc; } if (cpu_has_mips16) { if (inst.ri.opcode == MIPS16e_jal_op) epc += 4; else epc += 2; } else if (mm_is16bit(inst.full)) epc += 2; else epc += 4; return epc; } /* * Compute the return address and do emulate branch simulation in MIPS16e mode, * if required. * After exception only - doesn't do 'compact' branch/jumps and can't be used * during interrupt (compact B/J doesn't do exception) */ int __MIPS16e_compute_return_epc(struct pt_regs *regs) { u16 __user *addr; union mips16e_instruction inst; u16 inst2; u32 fullinst; long epc; epc = regs->cp0_epc; /* * Read the instruction */ addr = (u16 __user *) (epc & ~MIPS_ISA_MODE); if (__get_user(inst.full, addr)) { force_sig(SIGSEGV, current); return -EFAULT; } switch (inst.ri.opcode) { case MIPS16e_extend_op: regs->cp0_epc += 4; return 0; /* * JAL and JALX in MIPS16e mode */ case MIPS16e_jal_op: addr += 1; if (__get_user(inst2, addr)) { force_sig(SIGSEGV, current); return -EFAULT; } fullinst = ((unsigned)inst.full << 16) | inst2; regs->regs[31] = epc + 6; epc += 4; epc >>= 28; epc <<= 28; /* * JAL:5 X:1 TARGET[20-16]:5 TARGET[25:21]:5 TARGET[15:0]:16 * * ......TARGET[15:0].................TARGET[20:16]........... * ......TARGET[25:21] */ epc |= ((fullinst & 0xffff) << 2) | ((fullinst & 0x3e00000) >> 3) | ((fullinst & 0x1f0000) << 7); if (!inst.jal.x) epc |= MIPS_ISA_MODE; /* set ISA mode 1 */ regs->cp0_epc = epc; return 0; /* * J(AL)R(C) */ case MIPS16e_rr_op: if (inst.rr.func == MIPS16e_jr_func) { if (inst.rr.ra) regs->cp0_epc = regs->regs[31]; else regs->cp0_epc = regs->regs[mips16e_reg2gpr[inst.rr.rx]]; if (inst.rr.l) { if (inst.rr.nd) regs->regs[31] = epc + 2; else regs->regs[31] = epc + 4; } return 0; } break; } /* all other cases have no branch delay slot and are 16bits, and branches do not do exception */ regs->cp0_epc += 2; return 0; } /* * Compute the return address and do emulate branch simulation in * microMIPS mode, if required. * After exception only - doesn't do 'compact' branch/jumps and can't be used * during interrupt (compact B/J doesn't do exception) */ int __microMIPS_compute_return_epc(struct pt_regs *regs) { u16 __user *pc16; u16 halfword; unsigned int word; unsigned long contpc; struct decoded_instn mminst = { 0 }; mminst.micro_mips_mode = 1; /* * This load never faults. */ pc16 = (unsigned short __user *)(regs->cp0_epc & ~MIPS_ISA_MODE); __get_user(halfword, pc16); pc16++; contpc = regs->cp0_epc + 2; word = ((unsigned int)halfword << 16); mminst.pc_inc = 2; if (!mm_is16bit(halfword)) { __get_user(halfword, pc16); pc16++; contpc = regs->cp0_epc + 4; mminst.pc_inc = 4; word |= halfword; } mminst.insn = word; if (get_user(halfword, pc16)) goto sigsegv; mminst.next_pc_inc = 2; word = ((unsigned int)halfword << 16); if (!mm_is16bit(halfword)) { pc16++; if (get_user(halfword, pc16)) goto sigsegv; mminst.next_pc_inc = 4; word |= halfword; } mminst.next_insn = word; mm_isBranchInstr(regs, mminst, &contpc); regs->cp0_epc = contpc; return 0; sigsegv: force_sig(SIGSEGV, current); return -EFAULT; } /* * Compute the return address and do emulate branch simulation, if required. * This function should be called only in branch delay slot active. */ int __compute_return_epc(struct pt_regs *regs) { unsigned int __user *addr; unsigned int bit, fcr31, dspcontrol; long epc; union mips_instruction insn; epc = regs->cp0_epc; if (epc & 3) goto unaligned; /* * Read the instruction */ addr = (unsigned int __user *) epc; if (__get_user(insn.word, addr)) { force_sig(SIGSEGV, current); return -EFAULT; } switch (insn.i_format.opcode) { /* * jr and jalr are in r_format format. */ case spec_op: switch (insn.r_format.func) { case jalr_op: regs->regs[insn.r_format.rd] = epc + 8; /* Fall through */ case jr_op: regs->cp0_epc = regs->regs[insn.r_format.rs]; break; } break; /* * This group contains: * bltz_op, bgez_op, bltzl_op, bgezl_op, * bltzal_op, bgezal_op, bltzall_op, bgezall_op. */ case bcond_op: switch (insn.i_format.rt) { case bltz_op: case bltzl_op: if ((long)regs->regs[insn.i_format.rs] < 0) epc = epc + 4 + (insn.i_format.simmediate << 2); else epc += 8; regs->cp0_epc = epc; break; case bgez_op: case bgezl_op: if ((long)regs->regs[insn.i_format.rs] >= 0) epc = epc + 4 + (insn.i_format.simmediate << 2); else epc += 8; regs->cp0_epc = epc; break; case bltzal_op: case bltzall_op: regs->regs[31] = epc + 8; if ((long)regs->regs[insn.i_format.rs] < 0) epc = epc + 4 + (insn.i_format.simmediate << 2); else epc += 8; regs->cp0_epc = epc; break; case bgezal_op: case bgezall_op: regs->regs[31] = epc + 8; if ((long)regs->regs[insn.i_format.rs] >= 0) epc = epc + 4 + (insn.i_format.simmediate << 2); else 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; /* * These are unconditional and in j_format. */ case jal_op: regs->regs[31] = regs->cp0_epc + 8; case j_op: epc += 4; epc >>= 28; epc <<= 28; epc |= (insn.j_format.target << 2); regs->cp0_epc = epc; if (insn.i_format.opcode == jalx_op) regs->cp0_epc |= MIPS_ISA_MODE; break; /* * These are conditional and in i_format. */ case beq_op: case beql_op: if (regs->regs[insn.i_format.rs] == regs->regs[insn.i_format.rt]) epc = epc + 4 + (insn.i_format.simmediate << 2); else epc += 8; regs->cp0_epc = epc; break; case bne_op: case bnel_op: if (regs->regs[insn.i_format.rs] != regs->regs[insn.i_format.rt]) epc = epc + 4 + (insn.i_format.simmediate << 2); else epc += 8; regs->cp0_epc = epc; break; case blez_op: /* not really i_format */ case blezl_op: /* rt field assumed to be zero */ if ((long)regs->regs[insn.i_format.rs] <= 0) epc = epc + 4 + (insn.i_format.simmediate << 2); else epc += 8; regs->cp0_epc = epc; break; case bgtz_op: case bgtzl_op: /* rt field assumed to be zero */ if ((long)regs->regs[insn.i_format.rs] > 0) epc = epc + 4 + (insn.i_format.simmediate << 2); else epc += 8; regs->cp0_epc = epc; break; /* * And now the FPA/cp1 branch instructions. */ case cop1_op: preempt_disable(); if (is_fpu_owner()) asm volatile("cfc1\t%0,$31" : "=r" (fcr31)); else fcr31 = current->thread.fpu.fcr31; preempt_enable(); bit = (insn.i_format.rt >> 2); bit += (bit != 0); bit += 23; switch (insn.i_format.rt & 3) { case 0: /* bc1f */ case 2: /* bc1fl */ if (~fcr31 & (1 << bit)) epc = epc + 4 + (insn.i_format.simmediate << 2); else epc += 8; regs->cp0_epc = epc; break; case 1: /* bc1t */ case 3: /* bc1tl */ if (fcr31 & (1 << bit)) epc = epc + 4 + (insn.i_format.simmediate << 2); else epc += 8; regs->cp0_epc = epc; break; } break; #ifdef CONFIG_CPU_CAVIUM_OCTEON case lwc2_op: /* This is bbit0 on Octeon */ if ((regs->regs[insn.i_format.rs] & (1ull << insn.i_format.rt)) == 0) epc = epc + 4 + (insn.i_format.simmediate << 2); else epc += 8; regs->cp0_epc = epc; break; case ldc2_op: /* This is bbit032 on Octeon */ if ((regs->regs[insn.i_format.rs] & (1ull << (insn.i_format.rt + 32))) == 0) epc = epc + 4 + (insn.i_format.simmediate << 2); else epc += 8; regs->cp0_epc = epc; break; case swc2_op: /* This is bbit1 on Octeon */ if (regs->regs[insn.i_format.rs] & (1ull << insn.i_format.rt)) epc = epc + 4 + (insn.i_format.simmediate << 2); else epc += 8; regs->cp0_epc = epc; break; case sdc2_op: /* This is bbit132 on Octeon */ if (regs->regs[insn.i_format.rs] & (1ull << (insn.i_format.rt + 32))) epc = epc + 4 + (insn.i_format.simmediate << 2); else epc += 8; regs->cp0_epc = epc; break; #endif } return 0; 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; }