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