/*
* 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)
{
unsigned short 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;
inst_mips16e.full = inst;
if (inst_mips16e.ri.opcode == MIPS16e_jal_op)
epc += 4 ;
else
epc += 2 ;
} else if (mm_insn_16bit(inst))
epc += 2 ;
else
epc += 4 ;
return epc;
}
/* (microMIPS) Convert 16-bit register encoding to 32-bit register encoding. */
static const unsigned int reg16to32map[8 ] = {16 , 17 , 2 , 3 , 4 , 5 , 6 , 7 };
int __mm_isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
unsigned long *contpc)
{
union mips_instruction insn = (union mips_instruction)dec_insn.insn;
int __maybe_unused bc_false = 0 ;
if (!cpu_has_mmips)
return 0 ;
switch (insn.mm_i_format.opcode) {
case mm_pool32a_op:
if ((insn.mm_i_format.simmediate & MM_POOL32A_MINOR_MASK) ==
mm_pool32axf_op) {
switch (insn.mm_i_format.simmediate >>
MM_POOL32A_MINOR_SHIFT) {
case mm_jalr_op:
case mm_jalrhb_op:
case mm_jalrs_op:
case mm_jalrshb_op:
if (insn.mm_i_format.rt != 0 ) /* Not mm_jr */
regs->regs[insn.mm_i_format.rt] =
regs->cp0_epc +
dec_insn.pc_inc +
dec_insn.next_pc_inc;
*contpc = regs->regs[insn.mm_i_format.rs];
return 1 ;
}
}
break ;
case mm_pool32i_op:
switch (insn.mm_i_format.rt) {
case mm_bltzals_op:
case mm_bltzal_op:
regs->regs[31 ] = regs->cp0_epc +
dec_insn.pc_inc +
dec_insn.next_pc_inc;
fallthrough;
case mm_bltz_op:
if ((long )regs->regs[insn.mm_i_format.rs] < 0 )
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
(insn.mm_i_format.simmediate << 1 );
else
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
dec_insn.next_pc_inc;
return 1 ;
case mm_bgezals_op:
case mm_bgezal_op:
regs->regs[31 ] = regs->cp0_epc +
dec_insn.pc_inc +
dec_insn.next_pc_inc;
fallthrough;
case mm_bgez_op:
if ((long )regs->regs[insn.mm_i_format.rs] >= 0 )
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
(insn.mm_i_format.simmediate << 1 );
else
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
dec_insn.next_pc_inc;
return 1 ;
case mm_blez_op:
if ((long )regs->regs[insn.mm_i_format.rs] <= 0 )
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
(insn.mm_i_format.simmediate << 1 );
else
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
dec_insn.next_pc_inc;
return 1 ;
case mm_bgtz_op:
if ((long )regs->regs[insn.mm_i_format.rs] <= 0 )
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
(insn.mm_i_format.simmediate << 1 );
else
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
dec_insn.next_pc_inc;
return 1 ;
#ifdef CONFIG_MIPS_FP_SUPPORT
case mm_bc2f_op:
case mm_bc1f_op: {
unsigned int fcr31;
unsigned int bit;
bc_false = 1 ;
fallthrough;
case mm_bc2t_op:
case mm_bc1t_op:
preempt_disable();
if (is_fpu_owner())
fcr31 = read_32bit_cp1_register(CP1_STATUS);
else
fcr31 = current->thread.fpu.fcr31;
preempt_enable();
if (bc_false)
fcr31 = ~fcr31;
bit = (insn.mm_i_format.rs >> 2 );
bit += (bit != 0 );
bit += 23 ;
if (fcr31 & (1 << bit))
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
(insn.mm_i_format.simmediate << 1 );
else
*contpc = regs->cp0_epc +
dec_insn.pc_inc + dec_insn.next_pc_inc;
return 1 ;
}
#endif /* CONFIG_MIPS_FP_SUPPORT */
}
break ;
case mm_pool16c_op:
switch (insn.mm_i_format.rt) {
case mm_jalr16_op:
case mm_jalrs16_op:
regs->regs[31 ] = regs->cp0_epc +
dec_insn.pc_inc + dec_insn.next_pc_inc;
fallthrough;
case mm_jr16_op:
*contpc = regs->regs[insn.mm_i_format.rs];
return 1 ;
}
break ;
case mm_beqz16_op:
if ((long )regs->regs[reg16to32map[insn.mm_b1_format.rs]] == 0 )
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
(insn.mm_b1_format.simmediate << 1 );
else
*contpc = regs->cp0_epc +
dec_insn.pc_inc + dec_insn.next_pc_inc;
return 1 ;
case mm_bnez16_op:
if ((long )regs->regs[reg16to32map[insn.mm_b1_format.rs]] != 0 )
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
(insn.mm_b1_format.simmediate << 1 );
else
*contpc = regs->cp0_epc +
dec_insn.pc_inc + dec_insn.next_pc_inc;
return 1 ;
case mm_b16_op:
*contpc = regs->cp0_epc + dec_insn.pc_inc +
(insn.mm_b0_format.simmediate << 1 );
return 1 ;
case mm_beq32_op:
if (regs->regs[insn.mm_i_format.rs] ==
regs->regs[insn.mm_i_format.rt])
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
(insn.mm_i_format.simmediate << 1 );
else
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
dec_insn.next_pc_inc;
return 1 ;
case mm_bne32_op:
if (regs->regs[insn.mm_i_format.rs] !=
regs->regs[insn.mm_i_format.rt])
*contpc = regs->cp0_epc +
dec_insn.pc_inc +
(insn.mm_i_format.simmediate << 1 );
else
*contpc = regs->cp0_epc +
dec_insn.pc_inc + dec_insn.next_pc_inc;
return 1 ;
case mm_jalx32_op:
regs->regs[31 ] = regs->cp0_epc +
dec_insn.pc_inc + dec_insn.next_pc_inc;
*contpc = regs->cp0_epc + dec_insn.pc_inc;
*contpc >>= 28 ;
*contpc <<= 28 ;
*contpc |= (insn.j_format.target << 2 );
return 1 ;
case mm_jals32_op:
case mm_jal32_op:
regs->regs[31 ] = regs->cp0_epc +
dec_insn.pc_inc + dec_insn.next_pc_inc;
fallthrough;
case mm_j32_op:
*contpc = regs->cp0_epc + dec_insn.pc_inc;
*contpc >>= 27 ;
*contpc <<= 27 ;
*contpc |= (insn.j_format.target << 1 );
set_isa16_mode(*contpc);
return 1 ;
}
return 0 ;
}
/*
* 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;
unsigned int word;
unsigned long contpc;
struct mm_decoded_insn mminsn = { 0 };
mminsn.micro_mips_mode = 1 ;
/* This load never faults. */
pc16 = (unsigned short __user *)msk_isa16_mode(regs->cp0_epc);
__get_user(halfword, pc16);
pc16++;
contpc = regs->cp0_epc + 2 ;
word = ((unsigned int )halfword << 16 );
mminsn.pc_inc = 2 ;
if (!mm_insn_16bit(halfword)) {
__get_user(halfword, pc16);
pc16++;
contpc = regs->cp0_epc + 4 ;
mminsn.pc_inc = 4 ;
word |= halfword;
}
mminsn.insn = word;
if (get_user(halfword, pc16))
goto sigsegv;
mminsn.next_pc_inc = 2 ;
word = ((unsigned int )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 ;
/*
* JAL and JALX in MIPS16e mode
*/
case MIPS16e_jal_op:
addr += 1 ;
if (__get_user(inst2, addr)) {
force_sig(SIGSEGV);
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 & 0 xffff) << 2 ) | ((fullinst & 0 x3e00000) >> 3 ) |
((fullinst & 0 x1f0000) << 7 );
if (!inst.jal.x)
set_isa16_mode(epc); /* Set ISA mode bit. */
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[reg16to32[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 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;
unsigned int 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;
dspcontrol = rddsp(0 x01);
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 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 ;
case bgtzl_op:
if (!insn.i_format.rt && NO_R6EMU)
goto sigill_r2r6;
fallthrough;
case bgtz_op:
/*
* Compact branches for R6 for the
* bgtz and bgtzl opcodes .
* BGTZ | rs = 0 | rt ! = 0 = = BGTZALC
* BGTZ | rs = rt ! = 0 = = BLTZALC
* BGTZ | rs ! = 0 | rt ! = 0 = = BLTUC
* BGTZL | rs = 0 | rt ! = 0 = = BGTZC
* BGTZL | rs = rt ! = 0 = = BLTZC
* BGTZL | rs ! = 0 | rt ! = 0 = = BLTC
*
* * ZALC varint for BGTZ & & & rt ! = 0
* For real GTZ { , 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 == bgtzl_op)
ret = BRANCH_LIKELY_TAKEN;
} else
epc += 8 ;
regs->cp0_epc = epc;
break ;
#ifdef CONFIG_MIPS_FP_SUPPORT
/*
* And now the FPA / cp1 branch instructions .
*/
case cop1_op: {
unsigned int bit, fcr31, reg;
if (cpu_has_mips_r6 &&
((insn.i_format.rs == bc1eqz_op) ||
(insn.i_format.rs == bc1nez_op))) {
if (!init_fp_ctx(current))
lose_fpu(1 );
reg = insn.i_format.rt;
bit = get_fpr32(¤t->thread.fpu.fpr[reg], 0 ) & 0 x1;
if (insn.i_format.rs == bc1eqz_op)
bit = !bit;
own_fpu(1 );
if (bit)
epc = epc + 4 +
(insn.i_format.simmediate << 2 );
else
epc += 8 ;
regs->cp0_epc = epc;
break ;
} else {
preempt_disable();
if (is_fpu_owner())
fcr31 = read_32bit_cp1_register(CP1_STATUS);
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 );
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] & (1 ull<<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] &
(1 ull<<(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] & (1 ull<<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] &
(1 ull<<(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)
{
unsigned int __user *addr;
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);
return -EFAULT;
}
return __compute_return_epc_for_insn(regs, insn);
unaligned:
printk("%s: unaligned epc - sending SIGBUS.\n" , current->comm);
force_sig(SIGBUS);
return -EFAULT;
}
#if (defined CONFIG_KPROBES) || (defined CONFIG_UPROBES)
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 ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL(__insn_is_compact_branch);
#endif /* CONFIG_KPROBES || CONFIG_UPROBES */
Messung V0.5 in Prozent C=95 H=87 G=90
¤ Dauer der Verarbeitung: 0.18 Sekunden
(vorverarbeitet am 2026-09-30)
¤
*© Formatika GbR, Deutschland