aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/kernel/kprobes.c122
-rw-r--r--arch/arm/kernel/kprobes.h7
2 files changed, 115 insertions, 14 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}
diff --git a/arch/arm/kernel/kprobes.h b/arch/arm/kernel/kprobes.h
index 86abfabe83f2..a84b14d8cdc8 100644
--- a/arch/arm/kernel/kprobes.h
+++ b/arch/arm/kernel/kprobes.h
@@ -18,10 +18,13 @@
18#define _ARM_KERNEL_KPROBES_H 18#define _ARM_KERNEL_KPROBES_H
19 19
20/* 20/*
21 * This undefined instruction must be unique and 21 * These undefined instructions must be unique and
22 * reserved solely for kprobes' use. 22 * reserved solely for kprobes' use.
23 */ 23 */
24#define KPROBE_BREAKPOINT_INSTRUCTION 0xe7f001f8 24#define KPROBE_ARM_BREAKPOINT_INSTRUCTION 0xe7f001f8
25#define KPROBE_THUMB16_BREAKPOINT_INSTRUCTION 0xde18
26#define KPROBE_THUMB32_BREAKPOINT_INSTRUCTION 0xf7f0a018
27
25 28
26enum kprobe_insn { 29enum kprobe_insn {
27 INSN_REJECTED, 30 INSN_REJECTED,