/* * 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 <linux/kernel.h> #include <linux/sched/signal.h> #include <linux/signal.h> #include <linux/export.h> #include <asm/branch.h> #include <asm/cpu.h> #include <asm/cpu-features.h> #include <asm/fpu.h> #include <asm/fpu_emulator.h> #include <asm/inst.h> #include <asm/mips-r2-to-r6-emul.h> #include <asm/ptrace.h> #include <linux/uaccess.h>
#include"probes-common.h"
/* * Calculate and return exception PC in case of branch delay slot * for microMIPS and MIPS16e. It does not clear the ISA mode bit.
*/ int __isa_exception_epc(struct pt_regs *regs)
{ unsignedshort inst; long epc = regs->cp0_epc;
/* Calculate exception PC in branch delay slot. */ if (__get_user(inst, (u16 __user *) msk_isa16_mode(epc))) { /* This should never happen because delay slot was checked. */
force_sig(SIGSEGV); return epc;
} if (cpu_has_mips16) { union mips16e_instruction inst_mips16e;
/* * Compute return address and emulate branch in microMIPS mode after an * exception only. It does not handle compact branches/jumps and cannot * be used in interrupt context. (Compact branches/jumps do not cause * exceptions.)
*/ int __microMIPS_compute_return_epc(struct pt_regs *regs)
{
u16 __user *pc16;
u16 halfword; unsignedint word; unsignedlong contpc; struct mm_decoded_insn mminsn = { 0 };
mminsn.micro_mips_mode = 1;
/* This load never faults. */
pc16 = (unsignedshort __user *)msk_isa16_mode(regs->cp0_epc);
__get_user(halfword, pc16);
pc16++;
contpc = regs->cp0_epc + 2;
word = ((unsignedint)halfword << 16);
mminsn.pc_inc = 2;
if (get_user(halfword, pc16)) goto sigsegv;
mminsn.next_pc_inc = 2;
word = ((unsignedint)halfword << 16);
if (!mm_insn_16bit(halfword)) {
pc16++; if (get_user(halfword, pc16)) goto sigsegv;
mminsn.next_pc_inc = 4;
word |= halfword;
}
mminsn.next_insn = word;
mm_isBranchInstr(regs, mminsn, &contpc);
regs->cp0_epc = contpc;
return 0;
sigsegv:
force_sig(SIGSEGV); return -EFAULT;
}
/* * Compute return address and emulate branch in MIPS16e mode after an * exception only. It does not handle compact branches/jumps and cannot * be used in interrupt context. (Compact branches/jumps do not cause * exceptions.)
*/ 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 *)msk_isa16_mode(epc); if (__get_user(inst.full, addr)) {
force_sig(SIGSEGV); return -EFAULT;
}
switch (inst.ri.opcode) { case MIPS16e_extend_op:
regs->cp0_epc += 4; return 0;
/* * All other cases have no branch delay slot and are 16-bits. * Branches do not cause an exception.
*/
regs->cp0_epc += 2;
return 0;
}
/** * __compute_return_epc_for_insn - Computes the return address and do emulate * branch simulation, if required. * * @regs: Pointer to pt_regs * @insn: branch instruction to decode * Return: -EFAULT on error and forces SIGILL, and on success * returns 0 or BRANCH_LIKELY_TAKEN as appropriate after * evaluating the branch. * * MIPS R6 Compact branches and forbidden slots: * Compact branches do not throw exceptions because they do * not have delay slots. The forbidden slot instruction ($PC+4) * is only executed if the branch was not taken. Otherwise the * forbidden slot is skipped entirely. This means that the * only possible reason to be here because of a MIPS R6 compact * branch instruction is that the forbidden slot has thrown one. * In that case the branch was not taken, so the EPC can be safely * set to EPC + 8.
*/ int __compute_return_epc_for_insn(struct pt_regs *regs, union mips_instruction insn)
{ long epc = regs->cp0_epc; unsignedint dspcontrol; int ret = 0;
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;
fallthrough; case jr_op: if (NO_R6EMU && insn.r_format.func == jr_op) goto sigill_r2r6;
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 bltzl_op: if (NO_R6EMU) goto sigill_r2r6;
fallthrough; case bltz_op: if ((long)regs->regs[insn.i_format.rs] < 0) {
epc = epc + 4 + (insn.i_format.simmediate << 2); if (insn.i_format.rt == bltzl_op)
ret = BRANCH_LIKELY_TAKEN;
} else
epc += 8;
regs->cp0_epc = epc; break;
case bgezl_op: if (NO_R6EMU) goto sigill_r2r6;
fallthrough; case bgez_op: if ((long)regs->regs[insn.i_format.rs] >= 0) {
epc = epc + 4 + (insn.i_format.simmediate << 2); if (insn.i_format.rt == bgezl_op)
ret = BRANCH_LIKELY_TAKEN;
} else
epc += 8;
regs->cp0_epc = epc; break;
case bltzal_op: case bltzall_op: if (NO_R6EMU && (insn.i_format.rs ||
insn.i_format.rt == bltzall_op)) goto sigill_r2r6;
regs->regs[31] = epc + 8; /* * OK we are here either because we hit a NAL * instruction or because we are emulating an * old bltzal{,l} one. Let's figure out what the * case really is.
*/ if (!insn.i_format.rs) { /* * NAL or BLTZAL with rs == 0 * Doesn't matter if we are R6 or not. The * result is the same
*/
regs->cp0_epc += 4 +
(insn.i_format.simmediate << 2); break;
} /* Now do the real thing for non-R6 BLTZAL{,L} */ if ((long)regs->regs[insn.i_format.rs] < 0) {
epc = epc + 4 + (insn.i_format.simmediate << 2); if (insn.i_format.rt == bltzall_op)
ret = BRANCH_LIKELY_TAKEN;
} else
epc += 8;
regs->cp0_epc = epc; break;
case bgezal_op: case bgezall_op: if (NO_R6EMU && (insn.i_format.rs ||
insn.i_format.rt == bgezall_op)) goto sigill_r2r6;
regs->regs[31] = epc + 8; /* * OK we are here either because we hit a BAL * instruction or because we are emulating an * old bgezal{,l} one. Let's figure out what the * case really is.
*/ if (!insn.i_format.rs) { /* * BAL or BGEZAL with rs == 0 * Doesn't matter if we are R6 or not. The * result is the same
*/
regs->cp0_epc += 4 +
(insn.i_format.simmediate << 2); break;
} /* Now do the real thing for non-R6 BGEZAL{,L} */ if ((long)regs->regs[insn.i_format.rs] >= 0) {
epc = epc + 4 + (insn.i_format.simmediate << 2); if (insn.i_format.rt == bgezall_op)
ret = BRANCH_LIKELY_TAKEN;
} else
epc += 8;
regs->cp0_epc = epc; break;
case bposge32_op: if (!cpu_has_dsp) goto sigill_dsp;
/* * These are unconditional and in j_format.
*/ case jalx_op: case jal_op:
regs->regs[31] = regs->cp0_epc + 8;
fallthrough; 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)
set_isa16_mode(regs->cp0_epc); break;
/* * These are conditional and in i_format.
*/ case beql_op: if (NO_R6EMU) goto sigill_r2r6;
fallthrough; case beq_op: if (regs->regs[insn.i_format.rs] ==
regs->regs[insn.i_format.rt]) {
epc = epc + 4 + (insn.i_format.simmediate << 2); if (insn.i_format.opcode == beql_op)
ret = BRANCH_LIKELY_TAKEN;
} else
epc += 8;
regs->cp0_epc = epc; break;
case bnel_op: if (NO_R6EMU) goto sigill_r2r6;
fallthrough; case bne_op: if (regs->regs[insn.i_format.rs] !=
regs->regs[insn.i_format.rt]) {
epc = epc + 4 + (insn.i_format.simmediate << 2); if (insn.i_format.opcode == bnel_op)
ret = BRANCH_LIKELY_TAKEN;
} else
epc += 8;
regs->cp0_epc = epc; break;
case blezl_op: /* not really i_format */ if (!insn.i_format.rt && NO_R6EMU) goto sigill_r2r6;
fallthrough; case blez_op: /* * Compact branches for R6 for the * blez and blezl opcodes. * BLEZ | rs = 0 | rt != 0 == BLEZALC * BLEZ | rs = rt != 0 == BGEZALC * BLEZ | rs != 0 | rt != 0 == BGEUC * BLEZL | rs = 0 | rt != 0 == BLEZC * BLEZL | rs = rt != 0 == BGEZC * BLEZL | rs != 0 | rt != 0 == BGEC * * For real BLEZ{,L}, rt is always 0.
*/
if (cpu_has_mips_r6 && insn.i_format.rt) { if ((insn.i_format.opcode == blez_op) &&
((!insn.i_format.rs && insn.i_format.rt) ||
(insn.i_format.rs == insn.i_format.rt)))
regs->regs[31] = epc + 4;
regs->cp0_epc += 8; break;
} /* rt field assumed to be zero */ if ((long)regs->regs[insn.i_format.rs] <= 0) {
epc = epc + 4 + (insn.i_format.simmediate << 2); if (insn.i_format.opcode == blezl_op)
ret = BRANCH_LIKELY_TAKEN;
} else
epc += 8;
regs->cp0_epc = epc; break;
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); if (insn.i_format.rt == 2)
ret = BRANCH_LIKELY_TAKEN;
} 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); if (insn.i_format.rt == 3)
ret = BRANCH_LIKELY_TAKEN;
} else
epc += 8;
regs->cp0_epc = epc; break;
} break;
}
} #endif/* CONFIG_MIPS_FP_SUPPORT */
#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; #else case bc6_op: /* Only valid for MIPS R6 */ if (!cpu_has_mips_r6) goto sigill_r6;
regs->cp0_epc += 8; break; case balc6_op: if (!cpu_has_mips_r6) goto sigill_r6; /* Compact branch: BALC */
regs->regs[31] = epc + 4;
epc += 4 + (insn.i_format.simmediate << 2);
regs->cp0_epc = epc; break; case pop66_op: if (!cpu_has_mips_r6) goto sigill_r6; /* Compact branch: BEQZC || JIC */
regs->cp0_epc += 8; break; case pop76_op: if (!cpu_has_mips_r6) goto sigill_r6; /* Compact branch: BNEZC || JIALC */ if (!insn.i_format.rs) { /* JIALC: set $31/ra */
regs->regs[31] = epc + 4;
}
regs->cp0_epc += 8; break; #endif case pop10_op: case pop30_op: /* Only valid for MIPS R6 */ if (!cpu_has_mips_r6) goto sigill_r6; /* * Compact branches: * bovc, beqc, beqzalc, bnvc, bnec, bnezlac
*/ if (insn.i_format.rt && !insn.i_format.rs)
regs->regs[31] = epc + 4;
regs->cp0_epc += 8; break;
}
return ret;
sigill_dsp:
pr_debug("%s: DSP branch but not DSP ASE - sending SIGILL.\n",
current->comm);
force_sig(SIGILL); return -EFAULT;
sigill_r2r6:
pr_debug("%s: R2 branch but r2-to-r6 emulator is not present - sending SIGILL.\n",
current->comm);
force_sig(SIGILL); return -EFAULT;
sigill_r6:
pr_debug("%s: R6 branch but no MIPSr6 ISA support - sending SIGILL.\n",
current->comm);
force_sig(SIGILL); return -EFAULT;
}
EXPORT_SYMBOL_GPL(__compute_return_epc_for_insn);
int __compute_return_epc(struct pt_regs *regs)
{ unsignedint __user *addr; long epc; union mips_instruction insn;
epc = regs->cp0_epc; if (epc & 3) goto unaligned;
/* * Read the instruction
*/
addr = (unsignedint __user *) epc; if (__get_user(insn.word, addr)) {
force_sig(SIGSEGV); return -EFAULT;
}
int __insn_is_compact_branch(union mips_instruction insn)
{ if (!cpu_has_mips_r6) return 0;
switch (insn.i_format.opcode) { case blezl_op: case bgtzl_op: case blez_op: case bgtz_op: /* * blez[l] and bgtz[l] opcodes with non-zero rt * are MIPS R6 compact branches
*/ if (insn.i_format.rt) return 1; break; case bc6_op: case balc6_op: case pop10_op: case pop30_op: case pop66_op: case pop76_op: return 1;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.