1
2
3
4
5
6
7
8
9
10
11
12#include <linux/kernel.h>
13#include <linux/highmem.h>
14#include <linux/uprobes.h>
15#include <linux/uaccess.h>
16#include <linux/sched.h>
17#include <linux/kdebug.h>
18
19#include <asm/cacheflush.h>
20
21
22
23
24
25
26unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
27{
28 return instruction_pointer(regs);
29}
30
31static void copy_to_page(struct page *page, unsigned long vaddr,
32 const void *src, int len)
33{
34 void *kaddr = kmap_atomic(page);
35
36 memcpy(kaddr + (vaddr & ~PAGE_MASK), src, len);
37 kunmap_atomic(kaddr);
38}
39
40
41
42
43
44
45
46
47void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
48 void *src, unsigned long len)
49{
50 const u32 stp_insn = UPROBE_STP_INSN;
51 u32 insn = *(u32 *) src;
52
53
54
55
56
57
58
59 u32 op = (insn >> 30) & 0x3;
60 u32 op2 = (insn >> 22) & 0x7;
61
62 if (op == 0 &&
63 (op2 == 1 || op2 == 2 || op2 == 3 || op2 == 5 || op2 == 6) &&
64 (insn & ANNUL_BIT) == ANNUL_BIT)
65 insn &= ~ANNUL_BIT;
66
67 copy_to_page(page, vaddr, &insn, len);
68 copy_to_page(page, vaddr+len, &stp_insn, 4);
69}
70
71
72
73
74
75
76int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe,
77 struct mm_struct *mm, unsigned long addr)
78{
79
80 return 0;
81}
82
83
84
85
86
87
88
89
90
91static unsigned long relbranch_fixup(u32 insn, struct uprobe_task *utask,
92 struct pt_regs *regs)
93{
94
95 if (regs->tnpc == regs->tpc + 0x4UL)
96 return utask->autask.saved_tnpc + 0x4UL;
97
98
99
100
101 if ((insn & 0xc0000000) == 0x40000000 ||
102 (insn & 0xc1c00000) == 0x00400000 ||
103 (insn & 0xc1c00000) == 0x00800000) {
104 unsigned long real_pc = (unsigned long) utask->vaddr;
105 unsigned long ixol_addr = utask->xol_vaddr;
106
107
108
109
110
111 return (real_pc + (regs->tnpc - ixol_addr));
112 }
113
114
115
116
117 return regs->tnpc;
118}
119
120
121
122
123static int retpc_fixup(struct pt_regs *regs, u32 insn,
124 unsigned long real_pc)
125{
126 unsigned long *slot = NULL;
127 int rc = 0;
128
129
130 if ((insn & 0xc0000000) == 0x40000000)
131 slot = ®s->u_regs[UREG_I7];
132
133
134 if ((insn & 0xc1f80000) == 0x81c00000) {
135 unsigned long rd = ((insn >> 25) & 0x1f);
136
137 if (rd <= 15) {
138 slot = ®s->u_regs[rd];
139 } else {
140 unsigned long fp = regs->u_regs[UREG_FP];
141
142 flushw_all();
143
144 rd -= 16;
145 if (test_thread_64bit_stack(fp)) {
146 unsigned long __user *uslot =
147 (unsigned long __user *) (fp + STACK_BIAS) + rd;
148 rc = __put_user(real_pc, uslot);
149 } else {
150 unsigned int __user *uslot = (unsigned int
151 __user *) fp + rd;
152 rc = __put_user((u32) real_pc, uslot);
153 }
154 }
155 }
156 if (slot != NULL)
157 *slot = real_pc;
158 return rc;
159}
160
161
162
163
164
165
166
167
168
169bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
170{
171
172
173
174 if (auprobe->ixol == (1 << 24)) {
175 regs->tnpc += 4;
176 regs->tpc += 4;
177 return true;
178 }
179
180 return false;
181}
182
183
184
185
186
187
188
189
190int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
191{
192 struct uprobe_task *utask = current->utask;
193 struct arch_uprobe_task *autask = ¤t->utask->autask;
194
195
196
197
198 autask->saved_tpc = regs->tpc;
199 autask->saved_tnpc = regs->tnpc;
200
201
202
203
204 instruction_pointer_set(regs, utask->xol_vaddr);
205
206 return 0;
207}
208
209
210
211
212
213
214
215
216int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
217{
218 struct uprobe_task *utask = current->utask;
219 struct arch_uprobe_task *autask = &utask->autask;
220 u32 insn = auprobe->ixol;
221 int rc = 0;
222
223 if (utask->state == UTASK_SSTEP_ACK) {
224 regs->tnpc = relbranch_fixup(insn, utask, regs);
225 regs->tpc = autask->saved_tnpc;
226 rc = retpc_fixup(regs, insn, (unsigned long) utask->vaddr);
227 } else {
228 regs->tnpc = utask->vaddr+4;
229 regs->tpc = autask->saved_tnpc+4;
230 }
231 return rc;
232}
233
234
235
236
237asmlinkage void uprobe_trap(struct pt_regs *regs,
238 unsigned long trap_level)
239{
240 BUG_ON(trap_level != 0x173 && trap_level != 0x174);
241
242
243
244
245 if (!user_mode(regs)) {
246 local_irq_enable();
247 bad_trap(regs, trap_level);
248 return;
249 }
250
251
252
253
254 if (notify_die((trap_level == 0x173) ? DIE_BPT : DIE_SSTEP,
255 (trap_level == 0x173) ? "bpt" : "sstep",
256 regs, 0, trap_level, SIGTRAP) != NOTIFY_STOP)
257 bad_trap(regs, trap_level);
258}
259
260
261
262int arch_uprobe_exception_notify(struct notifier_block *self,
263 unsigned long val, void *data)
264{
265 int ret = NOTIFY_DONE;
266 struct die_args *args = (struct die_args *)data;
267
268
269 if (args->regs && !user_mode(args->regs))
270 return NOTIFY_DONE;
271
272 switch (val) {
273 case DIE_BPT:
274 if (uprobe_pre_sstep_notifier(args->regs))
275 ret = NOTIFY_STOP;
276 break;
277
278 case DIE_SSTEP:
279 if (uprobe_post_sstep_notifier(args->regs))
280 ret = NOTIFY_STOP;
281
282 default:
283 break;
284 }
285
286 return ret;
287}
288
289
290
291
292
293void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
294{
295 struct uprobe_task *utask = current->utask;
296
297 instruction_pointer_set(regs, utask->vaddr);
298}
299
300
301
302
303
304bool arch_uprobe_xol_was_trapped(struct task_struct *t)
305{
306 return false;
307}
308
309unsigned long
310arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
311 struct pt_regs *regs)
312{
313 unsigned long orig_ret_vaddr = regs->u_regs[UREG_I7];
314
315 regs->u_regs[UREG_I7] = trampoline_vaddr-8;
316
317 return orig_ret_vaddr + 8;
318}
319