MIPS: MIPS16e: Support handling of delay slots.

Add logic needed to properly calculate exceptions for delay slots
when in MIPS16e mode.

Signed-off-by: Steven J. Hill <Steven.Hill@imgtec.com>
This commit is contained in:
Steven J. Hill 2013-03-25 13:45:19 -05:00 committed by Ralf Baechle
parent cd574704ec
commit 8508488fe7
3 changed files with 119 additions and 5 deletions

View File

@ -16,6 +16,7 @@ extern int __compute_return_epc(struct pt_regs *regs);
extern int __compute_return_epc_for_insn(struct pt_regs *regs, extern int __compute_return_epc_for_insn(struct pt_regs *regs,
union mips_instruction insn); union mips_instruction insn);
extern int __microMIPS_compute_return_epc(struct pt_regs *regs); extern int __microMIPS_compute_return_epc(struct pt_regs *regs);
extern int __MIPS16e_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)
@ -41,6 +42,8 @@ static inline int compute_return_epc(struct pt_regs *regs)
if (get_isa16_mode(regs->cp0_epc)) { if (get_isa16_mode(regs->cp0_epc)) {
if (cpu_has_mmips) if (cpu_has_mmips)
return __microMIPS_compute_return_epc(regs); return __microMIPS_compute_return_epc(regs);
if (cpu_has_mips16)
return __MIPS16e_compute_return_epc(regs);
return regs->cp0_epc; return regs->cp0_epc;
} }
@ -52,4 +55,19 @@ static inline int compute_return_epc(struct pt_regs *regs)
return __compute_return_epc(regs); return __compute_return_epc(regs);
} }
static inline int MIPS16e_compute_return_epc(struct pt_regs *regs,
union mips16e_instruction *inst)
{
if (likely(!delay_slot(regs))) {
if (inst->ri.opcode == MIPS16e_extend_op) {
regs->cp0_epc += 4;
return 0;
}
regs->cp0_epc += 2;
return 0;
}
return __MIPS16e_compute_return_epc(regs);
}
#endif /* _ASM_BRANCH_H */ #endif /* _ASM_BRANCH_H */

View File

@ -82,4 +82,7 @@ struct mm_decoded_insn {
int micro_mips_mode; int micro_mips_mode;
}; };
/* Recode table from 16-bit register notation to 32-bit GPR. Do NOT export!!! */
extern const int reg16to32[];
#endif /* _ASM_INST_H */ #endif /* _ASM_INST_H */

View File

@ -20,13 +20,13 @@
#include <asm/uaccess.h> #include <asm/uaccess.h>
/* /*
* Calculate and return exception PC in case of branch delay * Calculate and return exception PC in case of branch delay slot
* slot for microMIPS. It does not clear the ISA mode bit. * for microMIPS and MIPS16e. It does not clear the ISA mode bit.
*/ */
int __isa_exception_epc(struct pt_regs *regs) int __isa_exception_epc(struct pt_regs *regs)
{ {
long epc = regs->cp0_epc;
unsigned short inst; unsigned short inst;
long epc = regs->cp0_epc;
/* Calculate exception PC in branch delay slot. */ /* Calculate exception PC in branch delay slot. */
if (__get_user(inst, (u16 __user *) msk_isa16_mode(epc))) { if (__get_user(inst, (u16 __user *) msk_isa16_mode(epc))) {
@ -34,8 +34,13 @@ int __isa_exception_epc(struct pt_regs *regs)
force_sig(SIGSEGV, current); force_sig(SIGSEGV, current);
return epc; return epc;
} }
if (cpu_has_mips16) {
if (mm_insn_16bit(inst)) if (((union mips16e_instruction)inst).ri.opcode
== MIPS16e_jal_op)
epc += 4;
else
epc += 2;
} else if (mm_insn_16bit(inst))
epc += 2; epc += 2;
else else
epc += 4; epc += 4;
@ -101,6 +106,94 @@ int __microMIPS_compute_return_epc(struct pt_regs *regs)
return -EFAULT; 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, current);
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, current);
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 & 0xffff) << 2) | ((fullinst & 0x3e00000) >> 3) |
((fullinst & 0x1f0000) << 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 * __compute_return_epc_for_insn - Computes the return address and do emulate
* branch simulation, if required. * branch simulation, if required.