Commit | Line | Data |
---|---|---|
35aa1df4 QB |
1 | /* |
2 | * arch/arm/kernel/kprobes-decode.c | |
3 | * | |
4 | * Copyright (C) 2006, 2007 Motorola Inc. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * General Public License for more details. | |
14 | */ | |
15 | ||
16 | /* | |
17 | * We do not have hardware single-stepping on ARM, This | |
18 | * effort is further complicated by the ARM not having a | |
19 | * "next PC" register. Instructions that change the PC | |
20 | * can't be safely single-stepped in a MP environment, so | |
21 | * we have a lot of work to do: | |
22 | * | |
23 | * In the prepare phase: | |
24 | * *) If it is an instruction that does anything | |
25 | * with the CPU mode, we reject it for a kprobe. | |
26 | * (This is out of laziness rather than need. The | |
27 | * instructions could be simulated.) | |
28 | * | |
29 | * *) Otherwise, decode the instruction rewriting its | |
30 | * registers to take fixed, ordered registers and | |
31 | * setting a handler for it to run the instruction. | |
32 | * | |
33 | * In the execution phase by an instruction's handler: | |
34 | * | |
35 | * *) If the PC is written to by the instruction, the | |
36 | * instruction must be fully simulated in software. | |
35aa1df4 QB |
37 | * |
38 | * *) Otherwise, a modified form of the instruction is | |
39 | * directly executed. Its handler calls the | |
40 | * instruction in insn[0]. In insn[1] is a | |
41 | * "mov pc, lr" to return. | |
42 | * | |
43 | * Before calling, load up the reordered registers | |
44 | * from the original instruction's registers. If one | |
45 | * of the original input registers is the PC, compute | |
46 | * and adjust the appropriate input register. | |
47 | * | |
48 | * After call completes, copy the output registers to | |
49 | * the original instruction's original registers. | |
50 | * | |
51 | * We don't use a real breakpoint instruction since that | |
52 | * would have us in the kernel go from SVC mode to SVC | |
53 | * mode losing the link register. Instead we use an | |
54 | * undefined instruction. To simplify processing, the | |
55 | * undefined instruction used for kprobes must be reserved | |
56 | * exclusively for kprobes use. | |
57 | * | |
58 | * TODO: ifdef out some instruction decoding based on architecture. | |
59 | */ | |
60 | ||
61 | #include <linux/kernel.h> | |
62 | #include <linux/kprobes.h> | |
63 | ||
64 | #define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit))))) | |
65 | ||
66 | #define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25) | |
67 | ||
983ebd93 JM |
68 | #define is_r15(insn, bitpos) (((insn) & (0xf << bitpos)) == (0xf << bitpos)) |
69 | ||
54823acc JM |
70 | /* |
71 | * Test if load/store instructions writeback the address register. | |
72 | * if P (bit 24) == 0 or W (bit 21) == 1 | |
73 | */ | |
74 | #define is_writeback(insn) ((insn ^ 0x01000000) & 0x01200000) | |
75 | ||
35aa1df4 QB |
76 | #define PSR_fs (PSR_f|PSR_s) |
77 | ||
78 | #define KPROBE_RETURN_INSTRUCTION 0xe1a0f00e /* mov pc, lr */ | |
35aa1df4 QB |
79 | |
80 | typedef long (insn_0arg_fn_t)(void); | |
81 | typedef long (insn_1arg_fn_t)(long); | |
82 | typedef long (insn_2arg_fn_t)(long, long); | |
83 | typedef long (insn_3arg_fn_t)(long, long, long); | |
84 | typedef long (insn_4arg_fn_t)(long, long, long, long); | |
85 | typedef long long (insn_llret_0arg_fn_t)(void); | |
86 | typedef long long (insn_llret_3arg_fn_t)(long, long, long); | |
87 | typedef long long (insn_llret_4arg_fn_t)(long, long, long, long); | |
88 | ||
89 | union reg_pair { | |
90 | long long dr; | |
91 | #ifdef __LITTLE_ENDIAN | |
92 | struct { long r0, r1; }; | |
93 | #else | |
94 | struct { long r1, r0; }; | |
95 | #endif | |
96 | }; | |
97 | ||
98 | /* | |
99 | * For STR and STM instructions, an ARM core may choose to use either | |
100 | * a +8 or a +12 displacement from the current instruction's address. | |
101 | * Whichever value is chosen for a given core, it must be the same for | |
102 | * both instructions and may not change. This function measures it. | |
103 | */ | |
104 | ||
105 | static int str_pc_offset; | |
106 | ||
107 | static void __init find_str_pc_offset(void) | |
108 | { | |
109 | int addr, scratch, ret; | |
110 | ||
111 | __asm__ ( | |
112 | "sub %[ret], pc, #4 \n\t" | |
113 | "str pc, %[addr] \n\t" | |
114 | "ldr %[scr], %[addr] \n\t" | |
115 | "sub %[ret], %[scr], %[ret] \n\t" | |
116 | : [ret] "=r" (ret), [scr] "=r" (scratch), [addr] "+m" (addr)); | |
117 | ||
118 | str_pc_offset = ret; | |
119 | } | |
120 | ||
121 | /* | |
122 | * The insnslot_?arg_r[w]flags() functions below are to keep the | |
123 | * msr -> *fn -> mrs instruction sequences indivisible so that | |
124 | * the state of the CPSR flags aren't inadvertently modified | |
125 | * just before or just after the call. | |
126 | */ | |
127 | ||
128 | static inline long __kprobes | |
129 | insnslot_0arg_rflags(long cpsr, insn_0arg_fn_t *fn) | |
130 | { | |
131 | register long ret asm("r0"); | |
132 | ||
133 | __asm__ __volatile__ ( | |
134 | "msr cpsr_fs, %[cpsr] \n\t" | |
135 | "mov lr, pc \n\t" | |
136 | "mov pc, %[fn] \n\t" | |
137 | : "=r" (ret) | |
138 | : [cpsr] "r" (cpsr), [fn] "r" (fn) | |
139 | : "lr", "cc" | |
140 | ); | |
141 | return ret; | |
142 | } | |
143 | ||
144 | static inline long long __kprobes | |
145 | insnslot_llret_0arg_rflags(long cpsr, insn_llret_0arg_fn_t *fn) | |
146 | { | |
147 | register long ret0 asm("r0"); | |
148 | register long ret1 asm("r1"); | |
149 | union reg_pair fnr; | |
150 | ||
151 | __asm__ __volatile__ ( | |
152 | "msr cpsr_fs, %[cpsr] \n\t" | |
153 | "mov lr, pc \n\t" | |
154 | "mov pc, %[fn] \n\t" | |
155 | : "=r" (ret0), "=r" (ret1) | |
156 | : [cpsr] "r" (cpsr), [fn] "r" (fn) | |
157 | : "lr", "cc" | |
158 | ); | |
159 | fnr.r0 = ret0; | |
160 | fnr.r1 = ret1; | |
161 | return fnr.dr; | |
162 | } | |
163 | ||
164 | static inline long __kprobes | |
165 | insnslot_1arg_rflags(long r0, long cpsr, insn_1arg_fn_t *fn) | |
166 | { | |
167 | register long rr0 asm("r0") = r0; | |
168 | register long ret asm("r0"); | |
169 | ||
170 | __asm__ __volatile__ ( | |
171 | "msr cpsr_fs, %[cpsr] \n\t" | |
172 | "mov lr, pc \n\t" | |
173 | "mov pc, %[fn] \n\t" | |
174 | : "=r" (ret) | |
175 | : "0" (rr0), [cpsr] "r" (cpsr), [fn] "r" (fn) | |
176 | : "lr", "cc" | |
177 | ); | |
178 | return ret; | |
179 | } | |
180 | ||
181 | static inline long __kprobes | |
182 | insnslot_2arg_rflags(long r0, long r1, long cpsr, insn_2arg_fn_t *fn) | |
183 | { | |
184 | register long rr0 asm("r0") = r0; | |
185 | register long rr1 asm("r1") = r1; | |
186 | register long ret asm("r0"); | |
187 | ||
188 | __asm__ __volatile__ ( | |
189 | "msr cpsr_fs, %[cpsr] \n\t" | |
190 | "mov lr, pc \n\t" | |
191 | "mov pc, %[fn] \n\t" | |
192 | : "=r" (ret) | |
193 | : "0" (rr0), "r" (rr1), | |
194 | [cpsr] "r" (cpsr), [fn] "r" (fn) | |
195 | : "lr", "cc" | |
196 | ); | |
197 | return ret; | |
198 | } | |
199 | ||
200 | static inline long __kprobes | |
201 | insnslot_3arg_rflags(long r0, long r1, long r2, long cpsr, insn_3arg_fn_t *fn) | |
202 | { | |
203 | register long rr0 asm("r0") = r0; | |
204 | register long rr1 asm("r1") = r1; | |
205 | register long rr2 asm("r2") = r2; | |
206 | register long ret asm("r0"); | |
207 | ||
208 | __asm__ __volatile__ ( | |
209 | "msr cpsr_fs, %[cpsr] \n\t" | |
210 | "mov lr, pc \n\t" | |
211 | "mov pc, %[fn] \n\t" | |
212 | : "=r" (ret) | |
213 | : "0" (rr0), "r" (rr1), "r" (rr2), | |
214 | [cpsr] "r" (cpsr), [fn] "r" (fn) | |
215 | : "lr", "cc" | |
216 | ); | |
217 | return ret; | |
218 | } | |
219 | ||
220 | static inline long long __kprobes | |
221 | insnslot_llret_3arg_rflags(long r0, long r1, long r2, long cpsr, | |
222 | insn_llret_3arg_fn_t *fn) | |
223 | { | |
224 | register long rr0 asm("r0") = r0; | |
225 | register long rr1 asm("r1") = r1; | |
226 | register long rr2 asm("r2") = r2; | |
227 | register long ret0 asm("r0"); | |
228 | register long ret1 asm("r1"); | |
229 | union reg_pair fnr; | |
230 | ||
231 | __asm__ __volatile__ ( | |
232 | "msr cpsr_fs, %[cpsr] \n\t" | |
233 | "mov lr, pc \n\t" | |
234 | "mov pc, %[fn] \n\t" | |
235 | : "=r" (ret0), "=r" (ret1) | |
236 | : "0" (rr0), "r" (rr1), "r" (rr2), | |
237 | [cpsr] "r" (cpsr), [fn] "r" (fn) | |
238 | : "lr", "cc" | |
239 | ); | |
240 | fnr.r0 = ret0; | |
241 | fnr.r1 = ret1; | |
242 | return fnr.dr; | |
243 | } | |
244 | ||
245 | static inline long __kprobes | |
246 | insnslot_4arg_rflags(long r0, long r1, long r2, long r3, long cpsr, | |
247 | insn_4arg_fn_t *fn) | |
248 | { | |
249 | register long rr0 asm("r0") = r0; | |
250 | register long rr1 asm("r1") = r1; | |
251 | register long rr2 asm("r2") = r2; | |
252 | register long rr3 asm("r3") = r3; | |
253 | register long ret asm("r0"); | |
254 | ||
255 | __asm__ __volatile__ ( | |
256 | "msr cpsr_fs, %[cpsr] \n\t" | |
257 | "mov lr, pc \n\t" | |
258 | "mov pc, %[fn] \n\t" | |
259 | : "=r" (ret) | |
260 | : "0" (rr0), "r" (rr1), "r" (rr2), "r" (rr3), | |
261 | [cpsr] "r" (cpsr), [fn] "r" (fn) | |
262 | : "lr", "cc" | |
263 | ); | |
264 | return ret; | |
265 | } | |
266 | ||
267 | static inline long __kprobes | |
268 | insnslot_1arg_rwflags(long r0, long *cpsr, insn_1arg_fn_t *fn) | |
269 | { | |
270 | register long rr0 asm("r0") = r0; | |
271 | register long ret asm("r0"); | |
272 | long oldcpsr = *cpsr; | |
273 | long newcpsr; | |
274 | ||
275 | __asm__ __volatile__ ( | |
276 | "msr cpsr_fs, %[oldcpsr] \n\t" | |
277 | "mov lr, pc \n\t" | |
278 | "mov pc, %[fn] \n\t" | |
279 | "mrs %[newcpsr], cpsr \n\t" | |
280 | : "=r" (ret), [newcpsr] "=r" (newcpsr) | |
281 | : "0" (rr0), [oldcpsr] "r" (oldcpsr), [fn] "r" (fn) | |
282 | : "lr", "cc" | |
283 | ); | |
284 | *cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs); | |
285 | return ret; | |
286 | } | |
287 | ||
288 | static inline long __kprobes | |
289 | insnslot_2arg_rwflags(long r0, long r1, long *cpsr, insn_2arg_fn_t *fn) | |
290 | { | |
291 | register long rr0 asm("r0") = r0; | |
292 | register long rr1 asm("r1") = r1; | |
293 | register long ret asm("r0"); | |
294 | long oldcpsr = *cpsr; | |
295 | long newcpsr; | |
296 | ||
297 | __asm__ __volatile__ ( | |
298 | "msr cpsr_fs, %[oldcpsr] \n\t" | |
299 | "mov lr, pc \n\t" | |
300 | "mov pc, %[fn] \n\t" | |
301 | "mrs %[newcpsr], cpsr \n\t" | |
302 | : "=r" (ret), [newcpsr] "=r" (newcpsr) | |
303 | : "0" (rr0), "r" (rr1), [oldcpsr] "r" (oldcpsr), [fn] "r" (fn) | |
304 | : "lr", "cc" | |
305 | ); | |
306 | *cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs); | |
307 | return ret; | |
308 | } | |
309 | ||
310 | static inline long __kprobes | |
311 | insnslot_3arg_rwflags(long r0, long r1, long r2, long *cpsr, | |
312 | insn_3arg_fn_t *fn) | |
313 | { | |
314 | register long rr0 asm("r0") = r0; | |
315 | register long rr1 asm("r1") = r1; | |
316 | register long rr2 asm("r2") = r2; | |
317 | register long ret asm("r0"); | |
318 | long oldcpsr = *cpsr; | |
319 | long newcpsr; | |
320 | ||
321 | __asm__ __volatile__ ( | |
322 | "msr cpsr_fs, %[oldcpsr] \n\t" | |
323 | "mov lr, pc \n\t" | |
324 | "mov pc, %[fn] \n\t" | |
325 | "mrs %[newcpsr], cpsr \n\t" | |
326 | : "=r" (ret), [newcpsr] "=r" (newcpsr) | |
327 | : "0" (rr0), "r" (rr1), "r" (rr2), | |
328 | [oldcpsr] "r" (oldcpsr), [fn] "r" (fn) | |
329 | : "lr", "cc" | |
330 | ); | |
331 | *cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs); | |
332 | return ret; | |
333 | } | |
334 | ||
335 | static inline long __kprobes | |
336 | insnslot_4arg_rwflags(long r0, long r1, long r2, long r3, long *cpsr, | |
337 | insn_4arg_fn_t *fn) | |
338 | { | |
339 | register long rr0 asm("r0") = r0; | |
340 | register long rr1 asm("r1") = r1; | |
341 | register long rr2 asm("r2") = r2; | |
342 | register long rr3 asm("r3") = r3; | |
343 | register long ret asm("r0"); | |
344 | long oldcpsr = *cpsr; | |
345 | long newcpsr; | |
346 | ||
347 | __asm__ __volatile__ ( | |
348 | "msr cpsr_fs, %[oldcpsr] \n\t" | |
349 | "mov lr, pc \n\t" | |
350 | "mov pc, %[fn] \n\t" | |
351 | "mrs %[newcpsr], cpsr \n\t" | |
352 | : "=r" (ret), [newcpsr] "=r" (newcpsr) | |
353 | : "0" (rr0), "r" (rr1), "r" (rr2), "r" (rr3), | |
354 | [oldcpsr] "r" (oldcpsr), [fn] "r" (fn) | |
355 | : "lr", "cc" | |
356 | ); | |
357 | *cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs); | |
358 | return ret; | |
359 | } | |
360 | ||
361 | static inline long long __kprobes | |
362 | insnslot_llret_4arg_rwflags(long r0, long r1, long r2, long r3, long *cpsr, | |
363 | insn_llret_4arg_fn_t *fn) | |
364 | { | |
365 | register long rr0 asm("r0") = r0; | |
366 | register long rr1 asm("r1") = r1; | |
367 | register long rr2 asm("r2") = r2; | |
368 | register long rr3 asm("r3") = r3; | |
369 | register long ret0 asm("r0"); | |
370 | register long ret1 asm("r1"); | |
371 | long oldcpsr = *cpsr; | |
372 | long newcpsr; | |
373 | union reg_pair fnr; | |
374 | ||
375 | __asm__ __volatile__ ( | |
376 | "msr cpsr_fs, %[oldcpsr] \n\t" | |
377 | "mov lr, pc \n\t" | |
378 | "mov pc, %[fn] \n\t" | |
379 | "mrs %[newcpsr], cpsr \n\t" | |
380 | : "=r" (ret0), "=r" (ret1), [newcpsr] "=r" (newcpsr) | |
381 | : "0" (rr0), "r" (rr1), "r" (rr2), "r" (rr3), | |
382 | [oldcpsr] "r" (oldcpsr), [fn] "r" (fn) | |
383 | : "lr", "cc" | |
384 | ); | |
385 | *cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs); | |
386 | fnr.r0 = ret0; | |
387 | fnr.r1 = ret1; | |
388 | return fnr.dr; | |
389 | } | |
390 | ||
391 | /* | |
392 | * To avoid the complications of mimicing single-stepping on a | |
393 | * processor without a Next-PC or a single-step mode, and to | |
394 | * avoid having to deal with the side-effects of boosting, we | |
395 | * simulate or emulate (almost) all ARM instructions. | |
396 | * | |
397 | * "Simulation" is where the instruction's behavior is duplicated in | |
398 | * C code. "Emulation" is where the original instruction is rewritten | |
399 | * and executed, often by altering its registers. | |
400 | * | |
401 | * By having all behavior of the kprobe'd instruction completed before | |
402 | * returning from the kprobe_handler(), all locks (scheduler and | |
403 | * interrupt) can safely be released. There is no need for secondary | |
404 | * breakpoints, no race with MP or preemptable kernels, nor having to | |
405 | * clean up resources counts at a later time impacting overall system | |
406 | * performance. By rewriting the instruction, only the minimum registers | |
407 | * need to be loaded and saved back optimizing performance. | |
408 | * | |
409 | * Calling the insnslot_*_rwflags version of a function doesn't hurt | |
410 | * anything even when the CPSR flags aren't updated by the | |
411 | * instruction. It's just a little slower in return for saving | |
412 | * a little space by not having a duplicate function that doesn't | |
413 | * update the flags. (The same optimization can be said for | |
414 | * instructions that do or don't perform register writeback) | |
415 | * Also, instructions can either read the flags, only write the | |
416 | * flags, or read and write the flags. To save combinations | |
417 | * rather than for sheer performance, flag functions just assume | |
418 | * read and write of flags. | |
419 | */ | |
420 | ||
421 | static void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs) | |
422 | { | |
35aa1df4 QB |
423 | kprobe_opcode_t insn = p->opcode; |
424 | long iaddr = (long)p->addr; | |
425 | int disp = branch_displacement(insn); | |
426 | ||
35aa1df4 QB |
427 | if (insn & (1 << 24)) |
428 | regs->ARM_lr = iaddr + 4; | |
429 | ||
430 | regs->ARM_pc = iaddr + 8 + disp; | |
431 | } | |
432 | ||
433 | static void __kprobes simulate_blx1(struct kprobe *p, struct pt_regs *regs) | |
434 | { | |
435 | kprobe_opcode_t insn = p->opcode; | |
436 | long iaddr = (long)p->addr; | |
437 | int disp = branch_displacement(insn); | |
438 | ||
439 | regs->ARM_lr = iaddr + 4; | |
440 | regs->ARM_pc = iaddr + 8 + disp + ((insn >> 23) & 0x2); | |
441 | regs->ARM_cpsr |= PSR_T_BIT; | |
442 | } | |
443 | ||
444 | static void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs) | |
445 | { | |
35aa1df4 QB |
446 | kprobe_opcode_t insn = p->opcode; |
447 | int rm = insn & 0xf; | |
448 | long rmv = regs->uregs[rm]; | |
449 | ||
35aa1df4 QB |
450 | if (insn & (1 << 5)) |
451 | regs->ARM_lr = (long)p->addr + 4; | |
452 | ||
453 | regs->ARM_pc = rmv & ~0x1; | |
454 | regs->ARM_cpsr &= ~PSR_T_BIT; | |
455 | if (rmv & 0x1) | |
456 | regs->ARM_cpsr |= PSR_T_BIT; | |
457 | } | |
458 | ||
c412aba2 JM |
459 | static void __kprobes simulate_mrs(struct kprobe *p, struct pt_regs *regs) |
460 | { | |
461 | kprobe_opcode_t insn = p->opcode; | |
462 | int rd = (insn >> 12) & 0xf; | |
463 | unsigned long mask = 0xf8ff03df; /* Mask out execution state */ | |
464 | regs->uregs[rd] = regs->ARM_cpsr & mask; | |
465 | } | |
466 | ||
35aa1df4 QB |
467 | static void __kprobes simulate_ldm1stm1(struct kprobe *p, struct pt_regs *regs) |
468 | { | |
35aa1df4 QB |
469 | kprobe_opcode_t insn = p->opcode; |
470 | int rn = (insn >> 16) & 0xf; | |
471 | int lbit = insn & (1 << 20); | |
472 | int wbit = insn & (1 << 21); | |
473 | int ubit = insn & (1 << 23); | |
474 | int pbit = insn & (1 << 24); | |
475 | long *addr = (long *)regs->uregs[rn]; | |
476 | int reg_bit_vector; | |
477 | int reg_count; | |
478 | ||
35aa1df4 QB |
479 | reg_count = 0; |
480 | reg_bit_vector = insn & 0xffff; | |
481 | while (reg_bit_vector) { | |
482 | reg_bit_vector &= (reg_bit_vector - 1); | |
483 | ++reg_count; | |
484 | } | |
485 | ||
486 | if (!ubit) | |
487 | addr -= reg_count; | |
2d4b6c9a | 488 | addr += (!pbit == !ubit); |
35aa1df4 QB |
489 | |
490 | reg_bit_vector = insn & 0xffff; | |
491 | while (reg_bit_vector) { | |
492 | int reg = __ffs(reg_bit_vector); | |
493 | reg_bit_vector &= (reg_bit_vector - 1); | |
494 | if (lbit) | |
495 | regs->uregs[reg] = *addr++; | |
496 | else | |
497 | *addr++ = regs->uregs[reg]; | |
498 | } | |
499 | ||
500 | if (wbit) { | |
501 | if (!ubit) | |
502 | addr -= reg_count; | |
2d4b6c9a | 503 | addr -= (!pbit == !ubit); |
35aa1df4 QB |
504 | regs->uregs[rn] = (long)addr; |
505 | } | |
506 | } | |
507 | ||
508 | static void __kprobes simulate_stm1_pc(struct kprobe *p, struct pt_regs *regs) | |
509 | { | |
35aa1df4 QB |
510 | regs->ARM_pc = (long)p->addr + str_pc_offset; |
511 | simulate_ldm1stm1(p, regs); | |
512 | regs->ARM_pc = (long)p->addr + 4; | |
513 | } | |
514 | ||
515 | static void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs) | |
516 | { | |
517 | regs->uregs[12] = regs->uregs[13]; | |
518 | } | |
519 | ||
35aa1df4 QB |
520 | static void __kprobes emulate_ldrd(struct kprobe *p, struct pt_regs *regs) |
521 | { | |
522 | insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0]; | |
523 | kprobe_opcode_t insn = p->opcode; | |
cf3cc1aa | 524 | long ppc = (long)p->addr + 8; |
35aa1df4 QB |
525 | int rd = (insn >> 12) & 0xf; |
526 | int rn = (insn >> 16) & 0xf; | |
527 | int rm = insn & 0xf; /* rm may be invalid, don't care. */ | |
cf3cc1aa VR |
528 | long rmv = (rm == 15) ? ppc : regs->uregs[rm]; |
529 | long rnv = (rn == 15) ? ppc : regs->uregs[rn]; | |
35aa1df4 QB |
530 | |
531 | /* Not following the C calling convention here, so need asm(). */ | |
532 | __asm__ __volatile__ ( | |
533 | "ldr r0, %[rn] \n\t" | |
534 | "ldr r1, %[rm] \n\t" | |
535 | "msr cpsr_fs, %[cpsr]\n\t" | |
536 | "mov lr, pc \n\t" | |
537 | "mov pc, %[i_fn] \n\t" | |
538 | "str r0, %[rn] \n\t" /* in case of writeback */ | |
539 | "str r2, %[rd0] \n\t" | |
540 | "str r3, %[rd1] \n\t" | |
cf3cc1aa | 541 | : [rn] "+m" (rnv), |
35aa1df4 QB |
542 | [rd0] "=m" (regs->uregs[rd]), |
543 | [rd1] "=m" (regs->uregs[rd+1]) | |
cf3cc1aa | 544 | : [rm] "m" (rmv), |
35aa1df4 QB |
545 | [cpsr] "r" (regs->ARM_cpsr), |
546 | [i_fn] "r" (i_fn) | |
547 | : "r0", "r1", "r2", "r3", "lr", "cc" | |
548 | ); | |
5c6b76fc JM |
549 | if (is_writeback(insn)) |
550 | regs->uregs[rn] = rnv; | |
35aa1df4 QB |
551 | } |
552 | ||
553 | static void __kprobes emulate_strd(struct kprobe *p, struct pt_regs *regs) | |
554 | { | |
555 | insn_4arg_fn_t *i_fn = (insn_4arg_fn_t *)&p->ainsn.insn[0]; | |
556 | kprobe_opcode_t insn = p->opcode; | |
cf3cc1aa | 557 | long ppc = (long)p->addr + 8; |
35aa1df4 QB |
558 | int rd = (insn >> 12) & 0xf; |
559 | int rn = (insn >> 16) & 0xf; | |
560 | int rm = insn & 0xf; | |
cf3cc1aa VR |
561 | long rnv = (rn == 15) ? ppc : regs->uregs[rn]; |
562 | /* rm/rmv may be invalid, don't care. */ | |
563 | long rmv = (rm == 15) ? ppc : regs->uregs[rm]; | |
564 | long rnv_wb; | |
35aa1df4 | 565 | |
cf3cc1aa | 566 | rnv_wb = insnslot_4arg_rflags(rnv, rmv, regs->uregs[rd], |
35aa1df4 QB |
567 | regs->uregs[rd+1], |
568 | regs->ARM_cpsr, i_fn); | |
5c6b76fc JM |
569 | if (is_writeback(insn)) |
570 | regs->uregs[rn] = rnv_wb; | |
35aa1df4 QB |
571 | } |
572 | ||
573 | static void __kprobes emulate_ldr(struct kprobe *p, struct pt_regs *regs) | |
574 | { | |
575 | insn_llret_3arg_fn_t *i_fn = (insn_llret_3arg_fn_t *)&p->ainsn.insn[0]; | |
576 | kprobe_opcode_t insn = p->opcode; | |
0ebe25f9 | 577 | long ppc = (long)p->addr + 8; |
35aa1df4 QB |
578 | union reg_pair fnr; |
579 | int rd = (insn >> 12) & 0xf; | |
580 | int rn = (insn >> 16) & 0xf; | |
581 | int rm = insn & 0xf; | |
582 | long rdv; | |
0ebe25f9 NP |
583 | long rnv = (rn == 15) ? ppc : regs->uregs[rn]; |
584 | long rmv = (rm == 15) ? ppc : regs->uregs[rm]; | |
35aa1df4 QB |
585 | long cpsr = regs->ARM_cpsr; |
586 | ||
587 | fnr.dr = insnslot_llret_3arg_rflags(rnv, 0, rmv, cpsr, i_fn); | |
0652f067 VR |
588 | if (rn != 15) |
589 | regs->uregs[rn] = fnr.r0; /* Save Rn in case of writeback. */ | |
35aa1df4 QB |
590 | rdv = fnr.r1; |
591 | ||
592 | if (rd == 15) { | |
593 | #if __LINUX_ARM_ARCH__ >= 5 | |
594 | cpsr &= ~PSR_T_BIT; | |
595 | if (rdv & 0x1) | |
596 | cpsr |= PSR_T_BIT; | |
597 | regs->ARM_cpsr = cpsr; | |
598 | rdv &= ~0x1; | |
599 | #else | |
600 | rdv &= ~0x2; | |
601 | #endif | |
602 | } | |
603 | regs->uregs[rd] = rdv; | |
604 | } | |
605 | ||
606 | static void __kprobes emulate_str(struct kprobe *p, struct pt_regs *regs) | |
607 | { | |
608 | insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0]; | |
609 | kprobe_opcode_t insn = p->opcode; | |
610 | long iaddr = (long)p->addr; | |
611 | int rd = (insn >> 12) & 0xf; | |
612 | int rn = (insn >> 16) & 0xf; | |
613 | int rm = insn & 0xf; | |
614 | long rdv = (rd == 15) ? iaddr + str_pc_offset : regs->uregs[rd]; | |
615 | long rnv = (rn == 15) ? iaddr + 8 : regs->uregs[rn]; | |
616 | long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */ | |
0652f067 | 617 | long rnv_wb; |
35aa1df4 | 618 | |
0652f067 VR |
619 | rnv_wb = insnslot_3arg_rflags(rnv, rdv, rmv, regs->ARM_cpsr, i_fn); |
620 | if (rn != 15) | |
621 | regs->uregs[rn] = rnv_wb; /* Save Rn in case of writeback. */ | |
35aa1df4 QB |
622 | } |
623 | ||
35aa1df4 QB |
624 | static void __kprobes emulate_sat(struct kprobe *p, struct pt_regs *regs) |
625 | { | |
626 | insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; | |
627 | kprobe_opcode_t insn = p->opcode; | |
628 | int rd = (insn >> 12) & 0xf; | |
629 | int rm = insn & 0xf; | |
630 | long rmv = regs->uregs[rm]; | |
631 | ||
632 | /* Writes Q flag */ | |
633 | regs->uregs[rd] = insnslot_1arg_rwflags(rmv, ®s->ARM_cpsr, i_fn); | |
634 | } | |
635 | ||
636 | static void __kprobes emulate_sel(struct kprobe *p, struct pt_regs *regs) | |
637 | { | |
638 | insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0]; | |
639 | kprobe_opcode_t insn = p->opcode; | |
640 | int rd = (insn >> 12) & 0xf; | |
641 | int rn = (insn >> 16) & 0xf; | |
642 | int rm = insn & 0xf; | |
643 | long rnv = regs->uregs[rn]; | |
644 | long rmv = regs->uregs[rm]; | |
645 | ||
646 | /* Reads GE bits */ | |
647 | regs->uregs[rd] = insnslot_2arg_rflags(rnv, rmv, regs->ARM_cpsr, i_fn); | |
648 | } | |
649 | ||
650 | static void __kprobes emulate_none(struct kprobe *p, struct pt_regs *regs) | |
651 | { | |
652 | insn_0arg_fn_t *i_fn = (insn_0arg_fn_t *)&p->ainsn.insn[0]; | |
653 | ||
654 | insnslot_0arg_rflags(regs->ARM_cpsr, i_fn); | |
655 | } | |
656 | ||
41713d13 | 657 | static void __kprobes emulate_nop(struct kprobe *p, struct pt_regs *regs) |
35aa1df4 | 658 | { |
35aa1df4 QB |
659 | } |
660 | ||
c9836777 JM |
661 | static void __kprobes |
662 | emulate_rd12_modify(struct kprobe *p, struct pt_regs *regs) | |
663 | { | |
664 | insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; | |
665 | kprobe_opcode_t insn = p->opcode; | |
666 | int rd = (insn >> 12) & 0xf; | |
667 | long rdv = regs->uregs[rd]; | |
668 | ||
669 | regs->uregs[rd] = insnslot_1arg_rflags(rdv, regs->ARM_cpsr, i_fn); | |
670 | } | |
671 | ||
20e8155e JM |
672 | static void __kprobes |
673 | emulate_rd12rn0_modify(struct kprobe *p, struct pt_regs *regs) | |
674 | { | |
675 | insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0]; | |
676 | kprobe_opcode_t insn = p->opcode; | |
677 | int rd = (insn >> 12) & 0xf; | |
678 | int rn = insn & 0xf; | |
679 | long rdv = regs->uregs[rd]; | |
680 | long rnv = regs->uregs[rn]; | |
681 | ||
682 | regs->uregs[rd] = insnslot_2arg_rflags(rdv, rnv, regs->ARM_cpsr, i_fn); | |
683 | } | |
684 | ||
35aa1df4 QB |
685 | static void __kprobes emulate_rd12rm0(struct kprobe *p, struct pt_regs *regs) |
686 | { | |
687 | insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; | |
688 | kprobe_opcode_t insn = p->opcode; | |
689 | int rd = (insn >> 12) & 0xf; | |
690 | int rm = insn & 0xf; | |
691 | long rmv = regs->uregs[rm]; | |
692 | ||
693 | regs->uregs[rd] = insnslot_1arg_rflags(rmv, regs->ARM_cpsr, i_fn); | |
694 | } | |
695 | ||
696 | static void __kprobes | |
697 | emulate_rd12rn16rm0_rwflags(struct kprobe *p, struct pt_regs *regs) | |
698 | { | |
699 | insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0]; | |
700 | kprobe_opcode_t insn = p->opcode; | |
701 | int rd = (insn >> 12) & 0xf; | |
702 | int rn = (insn >> 16) & 0xf; | |
703 | int rm = insn & 0xf; | |
704 | long rnv = regs->uregs[rn]; | |
705 | long rmv = regs->uregs[rm]; | |
706 | ||
707 | regs->uregs[rd] = | |
708 | insnslot_2arg_rwflags(rnv, rmv, ®s->ARM_cpsr, i_fn); | |
709 | } | |
710 | ||
711 | static void __kprobes | |
712 | emulate_rd16rn12rs8rm0_rwflags(struct kprobe *p, struct pt_regs *regs) | |
713 | { | |
714 | insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0]; | |
715 | kprobe_opcode_t insn = p->opcode; | |
716 | int rd = (insn >> 16) & 0xf; | |
717 | int rn = (insn >> 12) & 0xf; | |
718 | int rs = (insn >> 8) & 0xf; | |
719 | int rm = insn & 0xf; | |
720 | long rnv = regs->uregs[rn]; | |
721 | long rsv = regs->uregs[rs]; | |
722 | long rmv = regs->uregs[rm]; | |
723 | ||
724 | regs->uregs[rd] = | |
725 | insnslot_3arg_rwflags(rnv, rsv, rmv, ®s->ARM_cpsr, i_fn); | |
726 | } | |
727 | ||
728 | static void __kprobes | |
729 | emulate_rd16rs8rm0_rwflags(struct kprobe *p, struct pt_regs *regs) | |
730 | { | |
731 | insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0]; | |
732 | kprobe_opcode_t insn = p->opcode; | |
733 | int rd = (insn >> 16) & 0xf; | |
734 | int rs = (insn >> 8) & 0xf; | |
735 | int rm = insn & 0xf; | |
736 | long rsv = regs->uregs[rs]; | |
737 | long rmv = regs->uregs[rm]; | |
738 | ||
739 | regs->uregs[rd] = | |
740 | insnslot_2arg_rwflags(rsv, rmv, ®s->ARM_cpsr, i_fn); | |
741 | } | |
742 | ||
743 | static void __kprobes | |
744 | emulate_rdhi16rdlo12rs8rm0_rwflags(struct kprobe *p, struct pt_regs *regs) | |
745 | { | |
746 | insn_llret_4arg_fn_t *i_fn = (insn_llret_4arg_fn_t *)&p->ainsn.insn[0]; | |
747 | kprobe_opcode_t insn = p->opcode; | |
748 | union reg_pair fnr; | |
749 | int rdhi = (insn >> 16) & 0xf; | |
750 | int rdlo = (insn >> 12) & 0xf; | |
751 | int rs = (insn >> 8) & 0xf; | |
752 | int rm = insn & 0xf; | |
753 | long rsv = regs->uregs[rs]; | |
754 | long rmv = regs->uregs[rm]; | |
755 | ||
756 | fnr.dr = insnslot_llret_4arg_rwflags(regs->uregs[rdhi], | |
757 | regs->uregs[rdlo], rsv, rmv, | |
758 | ®s->ARM_cpsr, i_fn); | |
759 | regs->uregs[rdhi] = fnr.r0; | |
760 | regs->uregs[rdlo] = fnr.r1; | |
761 | } | |
762 | ||
763 | static void __kprobes | |
764 | emulate_alu_imm_rflags(struct kprobe *p, struct pt_regs *regs) | |
765 | { | |
766 | insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; | |
767 | kprobe_opcode_t insn = p->opcode; | |
768 | int rd = (insn >> 12) & 0xf; | |
769 | int rn = (insn >> 16) & 0xf; | |
770 | long rnv = (rn == 15) ? (long)p->addr + 8 : regs->uregs[rn]; | |
771 | ||
772 | regs->uregs[rd] = insnslot_1arg_rflags(rnv, regs->ARM_cpsr, i_fn); | |
773 | } | |
774 | ||
775 | static void __kprobes | |
776 | emulate_alu_imm_rwflags(struct kprobe *p, struct pt_regs *regs) | |
777 | { | |
778 | insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; | |
779 | kprobe_opcode_t insn = p->opcode; | |
780 | int rd = (insn >> 12) & 0xf; | |
781 | int rn = (insn >> 16) & 0xf; | |
782 | long rnv = (rn == 15) ? (long)p->addr + 8 : regs->uregs[rn]; | |
783 | ||
784 | regs->uregs[rd] = insnslot_1arg_rwflags(rnv, ®s->ARM_cpsr, i_fn); | |
785 | } | |
786 | ||
ad111ce4 JM |
787 | static void __kprobes |
788 | emulate_alu_tests_imm(struct kprobe *p, struct pt_regs *regs) | |
789 | { | |
790 | insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; | |
791 | kprobe_opcode_t insn = p->opcode; | |
792 | int rn = (insn >> 16) & 0xf; | |
793 | long rnv = (rn == 15) ? (long)p->addr + 8 : regs->uregs[rn]; | |
794 | ||
795 | insnslot_1arg_rwflags(rnv, ®s->ARM_cpsr, i_fn); | |
796 | } | |
797 | ||
35aa1df4 QB |
798 | static void __kprobes |
799 | emulate_alu_rflags(struct kprobe *p, struct pt_regs *regs) | |
800 | { | |
801 | insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0]; | |
802 | kprobe_opcode_t insn = p->opcode; | |
803 | long ppc = (long)p->addr + 8; | |
804 | int rd = (insn >> 12) & 0xf; | |
805 | int rn = (insn >> 16) & 0xf; /* rn/rnv/rs/rsv may be */ | |
806 | int rs = (insn >> 8) & 0xf; /* invalid, don't care. */ | |
807 | int rm = insn & 0xf; | |
808 | long rnv = (rn == 15) ? ppc : regs->uregs[rn]; | |
809 | long rmv = (rm == 15) ? ppc : regs->uregs[rm]; | |
810 | long rsv = regs->uregs[rs]; | |
811 | ||
812 | regs->uregs[rd] = | |
813 | insnslot_3arg_rflags(rnv, rmv, rsv, regs->ARM_cpsr, i_fn); | |
814 | } | |
815 | ||
816 | static void __kprobes | |
817 | emulate_alu_rwflags(struct kprobe *p, struct pt_regs *regs) | |
818 | { | |
819 | insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0]; | |
820 | kprobe_opcode_t insn = p->opcode; | |
821 | long ppc = (long)p->addr + 8; | |
822 | int rd = (insn >> 12) & 0xf; | |
823 | int rn = (insn >> 16) & 0xf; /* rn/rnv/rs/rsv may be */ | |
824 | int rs = (insn >> 8) & 0xf; /* invalid, don't care. */ | |
825 | int rm = insn & 0xf; | |
826 | long rnv = (rn == 15) ? ppc : regs->uregs[rn]; | |
827 | long rmv = (rm == 15) ? ppc : regs->uregs[rm]; | |
828 | long rsv = regs->uregs[rs]; | |
829 | ||
830 | regs->uregs[rd] = | |
831 | insnslot_3arg_rwflags(rnv, rmv, rsv, ®s->ARM_cpsr, i_fn); | |
832 | } | |
833 | ||
ad111ce4 JM |
834 | static void __kprobes |
835 | emulate_alu_tests(struct kprobe *p, struct pt_regs *regs) | |
836 | { | |
837 | insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0]; | |
838 | kprobe_opcode_t insn = p->opcode; | |
839 | long ppc = (long)p->addr + 8; | |
840 | int rn = (insn >> 16) & 0xf; | |
841 | int rs = (insn >> 8) & 0xf; /* rs/rsv may be invalid, don't care. */ | |
842 | int rm = insn & 0xf; | |
843 | long rnv = (rn == 15) ? ppc : regs->uregs[rn]; | |
844 | long rmv = (rm == 15) ? ppc : regs->uregs[rm]; | |
845 | long rsv = regs->uregs[rs]; | |
846 | ||
847 | insnslot_3arg_rwflags(rnv, rmv, rsv, ®s->ARM_cpsr, i_fn); | |
848 | } | |
849 | ||
35aa1df4 QB |
850 | static enum kprobe_insn __kprobes |
851 | prep_emulate_ldr_str(kprobe_opcode_t insn, struct arch_specific_insn *asi) | |
852 | { | |
6823fc85 JM |
853 | int not_imm = (insn & (1 << 26)) ? (insn & (1 << 25)) |
854 | : (~insn & (1 << 22)); | |
35aa1df4 | 855 | |
54823acc JM |
856 | if (is_writeback(insn) && is_r15(insn, 16)) |
857 | return INSN_REJECTED; /* Writeback to PC */ | |
858 | ||
35aa1df4 QB |
859 | insn &= 0xfff00fff; |
860 | insn |= 0x00001000; /* Rn = r0, Rd = r1 */ | |
6823fc85 | 861 | if (not_imm) { |
35aa1df4 QB |
862 | insn &= ~0xf; |
863 | insn |= 2; /* Rm = r2 */ | |
864 | } | |
865 | asi->insn[0] = insn; | |
866 | asi->insn_handler = (insn & (1 << 20)) ? emulate_ldr : emulate_str; | |
867 | return INSN_GOOD; | |
868 | } | |
869 | ||
c9836777 JM |
870 | static enum kprobe_insn __kprobes |
871 | prep_emulate_rd12_modify(kprobe_opcode_t insn, struct arch_specific_insn *asi) | |
872 | { | |
873 | if (is_r15(insn, 12)) | |
874 | return INSN_REJECTED; /* Rd is PC */ | |
875 | ||
876 | insn &= 0xffff0fff; /* Rd = r0 */ | |
877 | asi->insn[0] = insn; | |
878 | asi->insn_handler = emulate_rd12_modify; | |
879 | return INSN_GOOD; | |
880 | } | |
881 | ||
20e8155e JM |
882 | static enum kprobe_insn __kprobes |
883 | prep_emulate_rd12rn0_modify(kprobe_opcode_t insn, | |
884 | struct arch_specific_insn *asi) | |
885 | { | |
886 | if (is_r15(insn, 12)) | |
887 | return INSN_REJECTED; /* Rd is PC */ | |
888 | ||
889 | insn &= 0xffff0ff0; /* Rd = r0 */ | |
890 | insn |= 0x00000001; /* Rn = r1 */ | |
891 | asi->insn[0] = insn; | |
892 | asi->insn_handler = emulate_rd12rn0_modify; | |
893 | return INSN_GOOD; | |
894 | } | |
895 | ||
35aa1df4 QB |
896 | static enum kprobe_insn __kprobes |
897 | prep_emulate_rd12rm0(kprobe_opcode_t insn, struct arch_specific_insn *asi) | |
898 | { | |
983ebd93 JM |
899 | if (is_r15(insn, 12)) |
900 | return INSN_REJECTED; /* Rd is PC */ | |
901 | ||
35aa1df4 QB |
902 | insn &= 0xffff0ff0; /* Rd = r0, Rm = r0 */ |
903 | asi->insn[0] = insn; | |
904 | asi->insn_handler = emulate_rd12rm0; | |
905 | return INSN_GOOD; | |
906 | } | |
907 | ||
35aa1df4 QB |
908 | static enum kprobe_insn __kprobes |
909 | prep_emulate_rd12rn16rm0_wflags(kprobe_opcode_t insn, | |
910 | struct arch_specific_insn *asi) | |
911 | { | |
983ebd93 JM |
912 | if (is_r15(insn, 12)) |
913 | return INSN_REJECTED; /* Rd is PC */ | |
914 | ||
35aa1df4 QB |
915 | insn &= 0xfff00ff0; /* Rd = r0, Rn = r0 */ |
916 | insn |= 0x00000001; /* Rm = r1 */ | |
917 | asi->insn[0] = insn; | |
918 | asi->insn_handler = emulate_rd12rn16rm0_rwflags; | |
919 | return INSN_GOOD; | |
920 | } | |
921 | ||
922 | static enum kprobe_insn __kprobes | |
923 | prep_emulate_rd16rs8rm0_wflags(kprobe_opcode_t insn, | |
924 | struct arch_specific_insn *asi) | |
925 | { | |
983ebd93 JM |
926 | if (is_r15(insn, 16)) |
927 | return INSN_REJECTED; /* Rd is PC */ | |
928 | ||
35aa1df4 QB |
929 | insn &= 0xfff0f0f0; /* Rd = r0, Rs = r0 */ |
930 | insn |= 0x00000001; /* Rm = r1 */ | |
931 | asi->insn[0] = insn; | |
932 | asi->insn_handler = emulate_rd16rs8rm0_rwflags; | |
933 | return INSN_GOOD; | |
934 | } | |
935 | ||
936 | static enum kprobe_insn __kprobes | |
937 | prep_emulate_rd16rn12rs8rm0_wflags(kprobe_opcode_t insn, | |
938 | struct arch_specific_insn *asi) | |
939 | { | |
983ebd93 JM |
940 | if (is_r15(insn, 16)) |
941 | return INSN_REJECTED; /* Rd is PC */ | |
942 | ||
35aa1df4 QB |
943 | insn &= 0xfff000f0; /* Rd = r0, Rn = r0 */ |
944 | insn |= 0x00000102; /* Rs = r1, Rm = r2 */ | |
945 | asi->insn[0] = insn; | |
946 | asi->insn_handler = emulate_rd16rn12rs8rm0_rwflags; | |
947 | return INSN_GOOD; | |
948 | } | |
949 | ||
950 | static enum kprobe_insn __kprobes | |
951 | prep_emulate_rdhi16rdlo12rs8rm0_wflags(kprobe_opcode_t insn, | |
952 | struct arch_specific_insn *asi) | |
953 | { | |
983ebd93 JM |
954 | if (is_r15(insn, 16) || is_r15(insn, 12)) |
955 | return INSN_REJECTED; /* RdHi or RdLo is PC */ | |
956 | ||
35aa1df4 QB |
957 | insn &= 0xfff000f0; /* RdHi = r0, RdLo = r1 */ |
958 | insn |= 0x00001203; /* Rs = r2, Rm = r3 */ | |
959 | asi->insn[0] = insn; | |
960 | asi->insn_handler = emulate_rdhi16rdlo12rs8rm0_rwflags; | |
961 | return INSN_GOOD; | |
962 | } | |
963 | ||
964 | /* | |
965 | * For the instruction masking and comparisons in all the "space_*" | |
966 | * functions below, Do _not_ rearrange the order of tests unless | |
967 | * you're very, very sure of what you are doing. For the sake of | |
968 | * efficiency, the masks for some tests sometimes assume other test | |
969 | * have been done prior to them so the number of patterns to test | |
970 | * for an instruction set can be as broad as possible to reduce the | |
971 | * number of tests needed. | |
972 | */ | |
973 | ||
974 | static enum kprobe_insn __kprobes | |
975 | space_1111(kprobe_opcode_t insn, struct arch_specific_insn *asi) | |
976 | { | |
41713d13 JM |
977 | /* memory hint : 1111 0100 x001 xxxx xxxx xxxx xxxx xxxx : */ |
978 | /* PLDI : 1111 0100 x101 xxxx xxxx xxxx xxxx xxxx : */ | |
979 | /* PLDW : 1111 0101 x001 xxxx xxxx xxxx xxxx xxxx : */ | |
980 | /* PLD : 1111 0101 x101 xxxx xxxx xxxx xxxx xxxx : */ | |
981 | if ((insn & 0xfe300000) == 0xf4100000) { | |
982 | asi->insn_handler = emulate_nop; | |
983 | return INSN_GOOD_NO_SLOT; | |
35aa1df4 QB |
984 | } |
985 | ||
986 | /* BLX(1) : 1111 101x xxxx xxxx xxxx xxxx xxxx xxxx : */ | |
987 | if ((insn & 0xfe000000) == 0xfa000000) { | |
988 | asi->insn_handler = simulate_blx1; | |
989 | return INSN_GOOD_NO_SLOT; | |
990 | } | |
991 | ||
72c2bab2 JM |
992 | /* CPS : 1111 0001 0000 xxx0 xxxx xxxx xx0x xxxx */ |
993 | /* SETEND: 1111 0001 0000 0001 xxxx xxxx 0000 xxxx */ | |
994 | ||
995 | /* SRS : 1111 100x x1x0 xxxx xxxx xxxx xxxx xxxx */ | |
996 | /* RFE : 1111 100x x0x1 xxxx xxxx xxxx xxxx xxxx */ | |
35aa1df4 | 997 | |
fa1a03b4 | 998 | /* Coprocessor instructions... */ |
35aa1df4 QB |
999 | /* MCRR2 : 1111 1100 0100 xxxx xxxx xxxx xxxx xxxx : (Rd != Rn) */ |
1000 | /* MRRC2 : 1111 1100 0101 xxxx xxxx xxxx xxxx xxxx : (Rd != Rn) */ | |
fa1a03b4 JM |
1001 | /* LDC2 : 1111 110x xxx1 xxxx xxxx xxxx xxxx xxxx */ |
1002 | /* STC2 : 1111 110x xxx0 xxxx xxxx xxxx xxxx xxxx */ | |
1003 | /* CDP2 : 1111 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */ | |
1004 | /* MCR2 : 1111 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */ | |
1005 | /* MRC2 : 1111 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */ | |
35aa1df4 | 1006 | |
fa1a03b4 | 1007 | return INSN_REJECTED; |
35aa1df4 QB |
1008 | } |
1009 | ||
1010 | static enum kprobe_insn __kprobes | |
1011 | space_cccc_000x(kprobe_opcode_t insn, struct arch_specific_insn *asi) | |
1012 | { | |
1013 | /* cccc 0001 0xx0 xxxx xxxx xxxx xxxx xxx0 xxxx */ | |
1014 | if ((insn & 0x0f900010) == 0x01000000) { | |
1015 | ||
51468ea9 | 1016 | /* MRS cpsr : cccc 0001 0000 xxxx xxxx xxxx 0000 xxxx */ |
c412aba2 | 1017 | if ((insn & 0x0ff000f0) == 0x01000000) { |
983ebd93 JM |
1018 | if (is_r15(insn, 12)) |
1019 | return INSN_REJECTED; /* Rd is PC */ | |
c412aba2 JM |
1020 | asi->insn_handler = simulate_mrs; |
1021 | return INSN_GOOD_NO_SLOT; | |
1022 | } | |
35aa1df4 QB |
1023 | |
1024 | /* SMLALxy : cccc 0001 0100 xxxx xxxx xxxx 1xx0 xxxx */ | |
1025 | if ((insn & 0x0ff00090) == 0x01400080) | |
cdc25361 JM |
1026 | return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn, |
1027 | asi); | |
35aa1df4 QB |
1028 | |
1029 | /* SMULWy : cccc 0001 0010 xxxx xxxx xxxx 1x10 xxxx */ | |
1030 | /* SMULxy : cccc 0001 0110 xxxx xxxx xxxx 1xx0 xxxx */ | |
1031 | if ((insn & 0x0ff000b0) == 0x012000a0 || | |
1032 | (insn & 0x0ff00090) == 0x01600080) | |
1033 | return prep_emulate_rd16rs8rm0_wflags(insn, asi); | |
1034 | ||
1035 | /* SMLAxy : cccc 0001 0000 xxxx xxxx xxxx 1xx0 xxxx : Q */ | |
75539aea | 1036 | /* SMLAWy : cccc 0001 0010 xxxx xxxx xxxx 1x00 xxxx : Q */ |
f704a6e2 JM |
1037 | if ((insn & 0x0ff00090) == 0x01000080 || |
1038 | (insn & 0x0ff000b0) == 0x01200080) | |
1039 | return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi); | |
35aa1df4 | 1040 | |
f704a6e2 JM |
1041 | /* BXJ : cccc 0001 0010 xxxx xxxx xxxx 0010 xxxx */ |
1042 | /* MSR : cccc 0001 0x10 xxxx xxxx xxxx 0000 xxxx */ | |
1043 | /* MRS spsr : cccc 0001 0100 xxxx xxxx xxxx 0000 xxxx */ | |
1044 | ||
1045 | /* Other instruction encodings aren't yet defined */ | |
1046 | return INSN_REJECTED; | |
35aa1df4 QB |
1047 | } |
1048 | ||
1049 | /* cccc 0001 0xx0 xxxx xxxx xxxx xxxx 0xx1 xxxx */ | |
1050 | else if ((insn & 0x0f900090) == 0x01000010) { | |
1051 | ||
35aa1df4 QB |
1052 | /* BLX(2) : cccc 0001 0010 xxxx xxxx xxxx 0011 xxxx */ |
1053 | /* BX : cccc 0001 0010 xxxx xxxx xxxx 0001 xxxx */ | |
1054 | if ((insn & 0x0ff000d0) == 0x01200010) { | |
983ebd93 JM |
1055 | if ((insn & 0x0ff000ff) == 0x0120003f) |
1056 | return INSN_REJECTED; /* BLX pc */ | |
35aa1df4 | 1057 | asi->insn_handler = simulate_blx2bx; |
a539f5d4 | 1058 | return INSN_GOOD_NO_SLOT; |
35aa1df4 QB |
1059 | } |
1060 | ||
1061 | /* CLZ : cccc 0001 0110 xxxx xxxx xxxx 0001 xxxx */ | |
1062 | if ((insn & 0x0ff000f0) == 0x01600010) | |
1063 | return prep_emulate_rd12rm0(insn, asi); | |
1064 | ||
1065 | /* QADD : cccc 0001 0000 xxxx xxxx xxxx 0101 xxxx :Q */ | |
1066 | /* QSUB : cccc 0001 0010 xxxx xxxx xxxx 0101 xxxx :Q */ | |
1067 | /* QDADD : cccc 0001 0100 xxxx xxxx xxxx 0101 xxxx :Q */ | |
1068 | /* QDSUB : cccc 0001 0110 xxxx xxxx xxxx 0101 xxxx :Q */ | |
f704a6e2 JM |
1069 | if ((insn & 0x0f9000f0) == 0x01000050) |
1070 | return prep_emulate_rd12rn16rm0_wflags(insn, asi); | |
1071 | ||
1072 | /* BKPT : 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx */ | |
1073 | /* SMC : cccc 0001 0110 xxxx xxxx xxxx 0111 xxxx */ | |
1074 | ||
1075 | /* Other instruction encodings aren't yet defined */ | |
1076 | return INSN_REJECTED; | |
35aa1df4 QB |
1077 | } |
1078 | ||
1079 | /* cccc 0000 xxxx xxxx xxxx xxxx xxxx 1001 xxxx */ | |
ba48d407 | 1080 | else if ((insn & 0x0f0000f0) == 0x00000090) { |
35aa1df4 QB |
1081 | |
1082 | /* MUL : cccc 0000 0000 xxxx xxxx xxxx 1001 xxxx : */ | |
1083 | /* MULS : cccc 0000 0001 xxxx xxxx xxxx 1001 xxxx :cc */ | |
1084 | /* MLA : cccc 0000 0010 xxxx xxxx xxxx 1001 xxxx : */ | |
1085 | /* MLAS : cccc 0000 0011 xxxx xxxx xxxx 1001 xxxx :cc */ | |
1086 | /* UMAAL : cccc 0000 0100 xxxx xxxx xxxx 1001 xxxx : */ | |
ba48d407 JM |
1087 | /* undef : cccc 0000 0101 xxxx xxxx xxxx 1001 xxxx : */ |
1088 | /* MLS : cccc 0000 0110 xxxx xxxx xxxx 1001 xxxx : */ | |
1089 | /* undef : cccc 0000 0111 xxxx xxxx xxxx 1001 xxxx : */ | |
35aa1df4 QB |
1090 | /* UMULL : cccc 0000 1000 xxxx xxxx xxxx 1001 xxxx : */ |
1091 | /* UMULLS : cccc 0000 1001 xxxx xxxx xxxx 1001 xxxx :cc */ | |
1092 | /* UMLAL : cccc 0000 1010 xxxx xxxx xxxx 1001 xxxx : */ | |
1093 | /* UMLALS : cccc 0000 1011 xxxx xxxx xxxx 1001 xxxx :cc */ | |
1094 | /* SMULL : cccc 0000 1100 xxxx xxxx xxxx 1001 xxxx : */ | |
1095 | /* SMULLS : cccc 0000 1101 xxxx xxxx xxxx 1001 xxxx :cc */ | |
1096 | /* SMLAL : cccc 0000 1110 xxxx xxxx xxxx 1001 xxxx : */ | |
1097 | /* SMLALS : cccc 0000 1111 xxxx xxxx xxxx 1001 xxxx :cc */ | |
cdc25361 | 1098 | if ((insn & 0x00d00000) == 0x00500000) |
ba48d407 | 1099 | return INSN_REJECTED; |
cdc25361 JM |
1100 | else if ((insn & 0x00e00000) == 0x00000000) |
1101 | return prep_emulate_rd16rs8rm0_wflags(insn, asi); | |
1102 | else if ((insn & 0x00a00000) == 0x00200000) | |
1103 | return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi); | |
1104 | else | |
1105 | return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn, | |
1106 | asi); | |
35aa1df4 QB |
1107 | } |
1108 | ||
1109 | /* cccc 000x xxxx xxxx xxxx xxxx xxxx 1xx1 xxxx */ | |
1110 | else if ((insn & 0x0e000090) == 0x00000090) { | |
1111 | ||
1112 | /* SWP : cccc 0001 0000 xxxx xxxx xxxx 1001 xxxx */ | |
1113 | /* SWPB : cccc 0001 0100 xxxx xxxx xxxx 1001 xxxx */ | |
ec58d7f2 JM |
1114 | /* ??? : cccc 0001 0x01 xxxx xxxx xxxx 1001 xxxx */ |
1115 | /* ??? : cccc 0001 0x10 xxxx xxxx xxxx 1001 xxxx */ | |
1116 | /* ??? : cccc 0001 0x11 xxxx xxxx xxxx 1001 xxxx */ | |
35aa1df4 QB |
1117 | /* STREX : cccc 0001 1000 xxxx xxxx xxxx 1001 xxxx */ |
1118 | /* LDREX : cccc 0001 1001 xxxx xxxx xxxx 1001 xxxx */ | |
ec58d7f2 JM |
1119 | /* STREXD: cccc 0001 1010 xxxx xxxx xxxx 1001 xxxx */ |
1120 | /* LDREXD: cccc 0001 1011 xxxx xxxx xxxx 1001 xxxx */ | |
1121 | /* STREXB: cccc 0001 1100 xxxx xxxx xxxx 1001 xxxx */ | |
1122 | /* LDREXB: cccc 0001 1101 xxxx xxxx xxxx 1001 xxxx */ | |
1123 | /* STREXH: cccc 0001 1110 xxxx xxxx xxxx 1001 xxxx */ | |
1124 | /* LDREXH: cccc 0001 1111 xxxx xxxx xxxx 1001 xxxx */ | |
1125 | ||
1126 | /* LDRD : cccc 000x xxx0 xxxx xxxx xxxx 1101 xxxx */ | |
1127 | /* STRD : cccc 000x xxx0 xxxx xxxx xxxx 1111 xxxx */ | |
35aa1df4 QB |
1128 | /* LDRH : cccc 000x xxx1 xxxx xxxx xxxx 1011 xxxx */ |
1129 | /* STRH : cccc 000x xxx0 xxxx xxxx xxxx 1011 xxxx */ | |
1130 | /* LDRSB : cccc 000x xxx1 xxxx xxxx xxxx 1101 xxxx */ | |
1131 | /* LDRSH : cccc 000x xxx1 xxxx xxxx xxxx 1111 xxxx */ | |
ec58d7f2 JM |
1132 | if ((insn & 0x0f0000f0) == 0x01000090) { |
1133 | if ((insn & 0x0fb000f0) == 0x01000090) { | |
1134 | /* SWP/SWPB */ | |
1135 | return prep_emulate_rd12rn16rm0_wflags(insn, | |
1136 | asi); | |
1137 | } else { | |
1138 | /* STREX/LDREX variants and unallocaed space */ | |
1139 | return INSN_REJECTED; | |
1140 | } | |
1141 | ||
35aa1df4 QB |
1142 | } else if ((insn & 0x0e1000d0) == 0x00000d0) { |
1143 | /* STRD/LDRD */ | |
54823acc JM |
1144 | if ((insn & 0x0000e000) == 0x0000e000) |
1145 | return INSN_REJECTED; /* Rd is LR or PC */ | |
1146 | if (is_writeback(insn) && is_r15(insn, 16)) | |
1147 | return INSN_REJECTED; /* Writeback to PC */ | |
1148 | ||
35aa1df4 QB |
1149 | insn &= 0xfff00fff; |
1150 | insn |= 0x00002000; /* Rn = r0, Rd = r2 */ | |
5c6b76fc JM |
1151 | if (!(insn & (1 << 22))) { |
1152 | /* Register index */ | |
35aa1df4 QB |
1153 | insn &= ~0xf; |
1154 | insn |= 1; /* Rm = r1 */ | |
1155 | } | |
1156 | asi->insn[0] = insn; | |
1157 | asi->insn_handler = | |
1158 | (insn & (1 << 5)) ? emulate_strd : emulate_ldrd; | |
1159 | return INSN_GOOD; | |
1160 | } | |
1161 | ||
54823acc JM |
1162 | /* LDRH/STRH/LDRSB/LDRSH */ |
1163 | if (is_r15(insn, 12)) | |
1164 | return INSN_REJECTED; /* Rd is PC */ | |
35aa1df4 QB |
1165 | return prep_emulate_ldr_str(insn, asi); |
1166 | } | |
1167 | ||
1168 | /* cccc 000x xxxx xxxx xxxx xxxx xxxx xxxx xxxx */ | |
1169 | ||
1170 | /* | |
1171 | * ALU op with S bit and Rd == 15 : | |
cdc25361 | 1172 | * cccc 000x xxx1 xxxx 1111 xxxx xxxx xxxx |
35aa1df4 QB |
1173 | */ |
1174 | if ((insn & 0x0e10f000) == 0x0010f000) | |
1175 | return INSN_REJECTED; | |
1176 | ||
1177 | /* | |
1178 | * "mov ip, sp" is the most common kprobe'd instruction by far. | |
1179 | * Check and optimize for it explicitly. | |
1180 | */ | |
1181 | if (insn == 0xe1a0c00d) { | |
1182 | asi->insn_handler = simulate_mov_ipsp; | |
1183 | return INSN_GOOD_NO_SLOT; | |
1184 | } | |
1185 | ||
1186 | /* | |
1187 | * Data processing: Immediate-shift / Register-shift | |
1188 | * ALU op : cccc 000x xxxx xxxx xxxx xxxx xxxx xxxx | |
1189 | * CPY : cccc 0001 1010 xxxx xxxx 0000 0000 xxxx | |
1190 | * MOV : cccc 0001 101x xxxx xxxx xxxx xxxx xxxx | |
1191 | * *S (bit 20) updates condition codes | |
1192 | * ADC/SBC/RSC reads the C flag | |
1193 | */ | |
1194 | insn &= 0xfff00ff0; /* Rn = r0, Rd = r0 */ | |
1195 | insn |= 0x00000001; /* Rm = r1 */ | |
1196 | if (insn & 0x010) { | |
1197 | insn &= 0xfffff0ff; /* register shift */ | |
1198 | insn |= 0x00000200; /* Rs = r2 */ | |
1199 | } | |
1200 | asi->insn[0] = insn; | |
ad111ce4 JM |
1201 | |
1202 | if ((insn & 0x0f900000) == 0x01100000) { | |
1203 | /* | |
1204 | * TST : cccc 0001 0001 xxxx xxxx xxxx xxxx xxxx | |
1205 | * TEQ : cccc 0001 0011 xxxx xxxx xxxx xxxx xxxx | |
1206 | * CMP : cccc 0001 0101 xxxx xxxx xxxx xxxx xxxx | |
1207 | * CMN : cccc 0001 0111 xxxx xxxx xxxx xxxx xxxx | |
1208 | */ | |
1209 | asi->insn_handler = emulate_alu_tests; | |
1210 | } else { | |
1211 | /* ALU ops which write to Rd */ | |
1212 | asi->insn_handler = (insn & (1 << 20)) ? /* S-bit */ | |
35aa1df4 | 1213 | emulate_alu_rwflags : emulate_alu_rflags; |
ad111ce4 | 1214 | } |
35aa1df4 QB |
1215 | return INSN_GOOD; |
1216 | } | |
1217 | ||
1218 | static enum kprobe_insn __kprobes | |
1219 | space_cccc_001x(kprobe_opcode_t insn, struct arch_specific_insn *asi) | |
1220 | { | |
c9836777 JM |
1221 | /* MOVW : cccc 0011 0000 xxxx xxxx xxxx xxxx xxxx */ |
1222 | /* MOVT : cccc 0011 0100 xxxx xxxx xxxx xxxx xxxx */ | |
1223 | if ((insn & 0x0fb00000) == 0x03000000) | |
1224 | return prep_emulate_rd12_modify(insn, asi); | |
1225 | ||
94254930 JM |
1226 | /* hints : cccc 0011 0010 0000 xxxx xxxx xxxx xxxx */ |
1227 | if ((insn & 0x0fff0000) == 0x03200000) { | |
1228 | unsigned op2 = insn & 0x000000ff; | |
1229 | if (op2 == 0x01 || op2 == 0x04) { | |
1230 | /* YIELD : cccc 0011 0010 0000 xxxx xxxx 0000 0001 */ | |
1231 | /* SEV : cccc 0011 0010 0000 xxxx xxxx 0000 0100 */ | |
1232 | asi->insn[0] = insn; | |
1233 | asi->insn_handler = emulate_none; | |
1234 | return INSN_GOOD; | |
1235 | } else if (op2 <= 0x03) { | |
1236 | /* NOP : cccc 0011 0010 0000 xxxx xxxx 0000 0000 */ | |
1237 | /* WFE : cccc 0011 0010 0000 xxxx xxxx 0000 0010 */ | |
1238 | /* WFI : cccc 0011 0010 0000 xxxx xxxx 0000 0011 */ | |
1239 | /* | |
1240 | * We make WFE and WFI true NOPs to avoid stalls due | |
1241 | * to missing events whilst processing the probe. | |
1242 | */ | |
1243 | asi->insn_handler = emulate_nop; | |
1244 | return INSN_GOOD_NO_SLOT; | |
1245 | } | |
1246 | /* For DBG and unallocated hints it's safest to reject them */ | |
1247 | return INSN_REJECTED; | |
1248 | } | |
1249 | ||
35aa1df4 QB |
1250 | /* |
1251 | * MSR : cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx | |
35aa1df4 QB |
1252 | * ALU op with S bit and Rd == 15 : |
1253 | * cccc 001x xxx1 xxxx 1111 xxxx xxxx xxxx | |
1254 | */ | |
ccdf2e1b | 1255 | if ((insn & 0x0fb00000) == 0x03200000 || /* MSR */ |
35aa1df4 QB |
1256 | (insn & 0x0e10f000) == 0x0210f000) /* ALU s-bit, R15 */ |
1257 | return INSN_REJECTED; | |
1258 | ||
1259 | /* | |
1260 | * Data processing: 32-bit Immediate | |
1261 | * ALU op : cccc 001x xxxx xxxx xxxx xxxx xxxx xxxx | |
1262 | * MOV : cccc 0011 101x xxxx xxxx xxxx xxxx xxxx | |
1263 | * *S (bit 20) updates condition codes | |
1264 | * ADC/SBC/RSC reads the C flag | |
1265 | */ | |
896a74e1 | 1266 | insn &= 0xfff00fff; /* Rn = r0 and Rd = r0 */ |
35aa1df4 | 1267 | asi->insn[0] = insn; |
ad111ce4 JM |
1268 | |
1269 | if ((insn & 0x0f900000) == 0x03100000) { | |
1270 | /* | |
1271 | * TST : cccc 0011 0001 xxxx xxxx xxxx xxxx xxxx | |
1272 | * TEQ : cccc 0011 0011 xxxx xxxx xxxx xxxx xxxx | |
1273 | * CMP : cccc 0011 0101 xxxx xxxx xxxx xxxx xxxx | |
1274 | * CMN : cccc 0011 0111 xxxx xxxx xxxx xxxx xxxx | |
1275 | */ | |
1276 | asi->insn_handler = emulate_alu_tests_imm; | |
1277 | } else { | |
1278 | /* ALU ops which write to Rd */ | |
1279 | asi->insn_handler = (insn & (1 << 20)) ? /* S-bit */ | |
35aa1df4 | 1280 | emulate_alu_imm_rwflags : emulate_alu_imm_rflags; |
ad111ce4 | 1281 | } |
35aa1df4 QB |
1282 | return INSN_GOOD; |
1283 | } | |
1284 | ||
1285 | static enum kprobe_insn __kprobes | |
1286 | space_cccc_0110__1(kprobe_opcode_t insn, struct arch_specific_insn *asi) | |
1287 | { | |
1288 | /* SEL : cccc 0110 1000 xxxx xxxx xxxx 1011 xxxx GE: !!! */ | |
1289 | if ((insn & 0x0ff000f0) == 0x068000b0) { | |
983ebd93 JM |
1290 | if (is_r15(insn, 12)) |
1291 | return INSN_REJECTED; /* Rd is PC */ | |
35aa1df4 QB |
1292 | insn &= 0xfff00ff0; /* Rd = r0, Rn = r0 */ |
1293 | insn |= 0x00000001; /* Rm = r1 */ | |
1294 | asi->insn[0] = insn; | |
1295 | asi->insn_handler = emulate_sel; | |
1296 | return INSN_GOOD; | |
1297 | } | |
1298 | ||
1299 | /* SSAT : cccc 0110 101x xxxx xxxx xxxx xx01 xxxx :Q */ | |
1300 | /* USAT : cccc 0110 111x xxxx xxxx xxxx xx01 xxxx :Q */ | |
1301 | /* SSAT16 : cccc 0110 1010 xxxx xxxx xxxx 0011 xxxx :Q */ | |
1302 | /* USAT16 : cccc 0110 1110 xxxx xxxx xxxx 0011 xxxx :Q */ | |
1303 | if ((insn & 0x0fa00030) == 0x06a00010 || | |
1304 | (insn & 0x0fb000f0) == 0x06a00030) { | |
983ebd93 JM |
1305 | if (is_r15(insn, 12)) |
1306 | return INSN_REJECTED; /* Rd is PC */ | |
35aa1df4 QB |
1307 | insn &= 0xffff0ff0; /* Rd = r0, Rm = r0 */ |
1308 | asi->insn[0] = insn; | |
1309 | asi->insn_handler = emulate_sat; | |
1310 | return INSN_GOOD; | |
1311 | } | |
1312 | ||
1313 | /* REV : cccc 0110 1011 xxxx xxxx xxxx 0011 xxxx */ | |
1314 | /* REV16 : cccc 0110 1011 xxxx xxxx xxxx 1011 xxxx */ | |
0e384ed1 | 1315 | /* RBIT : cccc 0110 1111 xxxx xxxx xxxx 0011 xxxx */ |
35aa1df4 QB |
1316 | /* REVSH : cccc 0110 1111 xxxx xxxx xxxx 1011 xxxx */ |
1317 | if ((insn & 0x0ff00070) == 0x06b00030 || | |
0e384ed1 | 1318 | (insn & 0x0ff00070) == 0x06f00030) |
35aa1df4 QB |
1319 | return prep_emulate_rd12rm0(insn, asi); |
1320 | ||
780b5c11 | 1321 | /* ??? : cccc 0110 0000 xxxx xxxx xxxx xxx1 xxxx : */ |
35aa1df4 QB |
1322 | /* SADD16 : cccc 0110 0001 xxxx xxxx xxxx 0001 xxxx :GE */ |
1323 | /* SADDSUBX : cccc 0110 0001 xxxx xxxx xxxx 0011 xxxx :GE */ | |
1324 | /* SSUBADDX : cccc 0110 0001 xxxx xxxx xxxx 0101 xxxx :GE */ | |
1325 | /* SSUB16 : cccc 0110 0001 xxxx xxxx xxxx 0111 xxxx :GE */ | |
1326 | /* SADD8 : cccc 0110 0001 xxxx xxxx xxxx 1001 xxxx :GE */ | |
780b5c11 JM |
1327 | /* ??? : cccc 0110 0001 xxxx xxxx xxxx 1011 xxxx : */ |
1328 | /* ??? : cccc 0110 0001 xxxx xxxx xxxx 1101 xxxx : */ | |
35aa1df4 QB |
1329 | /* SSUB8 : cccc 0110 0001 xxxx xxxx xxxx 1111 xxxx :GE */ |
1330 | /* QADD16 : cccc 0110 0010 xxxx xxxx xxxx 0001 xxxx : */ | |
1331 | /* QADDSUBX : cccc 0110 0010 xxxx xxxx xxxx 0011 xxxx : */ | |
1332 | /* QSUBADDX : cccc 0110 0010 xxxx xxxx xxxx 0101 xxxx : */ | |
1333 | /* QSUB16 : cccc 0110 0010 xxxx xxxx xxxx 0111 xxxx : */ | |
1334 | /* QADD8 : cccc 0110 0010 xxxx xxxx xxxx 1001 xxxx : */ | |
780b5c11 JM |
1335 | /* ??? : cccc 0110 0010 xxxx xxxx xxxx 1011 xxxx : */ |
1336 | /* ??? : cccc 0110 0010 xxxx xxxx xxxx 1101 xxxx : */ | |
35aa1df4 QB |
1337 | /* QSUB8 : cccc 0110 0010 xxxx xxxx xxxx 1111 xxxx : */ |
1338 | /* SHADD16 : cccc 0110 0011 xxxx xxxx xxxx 0001 xxxx : */ | |
1339 | /* SHADDSUBX : cccc 0110 0011 xxxx xxxx xxxx 0011 xxxx : */ | |
1340 | /* SHSUBADDX : cccc 0110 0011 xxxx xxxx xxxx 0101 xxxx : */ | |
1341 | /* SHSUB16 : cccc 0110 0011 xxxx xxxx xxxx 0111 xxxx : */ | |
1342 | /* SHADD8 : cccc 0110 0011 xxxx xxxx xxxx 1001 xxxx : */ | |
780b5c11 JM |
1343 | /* ??? : cccc 0110 0011 xxxx xxxx xxxx 1011 xxxx : */ |
1344 | /* ??? : cccc 0110 0011 xxxx xxxx xxxx 1101 xxxx : */ | |
35aa1df4 | 1345 | /* SHSUB8 : cccc 0110 0011 xxxx xxxx xxxx 1111 xxxx : */ |
780b5c11 | 1346 | /* ??? : cccc 0110 0100 xxxx xxxx xxxx xxx1 xxxx : */ |
35aa1df4 QB |
1347 | /* UADD16 : cccc 0110 0101 xxxx xxxx xxxx 0001 xxxx :GE */ |
1348 | /* UADDSUBX : cccc 0110 0101 xxxx xxxx xxxx 0011 xxxx :GE */ | |
1349 | /* USUBADDX : cccc 0110 0101 xxxx xxxx xxxx 0101 xxxx :GE */ | |
1350 | /* USUB16 : cccc 0110 0101 xxxx xxxx xxxx 0111 xxxx :GE */ | |
1351 | /* UADD8 : cccc 0110 0101 xxxx xxxx xxxx 1001 xxxx :GE */ | |
780b5c11 JM |
1352 | /* ??? : cccc 0110 0101 xxxx xxxx xxxx 1011 xxxx : */ |
1353 | /* ??? : cccc 0110 0101 xxxx xxxx xxxx 1101 xxxx : */ | |
35aa1df4 QB |
1354 | /* USUB8 : cccc 0110 0101 xxxx xxxx xxxx 1111 xxxx :GE */ |
1355 | /* UQADD16 : cccc 0110 0110 xxxx xxxx xxxx 0001 xxxx : */ | |
1356 | /* UQADDSUBX : cccc 0110 0110 xxxx xxxx xxxx 0011 xxxx : */ | |
1357 | /* UQSUBADDX : cccc 0110 0110 xxxx xxxx xxxx 0101 xxxx : */ | |
1358 | /* UQSUB16 : cccc 0110 0110 xxxx xxxx xxxx 0111 xxxx : */ | |
1359 | /* UQADD8 : cccc 0110 0110 xxxx xxxx xxxx 1001 xxxx : */ | |
780b5c11 JM |
1360 | /* ??? : cccc 0110 0110 xxxx xxxx xxxx 1011 xxxx : */ |
1361 | /* ??? : cccc 0110 0110 xxxx xxxx xxxx 1101 xxxx : */ | |
35aa1df4 QB |
1362 | /* UQSUB8 : cccc 0110 0110 xxxx xxxx xxxx 1111 xxxx : */ |
1363 | /* UHADD16 : cccc 0110 0111 xxxx xxxx xxxx 0001 xxxx : */ | |
1364 | /* UHADDSUBX : cccc 0110 0111 xxxx xxxx xxxx 0011 xxxx : */ | |
1365 | /* UHSUBADDX : cccc 0110 0111 xxxx xxxx xxxx 0101 xxxx : */ | |
1366 | /* UHSUB16 : cccc 0110 0111 xxxx xxxx xxxx 0111 xxxx : */ | |
1367 | /* UHADD8 : cccc 0110 0111 xxxx xxxx xxxx 1001 xxxx : */ | |
780b5c11 JM |
1368 | /* ??? : cccc 0110 0111 xxxx xxxx xxxx 1011 xxxx : */ |
1369 | /* ??? : cccc 0110 0111 xxxx xxxx xxxx 1101 xxxx : */ | |
35aa1df4 | 1370 | /* UHSUB8 : cccc 0110 0111 xxxx xxxx xxxx 1111 xxxx : */ |
780b5c11 JM |
1371 | if ((insn & 0x0f800010) == 0x06000010) { |
1372 | if ((insn & 0x00300000) == 0x00000000 || | |
1373 | (insn & 0x000000e0) == 0x000000a0 || | |
1374 | (insn & 0x000000e0) == 0x000000c0) | |
1375 | return INSN_REJECTED; /* Unallocated space */ | |
1376 | return prep_emulate_rd12rn16rm0_wflags(insn, asi); | |
1377 | } | |
1378 | ||
35aa1df4 QB |
1379 | /* PKHBT : cccc 0110 1000 xxxx xxxx xxxx x001 xxxx : */ |
1380 | /* PKHTB : cccc 0110 1000 xxxx xxxx xxxx x101 xxxx : */ | |
780b5c11 JM |
1381 | if ((insn & 0x0ff00030) == 0x06800010) |
1382 | return prep_emulate_rd12rn16rm0_wflags(insn, asi); | |
1383 | ||
35aa1df4 | 1384 | /* SXTAB16 : cccc 0110 1000 xxxx xxxx xxxx 0111 xxxx : */ |
8dd7cfbe | 1385 | /* SXTB16 : cccc 0110 1000 1111 xxxx xxxx 0111 xxxx : */ |
780b5c11 | 1386 | /* ??? : cccc 0110 1001 xxxx xxxx xxxx 0111 xxxx : */ |
35aa1df4 | 1387 | /* SXTAB : cccc 0110 1010 xxxx xxxx xxxx 0111 xxxx : */ |
8dd7cfbe | 1388 | /* SXTB : cccc 0110 1010 1111 xxxx xxxx 0111 xxxx : */ |
35aa1df4 | 1389 | /* SXTAH : cccc 0110 1011 xxxx xxxx xxxx 0111 xxxx : */ |
8dd7cfbe | 1390 | /* SXTH : cccc 0110 1011 1111 xxxx xxxx 0111 xxxx : */ |
35aa1df4 | 1391 | /* UXTAB16 : cccc 0110 1100 xxxx xxxx xxxx 0111 xxxx : */ |
8dd7cfbe | 1392 | /* UXTB16 : cccc 0110 1100 1111 xxxx xxxx 0111 xxxx : */ |
780b5c11 | 1393 | /* ??? : cccc 0110 1101 xxxx xxxx xxxx 0111 xxxx : */ |
35aa1df4 | 1394 | /* UXTAB : cccc 0110 1110 xxxx xxxx xxxx 0111 xxxx : */ |
8dd7cfbe | 1395 | /* UXTB : cccc 0110 1110 1111 xxxx xxxx 0111 xxxx : */ |
35aa1df4 | 1396 | /* UXTAH : cccc 0110 1111 xxxx xxxx xxxx 0111 xxxx : */ |
8dd7cfbe | 1397 | /* UXTH : cccc 0110 1111 1111 xxxx xxxx 0111 xxxx : */ |
780b5c11 JM |
1398 | if ((insn & 0x0f8000f0) == 0x06800070) { |
1399 | if ((insn & 0x00300000) == 0x00100000) | |
1400 | return INSN_REJECTED; /* Unallocated space */ | |
8dd7cfbe | 1401 | |
cdc25361 | 1402 | if ((insn & 0x000f0000) == 0x000f0000) |
8dd7cfbe | 1403 | return prep_emulate_rd12rm0(insn, asi); |
cdc25361 | 1404 | else |
8dd7cfbe | 1405 | return prep_emulate_rd12rn16rm0_wflags(insn, asi); |
780b5c11 JM |
1406 | } |
1407 | ||
1408 | /* Other instruction encodings aren't yet defined */ | |
1409 | return INSN_REJECTED; | |
35aa1df4 QB |
1410 | } |
1411 | ||
1412 | static enum kprobe_insn __kprobes | |
1413 | space_cccc_0111__1(kprobe_opcode_t insn, struct arch_specific_insn *asi) | |
1414 | { | |
1415 | /* Undef : cccc 0111 1111 xxxx xxxx xxxx 1111 xxxx */ | |
1416 | if ((insn & 0x0ff000f0) == 0x03f000f0) | |
1417 | return INSN_REJECTED; | |
1418 | ||
35aa1df4 QB |
1419 | /* SMLALD : cccc 0111 0100 xxxx xxxx xxxx 00x1 xxxx */ |
1420 | /* SMLSLD : cccc 0111 0100 xxxx xxxx xxxx 01x1 xxxx */ | |
1421 | if ((insn & 0x0ff00090) == 0x07400010) | |
1422 | return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn, asi); | |
1423 | ||
1424 | /* SMLAD : cccc 0111 0000 xxxx xxxx xxxx 00x1 xxxx :Q */ | |
038c3839 | 1425 | /* SMUAD : cccc 0111 0000 xxxx 1111 xxxx 00x1 xxxx :Q */ |
35aa1df4 | 1426 | /* SMLSD : cccc 0111 0000 xxxx xxxx xxxx 01x1 xxxx :Q */ |
038c3839 | 1427 | /* SMUSD : cccc 0111 0000 xxxx 1111 xxxx 01x1 xxxx : */ |
35aa1df4 | 1428 | /* SMMLA : cccc 0111 0101 xxxx xxxx xxxx 00x1 xxxx : */ |
038c3839 | 1429 | /* SMMUL : cccc 0111 0101 xxxx 1111 xxxx 00x1 xxxx : */ |
c6e4ae32 JM |
1430 | /* USADA8 : cccc 0111 1000 xxxx xxxx xxxx 0001 xxxx : */ |
1431 | /* USAD8 : cccc 0111 1000 xxxx 1111 xxxx 0001 xxxx : */ | |
35aa1df4 | 1432 | if ((insn & 0x0ff00090) == 0x07000010 || |
c6e4ae32 JM |
1433 | (insn & 0x0ff000d0) == 0x07500010 || |
1434 | (insn & 0x0ff000f0) == 0x07800010) { | |
038c3839 | 1435 | |
cdc25361 | 1436 | if ((insn & 0x0000f000) == 0x0000f000) |
038c3839 | 1437 | return prep_emulate_rd16rs8rm0_wflags(insn, asi); |
cdc25361 | 1438 | else |
038c3839 | 1439 | return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi); |
038c3839 JM |
1440 | } |
1441 | ||
1442 | /* SMMLS : cccc 0111 0101 xxxx xxxx xxxx 11x1 xxxx : */ | |
1443 | if ((insn & 0x0ff000d0) == 0x075000d0) | |
35aa1df4 QB |
1444 | return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi); |
1445 | ||
20e8155e JM |
1446 | /* SBFX : cccc 0111 101x xxxx xxxx xxxx x101 xxxx : */ |
1447 | /* UBFX : cccc 0111 111x xxxx xxxx xxxx x101 xxxx : */ | |
1448 | if ((insn & 0x0fa00070) == 0x07a00050) | |
1449 | return prep_emulate_rd12rm0(insn, asi); | |
1450 | ||
1451 | /* BFI : cccc 0111 110x xxxx xxxx xxxx x001 xxxx : */ | |
1452 | /* BFC : cccc 0111 110x xxxx xxxx xxxx x001 1111 : */ | |
1453 | if ((insn & 0x0fe00070) == 0x07c00010) { | |
1454 | ||
1455 | if ((insn & 0x0000000f) == 0x0000000f) | |
1456 | return prep_emulate_rd12_modify(insn, asi); | |
1457 | else | |
1458 | return prep_emulate_rd12rn0_modify(insn, asi); | |
1459 | } | |
1460 | ||
038c3839 | 1461 | return INSN_REJECTED; |
35aa1df4 QB |
1462 | } |
1463 | ||
1464 | static enum kprobe_insn __kprobes | |
1465 | space_cccc_01xx(kprobe_opcode_t insn, struct arch_specific_insn *asi) | |
1466 | { | |
1467 | /* LDR : cccc 01xx x0x1 xxxx xxxx xxxx xxxx xxxx */ | |
1468 | /* LDRB : cccc 01xx x1x1 xxxx xxxx xxxx xxxx xxxx */ | |
1469 | /* LDRBT : cccc 01x0 x111 xxxx xxxx xxxx xxxx xxxx */ | |
1470 | /* LDRT : cccc 01x0 x011 xxxx xxxx xxxx xxxx xxxx */ | |
1471 | /* STR : cccc 01xx x0x0 xxxx xxxx xxxx xxxx xxxx */ | |
1472 | /* STRB : cccc 01xx x1x0 xxxx xxxx xxxx xxxx xxxx */ | |
1473 | /* STRBT : cccc 01x0 x110 xxxx xxxx xxxx xxxx xxxx */ | |
1474 | /* STRT : cccc 01x0 x010 xxxx xxxx xxxx xxxx xxxx */ | |
81ff5720 JM |
1475 | |
1476 | if ((insn & 0x00500000) == 0x00500000 && is_r15(insn, 12)) | |
1477 | return INSN_REJECTED; /* LDRB into PC */ | |
1478 | ||
35aa1df4 QB |
1479 | return prep_emulate_ldr_str(insn, asi); |
1480 | } | |
1481 | ||
1482 | static enum kprobe_insn __kprobes | |
1483 | space_cccc_100x(kprobe_opcode_t insn, struct arch_specific_insn *asi) | |
1484 | { | |
1485 | /* LDM(2) : cccc 100x x101 xxxx 0xxx xxxx xxxx xxxx */ | |
1486 | /* LDM(3) : cccc 100x x1x1 xxxx 1xxx xxxx xxxx xxxx */ | |
1487 | if ((insn & 0x0e708000) == 0x85000000 || | |
1488 | (insn & 0x0e508000) == 0x85010000) | |
1489 | return INSN_REJECTED; | |
1490 | ||
1491 | /* LDM(1) : cccc 100x x0x1 xxxx xxxx xxxx xxxx xxxx */ | |
1492 | /* STM(1) : cccc 100x x0x0 xxxx xxxx xxxx xxxx xxxx */ | |
35aa1df4 QB |
1493 | asi->insn_handler = ((insn & 0x108000) == 0x008000) ? /* STM & R15 */ |
1494 | simulate_stm1_pc : simulate_ldm1stm1; | |
a539f5d4 | 1495 | return INSN_GOOD_NO_SLOT; |
35aa1df4 QB |
1496 | } |
1497 | ||
1498 | static enum kprobe_insn __kprobes | |
1499 | space_cccc_101x(kprobe_opcode_t insn, struct arch_specific_insn *asi) | |
1500 | { | |
1501 | /* B : cccc 1010 xxxx xxxx xxxx xxxx xxxx xxxx */ | |
1502 | /* BL : cccc 1011 xxxx xxxx xxxx xxxx xxxx xxxx */ | |
35aa1df4 | 1503 | asi->insn_handler = simulate_bbl; |
a539f5d4 | 1504 | return INSN_GOOD_NO_SLOT; |
35aa1df4 QB |
1505 | } |
1506 | ||
1507 | static enum kprobe_insn __kprobes | |
ac211c69 | 1508 | space_cccc_11xx(kprobe_opcode_t insn, struct arch_specific_insn *asi) |
35aa1df4 | 1509 | { |
ac211c69 | 1510 | /* Coprocessor instructions... */ |
35aa1df4 QB |
1511 | /* MCRR : cccc 1100 0100 xxxx xxxx xxxx xxxx xxxx : (Rd!=Rn) */ |
1512 | /* MRRC : cccc 1100 0101 xxxx xxxx xxxx xxxx xxxx : (Rd!=Rn) */ | |
ac211c69 JM |
1513 | /* LDC : cccc 110x xxx1 xxxx xxxx xxxx xxxx xxxx */ |
1514 | /* STC : cccc 110x xxx0 xxxx xxxx xxxx xxxx xxxx */ | |
1515 | /* CDP : cccc 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */ | |
1516 | /* MCR : cccc 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */ | |
1517 | /* MRC : cccc 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */ | |
35aa1df4 | 1518 | |
ac211c69 | 1519 | /* SVC : cccc 1111 xxxx xxxx xxxx xxxx xxxx xxxx */ |
35aa1df4 | 1520 | |
fa1a03b4 | 1521 | return INSN_REJECTED; |
35aa1df4 QB |
1522 | } |
1523 | ||
073090cb JM |
1524 | static unsigned long __kprobes __check_eq(unsigned long cpsr) |
1525 | { | |
1526 | return cpsr & PSR_Z_BIT; | |
1527 | } | |
1528 | ||
1529 | static unsigned long __kprobes __check_ne(unsigned long cpsr) | |
1530 | { | |
1531 | return (~cpsr) & PSR_Z_BIT; | |
1532 | } | |
1533 | ||
1534 | static unsigned long __kprobes __check_cs(unsigned long cpsr) | |
1535 | { | |
1536 | return cpsr & PSR_C_BIT; | |
1537 | } | |
1538 | ||
1539 | static unsigned long __kprobes __check_cc(unsigned long cpsr) | |
1540 | { | |
1541 | return (~cpsr) & PSR_C_BIT; | |
1542 | } | |
1543 | ||
1544 | static unsigned long __kprobes __check_mi(unsigned long cpsr) | |
1545 | { | |
1546 | return cpsr & PSR_N_BIT; | |
1547 | } | |
1548 | ||
1549 | static unsigned long __kprobes __check_pl(unsigned long cpsr) | |
1550 | { | |
1551 | return (~cpsr) & PSR_N_BIT; | |
1552 | } | |
1553 | ||
1554 | static unsigned long __kprobes __check_vs(unsigned long cpsr) | |
1555 | { | |
1556 | return cpsr & PSR_V_BIT; | |
1557 | } | |
1558 | ||
1559 | static unsigned long __kprobes __check_vc(unsigned long cpsr) | |
1560 | { | |
1561 | return (~cpsr) & PSR_V_BIT; | |
1562 | } | |
1563 | ||
1564 | static unsigned long __kprobes __check_hi(unsigned long cpsr) | |
1565 | { | |
1566 | cpsr &= ~(cpsr >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */ | |
1567 | return cpsr & PSR_C_BIT; | |
1568 | } | |
1569 | ||
1570 | static unsigned long __kprobes __check_ls(unsigned long cpsr) | |
1571 | { | |
1572 | cpsr &= ~(cpsr >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */ | |
1573 | return (~cpsr) & PSR_C_BIT; | |
1574 | } | |
1575 | ||
1576 | static unsigned long __kprobes __check_ge(unsigned long cpsr) | |
1577 | { | |
1578 | cpsr ^= (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ | |
1579 | return (~cpsr) & PSR_N_BIT; | |
1580 | } | |
1581 | ||
1582 | static unsigned long __kprobes __check_lt(unsigned long cpsr) | |
1583 | { | |
1584 | cpsr ^= (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ | |
1585 | return cpsr & PSR_N_BIT; | |
1586 | } | |
1587 | ||
1588 | static unsigned long __kprobes __check_gt(unsigned long cpsr) | |
1589 | { | |
1590 | unsigned long temp = cpsr ^ (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ | |
1591 | temp |= (cpsr << 1); /* PSR_N_BIT |= PSR_Z_BIT */ | |
1592 | return (~temp) & PSR_N_BIT; | |
1593 | } | |
1594 | ||
1595 | static unsigned long __kprobes __check_le(unsigned long cpsr) | |
1596 | { | |
1597 | unsigned long temp = cpsr ^ (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */ | |
1598 | temp |= (cpsr << 1); /* PSR_N_BIT |= PSR_Z_BIT */ | |
1599 | return temp & PSR_N_BIT; | |
1600 | } | |
1601 | ||
1602 | static unsigned long __kprobes __check_al(unsigned long cpsr) | |
1603 | { | |
1604 | return true; | |
1605 | } | |
1606 | ||
1607 | static kprobe_check_cc * const condition_checks[16] = { | |
1608 | &__check_eq, &__check_ne, &__check_cs, &__check_cc, | |
1609 | &__check_mi, &__check_pl, &__check_vs, &__check_vc, | |
1610 | &__check_hi, &__check_ls, &__check_ge, &__check_lt, | |
1611 | &__check_gt, &__check_le, &__check_al, &__check_al | |
1612 | }; | |
1613 | ||
35aa1df4 QB |
1614 | /* Return: |
1615 | * INSN_REJECTED If instruction is one not allowed to kprobe, | |
1616 | * INSN_GOOD If instruction is supported and uses instruction slot, | |
1617 | * INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot. | |
1618 | * | |
1619 | * For instructions we don't want to kprobe (INSN_REJECTED return result): | |
1620 | * These are generally ones that modify the processor state making | |
1621 | * them "hard" to simulate such as switches processor modes or | |
1622 | * make accesses in alternate modes. Any of these could be simulated | |
1623 | * if the work was put into it, but low return considering they | |
1624 | * should also be very rare. | |
1625 | */ | |
1626 | enum kprobe_insn __kprobes | |
1627 | arm_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi) | |
1628 | { | |
073090cb | 1629 | asi->insn_check_cc = condition_checks[insn>>28]; |
35aa1df4 QB |
1630 | asi->insn[1] = KPROBE_RETURN_INSTRUCTION; |
1631 | ||
cdc25361 | 1632 | if ((insn & 0xf0000000) == 0xf0000000) |
35aa1df4 QB |
1633 | |
1634 | return space_1111(insn, asi); | |
1635 | ||
cdc25361 | 1636 | else if ((insn & 0x0e000000) == 0x00000000) |
35aa1df4 QB |
1637 | |
1638 | return space_cccc_000x(insn, asi); | |
1639 | ||
cdc25361 | 1640 | else if ((insn & 0x0e000000) == 0x02000000) |
35aa1df4 QB |
1641 | |
1642 | return space_cccc_001x(insn, asi); | |
1643 | ||
cdc25361 | 1644 | else if ((insn & 0x0f000010) == 0x06000010) |
35aa1df4 QB |
1645 | |
1646 | return space_cccc_0110__1(insn, asi); | |
1647 | ||
cdc25361 | 1648 | else if ((insn & 0x0f000010) == 0x07000010) |
35aa1df4 QB |
1649 | |
1650 | return space_cccc_0111__1(insn, asi); | |
1651 | ||
cdc25361 | 1652 | else if ((insn & 0x0c000000) == 0x04000000) |
35aa1df4 QB |
1653 | |
1654 | return space_cccc_01xx(insn, asi); | |
1655 | ||
cdc25361 | 1656 | else if ((insn & 0x0e000000) == 0x08000000) |
35aa1df4 QB |
1657 | |
1658 | return space_cccc_100x(insn, asi); | |
1659 | ||
cdc25361 | 1660 | else if ((insn & 0x0e000000) == 0x0a000000) |
35aa1df4 QB |
1661 | |
1662 | return space_cccc_101x(insn, asi); | |
1663 | ||
ac211c69 | 1664 | return space_cccc_11xx(insn, asi); |
35aa1df4 QB |
1665 | } |
1666 | ||
1667 | void __init arm_kprobe_decode_init(void) | |
1668 | { | |
1669 | find_str_pc_offset(); | |
1670 | } |