aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
Diffstat (limited to 'arch')
-rw-r--r--arch/mips/include/asm/branch.h18
-rw-r--r--arch/mips/include/asm/inst.h3
-rw-r--r--arch/mips/kernel/branch.c103
3 files changed, 119 insertions, 5 deletions
diff --git a/arch/mips/include/asm/branch.h b/arch/mips/include/asm/branch.h
index 40bb9ebcc7aa..e28a3e0eb3cb 100644
--- a/arch/mips/include/asm/branch.h
+++ b/arch/mips/include/asm/branch.h
@@ -16,6 +16,7 @@ extern int __compute_return_epc(struct pt_regs *regs);
16extern int __compute_return_epc_for_insn(struct pt_regs *regs, 16extern int __compute_return_epc_for_insn(struct pt_regs *regs,
17 union mips_instruction insn); 17 union mips_instruction insn);
18extern int __microMIPS_compute_return_epc(struct pt_regs *regs); 18extern int __microMIPS_compute_return_epc(struct pt_regs *regs);
19extern int __MIPS16e_compute_return_epc(struct pt_regs *regs);
19 20
20 21
21static inline int delay_slot(struct pt_regs *regs) 22static inline int delay_slot(struct pt_regs *regs)
@@ -41,6 +42,8 @@ static inline int compute_return_epc(struct pt_regs *regs)
41 if (get_isa16_mode(regs->cp0_epc)) { 42 if (get_isa16_mode(regs->cp0_epc)) {
42 if (cpu_has_mmips) 43 if (cpu_has_mmips)
43 return __microMIPS_compute_return_epc(regs); 44 return __microMIPS_compute_return_epc(regs);
45 if (cpu_has_mips16)
46 return __MIPS16e_compute_return_epc(regs);
44 return regs->cp0_epc; 47 return regs->cp0_epc;
45 } 48 }
46 49
@@ -52,4 +55,19 @@ static inline int compute_return_epc(struct pt_regs *regs)
52 return __compute_return_epc(regs); 55 return __compute_return_epc(regs);
53} 56}
54 57
58static inline int MIPS16e_compute_return_epc(struct pt_regs *regs,
59 union mips16e_instruction *inst)
60{
61 if (likely(!delay_slot(regs))) {
62 if (inst->ri.opcode == MIPS16e_extend_op) {
63 regs->cp0_epc += 4;
64 return 0;
65 }
66 regs->cp0_epc += 2;
67 return 0;
68 }
69
70 return __MIPS16e_compute_return_epc(regs);
71}
72
55#endif /* _ASM_BRANCH_H */ 73#endif /* _ASM_BRANCH_H */
diff --git a/arch/mips/include/asm/inst.h b/arch/mips/include/asm/inst.h
index b27091e676c1..22912f78401c 100644
--- a/arch/mips/include/asm/inst.h
+++ b/arch/mips/include/asm/inst.h
@@ -82,4 +82,7 @@ struct mm_decoded_insn {
82 int micro_mips_mode; 82 int micro_mips_mode;
83}; 83};
84 84
85/* Recode table from 16-bit register notation to 32-bit GPR. Do NOT export!!! */
86extern const int reg16to32[];
87
85#endif /* _ASM_INST_H */ 88#endif /* _ASM_INST_H */
diff --git a/arch/mips/kernel/branch.c b/arch/mips/kernel/branch.c
index a03836b5b683..46c2ad0703a0 100644
--- a/arch/mips/kernel/branch.c
+++ b/arch/mips/kernel/branch.c
@@ -20,13 +20,13 @@
20#include <asm/uaccess.h> 20#include <asm/uaccess.h>
21 21
22/* 22/*
23 * Calculate and return exception PC in case of branch delay 23 * Calculate and return exception PC in case of branch delay slot
24 * slot for microMIPS. It does not clear the ISA mode bit. 24 * for microMIPS and MIPS16e. It does not clear the ISA mode bit.
25 */ 25 */
26int __isa_exception_epc(struct pt_regs *regs) 26int __isa_exception_epc(struct pt_regs *regs)
27{ 27{
28 long epc = regs->cp0_epc;
29 unsigned short inst; 28 unsigned short inst;
29 long epc = regs->cp0_epc;
30 30
31 /* Calculate exception PC in branch delay slot. */ 31 /* Calculate exception PC in branch delay slot. */
32 if (__get_user(inst, (u16 __user *) msk_isa16_mode(epc))) { 32 if (__get_user(inst, (u16 __user *) msk_isa16_mode(epc))) {
@@ -34,8 +34,13 @@ int __isa_exception_epc(struct pt_regs *regs)
34 force_sig(SIGSEGV, current); 34 force_sig(SIGSEGV, current);
35 return epc; 35 return epc;
36 } 36 }
37 37 if (cpu_has_mips16) {
38 if (mm_insn_16bit(inst)) 38 if (((union mips16e_instruction)inst).ri.opcode
39 == MIPS16e_jal_op)
40 epc += 4;
41 else
42 epc += 2;
43 } else if (mm_insn_16bit(inst))
39 epc += 2; 44 epc += 2;
40 else 45 else
41 epc += 4; 46 epc += 4;
@@ -101,6 +106,94 @@ sigsegv:
101 return -EFAULT; 106 return -EFAULT;
102} 107}
103 108
109/*
110 * Compute return address and emulate branch in MIPS16e mode after an
111 * exception only. It does not handle compact branches/jumps and cannot
112 * be used in interrupt context. (Compact branches/jumps do not cause
113 * exceptions.)
114 */
115int __MIPS16e_compute_return_epc(struct pt_regs *regs)
116{
117 u16 __user *addr;
118 union mips16e_instruction inst;
119 u16 inst2;
120 u32 fullinst;
121 long epc;
122
123 epc = regs->cp0_epc;
124
125 /* Read the instruction. */
126 addr = (u16 __user *)msk_isa16_mode(epc);
127 if (__get_user(inst.full, addr)) {
128 force_sig(SIGSEGV, current);
129 return -EFAULT;
130 }
131
132 switch (inst.ri.opcode) {
133 case MIPS16e_extend_op:
134 regs->cp0_epc += 4;
135 return 0;
136
137 /*
138 * JAL and JALX in MIPS16e mode
139 */
140 case MIPS16e_jal_op:
141 addr += 1;
142 if (__get_user(inst2, addr)) {
143 force_sig(SIGSEGV, current);
144 return -EFAULT;
145 }
146 fullinst = ((unsigned)inst.full << 16) | inst2;
147 regs->regs[31] = epc + 6;
148 epc += 4;
149 epc >>= 28;
150 epc <<= 28;
151 /*
152 * JAL:5 X:1 TARGET[20-16]:5 TARGET[25:21]:5 TARGET[15:0]:16
153 *
154 * ......TARGET[15:0].................TARGET[20:16]...........
155 * ......TARGET[25:21]
156 */
157 epc |=
158 ((fullinst & 0xffff) << 2) | ((fullinst & 0x3e00000) >> 3) |
159 ((fullinst & 0x1f0000) << 7);
160 if (!inst.jal.x)
161 set_isa16_mode(epc); /* Set ISA mode bit. */
162 regs->cp0_epc = epc;
163 return 0;
164
165 /*
166 * J(AL)R(C)
167 */
168 case MIPS16e_rr_op:
169 if (inst.rr.func == MIPS16e_jr_func) {
170
171 if (inst.rr.ra)
172 regs->cp0_epc = regs->regs[31];
173 else
174 regs->cp0_epc =
175 regs->regs[reg16to32[inst.rr.rx]];
176
177 if (inst.rr.l) {
178 if (inst.rr.nd)
179 regs->regs[31] = epc + 2;
180 else
181 regs->regs[31] = epc + 4;
182 }
183 return 0;
184 }
185 break;
186 }
187
188 /*
189 * All other cases have no branch delay slot and are 16-bits.
190 * Branches do not cause an exception.
191 */
192 regs->cp0_epc += 2;
193
194 return 0;
195}
196
104/** 197/**
105 * __compute_return_epc_for_insn - Computes the return address and do emulate 198 * __compute_return_epc_for_insn - Computes the return address and do emulate
106 * branch simulation, if required. 199 * branch simulation, if required.