Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 284772
b: refs/heads/master
c: d8d4e3a
h: refs/heads/master
v: v3
  • Loading branch information
Maneesh Soni authored and Ralf Baechle committed Dec 7, 2011
1 parent d966a1b commit 32fa626
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 46 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 9233c1ee71bdd3c8a918c8e17026cf3f7d99c90b
refs/heads/master: d8d4e3ae0b5c179c0bfd3f0af5b352d13bea9cfa
5 changes: 5 additions & 0 deletions trunk/arch/mips/include/asm/branch.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#define _ASM_BRANCH_H

#include <asm/ptrace.h>
#include <asm/inst.h>

static inline int delay_slot(struct pt_regs *regs)
{
Expand All @@ -23,7 +24,11 @@ static inline unsigned long exception_epc(struct pt_regs *regs)
return regs->cp0_epc + 4;
}

#define BRANCH_LIKELY_TAKEN 0x0001

extern int __compute_return_epc(struct pt_regs *regs);
extern int __compute_return_epc_for_insn(struct pt_regs *regs,
union mips_instruction insn);

static inline int compute_return_epc(struct pt_regs *regs)
{
Expand Down
128 changes: 84 additions & 44 deletions trunk/arch/mips/kernel/branch.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/module.h>
#include <asm/branch.h>
#include <asm/cpu.h>
#include <asm/cpu-features.h>
Expand All @@ -17,28 +18,22 @@
#include <asm/ptrace.h>
#include <asm/uaccess.h>

/*
* Compute the return address and do emulate branch simulation, if required.
/**
* __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
* @returns: -EFAULT on error and forces SIGBUS, and on success
* returns 0 or BRANCH_LIKELY_TAKEN as appropriate after
* evaluating the branch.
*/
int __compute_return_epc(struct pt_regs *regs)
int __compute_return_epc_for_insn(struct pt_regs *regs,
union mips_instruction insn)
{
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;
}
long epc = regs->cp0_epc;
int ret = 0;

switch (insn.i_format.opcode) {
/*
Expand All @@ -64,41 +59,50 @@ int __compute_return_epc(struct pt_regs *regs)
switch (insn.i_format.rt) {
case bltz_op:
case bltzl_op:
if ((long)regs->regs[insn.i_format.rs] < 0)
if ((long)regs->regs[insn.i_format.rs] < 0) {
epc = epc + 4 + (insn.i_format.simmediate << 2);
else
if (insn.i_format.rt == bltzl_op)
ret = BRANCH_LIKELY_TAKEN;
} else
epc += 8;
regs->cp0_epc = epc;
break;

case bgez_op:
case bgezl_op:
if ((long)regs->regs[insn.i_format.rs] >= 0)
if ((long)regs->regs[insn.i_format.rs] >= 0) {
epc = epc + 4 + (insn.i_format.simmediate << 2);
else
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:
regs->regs[31] = epc + 8;
if ((long)regs->regs[insn.i_format.rs] < 0)
if ((long)regs->regs[insn.i_format.rs] < 0) {
epc = epc + 4 + (insn.i_format.simmediate << 2);
else
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:
regs->regs[31] = epc + 8;
if ((long)regs->regs[insn.i_format.rs] >= 0)
if ((long)regs->regs[insn.i_format.rs] >= 0) {
epc = epc + 4 + (insn.i_format.simmediate << 2);
else
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;
Expand Down Expand Up @@ -133,39 +137,47 @@ int __compute_return_epc(struct pt_regs *regs)
case beq_op:
case beql_op:
if (regs->regs[insn.i_format.rs] ==
regs->regs[insn.i_format.rt])
regs->regs[insn.i_format.rt]) {
epc = epc + 4 + (insn.i_format.simmediate << 2);
else
if (insn.i_format.rt == beql_op)
ret = BRANCH_LIKELY_TAKEN;
} 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])
regs->regs[insn.i_format.rt]) {
epc = epc + 4 + (insn.i_format.simmediate << 2);
else
if (insn.i_format.rt == bnel_op)
ret = BRANCH_LIKELY_TAKEN;
} 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)
if ((long)regs->regs[insn.i_format.rs] <= 0) {
epc = epc + 4 + (insn.i_format.simmediate << 2);
else
if (insn.i_format.rt == bnel_op)
ret = BRANCH_LIKELY_TAKEN;
} 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)
if ((long)regs->regs[insn.i_format.rs] > 0) {
epc = epc + 4 + (insn.i_format.simmediate << 2);
else
if (insn.i_format.rt == bnel_op)
ret = BRANCH_LIKELY_TAKEN;
} else
epc += 8;
regs->cp0_epc = epc;
break;
Expand All @@ -187,18 +199,22 @@ int __compute_return_epc(struct pt_regs *regs)
switch (insn.i_format.rt & 3) {
case 0: /* bc1f */
case 2: /* bc1fl */
if (~fcr31 & (1 << bit))
if (~fcr31 & (1 << bit)) {
epc = epc + 4 + (insn.i_format.simmediate << 2);
else
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))
if (fcr31 & (1 << bit)) {
epc = epc + 4 + (insn.i_format.simmediate << 2);
else
if (insn.i_format.rt == 3)
ret = BRANCH_LIKELY_TAKEN;
} else
epc += 8;
regs->cp0_epc = epc;
break;
Expand Down Expand Up @@ -239,15 +255,39 @@ int __compute_return_epc(struct pt_regs *regs)
#endif
}

return 0;
return ret;

unaligned:
printk("%s: unaligned epc - sending SIGBUS.\n", current->comm);
sigill:
printk("%s: DSP branch but not DSP ASE - sending SIGBUS.\n", current->comm);
force_sig(SIGBUS, current);
return -EFAULT;
}
EXPORT_SYMBOL_GPL(__compute_return_epc_for_insn);

sigill:
printk("%s: DSP branch but not DSP ASE - sending SIGBUS.\n", current->comm);
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, current);
return -EFAULT;
}

return __compute_return_epc_for_insn(regs, insn);

unaligned:
printk("%s: unaligned epc - sending SIGBUS.\n", current->comm);
force_sig(SIGBUS, current);
return -EFAULT;

}
2 changes: 1 addition & 1 deletion trunk/arch/mips/math-emu/cp1emu.c
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
*/
emulpc = xcp->cp0_epc + 4; /* Snapshot emulation target */

if (__compute_return_epc(xcp)) {
if (__compute_return_epc(xcp) < 0) {
#ifdef CP1DBG
printk("failed to emulate branch at %p\n",
(void *) (xcp->cp0_epc));
Expand Down

0 comments on commit 32fa626

Please sign in to comment.