diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/mips/include/asm/branch.h | 18 | ||||
-rw-r--r-- | arch/mips/include/asm/inst.h | 3 | ||||
-rw-r--r-- | arch/mips/kernel/branch.c | 103 |
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); | |||
16 | extern int __compute_return_epc_for_insn(struct pt_regs *regs, | 16 | extern int __compute_return_epc_for_insn(struct pt_regs *regs, |
17 | union mips_instruction insn); | 17 | union mips_instruction insn); |
18 | extern int __microMIPS_compute_return_epc(struct pt_regs *regs); | 18 | extern int __microMIPS_compute_return_epc(struct pt_regs *regs); |
19 | extern int __MIPS16e_compute_return_epc(struct pt_regs *regs); | ||
19 | 20 | ||
20 | 21 | ||
21 | static inline int delay_slot(struct pt_regs *regs) | 22 | static 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 | ||
58 | static 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!!! */ | ||
86 | extern 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 | */ |
26 | int __isa_exception_epc(struct pt_regs *regs) | 26 | int __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 | */ | ||
115 | int __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. |