]>
Commit | Line | Data |
---|---|---|
2a0a5b22 JW |
1 | /* |
2 | * User-space Probes (UProbes) for s390 | |
3 | * | |
4 | * Copyright IBM Corp. 2014 | |
5 | * Author(s): Jan Willeke, | |
6 | */ | |
7 | ||
2a0a5b22 JW |
8 | #include <linux/uaccess.h> |
9 | #include <linux/uprobes.h> | |
10 | #include <linux/compat.h> | |
11 | #include <linux/kdebug.h> | |
68db0cf1 IM |
12 | #include <linux/sched/task_stack.h> |
13 | ||
2a0a5b22 JW |
14 | #include <asm/switch_to.h> |
15 | #include <asm/facility.h> | |
d6fe5be3 | 16 | #include <asm/kprobes.h> |
2a0a5b22 JW |
17 | #include <asm/dis.h> |
18 | #include "entry.h" | |
19 | ||
20 | #define UPROBE_TRAP_NR UINT_MAX | |
21 | ||
22 | int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, | |
23 | unsigned long addr) | |
24 | { | |
25 | return probe_is_prohibited_opcode(auprobe->insn); | |
26 | } | |
27 | ||
28 | int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | |
29 | { | |
30 | if (psw_bits(regs->psw).eaba == PSW_AMODE_24BIT) | |
31 | return -EINVAL; | |
32 | if (!is_compat_task() && psw_bits(regs->psw).eaba == PSW_AMODE_31BIT) | |
33 | return -EINVAL; | |
34 | clear_pt_regs_flag(regs, PIF_PER_TRAP); | |
35 | auprobe->saved_per = psw_bits(regs->psw).r; | |
36 | auprobe->saved_int_code = regs->int_code; | |
37 | regs->int_code = UPROBE_TRAP_NR; | |
38 | regs->psw.addr = current->utask->xol_vaddr; | |
39 | set_tsk_thread_flag(current, TIF_UPROBE_SINGLESTEP); | |
40 | update_cr_regs(current); | |
41 | return 0; | |
42 | } | |
43 | ||
44 | bool arch_uprobe_xol_was_trapped(struct task_struct *tsk) | |
45 | { | |
46 | struct pt_regs *regs = task_pt_regs(tsk); | |
47 | ||
48 | if (regs->int_code != UPROBE_TRAP_NR) | |
49 | return true; | |
50 | return false; | |
51 | } | |
52 | ||
8d1a2427 JW |
53 | static int check_per_event(unsigned short cause, unsigned long control, |
54 | struct pt_regs *regs) | |
55 | { | |
56 | if (!(regs->psw.mask & PSW_MASK_PER)) | |
57 | return 0; | |
58 | /* user space single step */ | |
59 | if (control == 0) | |
60 | return 1; | |
61 | /* over indication for storage alteration */ | |
62 | if ((control & 0x20200000) && (cause & 0x2000)) | |
63 | return 1; | |
64 | if (cause & 0x8000) { | |
65 | /* all branches */ | |
66 | if ((control & 0x80800000) == 0x80000000) | |
67 | return 1; | |
68 | /* branch into selected range */ | |
69 | if (((control & 0x80800000) == 0x80800000) && | |
70 | regs->psw.addr >= current->thread.per_user.start && | |
71 | regs->psw.addr <= current->thread.per_user.end) | |
72 | return 1; | |
73 | } | |
74 | return 0; | |
75 | } | |
76 | ||
2a0a5b22 JW |
77 | int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) |
78 | { | |
79 | int fixup = probe_get_fixup_type(auprobe->insn); | |
80 | struct uprobe_task *utask = current->utask; | |
81 | ||
82 | clear_tsk_thread_flag(current, TIF_UPROBE_SINGLESTEP); | |
83 | update_cr_regs(current); | |
84 | psw_bits(regs->psw).r = auprobe->saved_per; | |
85 | regs->int_code = auprobe->saved_int_code; | |
86 | ||
87 | if (fixup & FIXUP_PSW_NORMAL) | |
88 | regs->psw.addr += utask->vaddr - utask->xol_vaddr; | |
89 | if (fixup & FIXUP_RETURN_REGISTER) { | |
90 | int reg = (auprobe->insn[0] & 0xf0) >> 4; | |
91 | ||
92 | regs->gprs[reg] += utask->vaddr - utask->xol_vaddr; | |
93 | } | |
94 | if (fixup & FIXUP_BRANCH_NOT_TAKEN) { | |
95 | int ilen = insn_length(auprobe->insn[0] >> 8); | |
96 | ||
97 | if (regs->psw.addr - utask->xol_vaddr == ilen) | |
98 | regs->psw.addr = utask->vaddr + ilen; | |
99 | } | |
8d1a2427 JW |
100 | if (check_per_event(current->thread.per_event.cause, |
101 | current->thread.per_user.control, regs)) { | |
102 | /* fix per address */ | |
103 | current->thread.per_event.address = utask->vaddr; | |
104 | /* trigger per event */ | |
105 | set_pt_regs_flag(regs, PIF_PER_TRAP); | |
106 | } | |
2a0a5b22 JW |
107 | return 0; |
108 | } | |
109 | ||
110 | int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, | |
111 | void *data) | |
112 | { | |
113 | struct die_args *args = data; | |
114 | struct pt_regs *regs = args->regs; | |
115 | ||
116 | if (!user_mode(regs)) | |
117 | return NOTIFY_DONE; | |
118 | if (regs->int_code & 0x200) /* Trap during transaction */ | |
119 | return NOTIFY_DONE; | |
120 | switch (val) { | |
121 | case DIE_BPT: | |
122 | if (uprobe_pre_sstep_notifier(regs)) | |
123 | return NOTIFY_STOP; | |
124 | break; | |
125 | case DIE_SSTEP: | |
126 | if (uprobe_post_sstep_notifier(regs)) | |
127 | return NOTIFY_STOP; | |
128 | default: | |
129 | break; | |
130 | } | |
131 | return NOTIFY_DONE; | |
132 | } | |
133 | ||
134 | void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | |
135 | { | |
136 | clear_thread_flag(TIF_UPROBE_SINGLESTEP); | |
137 | regs->int_code = auprobe->saved_int_code; | |
138 | regs->psw.addr = current->utask->vaddr; | |
8d1a2427 | 139 | current->thread.per_event.address = current->utask->vaddr; |
2a0a5b22 JW |
140 | } |
141 | ||
142 | unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline, | |
143 | struct pt_regs *regs) | |
144 | { | |
145 | unsigned long orig; | |
146 | ||
147 | orig = regs->gprs[14]; | |
148 | regs->gprs[14] = trampoline; | |
149 | return orig; | |
150 | } | |
151 | ||
152 | /* Instruction Emulation */ | |
153 | ||
154 | static void adjust_psw_addr(psw_t *psw, unsigned long len) | |
155 | { | |
156 | psw->addr = __rewind_psw(*psw, -len); | |
157 | } | |
158 | ||
159 | #define EMU_ILLEGAL_OP 1 | |
160 | #define EMU_SPECIFICATION 2 | |
161 | #define EMU_ADDRESSING 3 | |
162 | ||
163 | #define emu_load_ril(ptr, output) \ | |
164 | ({ \ | |
165 | unsigned int mask = sizeof(*(ptr)) - 1; \ | |
166 | __typeof__(*(ptr)) input; \ | |
167 | int __rc = 0; \ | |
168 | \ | |
169 | if (!test_facility(34)) \ | |
170 | __rc = EMU_ILLEGAL_OP; \ | |
171 | else if ((u64 __force)ptr & mask) \ | |
172 | __rc = EMU_SPECIFICATION; \ | |
173 | else if (get_user(input, ptr)) \ | |
174 | __rc = EMU_ADDRESSING; \ | |
175 | else \ | |
176 | *(output) = input; \ | |
177 | __rc; \ | |
178 | }) | |
179 | ||
8d1a2427 | 180 | #define emu_store_ril(regs, ptr, input) \ |
2a0a5b22 JW |
181 | ({ \ |
182 | unsigned int mask = sizeof(*(ptr)) - 1; \ | |
8d1a2427 | 183 | __typeof__(ptr) __ptr = (ptr); \ |
2a0a5b22 JW |
184 | int __rc = 0; \ |
185 | \ | |
186 | if (!test_facility(34)) \ | |
187 | __rc = EMU_ILLEGAL_OP; \ | |
8d1a2427 | 188 | else if ((u64 __force)__ptr & mask) \ |
2a0a5b22 | 189 | __rc = EMU_SPECIFICATION; \ |
8d1a2427 | 190 | else if (put_user(*(input), __ptr)) \ |
2a0a5b22 | 191 | __rc = EMU_ADDRESSING; \ |
8d1a2427 | 192 | if (__rc == 0) \ |
9f9d86e1 HC |
193 | sim_stor_event(regs, \ |
194 | (void __force *)__ptr, \ | |
195 | mask + 1); \ | |
2a0a5b22 JW |
196 | __rc; \ |
197 | }) | |
198 | ||
199 | #define emu_cmp_ril(regs, ptr, cmp) \ | |
200 | ({ \ | |
201 | unsigned int mask = sizeof(*(ptr)) - 1; \ | |
202 | __typeof__(*(ptr)) input; \ | |
203 | int __rc = 0; \ | |
204 | \ | |
205 | if (!test_facility(34)) \ | |
206 | __rc = EMU_ILLEGAL_OP; \ | |
207 | else if ((u64 __force)ptr & mask) \ | |
208 | __rc = EMU_SPECIFICATION; \ | |
209 | else if (get_user(input, ptr)) \ | |
210 | __rc = EMU_ADDRESSING; \ | |
211 | else if (input > *(cmp)) \ | |
212 | psw_bits((regs)->psw).cc = 1; \ | |
213 | else if (input < *(cmp)) \ | |
214 | psw_bits((regs)->psw).cc = 2; \ | |
215 | else \ | |
216 | psw_bits((regs)->psw).cc = 0; \ | |
217 | __rc; \ | |
218 | }) | |
219 | ||
220 | struct insn_ril { | |
221 | u8 opc0; | |
222 | u8 reg : 4; | |
223 | u8 opc1 : 4; | |
224 | s32 disp; | |
225 | } __packed; | |
226 | ||
227 | union split_register { | |
228 | u64 u64; | |
229 | u32 u32[2]; | |
230 | u16 u16[4]; | |
231 | s64 s64; | |
232 | s32 s32[2]; | |
233 | s16 s16[4]; | |
234 | }; | |
235 | ||
8d1a2427 JW |
236 | /* |
237 | * If user per registers are setup to trace storage alterations and an | |
238 | * emulated store took place on a fitting address a user trap is generated. | |
239 | */ | |
240 | static void sim_stor_event(struct pt_regs *regs, void *addr, int len) | |
241 | { | |
242 | if (!(regs->psw.mask & PSW_MASK_PER)) | |
243 | return; | |
244 | if (!(current->thread.per_user.control & PER_EVENT_STORE)) | |
245 | return; | |
246 | if ((void *)current->thread.per_user.start > (addr + len)) | |
247 | return; | |
248 | if ((void *)current->thread.per_user.end < addr) | |
249 | return; | |
250 | current->thread.per_event.address = regs->psw.addr; | |
251 | current->thread.per_event.cause = PER_EVENT_STORE >> 16; | |
252 | set_pt_regs_flag(regs, PIF_PER_TRAP); | |
253 | } | |
254 | ||
2a0a5b22 JW |
255 | /* |
256 | * pc relative instructions are emulated, since parameters may not be | |
257 | * accessible from the xol area due to range limitations. | |
258 | */ | |
259 | static void handle_insn_ril(struct arch_uprobe *auprobe, struct pt_regs *regs) | |
260 | { | |
261 | union split_register *rx; | |
262 | struct insn_ril *insn; | |
263 | unsigned int ilen; | |
264 | void *uptr; | |
265 | int rc = 0; | |
266 | ||
267 | insn = (struct insn_ril *) &auprobe->insn; | |
268 | rx = (union split_register *) ®s->gprs[insn->reg]; | |
269 | uptr = (void *)(regs->psw.addr + (insn->disp * 2)); | |
270 | ilen = insn_length(insn->opc0); | |
271 | ||
272 | switch (insn->opc0) { | |
273 | case 0xc0: | |
274 | switch (insn->opc1) { | |
275 | case 0x00: /* larl */ | |
276 | rx->u64 = (unsigned long)uptr; | |
277 | break; | |
278 | } | |
279 | break; | |
280 | case 0xc4: | |
281 | switch (insn->opc1) { | |
282 | case 0x02: /* llhrl */ | |
283 | rc = emu_load_ril((u16 __user *)uptr, &rx->u32[1]); | |
284 | break; | |
285 | case 0x04: /* lghrl */ | |
286 | rc = emu_load_ril((s16 __user *)uptr, &rx->u64); | |
287 | break; | |
288 | case 0x05: /* lhrl */ | |
289 | rc = emu_load_ril((s16 __user *)uptr, &rx->u32[1]); | |
290 | break; | |
291 | case 0x06: /* llghrl */ | |
292 | rc = emu_load_ril((u16 __user *)uptr, &rx->u64); | |
293 | break; | |
294 | case 0x08: /* lgrl */ | |
295 | rc = emu_load_ril((u64 __user *)uptr, &rx->u64); | |
296 | break; | |
297 | case 0x0c: /* lgfrl */ | |
298 | rc = emu_load_ril((s32 __user *)uptr, &rx->u64); | |
299 | break; | |
300 | case 0x0d: /* lrl */ | |
301 | rc = emu_load_ril((u32 __user *)uptr, &rx->u32[1]); | |
302 | break; | |
303 | case 0x0e: /* llgfrl */ | |
304 | rc = emu_load_ril((u32 __user *)uptr, &rx->u64); | |
305 | break; | |
306 | case 0x07: /* sthrl */ | |
8d1a2427 | 307 | rc = emu_store_ril(regs, (u16 __user *)uptr, &rx->u16[3]); |
2a0a5b22 JW |
308 | break; |
309 | case 0x0b: /* stgrl */ | |
8d1a2427 | 310 | rc = emu_store_ril(regs, (u64 __user *)uptr, &rx->u64); |
2a0a5b22 JW |
311 | break; |
312 | case 0x0f: /* strl */ | |
8d1a2427 | 313 | rc = emu_store_ril(regs, (u32 __user *)uptr, &rx->u32[1]); |
2a0a5b22 JW |
314 | break; |
315 | } | |
316 | break; | |
317 | case 0xc6: | |
318 | switch (insn->opc1) { | |
319 | case 0x02: /* pfdrl */ | |
320 | if (!test_facility(34)) | |
321 | rc = EMU_ILLEGAL_OP; | |
322 | break; | |
323 | case 0x04: /* cghrl */ | |
324 | rc = emu_cmp_ril(regs, (s16 __user *)uptr, &rx->s64); | |
325 | break; | |
326 | case 0x05: /* chrl */ | |
327 | rc = emu_cmp_ril(regs, (s16 __user *)uptr, &rx->s32[1]); | |
328 | break; | |
329 | case 0x06: /* clghrl */ | |
330 | rc = emu_cmp_ril(regs, (u16 __user *)uptr, &rx->u64); | |
331 | break; | |
332 | case 0x07: /* clhrl */ | |
333 | rc = emu_cmp_ril(regs, (u16 __user *)uptr, &rx->u32[1]); | |
334 | break; | |
335 | case 0x08: /* cgrl */ | |
336 | rc = emu_cmp_ril(regs, (s64 __user *)uptr, &rx->s64); | |
337 | break; | |
338 | case 0x0a: /* clgrl */ | |
339 | rc = emu_cmp_ril(regs, (u64 __user *)uptr, &rx->u64); | |
340 | break; | |
341 | case 0x0c: /* cgfrl */ | |
342 | rc = emu_cmp_ril(regs, (s32 __user *)uptr, &rx->s64); | |
343 | break; | |
344 | case 0x0d: /* crl */ | |
345 | rc = emu_cmp_ril(regs, (s32 __user *)uptr, &rx->s32[1]); | |
346 | break; | |
347 | case 0x0e: /* clgfrl */ | |
348 | rc = emu_cmp_ril(regs, (u32 __user *)uptr, &rx->u64); | |
349 | break; | |
350 | case 0x0f: /* clrl */ | |
351 | rc = emu_cmp_ril(regs, (u32 __user *)uptr, &rx->u32[1]); | |
352 | break; | |
353 | } | |
354 | break; | |
355 | } | |
356 | adjust_psw_addr(®s->psw, ilen); | |
357 | switch (rc) { | |
358 | case EMU_ILLEGAL_OP: | |
359 | regs->int_code = ilen << 16 | 0x0001; | |
360 | do_report_trap(regs, SIGILL, ILL_ILLOPC, NULL); | |
361 | break; | |
362 | case EMU_SPECIFICATION: | |
363 | regs->int_code = ilen << 16 | 0x0006; | |
364 | do_report_trap(regs, SIGILL, ILL_ILLOPC , NULL); | |
365 | break; | |
366 | case EMU_ADDRESSING: | |
367 | regs->int_code = ilen << 16 | 0x0005; | |
368 | do_report_trap(regs, SIGSEGV, SEGV_MAPERR, NULL); | |
369 | break; | |
370 | } | |
371 | } | |
372 | ||
373 | bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) | |
374 | { | |
375 | if ((psw_bits(regs->psw).eaba == PSW_AMODE_24BIT) || | |
376 | ((psw_bits(regs->psw).eaba == PSW_AMODE_31BIT) && | |
377 | !is_compat_task())) { | |
378 | regs->psw.addr = __rewind_psw(regs->psw, UPROBE_SWBP_INSN_SIZE); | |
379 | do_report_trap(regs, SIGILL, ILL_ILLADR, NULL); | |
380 | return true; | |
381 | } | |
382 | if (probe_is_insn_relative_long(auprobe->insn)) { | |
383 | handle_insn_ril(auprobe, regs); | |
384 | return true; | |
385 | } | |
386 | return false; | |
387 | } |