diff options
author | Jon Medhurst <tixy@yxit.co.uk> | 2011-07-02 11:13:29 -0400 |
---|---|---|
committer | Tixy <tixy@medhuaa1.miniserver.com> | 2011-07-13 13:32:44 -0400 |
commit | fd0c8d8a48c57cb8a3f1fbbe46a2b208b57ff477 (patch) | |
tree | bbd1a7e1064a7e0be52ed8ba7cb2fab3a5c1d820 | |
parent | 32818f31f8ed811ea7ef924f24642580a63a7c85 (diff) |
ARM: kprobes: Decode 16-bit Thumb PUSH and POP instructions
These instructions are equivalent to
stmdb sp!,{r0-r7,lr}
ldmdb sp!,{r0-r7,pc}
and we emulate them by transforming them into the 32-bit Thumb
instructions
stmdb r9!,{r0-r7,r8}
ldmdb r9!,{r0-r7,r8}
This is simpler, and almost certainly executes faster, than writing
simulation functions.
Signed-off-by: Jon Medhurst <tixy@yxit.co.uk>
Acked-by: Nicolas Pitre <nicolas.pitre@linaro.org>
-rw-r--r-- | arch/arm/kernel/kprobes-thumb.c | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/arch/arm/kernel/kprobes-thumb.c b/arch/arm/kernel/kprobes-thumb.c index a5bdb2dc39e7..e0289493b4c6 100644 --- a/arch/arm/kernel/kprobes-thumb.c +++ b/arch/arm/kernel/kprobes-thumb.c | |||
@@ -187,6 +187,87 @@ t16_decode_hiregs(kprobe_opcode_t insn, struct arch_specific_insn *asi) | |||
187 | return INSN_GOOD; | 187 | return INSN_GOOD; |
188 | } | 188 | } |
189 | 189 | ||
190 | static void __kprobes | ||
191 | t16_emulate_push(struct kprobe *p, struct pt_regs *regs) | ||
192 | { | ||
193 | __asm__ __volatile__ ( | ||
194 | "ldr r9, [%[regs], #13*4] \n\t" | ||
195 | "ldr r8, [%[regs], #14*4] \n\t" | ||
196 | "ldmia %[regs], {r0-r7} \n\t" | ||
197 | "blx %[fn] \n\t" | ||
198 | "str r9, [%[regs], #13*4] \n\t" | ||
199 | : | ||
200 | : [regs] "r" (regs), [fn] "r" (p->ainsn.insn_fn) | ||
201 | : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", | ||
202 | "lr", "memory", "cc" | ||
203 | ); | ||
204 | } | ||
205 | |||
206 | static enum kprobe_insn __kprobes | ||
207 | t16_decode_push(kprobe_opcode_t insn, struct arch_specific_insn *asi) | ||
208 | { | ||
209 | /* | ||
210 | * To simulate a PUSH we use a Thumb-2 "STMDB R9!, {registers}" | ||
211 | * and call it with R9=SP and LR in the register list represented | ||
212 | * by R8. | ||
213 | */ | ||
214 | ((u16 *)asi->insn)[0] = 0xe929; /* 1st half STMDB R9!,{} */ | ||
215 | ((u16 *)asi->insn)[1] = insn & 0x1ff; /* 2nd half (register list) */ | ||
216 | asi->insn_handler = t16_emulate_push; | ||
217 | return INSN_GOOD; | ||
218 | } | ||
219 | |||
220 | static void __kprobes | ||
221 | t16_emulate_pop_nopc(struct kprobe *p, struct pt_regs *regs) | ||
222 | { | ||
223 | __asm__ __volatile__ ( | ||
224 | "ldr r9, [%[regs], #13*4] \n\t" | ||
225 | "ldmia %[regs], {r0-r7} \n\t" | ||
226 | "blx %[fn] \n\t" | ||
227 | "stmia %[regs], {r0-r7} \n\t" | ||
228 | "str r9, [%[regs], #13*4] \n\t" | ||
229 | : | ||
230 | : [regs] "r" (regs), [fn] "r" (p->ainsn.insn_fn) | ||
231 | : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r9", | ||
232 | "lr", "memory", "cc" | ||
233 | ); | ||
234 | } | ||
235 | |||
236 | static void __kprobes | ||
237 | t16_emulate_pop_pc(struct kprobe *p, struct pt_regs *regs) | ||
238 | { | ||
239 | register unsigned long pc asm("r8"); | ||
240 | |||
241 | __asm__ __volatile__ ( | ||
242 | "ldr r9, [%[regs], #13*4] \n\t" | ||
243 | "ldmia %[regs], {r0-r7} \n\t" | ||
244 | "blx %[fn] \n\t" | ||
245 | "stmia %[regs], {r0-r7} \n\t" | ||
246 | "str r9, [%[regs], #13*4] \n\t" | ||
247 | : "=r" (pc) | ||
248 | : [regs] "r" (regs), [fn] "r" (p->ainsn.insn_fn) | ||
249 | : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r9", | ||
250 | "lr", "memory", "cc" | ||
251 | ); | ||
252 | |||
253 | bx_write_pc(pc, regs); | ||
254 | } | ||
255 | |||
256 | static enum kprobe_insn __kprobes | ||
257 | t16_decode_pop(kprobe_opcode_t insn, struct arch_specific_insn *asi) | ||
258 | { | ||
259 | /* | ||
260 | * To simulate a POP we use a Thumb-2 "LDMDB R9!, {registers}" | ||
261 | * and call it with R9=SP and PC in the register list represented | ||
262 | * by R8. | ||
263 | */ | ||
264 | ((u16 *)asi->insn)[0] = 0xe8b9; /* 1st half LDMIA R9!,{} */ | ||
265 | ((u16 *)asi->insn)[1] = insn & 0x1ff; /* 2nd half (register list) */ | ||
266 | asi->insn_handler = insn & 0x100 ? t16_emulate_pop_pc | ||
267 | : t16_emulate_pop_nopc; | ||
268 | return INSN_GOOD; | ||
269 | } | ||
270 | |||
190 | static const union decode_item t16_table_1011[] = { | 271 | static const union decode_item t16_table_1011[] = { |
191 | /* Miscellaneous 16-bit instructions */ | 272 | /* Miscellaneous 16-bit instructions */ |
192 | 273 | ||
@@ -209,6 +290,11 @@ static const union decode_item t16_table_1011[] = { | |||
209 | DECODE_REJECT (0xffc0, 0xba80), | 290 | DECODE_REJECT (0xffc0, 0xba80), |
210 | DECODE_EMULATE (0xf500, 0xb000, t16_emulate_loregs_rwflags), | 291 | DECODE_EMULATE (0xf500, 0xb000, t16_emulate_loregs_rwflags), |
211 | 292 | ||
293 | /* PUSH 1011 010x xxxx xxxx */ | ||
294 | DECODE_CUSTOM (0xfe00, 0xb400, t16_decode_push), | ||
295 | /* POP 1011 110x xxxx xxxx */ | ||
296 | DECODE_CUSTOM (0xfe00, 0xbc00, t16_decode_pop), | ||
297 | |||
212 | /* | 298 | /* |
213 | * If-Then, and hints | 299 | * If-Then, and hints |
214 | * 1011 1111 xxxx xxxx | 300 | * 1011 1111 xxxx xxxx |