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> | |
c18377c3 | 63 | #include <linux/ptrace.h> |
35aa1df4 | 64 | |
221bf15f | 65 | #include "kprobes.h" |
c18377c3 | 66 | #include "probes-arm.h" |
35aa1df4 | 67 | |
7be7ee2d JM |
68 | #if __LINUX_ARM_ARCH__ >= 6 |
69 | #define BLX(reg) "blx "reg" \n\t" | |
70 | #else | |
71 | #define BLX(reg) "mov lr, pc \n\t" \ | |
72 | "mov pc, "reg" \n\t" | |
73 | #endif | |
74 | ||
3e6cd394 | 75 | static void __kprobes |
f145d664 | 76 | emulate_ldrdstrd(probes_opcode_t insn, |
b4cd605c | 77 | struct arch_probes_insn *asi, struct pt_regs *regs) |
8723942f | 78 | { |
7579f4b3 | 79 | unsigned long pc = regs->ARM_pc + 4; |
8723942f JM |
80 | int rt = (insn >> 12) & 0xf; |
81 | int rn = (insn >> 16) & 0xf; | |
82 | int rm = insn & 0xf; | |
83 | ||
84 | register unsigned long rtv asm("r0") = regs->uregs[rt]; | |
85 | register unsigned long rt2v asm("r1") = regs->uregs[rt+1]; | |
86 | register unsigned long rnv asm("r2") = (rn == 15) ? pc | |
87 | : regs->uregs[rn]; | |
88 | register unsigned long rmv asm("r3") = regs->uregs[rm]; | |
89 | ||
90 | __asm__ __volatile__ ( | |
91 | BLX("%[fn]") | |
92 | : "=r" (rtv), "=r" (rt2v), "=r" (rnv) | |
93 | : "0" (rtv), "1" (rt2v), "2" (rnv), "r" (rmv), | |
7579f4b3 | 94 | [fn] "r" (asi->insn_fn) |
8723942f JM |
95 | : "lr", "memory", "cc" |
96 | ); | |
97 | ||
98 | regs->uregs[rt] = rtv; | |
99 | regs->uregs[rt+1] = rt2v; | |
100 | if (is_writeback(insn)) | |
101 | regs->uregs[rn] = rnv; | |
102 | } | |
103 | ||
3e6cd394 | 104 | static void __kprobes |
f145d664 | 105 | emulate_ldr(probes_opcode_t insn, |
b4cd605c | 106 | struct arch_probes_insn *asi, struct pt_regs *regs) |
3c48fbb1 | 107 | { |
7579f4b3 | 108 | unsigned long pc = regs->ARM_pc + 4; |
3c48fbb1 JM |
109 | int rt = (insn >> 12) & 0xf; |
110 | int rn = (insn >> 16) & 0xf; | |
111 | int rm = insn & 0xf; | |
112 | ||
113 | register unsigned long rtv asm("r0"); | |
114 | register unsigned long rnv asm("r2") = (rn == 15) ? pc | |
115 | : regs->uregs[rn]; | |
116 | register unsigned long rmv asm("r3") = regs->uregs[rm]; | |
117 | ||
118 | __asm__ __volatile__ ( | |
119 | BLX("%[fn]") | |
120 | : "=r" (rtv), "=r" (rnv) | |
7579f4b3 | 121 | : "1" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn) |
3c48fbb1 JM |
122 | : "lr", "memory", "cc" |
123 | ); | |
124 | ||
125 | if (rt == 15) | |
126 | load_write_pc(rtv, regs); | |
127 | else | |
128 | regs->uregs[rt] = rtv; | |
129 | ||
130 | if (is_writeback(insn)) | |
131 | regs->uregs[rn] = rnv; | |
132 | } | |
133 | ||
3e6cd394 | 134 | static void __kprobes |
f145d664 | 135 | emulate_str(probes_opcode_t insn, |
b4cd605c | 136 | struct arch_probes_insn *asi, struct pt_regs *regs) |
3c48fbb1 | 137 | { |
7579f4b3 DL |
138 | unsigned long rtpc = regs->ARM_pc - 4 + str_pc_offset; |
139 | unsigned long rnpc = regs->ARM_pc + 4; | |
3c48fbb1 JM |
140 | int rt = (insn >> 12) & 0xf; |
141 | int rn = (insn >> 16) & 0xf; | |
142 | int rm = insn & 0xf; | |
143 | ||
144 | register unsigned long rtv asm("r0") = (rt == 15) ? rtpc | |
145 | : regs->uregs[rt]; | |
146 | register unsigned long rnv asm("r2") = (rn == 15) ? rnpc | |
147 | : regs->uregs[rn]; | |
148 | register unsigned long rmv asm("r3") = regs->uregs[rm]; | |
149 | ||
150 | __asm__ __volatile__ ( | |
151 | BLX("%[fn]") | |
152 | : "=r" (rnv) | |
7579f4b3 | 153 | : "r" (rtv), "0" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn) |
3c48fbb1 JM |
154 | : "lr", "memory", "cc" |
155 | ); | |
156 | ||
157 | if (is_writeback(insn)) | |
158 | regs->uregs[rn] = rnv; | |
159 | } | |
160 | ||
3e6cd394 | 161 | static void __kprobes |
f145d664 | 162 | emulate_rd12rn16rm0rs8_rwflags(probes_opcode_t insn, |
b4cd605c | 163 | struct arch_probes_insn *asi, struct pt_regs *regs) |
9f596e51 | 164 | { |
7579f4b3 | 165 | unsigned long pc = regs->ARM_pc + 4; |
9f596e51 JM |
166 | int rd = (insn >> 12) & 0xf; |
167 | int rn = (insn >> 16) & 0xf; | |
168 | int rm = insn & 0xf; | |
169 | int rs = (insn >> 8) & 0xf; | |
170 | ||
171 | register unsigned long rdv asm("r0") = regs->uregs[rd]; | |
172 | register unsigned long rnv asm("r2") = (rn == 15) ? pc | |
173 | : regs->uregs[rn]; | |
174 | register unsigned long rmv asm("r3") = (rm == 15) ? pc | |
175 | : regs->uregs[rm]; | |
176 | register unsigned long rsv asm("r1") = regs->uregs[rs]; | |
177 | unsigned long cpsr = regs->ARM_cpsr; | |
178 | ||
179 | __asm__ __volatile__ ( | |
180 | "msr cpsr_fs, %[cpsr] \n\t" | |
181 | BLX("%[fn]") | |
182 | "mrs %[cpsr], cpsr \n\t" | |
183 | : "=r" (rdv), [cpsr] "=r" (cpsr) | |
184 | : "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv), | |
7579f4b3 | 185 | "1" (cpsr), [fn] "r" (asi->insn_fn) |
9f596e51 JM |
186 | : "lr", "memory", "cc" |
187 | ); | |
188 | ||
189 | if (rd == 15) | |
190 | alu_write_pc(rdv, regs); | |
191 | else | |
192 | regs->uregs[rd] = rdv; | |
193 | regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); | |
194 | } | |
195 | ||
3e6cd394 | 196 | static void __kprobes |
f145d664 | 197 | emulate_rd12rn16rm0_rwflags_nopc(probes_opcode_t insn, |
b4cd605c | 198 | struct arch_probes_insn *asi, struct pt_regs *regs) |
0e44e9a0 | 199 | { |
0e44e9a0 JM |
200 | int rd = (insn >> 12) & 0xf; |
201 | int rn = (insn >> 16) & 0xf; | |
202 | int rm = insn & 0xf; | |
203 | ||
204 | register unsigned long rdv asm("r0") = regs->uregs[rd]; | |
205 | register unsigned long rnv asm("r2") = regs->uregs[rn]; | |
206 | register unsigned long rmv asm("r3") = regs->uregs[rm]; | |
207 | unsigned long cpsr = regs->ARM_cpsr; | |
208 | ||
209 | __asm__ __volatile__ ( | |
210 | "msr cpsr_fs, %[cpsr] \n\t" | |
211 | BLX("%[fn]") | |
212 | "mrs %[cpsr], cpsr \n\t" | |
213 | : "=r" (rdv), [cpsr] "=r" (cpsr) | |
214 | : "0" (rdv), "r" (rnv), "r" (rmv), | |
7579f4b3 | 215 | "1" (cpsr), [fn] "r" (asi->insn_fn) |
0e44e9a0 JM |
216 | : "lr", "memory", "cc" |
217 | ); | |
218 | ||
219 | regs->uregs[rd] = rdv; | |
220 | regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); | |
221 | } | |
222 | ||
3e6cd394 | 223 | static void __kprobes |
f145d664 | 224 | emulate_rd16rn12rm0rs8_rwflags_nopc(probes_opcode_t insn, |
b4cd605c | 225 | struct arch_probes_insn *asi, |
7579f4b3 | 226 | struct pt_regs *regs) |
6091dfae | 227 | { |
6091dfae JM |
228 | int rd = (insn >> 16) & 0xf; |
229 | int rn = (insn >> 12) & 0xf; | |
230 | int rm = insn & 0xf; | |
231 | int rs = (insn >> 8) & 0xf; | |
232 | ||
233 | register unsigned long rdv asm("r2") = regs->uregs[rd]; | |
234 | register unsigned long rnv asm("r0") = regs->uregs[rn]; | |
235 | register unsigned long rmv asm("r3") = regs->uregs[rm]; | |
236 | register unsigned long rsv asm("r1") = regs->uregs[rs]; | |
237 | unsigned long cpsr = regs->ARM_cpsr; | |
238 | ||
239 | __asm__ __volatile__ ( | |
240 | "msr cpsr_fs, %[cpsr] \n\t" | |
241 | BLX("%[fn]") | |
242 | "mrs %[cpsr], cpsr \n\t" | |
243 | : "=r" (rdv), [cpsr] "=r" (cpsr) | |
244 | : "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv), | |
7579f4b3 | 245 | "1" (cpsr), [fn] "r" (asi->insn_fn) |
6091dfae JM |
246 | : "lr", "memory", "cc" |
247 | ); | |
248 | ||
249 | regs->uregs[rd] = rdv; | |
250 | regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); | |
251 | } | |
252 | ||
3e6cd394 | 253 | static void __kprobes |
f145d664 | 254 | emulate_rd12rm0_noflags_nopc(probes_opcode_t insn, |
b4cd605c | 255 | struct arch_probes_insn *asi, struct pt_regs *regs) |
c82584eb | 256 | { |
c82584eb JM |
257 | int rd = (insn >> 12) & 0xf; |
258 | int rm = insn & 0xf; | |
259 | ||
260 | register unsigned long rdv asm("r0") = regs->uregs[rd]; | |
261 | register unsigned long rmv asm("r3") = regs->uregs[rm]; | |
262 | ||
263 | __asm__ __volatile__ ( | |
264 | BLX("%[fn]") | |
265 | : "=r" (rdv) | |
7579f4b3 | 266 | : "0" (rdv), "r" (rmv), [fn] "r" (asi->insn_fn) |
c82584eb JM |
267 | : "lr", "memory", "cc" |
268 | ); | |
269 | ||
270 | regs->uregs[rd] = rdv; | |
271 | } | |
272 | ||
3e6cd394 | 273 | static void __kprobes |
f145d664 | 274 | emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(probes_opcode_t insn, |
b4cd605c | 275 | struct arch_probes_insn *asi, |
7579f4b3 | 276 | struct pt_regs *regs) |
12ce5d33 | 277 | { |
12ce5d33 JM |
278 | int rdlo = (insn >> 12) & 0xf; |
279 | int rdhi = (insn >> 16) & 0xf; | |
280 | int rn = insn & 0xf; | |
281 | int rm = (insn >> 8) & 0xf; | |
282 | ||
283 | register unsigned long rdlov asm("r0") = regs->uregs[rdlo]; | |
284 | register unsigned long rdhiv asm("r2") = regs->uregs[rdhi]; | |
285 | register unsigned long rnv asm("r3") = regs->uregs[rn]; | |
286 | register unsigned long rmv asm("r1") = regs->uregs[rm]; | |
287 | unsigned long cpsr = regs->ARM_cpsr; | |
288 | ||
289 | __asm__ __volatile__ ( | |
290 | "msr cpsr_fs, %[cpsr] \n\t" | |
291 | BLX("%[fn]") | |
292 | "mrs %[cpsr], cpsr \n\t" | |
293 | : "=r" (rdlov), "=r" (rdhiv), [cpsr] "=r" (cpsr) | |
294 | : "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv), | |
7579f4b3 | 295 | "2" (cpsr), [fn] "r" (asi->insn_fn) |
12ce5d33 JM |
296 | : "lr", "memory", "cc" |
297 | ); | |
298 | ||
299 | regs->uregs[rdlo] = rdlov; | |
300 | regs->uregs[rdhi] = rdhiv; | |
301 | regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK); | |
302 | } | |
3e6cd394 DL |
303 | |
304 | const union decode_action kprobes_arm_actions[NUM_PROBES_ARM_ACTIONS] = { | |
eb73ea97 DL |
305 | [PROBES_EMULATE_NONE] = {.handler = probes_emulate_none}, |
306 | [PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop}, | |
307 | [PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop}, | |
308 | [PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop}, | |
3e6cd394 DL |
309 | [PROBES_BRANCH_IMM] = {.handler = simulate_blx1}, |
310 | [PROBES_MRS] = {.handler = simulate_mrs}, | |
311 | [PROBES_BRANCH_REG] = {.handler = simulate_blx2bx}, | |
312 | [PROBES_CLZ] = {.handler = emulate_rd12rm0_noflags_nopc}, | |
313 | [PROBES_SATURATING_ARITHMETIC] = { | |
314 | .handler = emulate_rd12rn16rm0_rwflags_nopc}, | |
315 | [PROBES_MUL1] = {.handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc}, | |
316 | [PROBES_MUL2] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc}, | |
317 | [PROBES_SWP] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, | |
318 | [PROBES_LDRSTRD] = {.handler = emulate_ldrdstrd}, | |
319 | [PROBES_LOAD_EXTRA] = {.handler = emulate_ldr}, | |
320 | [PROBES_LOAD] = {.handler = emulate_ldr}, | |
321 | [PROBES_STORE_EXTRA] = {.handler = emulate_str}, | |
322 | [PROBES_STORE] = {.handler = emulate_str}, | |
323 | [PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp}, | |
324 | [PROBES_DATA_PROCESSING_REG] = { | |
325 | .handler = emulate_rd12rn16rm0rs8_rwflags}, | |
326 | [PROBES_DATA_PROCESSING_IMM] = { | |
327 | .handler = emulate_rd12rn16rm0rs8_rwflags}, | |
328 | [PROBES_MOV_HALFWORD] = {.handler = emulate_rd12rm0_noflags_nopc}, | |
eb73ea97 DL |
329 | [PROBES_SEV] = {.handler = probes_emulate_none}, |
330 | [PROBES_WFE] = {.handler = probes_simulate_nop}, | |
3e6cd394 DL |
331 | [PROBES_SATURATE] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, |
332 | [PROBES_REV] = {.handler = emulate_rd12rm0_noflags_nopc}, | |
333 | [PROBES_MMI] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, | |
334 | [PROBES_PACK] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, | |
335 | [PROBES_EXTEND] = {.handler = emulate_rd12rm0_noflags_nopc}, | |
336 | [PROBES_EXTEND_ADD] = {.handler = emulate_rd12rn16rm0_rwflags_nopc}, | |
337 | [PROBES_MUL_ADD_LONG] = { | |
338 | .handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc}, | |
339 | [PROBES_MUL_ADD] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc}, | |
340 | [PROBES_BITFIELD] = {.handler = emulate_rd12rm0_noflags_nopc}, | |
341 | [PROBES_BRANCH] = {.handler = simulate_bbl}, | |
342 | [PROBES_LDMSTM] = {.decoder = kprobe_decode_ldmstm} | |
343 | }; |