diff options
author | David A. Long <dave.long@linaro.org> | 2014-03-07 11:23:04 -0500 |
---|---|---|
committer | David A. Long <dave.long@linaro.org> | 2014-03-18 16:39:40 -0400 |
commit | c7edc9e326d53ca5ef9bed82de0740c6b107d55b (patch) | |
tree | 262d901b5e4d61930d5bc8ff68b9ddd807e3f956 /arch/arm/kernel | |
parent | b4cd605ca92d9a8a2f71355cb45dd943ebcb0c97 (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/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/kernel/signal.c | 4 | ||||
-rw-r--r-- | arch/arm/kernel/uprobes-arm.c | 234 | ||||
-rw-r--r-- | arch/arm/kernel/uprobes.c | 210 | ||||
-rw-r--r-- | arch/arm/kernel/uprobes.h | 35 |
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 | |||
50 | obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o | 50 | obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o |
51 | obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o | 51 | obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o |
52 | obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o | 52 | obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o |
53 | obj-$(CONFIG_UPROBES) += probes.o probes-arm.o uprobes.o uprobes-arm.o | ||
53 | obj-$(CONFIG_KPROBES) += probes.o kprobes.o kprobes-common.o patch.o | 54 | obj-$(CONFIG_KPROBES) += probes.o kprobes.o kprobes-common.o patch.o |
54 | ifdef CONFIG_THUMB2_KERNEL | 55 | ifdef CONFIG_THUMB2_KERNEL |
55 | obj-$(CONFIG_KPROBES) += kprobes-thumb.o probes-thumb.o | 56 | obj-$(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 | |||
20 | static 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 | |||
69 | static 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 | |||
79 | static 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 | |||
87 | static 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 | |||
97 | static 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 | |||
107 | enum probes_insn | ||
108 | decode_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 | |||
131 | enum probes_insn | ||
132 | decode_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 | |||
146 | enum probes_insn | ||
147 | decode_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 | |||
154 | enum probes_insn | ||
155 | decode_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 | |||
161 | enum probes_insn | ||
162 | uprobe_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 | |||
197 | const 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 | |||
26 | bool is_swbp_insn(uprobe_opcode_t *insn) | ||
27 | { | ||
28 | return (__mem_to_opcode_arm(*insn) & 0x0fffffff) == | ||
29 | (UPROBE_SWBP_ARM_INSN & 0x0fffffff); | ||
30 | } | ||
31 | |||
32 | int 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 | |||
39 | bool 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 | |||
49 | bool 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 | |||
63 | unsigned long | ||
64 | arch_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 | |||
75 | int 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 | |||
116 | int 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 | |||
130 | int 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 | |||
145 | bool 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 | |||
153 | void 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 | |||
161 | int arch_uprobe_exception_notify(struct notifier_block *self, | ||
162 | unsigned long val, void *data) | ||
163 | { | ||
164 | return NOTIFY_DONE; | ||
165 | } | ||
166 | |||
167 | static 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 | |||
182 | unsigned long uprobe_get_swbp_addr(struct pt_regs *regs) | ||
183 | { | ||
184 | return instruction_pointer(regs); | ||
185 | } | ||
186 | |||
187 | static 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 | |||
195 | static 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 | |||
203 | static 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 | } | ||
210 | device_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 | |||
12 | enum probes_insn uprobe_decode_ldmstm(probes_opcode_t insn, | ||
13 | struct arch_probes_insn *asi, | ||
14 | const struct decode_header *d); | ||
15 | |||
16 | enum probes_insn decode_ldr(probes_opcode_t insn, | ||
17 | struct arch_probes_insn *asi, | ||
18 | const struct decode_header *d); | ||
19 | |||
20 | enum probes_insn | ||
21 | decode_rd12rn16rm0rs8_rwflags(probes_opcode_t insn, | ||
22 | struct arch_probes_insn *asi, | ||
23 | const struct decode_header *d); | ||
24 | |||
25 | enum probes_insn | ||
26 | decode_wb_pc(probes_opcode_t insn, struct arch_probes_insn *asi, | ||
27 | const struct decode_header *d, bool alu); | ||
28 | |||
29 | enum probes_insn | ||
30 | decode_pc_ro(probes_opcode_t insn, struct arch_probes_insn *asi, | ||
31 | const struct decode_header *d); | ||
32 | |||
33 | extern const union decode_action uprobes_probes_actions[]; | ||
34 | |||
35 | #endif | ||