aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/kernel/uprobes.c
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/uprobes.c
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/uprobes.c')
-rw-r--r--arch/arm/kernel/uprobes.c210
1 files changed, 210 insertions, 0 deletions
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);