Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 375206
b: refs/heads/master
c: fb6883e
h: refs/heads/master
v: v3
  • Loading branch information
Leonid Yegoshin authored and Ralf Baechle committed May 9, 2013
1 parent 9836878 commit 9e72fa3
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 6 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: 2a0b24f56c2492b932f1aed617ae80fb23500d21
refs/heads/master: fb6883e5809c08e43de23581759af4570ca91b0f
22 changes: 17 additions & 5 deletions trunk/arch/mips/include/asm/branch.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,39 @@
#include <asm/ptrace.h>
#include <asm/inst.h>

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


static inline int delay_slot(struct pt_regs *regs)
{
return regs->cp0_cause & CAUSEF_BD;
}

static inline unsigned long exception_epc(struct pt_regs *regs)
{
if (!delay_slot(regs))
if (likely(!delay_slot(regs)))
return regs->cp0_epc;

if (get_isa16_mode(regs->cp0_epc))
return __isa_exception_epc(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)
{
if (get_isa16_mode(regs->cp0_epc)) {
if (cpu_has_mmips)
return __microMIPS_compute_return_epc(regs);
return regs->cp0_epc;
}

if (!delay_slot(regs)) {
regs->cp0_epc += 4;
return 0;
Expand Down
85 changes: 85 additions & 0 deletions trunk/arch/mips/kernel/branch.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,93 @@
#include <asm/cpu.h>
#include <asm/cpu-features.h>
#include <asm/fpu.h>
#include <asm/fpu_emulator.h>
#include <asm/inst.h>
#include <asm/ptrace.h>
#include <asm/uaccess.h>

/*
* Calculate and return exception PC in case of branch delay
* slot for microMIPS. It does not clear the ISA mode bit.
*/
int __isa_exception_epc(struct pt_regs *regs)
{
long epc = regs->cp0_epc;
unsigned short inst;

/* 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, current);
return epc;
}

if (mm_insn_16bit(inst))
epc += 2;
else
epc += 4;

return epc;
}

/*
* 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, current);
return -EFAULT;
}

/**
* __compute_return_epc_for_insn - Computes the return address and do emulate
* branch simulation, if required.
Expand Down Expand Up @@ -129,6 +212,8 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
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;

/*
Expand Down

0 comments on commit 9e72fa3

Please sign in to comment.