MIPS: microMIPS: Support handling of delay slots.
Add logic needed to properly calculate exceptions for delay slots when in microMIPS mode. Signed-off-by: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com> Signed-off-by: Steven J. Hill <Steven.Hill@imgtec.com>
This commit is contained in:
parent
2a0b24f56c
commit
fb6883e580
@ -11,6 +11,13 @@
|
|||||||
#include <asm/ptrace.h>
|
#include <asm/ptrace.h>
|
||||||
#include <asm/inst.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)
|
static inline int delay_slot(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
return regs->cp0_cause & CAUSEF_BD;
|
return regs->cp0_cause & CAUSEF_BD;
|
||||||
@ -18,20 +25,25 @@ static inline int delay_slot(struct pt_regs *regs)
|
|||||||
|
|
||||||
static inline unsigned long exception_epc(struct pt_regs *regs)
|
static inline unsigned long exception_epc(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
if (!delay_slot(regs))
|
if (likely(!delay_slot(regs)))
|
||||||
return regs->cp0_epc;
|
return regs->cp0_epc;
|
||||||
|
|
||||||
|
if (get_isa16_mode(regs->cp0_epc))
|
||||||
|
return __isa_exception_epc(regs);
|
||||||
|
|
||||||
return regs->cp0_epc + 4;
|
return regs->cp0_epc + 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define BRANCH_LIKELY_TAKEN 0x0001
|
#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)
|
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)) {
|
if (!delay_slot(regs)) {
|
||||||
regs->cp0_epc += 4;
|
regs->cp0_epc += 4;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -14,10 +14,93 @@
|
|||||||
#include <asm/cpu.h>
|
#include <asm/cpu.h>
|
||||||
#include <asm/cpu-features.h>
|
#include <asm/cpu-features.h>
|
||||||
#include <asm/fpu.h>
|
#include <asm/fpu.h>
|
||||||
|
#include <asm/fpu_emulator.h>
|
||||||
#include <asm/inst.h>
|
#include <asm/inst.h>
|
||||||
#include <asm/ptrace.h>
|
#include <asm/ptrace.h>
|
||||||
#include <asm/uaccess.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
|
* __compute_return_epc_for_insn - Computes the return address and do emulate
|
||||||
* branch simulation, if required.
|
* branch simulation, if required.
|
||||||
@ -129,6 +212,8 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
|
|||||||
epc <<= 28;
|
epc <<= 28;
|
||||||
epc |= (insn.j_format.target << 2);
|
epc |= (insn.j_format.target << 2);
|
||||||
regs->cp0_epc = epc;
|
regs->cp0_epc = epc;
|
||||||
|
if (insn.i_format.opcode == jalx_op)
|
||||||
|
set_isa16_mode(regs->cp0_epc);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user