aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorJon Medhurst <tixy@yxit.co.uk>2011-07-03 09:53:45 -0400
committerTixy <tixy@medhuaa1.miniserver.com>2011-07-13 13:32:46 -0400
commitce715c772f0124f9d3f6f5cffcb85688c81d2c07 (patch)
tree98fb686d960ffb9391303af98c4284cfca14eb54 /arch
parentb06f3ee34d8b817d566d15d25a21f8320b3f7c57 (diff)
ARM: kprobes: Decode 32-bit Thumb branch instructions
Signed-off-by: Jon Medhurst <tixy@yxit.co.uk> Acked-by: Nicolas Pitre <nicolas.pitre@linaro.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/kernel/kprobes-thumb.c65
1 files changed, 63 insertions, 2 deletions
diff --git a/arch/arm/kernel/kprobes-thumb.c b/arch/arm/kernel/kprobes-thumb.c
index c07c2470ccaa..1677234000b2 100644
--- a/arch/arm/kernel/kprobes-thumb.c
+++ b/arch/arm/kernel/kprobes-thumb.c
@@ -49,9 +49,9 @@ t32_simulate_table_branch(struct kprobe *p, struct pt_regs *regs)
49 unsigned long rmv = regs->uregs[rm]; 49 unsigned long rmv = regs->uregs[rm];
50 unsigned int halfwords; 50 unsigned int halfwords;
51 51
52 if (insn & 0x10) 52 if (insn & 0x10) /* TBH */
53 halfwords = ((u16 *)rnv)[rmv]; 53 halfwords = ((u16 *)rnv)[rmv];
54 else 54 else /* TBB */
55 halfwords = ((u8 *)rnv)[rmv]; 55 halfwords = ((u8 *)rnv)[rmv];
56 56
57 regs->ARM_pc = pc + 2 * halfwords; 57 regs->ARM_pc = pc + 2 * halfwords;
@@ -66,6 +66,58 @@ t32_simulate_mrs(struct kprobe *p, struct pt_regs *regs)
66 regs->uregs[rd] = regs->ARM_cpsr & mask; 66 regs->uregs[rd] = regs->ARM_cpsr & mask;
67} 67}
68 68
69static void __kprobes
70t32_simulate_cond_branch(struct kprobe *p, struct pt_regs *regs)
71{
72 kprobe_opcode_t insn = p->opcode;
73 unsigned long pc = thumb_probe_pc(p);
74
75 long offset = insn & 0x7ff; /* imm11 */
76 offset += (insn & 0x003f0000) >> 5; /* imm6 */
77 offset += (insn & 0x00002000) << 4; /* J1 */
78 offset += (insn & 0x00000800) << 7; /* J2 */
79 offset -= (insn & 0x04000000) >> 7; /* Apply sign bit */
80
81 regs->ARM_pc = pc + (offset * 2);
82}
83
84static enum kprobe_insn __kprobes
85t32_decode_cond_branch(kprobe_opcode_t insn, struct arch_specific_insn *asi)
86{
87 int cc = (insn >> 22) & 0xf;
88 asi->insn_check_cc = kprobe_condition_checks[cc];
89 asi->insn_handler = t32_simulate_cond_branch;
90 return INSN_GOOD_NO_SLOT;
91}
92
93static void __kprobes
94t32_simulate_branch(struct kprobe *p, struct pt_regs *regs)
95{
96 kprobe_opcode_t insn = p->opcode;
97 unsigned long pc = thumb_probe_pc(p);
98
99 long offset = insn & 0x7ff; /* imm11 */
100 offset += (insn & 0x03ff0000) >> 5; /* imm10 */
101 offset += (insn & 0x00002000) << 9; /* J1 */
102 offset += (insn & 0x00000800) << 10; /* J2 */
103 if (insn & 0x04000000)
104 offset -= 0x00800000; /* Apply sign bit */
105 else
106 offset ^= 0x00600000; /* Invert J1 and J2 */
107
108 if (insn & (1 << 14)) {
109 /* BL or BLX */
110 regs->ARM_lr = (unsigned long)p->addr + 4;
111 if (!(insn & (1 << 12))) {
112 /* BLX so switch to ARM mode */
113 regs->ARM_cpsr &= ~PSR_T_BIT;
114 pc &= ~3;
115 }
116 }
117
118 regs->ARM_pc = pc + (offset * 2);
119}
120
69static enum kprobe_insn __kprobes 121static enum kprobe_insn __kprobes
70t32_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi) 122t32_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
71{ 123{
@@ -425,6 +477,15 @@ static const union decode_item t32_table_1111_0xxx___1[] = {
425 */ 477 */
426 DECODE_REJECT (0xfb80d000, 0xf3808000), 478 DECODE_REJECT (0xfb80d000, 0xf3808000),
427 479
480 /* Bcc 1111 0xxx xxxx xxxx 10x0 xxxx xxxx xxxx */
481 DECODE_CUSTOM (0xf800d000, 0xf0008000, t32_decode_cond_branch),
482
483 /* BLX 1111 0xxx xxxx xxxx 11x0 xxxx xxxx xxx0 */
484 DECODE_OR (0xf800d001, 0xf000c000),
485 /* B 1111 0xxx xxxx xxxx 10x1 xxxx xxxx xxxx */
486 /* BL 1111 0xxx xxxx xxxx 11x1 xxxx xxxx xxxx */
487 DECODE_SIMULATE (0xf8009000, 0xf0009000, t32_simulate_branch),
488
428 DECODE_END 489 DECODE_END
429}; 490};
430 491