summaryrefslogtreecommitdiffstats
path: root/arch/arm/kernel
diff options
context:
space:
mode:
authorDavid A. Long <dave.long@linaro.org>2014-03-07 11:23:04 -0500
committerDavid A. Long <dave.long@linaro.org>2014-03-18 16:39:40 -0400
commitc7edc9e326d53ca5ef9bed82de0740c6b107d55b (patch)
tree262d901b5e4d61930d5bc8ff68b9ddd807e3f956 /arch/arm/kernel
parentb4cd605ca92d9a8a2f71355cb45dd943ebcb0c97 (diff)
ARM: add uprobes support
Using Rabin Vincent's ARM uprobes patches as a base, enable uprobes support on ARM. Caveats: - Thumb is not supported Signed-off-by: Rabin Vincent <rabin@rab.in> Signed-off-by: David A. Long <dave.long@linaro.org>
Diffstat (limited to 'arch/arm/kernel')
-rw-r--r--arch/arm/kernel/Makefile1
-rw-r--r--arch/arm/kernel/signal.c4
-rw-r--r--arch/arm/kernel/uprobes-arm.c234
-rw-r--r--arch/arm/kernel/uprobes.c210
-rw-r--r--arch/arm/kernel/uprobes.h35
5 files changed, 484 insertions, 0 deletions
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index bb739f28dd80..a766bcbaf8ad 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -50,6 +50,7 @@ obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o insn.o
50obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o 50obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o
51obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o 51obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o
52obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o 52obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
53obj-$(CONFIG_UPROBES) += probes.o probes-arm.o uprobes.o uprobes-arm.o
53obj-$(CONFIG_KPROBES) += probes.o kprobes.o kprobes-common.o patch.o 54obj-$(CONFIG_KPROBES) += probes.o kprobes.o kprobes-common.o patch.o
54ifdef CONFIG_THUMB2_KERNEL 55ifdef CONFIG_THUMB2_KERNEL
55obj-$(CONFIG_KPROBES) += kprobes-thumb.o probes-thumb.o 56obj-$(CONFIG_KPROBES) += kprobes-thumb.o probes-thumb.o
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c
index 04d63880037f..bd1983437205 100644
--- a/arch/arm/kernel/signal.c
+++ b/arch/arm/kernel/signal.c
@@ -13,6 +13,7 @@
13#include <linux/personality.h> 13#include <linux/personality.h>
14#include <linux/uaccess.h> 14#include <linux/uaccess.h>
15#include <linux/tracehook.h> 15#include <linux/tracehook.h>
16#include <linux/uprobes.h>
16 17
17#include <asm/elf.h> 18#include <asm/elf.h>
18#include <asm/cacheflush.h> 19#include <asm/cacheflush.h>
@@ -590,6 +591,9 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
590 return restart; 591 return restart;
591 } 592 }
592 syscall = 0; 593 syscall = 0;
594 } else if (thread_flags & _TIF_UPROBE) {
595 clear_thread_flag(TIF_UPROBE);
596 uprobe_notify_resume(regs);
593 } else { 597 } else {
594 clear_thread_flag(TIF_NOTIFY_RESUME); 598 clear_thread_flag(TIF_NOTIFY_RESUME);
595 tracehook_notify_resume(regs); 599 tracehook_notify_resume(regs);
diff --git a/arch/arm/kernel/uprobes-arm.c b/arch/arm/kernel/uprobes-arm.c
new file mode 100644
index 000000000000..d3b655ff17da
--- /dev/null
+++ b/arch/arm/kernel/uprobes-arm.c
@@ -0,0 +1,234 @@
1/*
2 * Copyright (C) 2012 Rabin Vincent <rabin at rab.in>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#include <linux/kernel.h>
10#include <linux/types.h>
11#include <linux/stddef.h>
12#include <linux/wait.h>
13#include <linux/uprobes.h>
14#include <linux/module.h>
15
16#include "probes.h"
17#include "probes-arm.h"
18#include "uprobes.h"
19
20static int uprobes_substitute_pc(unsigned long *pinsn, u32 oregs)
21{
22 probes_opcode_t insn = __mem_to_opcode_arm(*pinsn);
23 probes_opcode_t temp;
24 probes_opcode_t mask;
25 int freereg;
26 u32 free = 0xffff;
27 u32 regs;
28
29 for (regs = oregs; regs; regs >>= 4, insn >>= 4) {
30 if ((regs & 0xf) == REG_TYPE_NONE)
31 continue;
32
33 free &= ~(1 << (insn & 0xf));
34 }
35
36 /* No PC, no problem */
37 if (free & (1 << 15))
38 return 15;
39
40 if (!free)
41 return -1;
42
43 /*
44 * fls instead of ffs ensures that for "ldrd r0, r1, [pc]" we would
45 * pick LR instead of R1.
46 */
47 freereg = free = fls(free) - 1;
48
49 temp = __mem_to_opcode_arm(*pinsn);
50 insn = temp;
51 regs = oregs;
52 mask = 0xf;
53
54 for (; regs; regs >>= 4, mask <<= 4, free <<= 4, temp >>= 4) {
55 if ((regs & 0xf) == REG_TYPE_NONE)
56 continue;
57
58 if ((temp & 0xf) != 15)
59 continue;
60
61 insn &= ~mask;
62 insn |= free & mask;
63 }
64
65 *pinsn = __opcode_to_mem_arm(insn);
66 return freereg;
67}
68
69static void uprobe_set_pc(struct arch_uprobe *auprobe,
70 struct arch_uprobe_task *autask,
71 struct pt_regs *regs)
72{
73 u32 pcreg = auprobe->pcreg;
74
75 autask->backup = regs->uregs[pcreg];
76 regs->uregs[pcreg] = regs->ARM_pc + 8;
77}
78
79static void uprobe_unset_pc(struct arch_uprobe *auprobe,
80 struct arch_uprobe_task *autask,
81 struct pt_regs *regs)
82{
83 /* PC will be taken care of by common code */
84 regs->uregs[auprobe->pcreg] = autask->backup;
85}
86
87static void uprobe_aluwrite_pc(struct arch_uprobe *auprobe,
88 struct arch_uprobe_task *autask,
89 struct pt_regs *regs)
90{
91 u32 pcreg = auprobe->pcreg;
92
93 alu_write_pc(regs->uregs[pcreg], regs);
94 regs->uregs[pcreg] = autask->backup;
95}
96
97static void uprobe_write_pc(struct arch_uprobe *auprobe,
98 struct arch_uprobe_task *autask,
99 struct pt_regs *regs)
100{
101 u32 pcreg = auprobe->pcreg;
102
103 load_write_pc(regs->uregs[pcreg], regs);
104 regs->uregs[pcreg] = autask->backup;
105}
106
107enum probes_insn
108decode_pc_ro(probes_opcode_t insn, struct arch_probes_insn *asi,
109 const struct decode_header *d)
110{
111 struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
112 asi);
113 struct decode_emulate *decode = (struct decode_emulate *) d;
114 u32 regs = decode->header.type_regs.bits >> DECODE_TYPE_BITS;
115 int reg;
116
117 reg = uprobes_substitute_pc(&auprobe->ixol[0], regs);
118 if (reg == 15)
119 return INSN_GOOD;
120
121 if (reg == -1)
122 return INSN_REJECTED;
123
124 auprobe->pcreg = reg;
125 auprobe->prehandler = uprobe_set_pc;
126 auprobe->posthandler = uprobe_unset_pc;
127
128 return INSN_GOOD;
129}
130
131enum probes_insn
132decode_wb_pc(probes_opcode_t insn, struct arch_probes_insn *asi,
133 const struct decode_header *d, bool alu)
134{
135 struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
136 asi);
137 enum probes_insn ret = decode_pc_ro(insn, asi, d);
138
139 if (((insn >> 12) & 0xf) == 15)
140 auprobe->posthandler = alu ? uprobe_aluwrite_pc
141 : uprobe_write_pc;
142
143 return ret;
144}
145
146enum probes_insn
147decode_rd12rn16rm0rs8_rwflags(probes_opcode_t insn,
148 struct arch_probes_insn *asi,
149 const struct decode_header *d)
150{
151 return decode_wb_pc(insn, asi, d, true);
152}
153
154enum probes_insn
155decode_ldr(probes_opcode_t insn, struct arch_probes_insn *asi,
156 const struct decode_header *d)
157{
158 return decode_wb_pc(insn, asi, d, false);
159}
160
161enum probes_insn
162uprobe_decode_ldmstm(probes_opcode_t insn,
163 struct arch_probes_insn *asi,
164 const struct decode_header *d)
165{
166 struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
167 asi);
168 unsigned reglist = insn & 0xffff;
169 int rn = (insn >> 16) & 0xf;
170 int lbit = insn & (1 << 20);
171 unsigned used = reglist | (1 << rn);
172
173 if (rn == 15)
174 return INSN_REJECTED;
175
176 if (!(used & (1 << 15)))
177 return INSN_GOOD;
178
179 if (used & (1 << 14))
180 return INSN_REJECTED;
181
182 /* Use LR instead of PC */
183 insn ^= 0xc000;
184
185 auprobe->pcreg = 14;
186 auprobe->ixol[0] = __opcode_to_mem_arm(insn);
187
188 auprobe->prehandler = uprobe_set_pc;
189 if (lbit)
190 auprobe->posthandler = uprobe_write_pc;
191 else
192 auprobe->posthandler = uprobe_unset_pc;
193
194 return INSN_GOOD;
195}
196
197const union decode_action uprobes_probes_actions[] = {
198 [PROBES_EMULATE_NONE] = {.handler = probes_simulate_nop},
199 [PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop},
200 [PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop},
201 [PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop},
202 [PROBES_BRANCH_IMM] = {.handler = simulate_blx1},
203 [PROBES_MRS] = {.handler = simulate_mrs},
204 [PROBES_BRANCH_REG] = {.handler = simulate_blx2bx},
205 [PROBES_CLZ] = {.handler = probes_simulate_nop},
206 [PROBES_SATURATING_ARITHMETIC] = {.handler = probes_simulate_nop},
207 [PROBES_MUL1] = {.handler = probes_simulate_nop},
208 [PROBES_MUL2] = {.handler = probes_simulate_nop},
209 [PROBES_SWP] = {.handler = probes_simulate_nop},
210 [PROBES_LDRSTRD] = {.decoder = decode_pc_ro},
211 [PROBES_LOAD_EXTRA] = {.decoder = decode_pc_ro},
212 [PROBES_LOAD] = {.decoder = decode_ldr},
213 [PROBES_STORE_EXTRA] = {.decoder = decode_pc_ro},
214 [PROBES_STORE] = {.decoder = decode_pc_ro},
215 [PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp},
216 [PROBES_DATA_PROCESSING_REG] = {
217 .decoder = decode_rd12rn16rm0rs8_rwflags},
218 [PROBES_DATA_PROCESSING_IMM] = {
219 .decoder = decode_rd12rn16rm0rs8_rwflags},
220 [PROBES_MOV_HALFWORD] = {.handler = probes_simulate_nop},
221 [PROBES_SEV] = {.handler = probes_simulate_nop},
222 [PROBES_WFE] = {.handler = probes_simulate_nop},
223 [PROBES_SATURATE] = {.handler = probes_simulate_nop},
224 [PROBES_REV] = {.handler = probes_simulate_nop},
225 [PROBES_MMI] = {.handler = probes_simulate_nop},
226 [PROBES_PACK] = {.handler = probes_simulate_nop},
227 [PROBES_EXTEND] = {.handler = probes_simulate_nop},
228 [PROBES_EXTEND_ADD] = {.handler = probes_simulate_nop},
229 [PROBES_MUL_ADD_LONG] = {.handler = probes_simulate_nop},
230 [PROBES_MUL_ADD] = {.handler = probes_simulate_nop},
231 [PROBES_BITFIELD] = {.handler = probes_simulate_nop},
232 [PROBES_BRANCH] = {.handler = simulate_bbl},
233 [PROBES_LDMSTM] = {.decoder = uprobe_decode_ldmstm}
234};
diff --git a/arch/arm/kernel/uprobes.c b/arch/arm/kernel/uprobes.c
new file mode 100644
index 000000000000..f9bacee973bf
--- /dev/null
+++ b/arch/arm/kernel/uprobes.c
@@ -0,0 +1,210 @@
1/*
2 * Copyright (C) 2012 Rabin Vincent <rabin at rab.in>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#include <linux/kernel.h>
10#include <linux/stddef.h>
11#include <linux/errno.h>
12#include <linux/highmem.h>
13#include <linux/sched.h>
14#include <linux/uprobes.h>
15#include <linux/notifier.h>
16
17#include <asm/opcodes.h>
18#include <asm/traps.h>
19
20#include "probes.h"
21#include "probes-arm.h"
22#include "uprobes.h"
23
24#define UPROBE_TRAP_NR UINT_MAX
25
26bool is_swbp_insn(uprobe_opcode_t *insn)
27{
28 return (__mem_to_opcode_arm(*insn) & 0x0fffffff) ==
29 (UPROBE_SWBP_ARM_INSN & 0x0fffffff);
30}
31
32int set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm,
33 unsigned long vaddr)
34{
35 return uprobe_write_opcode(mm, vaddr,
36 __opcode_to_mem_arm(auprobe->bpinsn));
37}
38
39bool arch_uprobe_ignore(struct arch_uprobe *auprobe, struct pt_regs *regs)
40{
41 if (!auprobe->asi.insn_check_cc(regs->ARM_cpsr)) {
42 regs->ARM_pc += 4;
43 return true;
44 }
45
46 return false;
47}
48
49bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
50{
51 probes_opcode_t opcode;
52
53 if (!auprobe->simulate)
54 return false;
55
56 opcode = __mem_to_opcode_arm(*(unsigned int *) auprobe->insn);
57
58 auprobe->asi.insn_singlestep(opcode, &auprobe->asi, regs);
59
60 return true;
61}
62
63unsigned long
64arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
65 struct pt_regs *regs)
66{
67 unsigned long orig_ret_vaddr;
68
69 orig_ret_vaddr = regs->ARM_lr;
70 /* Replace the return addr with trampoline addr */
71 regs->ARM_lr = trampoline_vaddr;
72 return orig_ret_vaddr;
73}
74
75int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
76 unsigned long addr)
77{
78 unsigned int insn;
79 unsigned int bpinsn;
80 enum probes_insn ret;
81
82 /* Thumb not yet support */
83 if (addr & 0x3)
84 return -EINVAL;
85
86 insn = __mem_to_opcode_arm(*(unsigned int *)auprobe->insn);
87 auprobe->ixol[0] = __opcode_to_mem_arm(insn);
88 auprobe->ixol[1] = __opcode_to_mem_arm(UPROBE_SS_ARM_INSN);
89
90 ret = arm_probes_decode_insn(insn, &auprobe->asi, false,
91 uprobes_probes_actions);
92 switch (ret) {
93 case INSN_REJECTED:
94 return -EINVAL;
95
96 case INSN_GOOD_NO_SLOT:
97 auprobe->simulate = true;
98 break;
99
100 case INSN_GOOD:
101 default:
102 break;
103 }
104
105 bpinsn = UPROBE_SWBP_ARM_INSN & 0x0fffffff;
106 if (insn >= 0xe0000000)
107 bpinsn |= 0xe0000000; /* Unconditional instruction */
108 else
109 bpinsn |= insn & 0xf0000000; /* Copy condition from insn */
110
111 auprobe->bpinsn = bpinsn;
112
113 return 0;
114}
115
116int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
117{
118 struct uprobe_task *utask = current->utask;
119
120 if (auprobe->prehandler)
121 auprobe->prehandler(auprobe, &utask->autask, regs);
122
123 utask->autask.saved_trap_no = current->thread.trap_no;
124 current->thread.trap_no = UPROBE_TRAP_NR;
125 regs->ARM_pc = utask->xol_vaddr;
126
127 return 0;
128}
129
130int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
131{
132 struct uprobe_task *utask = current->utask;
133
134 WARN_ON_ONCE(current->thread.trap_no != UPROBE_TRAP_NR);
135
136 current->thread.trap_no = utask->autask.saved_trap_no;
137 regs->ARM_pc = utask->vaddr + 4;
138
139 if (auprobe->posthandler)
140 auprobe->posthandler(auprobe, &utask->autask, regs);
141
142 return 0;
143}
144
145bool arch_uprobe_xol_was_trapped(struct task_struct *t)
146{
147 if (t->thread.trap_no != UPROBE_TRAP_NR)
148 return true;
149
150 return false;
151}
152
153void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
154{
155 struct uprobe_task *utask = current->utask;
156
157 current->thread.trap_no = utask->autask.saved_trap_no;
158 instruction_pointer_set(regs, utask->vaddr);
159}
160
161int arch_uprobe_exception_notify(struct notifier_block *self,
162 unsigned long val, void *data)
163{
164 return NOTIFY_DONE;
165}
166
167static int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
168{
169 unsigned long flags;
170
171 local_irq_save(flags);
172 instr &= 0x0fffffff;
173 if (instr == (UPROBE_SWBP_ARM_INSN & 0x0fffffff))
174 uprobe_pre_sstep_notifier(regs);
175 else if (instr == (UPROBE_SS_ARM_INSN & 0x0fffffff))
176 uprobe_post_sstep_notifier(regs);
177 local_irq_restore(flags);
178
179 return 0;
180}
181
182unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
183{
184 return instruction_pointer(regs);
185}
186
187static struct undef_hook uprobes_arm_break_hook = {
188 .instr_mask = 0x0fffffff,
189 .instr_val = (UPROBE_SWBP_ARM_INSN & 0x0fffffff),
190 .cpsr_mask = MODE_MASK,
191 .cpsr_val = USR_MODE,
192 .fn = uprobe_trap_handler,
193};
194
195static struct undef_hook uprobes_arm_ss_hook = {
196 .instr_mask = 0x0fffffff,
197 .instr_val = (UPROBE_SS_ARM_INSN & 0x0fffffff),
198 .cpsr_mask = MODE_MASK,
199 .cpsr_val = USR_MODE,
200 .fn = uprobe_trap_handler,
201};
202
203static int arch_uprobes_init(void)
204{
205 register_undef_hook(&uprobes_arm_break_hook);
206 register_undef_hook(&uprobes_arm_ss_hook);
207
208 return 0;
209}
210device_initcall(arch_uprobes_init);
diff --git a/arch/arm/kernel/uprobes.h b/arch/arm/kernel/uprobes.h
new file mode 100644
index 000000000000..1d0c12dfbd03
--- /dev/null
+++ b/arch/arm/kernel/uprobes.h
@@ -0,0 +1,35 @@
1/*
2 * Copyright (C) 2012 Rabin Vincent <rabin at rab.in>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#ifndef __ARM_KERNEL_UPROBES_H
10#define __ARM_KERNEL_UPROBES_H
11
12enum probes_insn uprobe_decode_ldmstm(probes_opcode_t insn,
13 struct arch_probes_insn *asi,
14 const struct decode_header *d);
15
16enum probes_insn decode_ldr(probes_opcode_t insn,
17 struct arch_probes_insn *asi,
18 const struct decode_header *d);
19
20enum probes_insn
21decode_rd12rn16rm0rs8_rwflags(probes_opcode_t insn,
22 struct arch_probes_insn *asi,
23 const struct decode_header *d);
24
25enum probes_insn
26decode_wb_pc(probes_opcode_t insn, struct arch_probes_insn *asi,
27 const struct decode_header *d, bool alu);
28
29enum probes_insn
30decode_pc_ro(probes_opcode_t insn, struct arch_probes_insn *asi,
31 const struct decode_header *d);
32
33extern const union decode_action uprobes_probes_actions[];
34
35#endif