Branch data Line data Source code
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 : 0 : bool is_swbp_insn(uprobe_opcode_t *insn)
27 : : {
28 : 0 : return (__mem_to_opcode_arm(*insn) & 0x0fffffff) ==
29 : : (UPROBE_SWBP_ARM_INSN & 0x0fffffff);
30 : : }
31 : :
32 : 0 : int set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm,
33 : : unsigned long vaddr)
34 : : {
35 : 0 : return uprobe_write_opcode(mm, vaddr,
36 : : __opcode_to_mem_arm(auprobe->bpinsn));
37 : : }
38 : :
39 : 0 : bool arch_uprobe_ignore(struct arch_uprobe *auprobe, struct pt_regs *regs)
40 : : {
41 [ # # ]: 0 : if (!auprobe->asi.insn_check_cc(regs->ARM_cpsr)) {
42 : 0 : regs->ARM_pc += 4;
43 : 0 : return true;
44 : : }
45 : :
46 : : return false;
47 : : }
48 : :
49 : 0 : bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
50 : : {
51 : : probes_opcode_t opcode;
52 : :
53 [ # # ]: 0 : if (!auprobe->simulate)
54 : : return false;
55 : :
56 : 0 : opcode = __mem_to_opcode_arm(*(unsigned int *) auprobe->insn);
57 : :
58 : 0 : auprobe->asi.insn_singlestep(opcode, &auprobe->asi, regs);
59 : :
60 : 0 : return true;
61 : : }
62 : :
63 : : unsigned long
64 : 0 : arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
65 : : struct pt_regs *regs)
66 : : {
67 : : unsigned long orig_ret_vaddr;
68 : :
69 : 0 : orig_ret_vaddr = regs->ARM_lr;
70 : : /* Replace the return addr with trampoline addr */
71 : 0 : regs->ARM_lr = trampoline_vaddr;
72 : 0 : return orig_ret_vaddr;
73 : : }
74 : :
75 : 0 : 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 [ # # ]: 0 : if (addr & 0x3)
84 : : return -EINVAL;
85 : :
86 : 0 : insn = __mem_to_opcode_arm(*(unsigned int *)auprobe->insn);
87 : 0 : auprobe->ixol[0] = __opcode_to_mem_arm(insn);
88 : 0 : auprobe->ixol[1] = __opcode_to_mem_arm(UPROBE_SS_ARM_INSN);
89 : :
90 : 0 : ret = arm_probes_decode_insn(insn, &auprobe->asi, false,
91 : : uprobes_probes_actions);
92 [ # # # ]: 0 : switch (ret) {
93 : : case INSN_REJECTED:
94 : : return -EINVAL;
95 : :
96 : : case INSN_GOOD_NO_SLOT:
97 : 0 : auprobe->simulate = true;
98 : 0 : break;
99 : :
100 : : case INSN_GOOD:
101 : : default:
102 : : break;
103 : : }
104 : :
105 : : bpinsn = UPROBE_SWBP_ARM_INSN & 0x0fffffff;
106 [ # # ]: 0 : if (insn >= 0xe0000000)
107 : : bpinsn |= 0xe0000000; /* Unconditional instruction */
108 : : else
109 : 0 : bpinsn |= insn & 0xf0000000; /* Copy condition from insn */
110 : :
111 : 0 : auprobe->bpinsn = bpinsn;
112 : :
113 : 0 : return 0;
114 : : }
115 : :
116 : 0 : int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
117 : : {
118 : 0 : struct uprobe_task *utask = current->utask;
119 : :
120 [ # # ]: 0 : if (auprobe->prehandler)
121 : 0 : auprobe->prehandler(auprobe, &utask->autask, regs);
122 : :
123 : 0 : utask->autask.saved_trap_no = current->thread.trap_no;
124 : 0 : current->thread.trap_no = UPROBE_TRAP_NR;
125 : 0 : regs->ARM_pc = utask->xol_vaddr;
126 : :
127 : 0 : return 0;
128 : : }
129 : :
130 : 0 : int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
131 : : {
132 : 0 : struct uprobe_task *utask = current->utask;
133 : :
134 [ # # ][ # # ]: 0 : WARN_ON_ONCE(current->thread.trap_no != UPROBE_TRAP_NR);
[ # # ]
135 : :
136 : 0 : current->thread.trap_no = utask->autask.saved_trap_no;
137 : 0 : regs->ARM_pc = utask->vaddr + 4;
138 : :
139 [ # # ]: 0 : if (auprobe->posthandler)
140 : 0 : auprobe->posthandler(auprobe, &utask->autask, regs);
141 : :
142 : 0 : return 0;
143 : : }
144 : :
145 : 0 : bool arch_uprobe_xol_was_trapped(struct task_struct *t)
146 : : {
147 [ # # ]: 0 : if (t->thread.trap_no != UPROBE_TRAP_NR)
148 : : return true;
149 : :
150 : 0 : return false;
151 : : }
152 : :
153 : 0 : void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
154 : : {
155 : 0 : struct uprobe_task *utask = current->utask;
156 : :
157 : 0 : current->thread.trap_no = utask->autask.saved_trap_no;
158 : 0 : instruction_pointer_set(regs, utask->vaddr);
159 : 0 : }
160 : :
161 : 0 : int arch_uprobe_exception_notify(struct notifier_block *self,
162 : : unsigned long val, void *data)
163 : : {
164 : 0 : return NOTIFY_DONE;
165 : : }
166 : :
167 : 0 : static int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
168 : : {
169 : : unsigned long flags;
170 : :
171 : : local_irq_save(flags);
172 : 0 : instr &= 0x0fffffff;
173 [ # # ]: 0 : if (instr == (UPROBE_SWBP_ARM_INSN & 0x0fffffff))
174 : 0 : uprobe_pre_sstep_notifier(regs);
175 [ # # ]: 0 : else if (instr == (UPROBE_SS_ARM_INSN & 0x0fffffff))
176 : 0 : uprobe_post_sstep_notifier(regs);
177 [ # # ]: 0 : local_irq_restore(flags);
178 : :
179 : 0 : return 0;
180 : : }
181 : :
182 : 0 : unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
183 : : {
184 : 0 : 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 : 0 : static int arch_uprobes_init(void)
204 : : {
205 : 0 : register_undef_hook(&uprobes_arm_break_hook);
206 : 0 : register_undef_hook(&uprobes_arm_ss_hook);
207 : :
208 : 0 : return 0;
209 : : }
210 : : device_initcall(arch_uprobes_init);
|