aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorLeonid Yegoshin <Leonid.Yegoshin@imgtec.com>2013-03-25 14:08:40 -0400
committerRalf Baechle <ralf@linux-mips.org>2013-05-09 11:55:18 -0400
commitfb6883e5809c08e43de23581759af4570ca91b0f (patch)
tree2bf479748d969f85c79cb8880a1573b2ca7c61ee /arch
parent2a0b24f56c2492b932f1aed617ae80fb23500d21 (diff)
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>
Diffstat (limited to 'arch')
-rw-r--r--arch/mips/include/asm/branch.h22
-rw-r--r--arch/mips/kernel/branch.c85
2 files changed, 102 insertions, 5 deletions
diff --git a/arch/mips/include/asm/branch.h b/arch/mips/include/asm/branch.h
index 888766ae1f85..40bb9ebcc7aa 100644
--- a/arch/mips/include/asm/branch.h
+++ b/arch/mips/include/asm/branch.h
@@ -11,6 +11,13 @@
11#include <asm/ptrace.h> 11#include <asm/ptrace.h>
12#include <asm/inst.h> 12#include <asm/inst.h>
13 13
14extern int __isa_exception_epc(struct pt_regs *regs);
15extern int __compute_return_epc(struct pt_regs *regs);
16extern int __compute_return_epc_for_insn(struct pt_regs *regs,
17 union mips_instruction insn);
18extern int __microMIPS_compute_return_epc(struct pt_regs *regs);
19
20
14static inline int delay_slot(struct pt_regs *regs) 21static inline int delay_slot(struct pt_regs *regs)
15{ 22{
16 return regs->cp0_cause & CAUSEF_BD; 23 return regs->cp0_cause & CAUSEF_BD;
@@ -18,20 +25,25 @@ static inline int delay_slot(struct pt_regs *regs)
18 25
19static inline unsigned long exception_epc(struct pt_regs *regs) 26static inline unsigned long exception_epc(struct pt_regs *regs)
20{ 27{
21 if (!delay_slot(regs)) 28 if (likely(!delay_slot(regs)))
22 return regs->cp0_epc; 29 return regs->cp0_epc;
23 30
31 if (get_isa16_mode(regs->cp0_epc))
32 return __isa_exception_epc(regs);
33
24 return regs->cp0_epc + 4; 34 return regs->cp0_epc + 4;
25} 35}
26 36
27#define BRANCH_LIKELY_TAKEN 0x0001 37#define BRANCH_LIKELY_TAKEN 0x0001
28 38
29extern int __compute_return_epc(struct pt_regs *regs);
30extern int __compute_return_epc_for_insn(struct pt_regs *regs,
31 union mips_instruction insn);
32
33static inline int compute_return_epc(struct pt_regs *regs) 39static inline int compute_return_epc(struct pt_regs *regs)
34{ 40{
41 if (get_isa16_mode(regs->cp0_epc)) {
42 if (cpu_has_mmips)
43 return __microMIPS_compute_return_epc(regs);
44 return regs->cp0_epc;
45 }
46
35 if (!delay_slot(regs)) { 47 if (!delay_slot(regs)) {
36 regs->cp0_epc += 4; 48 regs->cp0_epc += 4;
37 return 0; 49 return 0;
diff --git a/arch/mips/kernel/branch.c b/arch/mips/kernel/branch.c
index 83ffe950f710..a03836b5b683 100644
--- a/arch/mips/kernel/branch.c
+++ b/arch/mips/kernel/branch.c
@@ -14,10 +14,93 @@
14#include <asm/cpu.h> 14#include <asm/cpu.h>
15#include <asm/cpu-features.h> 15#include <asm/cpu-features.h>
16#include <asm/fpu.h> 16#include <asm/fpu.h>
17#include <asm/fpu_emulator.h>
17#include <asm/inst.h> 18#include <asm/inst.h>
18#include <asm/ptrace.h> 19#include <asm/ptrace.h>
19#include <asm/uaccess.h> 20#include <asm/uaccess.h>
20 21
22/*
23 * Calculate and return exception PC in case of branch delay
24 * slot for microMIPS. It does not clear the ISA mode bit.
25 */
26int __isa_exception_epc(struct pt_regs *regs)
27{
28 long epc = regs->cp0_epc;
29 unsigned short inst;
30
31 /* Calculate exception PC in branch delay slot. */
32 if (__get_user(inst, (u16 __user *) msk_isa16_mode(epc))) {
33 /* This should never happen because delay slot was checked. */
34 force_sig(SIGSEGV, current);
35 return epc;
36 }
37
38 if (mm_insn_16bit(inst))
39 epc += 2;
40 else
41 epc += 4;
42
43 return epc;
44}
45
46/*
47 * Compute return address and emulate branch in microMIPS mode after an
48 * exception only. It does not handle compact branches/jumps and cannot
49 * be used in interrupt context. (Compact branches/jumps do not cause
50 * exceptions.)
51 */
52int __microMIPS_compute_return_epc(struct pt_regs *regs)
53{
54 u16 __user *pc16;
55 u16 halfword;
56 unsigned int word;
57 unsigned long contpc;
58 struct mm_decoded_insn mminsn = { 0 };
59
60 mminsn.micro_mips_mode = 1;
61
62 /* This load never faults. */
63 pc16 = (unsigned short __user *)msk_isa16_mode(regs->cp0_epc);
64 __get_user(halfword, pc16);
65 pc16++;
66 contpc = regs->cp0_epc + 2;
67 word = ((unsigned int)halfword << 16);
68 mminsn.pc_inc = 2;
69
70 if (!mm_insn_16bit(halfword)) {
71 __get_user(halfword, pc16);
72 pc16++;
73 contpc = regs->cp0_epc + 4;
74 mminsn.pc_inc = 4;
75 word |= halfword;
76 }
77 mminsn.insn = word;
78
79 if (get_user(halfword, pc16))
80 goto sigsegv;
81 mminsn.next_pc_inc = 2;
82 word = ((unsigned int)halfword << 16);
83
84 if (!mm_insn_16bit(halfword)) {
85 pc16++;
86 if (get_user(halfword, pc16))
87 goto sigsegv;
88 mminsn.next_pc_inc = 4;
89 word |= halfword;
90 }
91 mminsn.next_insn = word;
92
93 mm_isBranchInstr(regs, mminsn, &contpc);
94
95 regs->cp0_epc = contpc;
96
97 return 0;
98
99sigsegv:
100 force_sig(SIGSEGV, current);
101 return -EFAULT;
102}
103
21/** 104/**
22 * __compute_return_epc_for_insn - Computes the return address and do emulate 105 * __compute_return_epc_for_insn - Computes the return address and do emulate
23 * branch simulation, if required. 106 * branch simulation, if required.
@@ -129,6 +212,8 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
129 epc <<= 28; 212 epc <<= 28;
130 epc |= (insn.j_format.target << 2); 213 epc |= (insn.j_format.target << 2);
131 regs->cp0_epc = epc; 214 regs->cp0_epc = epc;
215 if (insn.i_format.opcode == jalx_op)
216 set_isa16_mode(regs->cp0_epc);
132 break; 217 break;
133 218
134 /* 219 /*