Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * This file is subject to the terms and conditions of the GNU General Public | |
3 | * License. See the file "COPYING" in the main directory of this archive | |
4 | * for more details. | |
5 | * | |
6 | * Copyright (C) 1996, 97, 2000, 2001 by Ralf Baechle | |
7 | * Copyright (C) 2001 MIPS Technologies, Inc. | |
8 | */ | |
9 | #include <linux/kernel.h> | |
10 | #include <linux/sched.h> | |
11 | #include <linux/signal.h> | |
d8d4e3ae | 12 | #include <linux/module.h> |
1da177e4 LT |
13 | #include <asm/branch.h> |
14 | #include <asm/cpu.h> | |
15 | #include <asm/cpu-features.h> | |
1d74f6bc | 16 | #include <asm/fpu.h> |
1da177e4 LT |
17 | #include <asm/inst.h> |
18 | #include <asm/ptrace.h> | |
19 | #include <asm/uaccess.h> | |
20 | ||
d8d4e3ae MS |
21 | /** |
22 | * __compute_return_epc_for_insn - Computes the return address and do emulate | |
23 | * branch simulation, if required. | |
24 | * | |
25 | * @regs: Pointer to pt_regs | |
26 | * @insn: branch instruction to decode | |
27 | * @returns: -EFAULT on error and forces SIGBUS, and on success | |
28 | * returns 0 or BRANCH_LIKELY_TAKEN as appropriate after | |
29 | * evaluating the branch. | |
1da177e4 | 30 | */ |
d8d4e3ae MS |
31 | int __compute_return_epc_for_insn(struct pt_regs *regs, |
32 | union mips_instruction insn) | |
1da177e4 | 33 | { |
5e0373b8 | 34 | unsigned int bit, fcr31, dspcontrol; |
d8d4e3ae MS |
35 | long epc = regs->cp0_epc; |
36 | int ret = 0; | |
1da177e4 | 37 | |
1da177e4 LT |
38 | switch (insn.i_format.opcode) { |
39 | /* | |
40 | * jr and jalr are in r_format format. | |
41 | */ | |
42 | case spec_op: | |
43 | switch (insn.r_format.func) { | |
44 | case jalr_op: | |
45 | regs->regs[insn.r_format.rd] = epc + 8; | |
46 | /* Fall through */ | |
47 | case jr_op: | |
48 | regs->cp0_epc = regs->regs[insn.r_format.rs]; | |
49 | break; | |
50 | } | |
51 | break; | |
52 | ||
53 | /* | |
54 | * This group contains: | |
55 | * bltz_op, bgez_op, bltzl_op, bgezl_op, | |
56 | * bltzal_op, bgezal_op, bltzall_op, bgezall_op. | |
57 | */ | |
58 | case bcond_op: | |
59 | switch (insn.i_format.rt) { | |
60 | case bltz_op: | |
61 | case bltzl_op: | |
d8d4e3ae | 62 | if ((long)regs->regs[insn.i_format.rs] < 0) { |
1da177e4 | 63 | epc = epc + 4 + (insn.i_format.simmediate << 2); |
d8d4e3ae MS |
64 | if (insn.i_format.rt == bltzl_op) |
65 | ret = BRANCH_LIKELY_TAKEN; | |
66 | } else | |
1da177e4 LT |
67 | epc += 8; |
68 | regs->cp0_epc = epc; | |
69 | break; | |
70 | ||
71 | case bgez_op: | |
72 | case bgezl_op: | |
d8d4e3ae | 73 | if ((long)regs->regs[insn.i_format.rs] >= 0) { |
1da177e4 | 74 | epc = epc + 4 + (insn.i_format.simmediate << 2); |
d8d4e3ae MS |
75 | if (insn.i_format.rt == bgezl_op) |
76 | ret = BRANCH_LIKELY_TAKEN; | |
77 | } else | |
1da177e4 LT |
78 | epc += 8; |
79 | regs->cp0_epc = epc; | |
80 | break; | |
81 | ||
82 | case bltzal_op: | |
83 | case bltzall_op: | |
84 | regs->regs[31] = epc + 8; | |
d8d4e3ae | 85 | if ((long)regs->regs[insn.i_format.rs] < 0) { |
1da177e4 | 86 | epc = epc + 4 + (insn.i_format.simmediate << 2); |
d8d4e3ae MS |
87 | if (insn.i_format.rt == bltzall_op) |
88 | ret = BRANCH_LIKELY_TAKEN; | |
89 | } else | |
1da177e4 LT |
90 | epc += 8; |
91 | regs->cp0_epc = epc; | |
92 | break; | |
93 | ||
94 | case bgezal_op: | |
95 | case bgezall_op: | |
96 | regs->regs[31] = epc + 8; | |
d8d4e3ae | 97 | if ((long)regs->regs[insn.i_format.rs] >= 0) { |
1da177e4 | 98 | epc = epc + 4 + (insn.i_format.simmediate << 2); |
d8d4e3ae MS |
99 | if (insn.i_format.rt == bgezall_op) |
100 | ret = BRANCH_LIKELY_TAKEN; | |
101 | } else | |
1da177e4 LT |
102 | epc += 8; |
103 | regs->cp0_epc = epc; | |
104 | break; | |
d8d4e3ae | 105 | |
e50c0a8f RB |
106 | case bposge32_op: |
107 | if (!cpu_has_dsp) | |
108 | goto sigill; | |
109 | ||
110 | dspcontrol = rddsp(0x01); | |
111 | ||
112 | if (dspcontrol >= 32) { | |
113 | epc = epc + 4 + (insn.i_format.simmediate << 2); | |
114 | } else | |
115 | epc += 8; | |
116 | regs->cp0_epc = epc; | |
117 | break; | |
1da177e4 LT |
118 | } |
119 | break; | |
120 | ||
121 | /* | |
122 | * These are unconditional and in j_format. | |
123 | */ | |
124 | case jal_op: | |
125 | regs->regs[31] = regs->cp0_epc + 8; | |
126 | case j_op: | |
127 | epc += 4; | |
128 | epc >>= 28; | |
129 | epc <<= 28; | |
130 | epc |= (insn.j_format.target << 2); | |
131 | regs->cp0_epc = epc; | |
132 | break; | |
133 | ||
134 | /* | |
135 | * These are conditional and in i_format. | |
136 | */ | |
137 | case beq_op: | |
138 | case beql_op: | |
139 | if (regs->regs[insn.i_format.rs] == | |
d8d4e3ae | 140 | regs->regs[insn.i_format.rt]) { |
1da177e4 | 141 | epc = epc + 4 + (insn.i_format.simmediate << 2); |
d8d4e3ae MS |
142 | if (insn.i_format.rt == beql_op) |
143 | ret = BRANCH_LIKELY_TAKEN; | |
144 | } else | |
1da177e4 LT |
145 | epc += 8; |
146 | regs->cp0_epc = epc; | |
147 | break; | |
148 | ||
149 | case bne_op: | |
150 | case bnel_op: | |
151 | if (regs->regs[insn.i_format.rs] != | |
d8d4e3ae | 152 | regs->regs[insn.i_format.rt]) { |
1da177e4 | 153 | epc = epc + 4 + (insn.i_format.simmediate << 2); |
d8d4e3ae MS |
154 | if (insn.i_format.rt == bnel_op) |
155 | ret = BRANCH_LIKELY_TAKEN; | |
156 | } else | |
1da177e4 LT |
157 | epc += 8; |
158 | regs->cp0_epc = epc; | |
159 | break; | |
160 | ||
161 | case blez_op: /* not really i_format */ | |
162 | case blezl_op: | |
163 | /* rt field assumed to be zero */ | |
d8d4e3ae | 164 | if ((long)regs->regs[insn.i_format.rs] <= 0) { |
1da177e4 | 165 | epc = epc + 4 + (insn.i_format.simmediate << 2); |
d8d4e3ae MS |
166 | if (insn.i_format.rt == bnel_op) |
167 | ret = BRANCH_LIKELY_TAKEN; | |
168 | } else | |
1da177e4 LT |
169 | epc += 8; |
170 | regs->cp0_epc = epc; | |
171 | break; | |
172 | ||
173 | case bgtz_op: | |
174 | case bgtzl_op: | |
175 | /* rt field assumed to be zero */ | |
d8d4e3ae | 176 | if ((long)regs->regs[insn.i_format.rs] > 0) { |
1da177e4 | 177 | epc = epc + 4 + (insn.i_format.simmediate << 2); |
d8d4e3ae MS |
178 | if (insn.i_format.rt == bnel_op) |
179 | ret = BRANCH_LIKELY_TAKEN; | |
180 | } else | |
1da177e4 LT |
181 | epc += 8; |
182 | regs->cp0_epc = epc; | |
183 | break; | |
184 | ||
185 | /* | |
186 | * And now the FPA/cp1 branch instructions. | |
187 | */ | |
188 | case cop1_op: | |
1d74f6bc RB |
189 | preempt_disable(); |
190 | if (is_fpu_owner()) | |
1da177e4 | 191 | asm volatile("cfc1\t%0,$31" : "=r" (fcr31)); |
1d74f6bc | 192 | else |
eae89076 | 193 | fcr31 = current->thread.fpu.fcr31; |
1d74f6bc RB |
194 | preempt_enable(); |
195 | ||
1da177e4 LT |
196 | bit = (insn.i_format.rt >> 2); |
197 | bit += (bit != 0); | |
198 | bit += 23; | |
ee1cca1b | 199 | switch (insn.i_format.rt & 3) { |
1da177e4 LT |
200 | case 0: /* bc1f */ |
201 | case 2: /* bc1fl */ | |
d8d4e3ae | 202 | if (~fcr31 & (1 << bit)) { |
1da177e4 | 203 | epc = epc + 4 + (insn.i_format.simmediate << 2); |
d8d4e3ae MS |
204 | if (insn.i_format.rt == 2) |
205 | ret = BRANCH_LIKELY_TAKEN; | |
206 | } else | |
1da177e4 LT |
207 | epc += 8; |
208 | regs->cp0_epc = epc; | |
209 | break; | |
210 | ||
211 | case 1: /* bc1t */ | |
212 | case 3: /* bc1tl */ | |
d8d4e3ae | 213 | if (fcr31 & (1 << bit)) { |
1da177e4 | 214 | epc = epc + 4 + (insn.i_format.simmediate << 2); |
d8d4e3ae MS |
215 | if (insn.i_format.rt == 3) |
216 | ret = BRANCH_LIKELY_TAKEN; | |
217 | } else | |
1da177e4 LT |
218 | epc += 8; |
219 | regs->cp0_epc = epc; | |
220 | break; | |
221 | } | |
222 | break; | |
126336f0 DD |
223 | #ifdef CONFIG_CPU_CAVIUM_OCTEON |
224 | case lwc2_op: /* This is bbit0 on Octeon */ | |
225 | if ((regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt)) | |
226 | == 0) | |
227 | epc = epc + 4 + (insn.i_format.simmediate << 2); | |
228 | else | |
229 | epc += 8; | |
230 | regs->cp0_epc = epc; | |
231 | break; | |
232 | case ldc2_op: /* This is bbit032 on Octeon */ | |
233 | if ((regs->regs[insn.i_format.rs] & | |
234 | (1ull<<(insn.i_format.rt+32))) == 0) | |
235 | epc = epc + 4 + (insn.i_format.simmediate << 2); | |
236 | else | |
237 | epc += 8; | |
238 | regs->cp0_epc = epc; | |
239 | break; | |
240 | case swc2_op: /* This is bbit1 on Octeon */ | |
241 | if (regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt)) | |
242 | epc = epc + 4 + (insn.i_format.simmediate << 2); | |
243 | else | |
244 | epc += 8; | |
245 | regs->cp0_epc = epc; | |
246 | break; | |
247 | case sdc2_op: /* This is bbit132 on Octeon */ | |
248 | if (regs->regs[insn.i_format.rs] & | |
249 | (1ull<<(insn.i_format.rt+32))) | |
250 | epc = epc + 4 + (insn.i_format.simmediate << 2); | |
251 | else | |
252 | epc += 8; | |
253 | regs->cp0_epc = epc; | |
254 | break; | |
255 | #endif | |
1da177e4 LT |
256 | } |
257 | ||
d8d4e3ae | 258 | return ret; |
1da177e4 | 259 | |
d8d4e3ae MS |
260 | sigill: |
261 | printk("%s: DSP branch but not DSP ASE - sending SIGBUS.\n", current->comm); | |
1da177e4 LT |
262 | force_sig(SIGBUS, current); |
263 | return -EFAULT; | |
d8d4e3ae MS |
264 | } |
265 | EXPORT_SYMBOL_GPL(__compute_return_epc_for_insn); | |
e50c0a8f | 266 | |
d8d4e3ae MS |
267 | int __compute_return_epc(struct pt_regs *regs) |
268 | { | |
269 | unsigned int __user *addr; | |
270 | long epc; | |
271 | union mips_instruction insn; | |
272 | ||
273 | epc = regs->cp0_epc; | |
274 | if (epc & 3) | |
275 | goto unaligned; | |
276 | ||
277 | /* | |
278 | * Read the instruction | |
279 | */ | |
280 | addr = (unsigned int __user *) epc; | |
281 | if (__get_user(insn.word, addr)) { | |
282 | force_sig(SIGSEGV, current); | |
283 | return -EFAULT; | |
284 | } | |
285 | ||
286 | return __compute_return_epc_for_insn(regs, insn); | |
287 | ||
288 | unaligned: | |
289 | printk("%s: unaligned epc - sending SIGBUS.\n", current->comm); | |
e50c0a8f RB |
290 | force_sig(SIGBUS, current); |
291 | return -EFAULT; | |
d8d4e3ae | 292 | |
1da177e4 | 293 | } |