Commit d8d4e3ae0b5c179c0bfd3f0af5b352d13bea9cfa
Committed by
Ralf Baechle
1 parent
9233c1ee71
Exists in
master
and in
6 other branches
MIPS Kprobes: Refactor branch emulation
This patch refactors MIPS branch emulation code so as to allow skipping delay slot instruction in case of branch likely instructions when branch is not taken. This is useful for keeping the code common for use cases like kprobes where one would like to handle the branch instructions keeping the delay slot instuction also in picture for branch likely instructions. Also allow emulation when instruction to be decoded is not at pt_regs->cp0_epc as in case of kprobes where pt_regs->cp0_epc points to the breakpoint instruction. The patch also exports the function for modules. Signed-off-by: Maneesh Soni <manesoni@cisco.com> Signed-off-by: Victor Kamensky <kamensky@cisco.com> Cc: David Daney <david.daney@cavium.com> Cc: ananth@in.ibm.com Cc: linux-kernel@vger.kernel.org Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/2913/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Showing 3 changed files with 90 additions and 45 deletions Side-by-side Diff
arch/mips/include/asm/branch.h
... | ... | @@ -9,6 +9,7 @@ |
9 | 9 | #define _ASM_BRANCH_H |
10 | 10 | |
11 | 11 | #include <asm/ptrace.h> |
12 | +#include <asm/inst.h> | |
12 | 13 | |
13 | 14 | static inline int delay_slot(struct pt_regs *regs) |
14 | 15 | { |
15 | 16 | |
... | ... | @@ -23,7 +24,11 @@ |
23 | 24 | return regs->cp0_epc + 4; |
24 | 25 | } |
25 | 26 | |
27 | +#define BRANCH_LIKELY_TAKEN 0x0001 | |
28 | + | |
26 | 29 | extern int __compute_return_epc(struct pt_regs *regs); |
30 | +extern int __compute_return_epc_for_insn(struct pt_regs *regs, | |
31 | + union mips_instruction insn); | |
27 | 32 | |
28 | 33 | static inline int compute_return_epc(struct pt_regs *regs) |
29 | 34 | { |
arch/mips/kernel/branch.c
... | ... | @@ -9,6 +9,7 @@ |
9 | 9 | #include <linux/kernel.h> |
10 | 10 | #include <linux/sched.h> |
11 | 11 | #include <linux/signal.h> |
12 | +#include <linux/module.h> | |
12 | 13 | #include <asm/branch.h> |
13 | 14 | #include <asm/cpu.h> |
14 | 15 | #include <asm/cpu-features.h> |
15 | 16 | |
16 | 17 | |
17 | 18 | |
18 | 19 | |
... | ... | @@ -17,29 +18,23 @@ |
17 | 18 | #include <asm/ptrace.h> |
18 | 19 | #include <asm/uaccess.h> |
19 | 20 | |
20 | -/* | |
21 | - * Compute the return address and do emulate branch simulation, if required. | |
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. | |
22 | 30 | */ |
23 | -int __compute_return_epc(struct pt_regs *regs) | |
31 | +int __compute_return_epc_for_insn(struct pt_regs *regs, | |
32 | + union mips_instruction insn) | |
24 | 33 | { |
25 | - unsigned int __user *addr; | |
26 | 34 | unsigned int bit, fcr31, dspcontrol; |
27 | - long epc; | |
28 | - union mips_instruction insn; | |
35 | + long epc = regs->cp0_epc; | |
36 | + int ret = 0; | |
29 | 37 | |
30 | - epc = regs->cp0_epc; | |
31 | - if (epc & 3) | |
32 | - goto unaligned; | |
33 | - | |
34 | - /* | |
35 | - * Read the instruction | |
36 | - */ | |
37 | - addr = (unsigned int __user *) epc; | |
38 | - if (__get_user(insn.word, addr)) { | |
39 | - force_sig(SIGSEGV, current); | |
40 | - return -EFAULT; | |
41 | - } | |
42 | - | |
43 | 38 | switch (insn.i_format.opcode) { |
44 | 39 | /* |
45 | 40 | * jr and jalr are in r_format format. |
46 | 41 | |
47 | 42 | |
48 | 43 | |
... | ... | @@ -64,18 +59,22 @@ |
64 | 59 | switch (insn.i_format.rt) { |
65 | 60 | case bltz_op: |
66 | 61 | case bltzl_op: |
67 | - if ((long)regs->regs[insn.i_format.rs] < 0) | |
62 | + if ((long)regs->regs[insn.i_format.rs] < 0) { | |
68 | 63 | epc = epc + 4 + (insn.i_format.simmediate << 2); |
69 | - else | |
64 | + if (insn.i_format.rt == bltzl_op) | |
65 | + ret = BRANCH_LIKELY_TAKEN; | |
66 | + } else | |
70 | 67 | epc += 8; |
71 | 68 | regs->cp0_epc = epc; |
72 | 69 | break; |
73 | 70 | |
74 | 71 | case bgez_op: |
75 | 72 | case bgezl_op: |
76 | - if ((long)regs->regs[insn.i_format.rs] >= 0) | |
73 | + if ((long)regs->regs[insn.i_format.rs] >= 0) { | |
77 | 74 | epc = epc + 4 + (insn.i_format.simmediate << 2); |
78 | - else | |
75 | + if (insn.i_format.rt == bgezl_op) | |
76 | + ret = BRANCH_LIKELY_TAKEN; | |
77 | + } else | |
79 | 78 | epc += 8; |
80 | 79 | regs->cp0_epc = epc; |
81 | 80 | break; |
82 | 81 | |
... | ... | @@ -83,9 +82,11 @@ |
83 | 82 | case bltzal_op: |
84 | 83 | case bltzall_op: |
85 | 84 | regs->regs[31] = epc + 8; |
86 | - if ((long)regs->regs[insn.i_format.rs] < 0) | |
85 | + if ((long)regs->regs[insn.i_format.rs] < 0) { | |
87 | 86 | epc = epc + 4 + (insn.i_format.simmediate << 2); |
88 | - else | |
87 | + if (insn.i_format.rt == bltzall_op) | |
88 | + ret = BRANCH_LIKELY_TAKEN; | |
89 | + } else | |
89 | 90 | epc += 8; |
90 | 91 | regs->cp0_epc = epc; |
91 | 92 | break; |
92 | 93 | |
93 | 94 | |
... | ... | @@ -93,12 +94,15 @@ |
93 | 94 | case bgezal_op: |
94 | 95 | case bgezall_op: |
95 | 96 | regs->regs[31] = epc + 8; |
96 | - if ((long)regs->regs[insn.i_format.rs] >= 0) | |
97 | + if ((long)regs->regs[insn.i_format.rs] >= 0) { | |
97 | 98 | epc = epc + 4 + (insn.i_format.simmediate << 2); |
98 | - else | |
99 | + if (insn.i_format.rt == bgezall_op) | |
100 | + ret = BRANCH_LIKELY_TAKEN; | |
101 | + } else | |
99 | 102 | epc += 8; |
100 | 103 | regs->cp0_epc = epc; |
101 | 104 | break; |
105 | + | |
102 | 106 | case bposge32_op: |
103 | 107 | if (!cpu_has_dsp) |
104 | 108 | goto sigill; |
105 | 109 | |
... | ... | @@ -133,9 +137,11 @@ |
133 | 137 | case beq_op: |
134 | 138 | case beql_op: |
135 | 139 | if (regs->regs[insn.i_format.rs] == |
136 | - regs->regs[insn.i_format.rt]) | |
140 | + regs->regs[insn.i_format.rt]) { | |
137 | 141 | epc = epc + 4 + (insn.i_format.simmediate << 2); |
138 | - else | |
142 | + if (insn.i_format.rt == beql_op) | |
143 | + ret = BRANCH_LIKELY_TAKEN; | |
144 | + } else | |
139 | 145 | epc += 8; |
140 | 146 | regs->cp0_epc = epc; |
141 | 147 | break; |
142 | 148 | |
... | ... | @@ -143,9 +149,11 @@ |
143 | 149 | case bne_op: |
144 | 150 | case bnel_op: |
145 | 151 | if (regs->regs[insn.i_format.rs] != |
146 | - regs->regs[insn.i_format.rt]) | |
152 | + regs->regs[insn.i_format.rt]) { | |
147 | 153 | epc = epc + 4 + (insn.i_format.simmediate << 2); |
148 | - else | |
154 | + if (insn.i_format.rt == bnel_op) | |
155 | + ret = BRANCH_LIKELY_TAKEN; | |
156 | + } else | |
149 | 157 | epc += 8; |
150 | 158 | regs->cp0_epc = epc; |
151 | 159 | break; |
152 | 160 | |
... | ... | @@ -153,9 +161,11 @@ |
153 | 161 | case blez_op: /* not really i_format */ |
154 | 162 | case blezl_op: |
155 | 163 | /* rt field assumed to be zero */ |
156 | - if ((long)regs->regs[insn.i_format.rs] <= 0) | |
164 | + if ((long)regs->regs[insn.i_format.rs] <= 0) { | |
157 | 165 | epc = epc + 4 + (insn.i_format.simmediate << 2); |
158 | - else | |
166 | + if (insn.i_format.rt == bnel_op) | |
167 | + ret = BRANCH_LIKELY_TAKEN; | |
168 | + } else | |
159 | 169 | epc += 8; |
160 | 170 | regs->cp0_epc = epc; |
161 | 171 | break; |
162 | 172 | |
... | ... | @@ -163,9 +173,11 @@ |
163 | 173 | case bgtz_op: |
164 | 174 | case bgtzl_op: |
165 | 175 | /* rt field assumed to be zero */ |
166 | - if ((long)regs->regs[insn.i_format.rs] > 0) | |
176 | + if ((long)regs->regs[insn.i_format.rs] > 0) { | |
167 | 177 | epc = epc + 4 + (insn.i_format.simmediate << 2); |
168 | - else | |
178 | + if (insn.i_format.rt == bnel_op) | |
179 | + ret = BRANCH_LIKELY_TAKEN; | |
180 | + } else | |
169 | 181 | epc += 8; |
170 | 182 | regs->cp0_epc = epc; |
171 | 183 | break; |
172 | 184 | |
173 | 185 | |
174 | 186 | |
... | ... | @@ -187,18 +199,22 @@ |
187 | 199 | switch (insn.i_format.rt & 3) { |
188 | 200 | case 0: /* bc1f */ |
189 | 201 | case 2: /* bc1fl */ |
190 | - if (~fcr31 & (1 << bit)) | |
202 | + if (~fcr31 & (1 << bit)) { | |
191 | 203 | epc = epc + 4 + (insn.i_format.simmediate << 2); |
192 | - else | |
204 | + if (insn.i_format.rt == 2) | |
205 | + ret = BRANCH_LIKELY_TAKEN; | |
206 | + } else | |
193 | 207 | epc += 8; |
194 | 208 | regs->cp0_epc = epc; |
195 | 209 | break; |
196 | 210 | |
197 | 211 | case 1: /* bc1t */ |
198 | 212 | case 3: /* bc1tl */ |
199 | - if (fcr31 & (1 << bit)) | |
213 | + if (fcr31 & (1 << bit)) { | |
200 | 214 | epc = epc + 4 + (insn.i_format.simmediate << 2); |
201 | - else | |
215 | + if (insn.i_format.rt == 3) | |
216 | + ret = BRANCH_LIKELY_TAKEN; | |
217 | + } else | |
202 | 218 | epc += 8; |
203 | 219 | regs->cp0_epc = epc; |
204 | 220 | break; |
205 | 221 | |
206 | 222 | |
207 | 223 | |
208 | 224 | |
... | ... | @@ -239,16 +255,40 @@ |
239 | 255 | #endif |
240 | 256 | } |
241 | 257 | |
242 | - return 0; | |
258 | + return ret; | |
243 | 259 | |
244 | -unaligned: | |
245 | - printk("%s: unaligned epc - sending SIGBUS.\n", current->comm); | |
260 | +sigill: | |
261 | + printk("%s: DSP branch but not DSP ASE - sending SIGBUS.\n", current->comm); | |
246 | 262 | force_sig(SIGBUS, current); |
247 | 263 | return -EFAULT; |
264 | +} | |
265 | +EXPORT_SYMBOL_GPL(__compute_return_epc_for_insn); | |
248 | 266 | |
249 | -sigill: | |
250 | - printk("%s: DSP branch but not DSP ASE - sending SIGBUS.\n", current->comm); | |
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); | |
251 | 290 | force_sig(SIGBUS, current); |
252 | 291 | return -EFAULT; |
292 | + | |
253 | 293 | } |
arch/mips/math-emu/cp1emu.c
... | ... | @@ -245,7 +245,7 @@ |
245 | 245 | */ |
246 | 246 | emulpc = xcp->cp0_epc + 4; /* Snapshot emulation target */ |
247 | 247 | |
248 | - if (__compute_return_epc(xcp)) { | |
248 | + if (__compute_return_epc(xcp) < 0) { | |
249 | 249 | #ifdef CP1DBG |
250 | 250 | printk("failed to emulate branch at %p\n", |
251 | 251 | (void *) (xcp->cp0_epc)); |