aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/kernel/kprobes.c
diff options
context:
space:
mode:
authorJon Medhurst <tixy@yxit.co.uk>2011-04-19 12:18:35 -0400
committerTixy <tixy@medhuaa1.miniserver.com>2011-07-13 13:32:42 -0400
commitaceb487ab2ea0dca43f5131e456f45bc9f8c1bed (patch)
treeef146fd590795a13a477961afb60f5468d38f566 /arch/arm/kernel/kprobes.c
parent856bc356394a28edb8c388e31212f6ceb0a04ce6 (diff)
ARM: kprobes: Add Thumb breakpoint support
Extend the breakpoint insertion and catching functions to support Thumb code. As breakpoints are no longer of a fixed size, the flush_insns macro is modified to take a size argument instead of an instruction count. Note, we need both 16- and 32-bit Thumb breakpoints, because if we were to use a 16-bit breakpoint to replace a 32-bit instruction which was in an IT block, and the condition check failed, then the breakpoint may not fire (it's unpredictable behaviour) and the CPU could then try and execute the second half of the 32-bit Thumb instruction. Signed-off-by: Jon Medhurst <tixy@yxit.co.uk> Acked-by: Nicolas Pitre <nicolas.pitre@linaro.org>
Diffstat (limited to 'arch/arm/kernel/kprobes.c')
-rw-r--r--arch/arm/kernel/kprobes.c122
1 files changed, 110 insertions, 12 deletions
diff --git a/arch/arm/kernel/kprobes.c b/arch/arm/kernel/kprobes.c
index 0df2d6d57c04..a9050bad4434 100644
--- a/arch/arm/kernel/kprobes.c
+++ b/arch/arm/kernel/kprobes.c
@@ -34,10 +34,10 @@
34 min((unsigned long)MAX_STACK_SIZE, \ 34 min((unsigned long)MAX_STACK_SIZE, \
35 (unsigned long)current_thread_info() + THREAD_START_SP - (addr)) 35 (unsigned long)current_thread_info() + THREAD_START_SP - (addr))
36 36
37#define flush_insns(addr, cnt) \ 37#define flush_insns(addr, size) \
38 flush_icache_range((unsigned long)(addr), \ 38 flush_icache_range((unsigned long)(addr), \
39 (unsigned long)(addr) + \ 39 (unsigned long)(addr) + \
40 sizeof(kprobe_opcode_t) * (cnt)) 40 (size))
41 41
42/* Used as a marker in ARM_pc to note when we're in a jprobe. */ 42/* Used as a marker in ARM_pc to note when we're in a jprobe. */
43#define JPROBE_MAGIC_ADDR 0xffffffff 43#define JPROBE_MAGIC_ADDR 0xffffffff
@@ -86,7 +86,8 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
86 return -ENOMEM; 86 return -ENOMEM;
87 for (is = 0; is < MAX_INSN_SIZE; ++is) 87 for (is = 0; is < MAX_INSN_SIZE; ++is)
88 p->ainsn.insn[is] = tmp_insn[is]; 88 p->ainsn.insn[is] = tmp_insn[is];
89 flush_insns(p->ainsn.insn, MAX_INSN_SIZE); 89 flush_insns(p->ainsn.insn,
90 sizeof(p->ainsn.insn[0]) * MAX_INSN_SIZE);
90 break; 91 break;
91 92
92 case INSN_GOOD_NO_SLOT: /* instruction doesn't need insn slot */ 93 case INSN_GOOD_NO_SLOT: /* instruction doesn't need insn slot */
@@ -97,24 +98,82 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
97 return 0; 98 return 0;
98} 99}
99 100
101#ifdef CONFIG_THUMB2_KERNEL
102
103/*
104 * For a 32-bit Thumb breakpoint spanning two memory words we need to take
105 * special precautions to insert the breakpoint atomically, especially on SMP
106 * systems. This is achieved by calling this arming function using stop_machine.
107 */
108static int __kprobes set_t32_breakpoint(void *addr)
109{
110 ((u16 *)addr)[0] = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION >> 16;
111 ((u16 *)addr)[1] = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION & 0xffff;
112 flush_insns(addr, 2*sizeof(u16));
113 return 0;
114}
115
100void __kprobes arch_arm_kprobe(struct kprobe *p) 116void __kprobes arch_arm_kprobe(struct kprobe *p)
101{ 117{
102 *p->addr = KPROBE_BREAKPOINT_INSTRUCTION; 118 uintptr_t addr = (uintptr_t)p->addr & ~1; /* Remove any Thumb flag */
103 flush_insns(p->addr, 1); 119
120 if (!is_wide_instruction(p->opcode)) {
121 *(u16 *)addr = KPROBE_THUMB16_BREAKPOINT_INSTRUCTION;
122 flush_insns(addr, sizeof(u16));
123 } else if (addr & 2) {
124 /* A 32-bit instruction spanning two words needs special care */
125 stop_machine(set_t32_breakpoint, (void *)addr, &cpu_online_map);
126 } else {
127 /* Word aligned 32-bit instruction can be written atomically */
128 u32 bkp = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION;
129#ifndef __ARMEB__ /* Swap halfwords for little-endian */
130 bkp = (bkp >> 16) | (bkp << 16);
131#endif
132 *(u32 *)addr = bkp;
133 flush_insns(addr, sizeof(u32));
134 }
104} 135}
105 136
137#else /* !CONFIG_THUMB2_KERNEL */
138
139void __kprobes arch_arm_kprobe(struct kprobe *p)
140{
141 *p->addr = KPROBE_ARM_BREAKPOINT_INSTRUCTION;
142 flush_insns(p->addr, sizeof(p->addr[0]));
143}
144
145#endif /* !CONFIG_THUMB2_KERNEL */
146
106/* 147/*
107 * The actual disarming is done here on each CPU and synchronized using 148 * The actual disarming is done here on each CPU and synchronized using
108 * stop_machine. This synchronization is necessary on SMP to avoid removing 149 * stop_machine. This synchronization is necessary on SMP to avoid removing
109 * a probe between the moment the 'Undefined Instruction' exception is raised 150 * a probe between the moment the 'Undefined Instruction' exception is raised
110 * and the moment the exception handler reads the faulting instruction from 151 * and the moment the exception handler reads the faulting instruction from
111 * memory. 152 * memory. It is also needed to atomically set the two half-words of a 32-bit
153 * Thumb breakpoint.
112 */ 154 */
113int __kprobes __arch_disarm_kprobe(void *p) 155int __kprobes __arch_disarm_kprobe(void *p)
114{ 156{
115 struct kprobe *kp = p; 157 struct kprobe *kp = p;
158#ifdef CONFIG_THUMB2_KERNEL
159 u16 *addr = (u16 *)((uintptr_t)kp->addr & ~1);
160 kprobe_opcode_t insn = kp->opcode;
161 unsigned int len;
162
163 if (is_wide_instruction(insn)) {
164 ((u16 *)addr)[0] = insn>>16;
165 ((u16 *)addr)[1] = insn;
166 len = 2*sizeof(u16);
167 } else {
168 ((u16 *)addr)[0] = insn;
169 len = sizeof(u16);
170 }
171 flush_insns(addr, len);
172
173#else /* !CONFIG_THUMB2_KERNEL */
116 *kp->addr = kp->opcode; 174 *kp->addr = kp->opcode;
117 flush_insns(kp->addr, 1); 175 flush_insns(kp->addr, sizeof(kp->addr[0]));
176#endif
118 return 0; 177 return 0;
119} 178}
120 179
@@ -167,11 +226,23 @@ void __kprobes kprobe_handler(struct pt_regs *regs)
167{ 226{
168 struct kprobe *p, *cur; 227 struct kprobe *p, *cur;
169 struct kprobe_ctlblk *kcb; 228 struct kprobe_ctlblk *kcb;
170 kprobe_opcode_t *addr = (kprobe_opcode_t *)regs->ARM_pc;
171 229
172 kcb = get_kprobe_ctlblk(); 230 kcb = get_kprobe_ctlblk();
173 cur = kprobe_running(); 231 cur = kprobe_running();
174 p = get_kprobe(addr); 232
233#ifdef CONFIG_THUMB2_KERNEL
234 /*
235 * First look for a probe which was registered using an address with
236 * bit 0 set, this is the usual situation for pointers to Thumb code.
237 * If not found, fallback to looking for one with bit 0 clear.
238 */
239 p = get_kprobe((kprobe_opcode_t *)(regs->ARM_pc | 1));
240 if (!p)
241 p = get_kprobe((kprobe_opcode_t *)regs->ARM_pc);
242
243#else /* ! CONFIG_THUMB2_KERNEL */
244 p = get_kprobe((kprobe_opcode_t *)regs->ARM_pc);
245#endif
175 246
176 if (p) { 247 if (p) {
177 if (cur) { 248 if (cur) {
@@ -511,17 +582,44 @@ int __kprobes arch_trampoline_kprobe(struct kprobe *p)
511 return 0; 582 return 0;
512} 583}
513 584
514static struct undef_hook kprobes_break_hook = { 585#ifdef CONFIG_THUMB2_KERNEL
586
587static struct undef_hook kprobes_thumb16_break_hook = {
588 .instr_mask = 0xffff,
589 .instr_val = KPROBE_THUMB16_BREAKPOINT_INSTRUCTION,
590 .cpsr_mask = MODE_MASK,
591 .cpsr_val = SVC_MODE,
592 .fn = kprobe_trap_handler,
593};
594
595static struct undef_hook kprobes_thumb32_break_hook = {
596 .instr_mask = 0xffffffff,
597 .instr_val = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION,
598 .cpsr_mask = MODE_MASK,
599 .cpsr_val = SVC_MODE,
600 .fn = kprobe_trap_handler,
601};
602
603#else /* !CONFIG_THUMB2_KERNEL */
604
605static struct undef_hook kprobes_arm_break_hook = {
515 .instr_mask = 0xffffffff, 606 .instr_mask = 0xffffffff,
516 .instr_val = KPROBE_BREAKPOINT_INSTRUCTION, 607 .instr_val = KPROBE_ARM_BREAKPOINT_INSTRUCTION,
517 .cpsr_mask = MODE_MASK, 608 .cpsr_mask = MODE_MASK,
518 .cpsr_val = SVC_MODE, 609 .cpsr_val = SVC_MODE,
519 .fn = kprobe_trap_handler, 610 .fn = kprobe_trap_handler,
520}; 611};
521 612
613#endif /* !CONFIG_THUMB2_KERNEL */
614
522int __init arch_init_kprobes() 615int __init arch_init_kprobes()
523{ 616{
524 arm_kprobe_decode_init(); 617 arm_kprobe_decode_init();
525 register_undef_hook(&kprobes_break_hook); 618#ifdef CONFIG_THUMB2_KERNEL
619 register_undef_hook(&kprobes_thumb16_break_hook);
620 register_undef_hook(&kprobes_thumb32_break_hook);
621#else
622 register_undef_hook(&kprobes_arm_break_hook);
623#endif
526 return 0; 624 return 0;
527} 625}