diff options
author | Jan Willeke <willeke@de.ibm.com> | 2014-09-22 10:39:06 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2014-09-25 04:52:17 -0400 |
commit | 2a0a5b2299b9bef76123fac91e68d39cb361c33e (patch) | |
tree | fa8872070bf295dbbc57e7063db6e6baf739f543 | |
parent | 975fab17399a2b29985166181ad80e5f50fa42e9 (diff) |
s390/uprobes: architecture backend for uprobes
Signed-off-by: Jan Willeke <willeke@de.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r-- | arch/s390/Kconfig | 3 | ||||
-rw-r--r-- | arch/s390/include/asm/ptrace.h | 6 | ||||
-rw-r--r-- | arch/s390/include/asm/thread_info.h | 3 | ||||
-rw-r--r-- | arch/s390/include/asm/uprobes.h | 42 | ||||
-rw-r--r-- | arch/s390/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/s390/kernel/entry.h | 1 | ||||
-rw-r--r-- | arch/s390/kernel/entry64.S | 17 | ||||
-rw-r--r-- | arch/s390/kernel/ptrace.c | 5 | ||||
-rw-r--r-- | arch/s390/kernel/traps.c | 33 | ||||
-rw-r--r-- | arch/s390/kernel/uprobes.c | 332 |
10 files changed, 430 insertions, 13 deletions
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 47492fc692f4..608adfb65dd3 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig | |||
@@ -58,6 +58,9 @@ config NO_IOPORT_MAP | |||
58 | config PCI_QUIRKS | 58 | config PCI_QUIRKS |
59 | def_bool n | 59 | def_bool n |
60 | 60 | ||
61 | config ARCH_SUPPORTS_UPROBES | ||
62 | def_bool 64BIT | ||
63 | |||
61 | config S390 | 64 | config S390 |
62 | def_bool y | 65 | def_bool y |
63 | select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE | 66 | select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE |
diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h index 55d69dd7473c..be317feff7ac 100644 --- a/arch/s390/include/asm/ptrace.h +++ b/arch/s390/include/asm/ptrace.h | |||
@@ -161,6 +161,12 @@ static inline long regs_return_value(struct pt_regs *regs) | |||
161 | return regs->gprs[2]; | 161 | return regs->gprs[2]; |
162 | } | 162 | } |
163 | 163 | ||
164 | static inline void instruction_pointer_set(struct pt_regs *regs, | ||
165 | unsigned long val) | ||
166 | { | ||
167 | regs->psw.addr = val | PSW_ADDR_AMODE; | ||
168 | } | ||
169 | |||
164 | int regs_query_register_offset(const char *name); | 170 | int regs_query_register_offset(const char *name); |
165 | const char *regs_query_register_name(unsigned int offset); | 171 | const char *regs_query_register_name(unsigned int offset); |
166 | unsigned long regs_get_register(struct pt_regs *regs, unsigned int offset); | 172 | unsigned long regs_get_register(struct pt_regs *regs, unsigned int offset); |
diff --git a/arch/s390/include/asm/thread_info.h b/arch/s390/include/asm/thread_info.h index b833e9c0bfbf..4d62fd5b56e5 100644 --- a/arch/s390/include/asm/thread_info.h +++ b/arch/s390/include/asm/thread_info.h | |||
@@ -84,11 +84,13 @@ static inline struct thread_info *current_thread_info(void) | |||
84 | #define TIF_SYSCALL_AUDIT 4 /* syscall auditing active */ | 84 | #define TIF_SYSCALL_AUDIT 4 /* syscall auditing active */ |
85 | #define TIF_SECCOMP 5 /* secure computing */ | 85 | #define TIF_SECCOMP 5 /* secure computing */ |
86 | #define TIF_SYSCALL_TRACEPOINT 6 /* syscall tracepoint instrumentation */ | 86 | #define TIF_SYSCALL_TRACEPOINT 6 /* syscall tracepoint instrumentation */ |
87 | #define TIF_UPROBE 7 /* breakpointed or single-stepping */ | ||
87 | #define TIF_31BIT 16 /* 32bit process */ | 88 | #define TIF_31BIT 16 /* 32bit process */ |
88 | #define TIF_MEMDIE 17 /* is terminating due to OOM killer */ | 89 | #define TIF_MEMDIE 17 /* is terminating due to OOM killer */ |
89 | #define TIF_RESTORE_SIGMASK 18 /* restore signal mask in do_signal() */ | 90 | #define TIF_RESTORE_SIGMASK 18 /* restore signal mask in do_signal() */ |
90 | #define TIF_SINGLE_STEP 19 /* This task is single stepped */ | 91 | #define TIF_SINGLE_STEP 19 /* This task is single stepped */ |
91 | #define TIF_BLOCK_STEP 20 /* This task is block stepped */ | 92 | #define TIF_BLOCK_STEP 20 /* This task is block stepped */ |
93 | #define TIF_UPROBE_SINGLESTEP 21 /* This task is uprobe single stepped */ | ||
92 | 94 | ||
93 | #define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME) | 95 | #define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME) |
94 | #define _TIF_SIGPENDING (1<<TIF_SIGPENDING) | 96 | #define _TIF_SIGPENDING (1<<TIF_SIGPENDING) |
@@ -97,6 +99,7 @@ static inline struct thread_info *current_thread_info(void) | |||
97 | #define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT) | 99 | #define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT) |
98 | #define _TIF_SECCOMP (1<<TIF_SECCOMP) | 100 | #define _TIF_SECCOMP (1<<TIF_SECCOMP) |
99 | #define _TIF_SYSCALL_TRACEPOINT (1<<TIF_SYSCALL_TRACEPOINT) | 101 | #define _TIF_SYSCALL_TRACEPOINT (1<<TIF_SYSCALL_TRACEPOINT) |
102 | #define _TIF_UPROBE (1<<TIF_UPROBE) | ||
100 | #define _TIF_31BIT (1<<TIF_31BIT) | 103 | #define _TIF_31BIT (1<<TIF_31BIT) |
101 | #define _TIF_SINGLE_STEP (1<<TIF_SINGLE_STEP) | 104 | #define _TIF_SINGLE_STEP (1<<TIF_SINGLE_STEP) |
102 | 105 | ||
diff --git a/arch/s390/include/asm/uprobes.h b/arch/s390/include/asm/uprobes.h new file mode 100644 index 000000000000..1411dff7fea7 --- /dev/null +++ b/arch/s390/include/asm/uprobes.h | |||
@@ -0,0 +1,42 @@ | |||
1 | /* | ||
2 | * User-space Probes (UProbes) for s390 | ||
3 | * | ||
4 | * Copyright IBM Corp. 2014 | ||
5 | * Author(s): Jan Willeke, | ||
6 | */ | ||
7 | |||
8 | #ifndef _ASM_UPROBES_H | ||
9 | #define _ASM_UPROBES_H | ||
10 | |||
11 | #include <linux/notifier.h> | ||
12 | |||
13 | typedef u16 uprobe_opcode_t; | ||
14 | |||
15 | #define UPROBE_XOL_SLOT_BYTES 256 /* cache aligned */ | ||
16 | |||
17 | #define UPROBE_SWBP_INSN 0x0002 | ||
18 | #define UPROBE_SWBP_INSN_SIZE 2 | ||
19 | |||
20 | struct arch_uprobe { | ||
21 | union{ | ||
22 | uprobe_opcode_t insn[3]; | ||
23 | uprobe_opcode_t ixol[3]; | ||
24 | }; | ||
25 | unsigned int saved_per : 1; | ||
26 | unsigned int saved_int_code; | ||
27 | }; | ||
28 | |||
29 | struct arch_uprobe_task { | ||
30 | }; | ||
31 | |||
32 | int arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm, | ||
33 | unsigned long addr); | ||
34 | int arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs); | ||
35 | int arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs); | ||
36 | bool arch_uprobe_xol_was_trapped(struct task_struct *tsk); | ||
37 | int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, | ||
38 | void *data); | ||
39 | void arch_uprobe_abort_xol(struct arch_uprobe *ap, struct pt_regs *regs); | ||
40 | unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline, | ||
41 | struct pt_regs *regs); | ||
42 | #endif /* _ASM_UPROBES_H */ | ||
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index d44245d4df37..3249e1f36d55 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile | |||
@@ -55,6 +55,7 @@ obj-$(CONFIG_KPROBES) += kprobes.o | |||
55 | obj-$(CONFIG_FUNCTION_TRACER) += $(if $(CONFIG_64BIT),mcount64.o,mcount.o) | 55 | obj-$(CONFIG_FUNCTION_TRACER) += $(if $(CONFIG_64BIT),mcount64.o,mcount.o) |
56 | obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o | 56 | obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o |
57 | obj-$(CONFIG_CRASH_DUMP) += crash_dump.o | 57 | obj-$(CONFIG_CRASH_DUMP) += crash_dump.o |
58 | obj-$(CONFIG_UPROBES) += uprobes.o | ||
58 | 59 | ||
59 | ifdef CONFIG_64BIT | 60 | ifdef CONFIG_64BIT |
60 | obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_cpum_cf.o perf_cpum_sf.o \ | 61 | obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_cpum_cf.o perf_cpum_sf.o \ |
diff --git a/arch/s390/kernel/entry.h b/arch/s390/kernel/entry.h index 1aad48398d06..58541633b8d6 100644 --- a/arch/s390/kernel/entry.h +++ b/arch/s390/kernel/entry.h | |||
@@ -45,6 +45,7 @@ void transaction_exception(struct pt_regs *regs); | |||
45 | void translation_exception(struct pt_regs *regs); | 45 | void translation_exception(struct pt_regs *regs); |
46 | 46 | ||
47 | void do_per_trap(struct pt_regs *regs); | 47 | void do_per_trap(struct pt_regs *regs); |
48 | void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str); | ||
48 | void syscall_trace(struct pt_regs *regs, int entryexit); | 49 | void syscall_trace(struct pt_regs *regs, int entryexit); |
49 | void kernel_stack_overflow(struct pt_regs * regs); | 50 | void kernel_stack_overflow(struct pt_regs * regs); |
50 | void do_signal(struct pt_regs *regs); | 51 | void do_signal(struct pt_regs *regs); |
diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S index f2e674c702e1..7b2e03afd017 100644 --- a/arch/s390/kernel/entry64.S +++ b/arch/s390/kernel/entry64.S | |||
@@ -42,7 +42,8 @@ STACK_SHIFT = PAGE_SHIFT + THREAD_ORDER | |||
42 | STACK_SIZE = 1 << STACK_SHIFT | 42 | STACK_SIZE = 1 << STACK_SHIFT |
43 | STACK_INIT = STACK_SIZE - STACK_FRAME_OVERHEAD - __PT_SIZE | 43 | STACK_INIT = STACK_SIZE - STACK_FRAME_OVERHEAD - __PT_SIZE |
44 | 44 | ||
45 | _TIF_WORK = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED) | 45 | _TIF_WORK = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | \ |
46 | _TIF_UPROBE) | ||
46 | _TIF_TRACE = (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SECCOMP | \ | 47 | _TIF_TRACE = (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SECCOMP | \ |
47 | _TIF_SYSCALL_TRACEPOINT) | 48 | _TIF_SYSCALL_TRACEPOINT) |
48 | _CIF_WORK = (_CIF_MCCK_PENDING | _CIF_ASCE) | 49 | _CIF_WORK = (_CIF_MCCK_PENDING | _CIF_ASCE) |
@@ -265,6 +266,10 @@ sysc_work: | |||
265 | jo sysc_mcck_pending | 266 | jo sysc_mcck_pending |
266 | tm __TI_flags+7(%r12),_TIF_NEED_RESCHED | 267 | tm __TI_flags+7(%r12),_TIF_NEED_RESCHED |
267 | jo sysc_reschedule | 268 | jo sysc_reschedule |
269 | #ifdef CONFIG_UPROBES | ||
270 | tm __TI_flags+7(%r12),_TIF_UPROBE | ||
271 | jo sysc_uprobe_notify | ||
272 | #endif | ||
268 | tm __PT_FLAGS+7(%r11),_PIF_PER_TRAP | 273 | tm __PT_FLAGS+7(%r11),_PIF_PER_TRAP |
269 | jo sysc_singlestep | 274 | jo sysc_singlestep |
270 | tm __TI_flags+7(%r12),_TIF_SIGPENDING | 275 | tm __TI_flags+7(%r12),_TIF_SIGPENDING |
@@ -323,6 +328,16 @@ sysc_notify_resume: | |||
323 | jg do_notify_resume | 328 | jg do_notify_resume |
324 | 329 | ||
325 | # | 330 | # |
331 | # _TIF_UPROBE is set, call uprobe_notify_resume | ||
332 | # | ||
333 | #ifdef CONFIG_UPROBES | ||
334 | sysc_uprobe_notify: | ||
335 | lgr %r2,%r11 # pass pointer to pt_regs | ||
336 | larl %r14,sysc_return | ||
337 | jg uprobe_notify_resume | ||
338 | #endif | ||
339 | |||
340 | # | ||
326 | # _PIF_PER_TRAP is set, call do_per_trap | 341 | # _PIF_PER_TRAP is set, call do_per_trap |
327 | # | 342 | # |
328 | sysc_singlestep: | 343 | sysc_singlestep: |
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index 5dc7ad9e2fbf..fe99d6b3f185 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c | |||
@@ -84,7 +84,8 @@ void update_cr_regs(struct task_struct *task) | |||
84 | new.end = thread->per_user.end; | 84 | new.end = thread->per_user.end; |
85 | 85 | ||
86 | /* merge TIF_SINGLE_STEP into user specified PER registers. */ | 86 | /* merge TIF_SINGLE_STEP into user specified PER registers. */ |
87 | if (test_tsk_thread_flag(task, TIF_SINGLE_STEP)) { | 87 | if (test_tsk_thread_flag(task, TIF_SINGLE_STEP) || |
88 | test_tsk_thread_flag(task, TIF_UPROBE_SINGLESTEP)) { | ||
88 | if (test_tsk_thread_flag(task, TIF_BLOCK_STEP)) | 89 | if (test_tsk_thread_flag(task, TIF_BLOCK_STEP)) |
89 | new.control |= PER_EVENT_BRANCH; | 90 | new.control |= PER_EVENT_BRANCH; |
90 | else | 91 | else |
@@ -93,6 +94,8 @@ void update_cr_regs(struct task_struct *task) | |||
93 | new.control |= PER_CONTROL_SUSPENSION; | 94 | new.control |= PER_CONTROL_SUSPENSION; |
94 | new.control |= PER_EVENT_TRANSACTION_END; | 95 | new.control |= PER_EVENT_TRANSACTION_END; |
95 | #endif | 96 | #endif |
97 | if (test_tsk_thread_flag(task, TIF_UPROBE_SINGLESTEP)) | ||
98 | new.control |= PER_EVENT_IFETCH; | ||
96 | new.start = 0; | 99 | new.start = 0; |
97 | new.end = PSW_ADDR_INSN; | 100 | new.end = PSW_ADDR_INSN; |
98 | } | 101 | } |
diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index c5762324d9ee..e3e06a4fdfce 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c | |||
@@ -58,15 +58,10 @@ int is_valid_bugaddr(unsigned long addr) | |||
58 | return 1; | 58 | return 1; |
59 | } | 59 | } |
60 | 60 | ||
61 | static void __kprobes do_trap(struct pt_regs *regs, | 61 | void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str) |
62 | int si_signo, int si_code, char *str) | ||
63 | { | 62 | { |
64 | siginfo_t info; | 63 | siginfo_t info; |
65 | 64 | ||
66 | if (notify_die(DIE_TRAP, str, regs, 0, | ||
67 | regs->int_code, si_signo) == NOTIFY_STOP) | ||
68 | return; | ||
69 | |||
70 | if (user_mode(regs)) { | 65 | if (user_mode(regs)) { |
71 | info.si_signo = si_signo; | 66 | info.si_signo = si_signo; |
72 | info.si_errno = 0; | 67 | info.si_errno = 0; |
@@ -90,6 +85,15 @@ static void __kprobes do_trap(struct pt_regs *regs, | |||
90 | } | 85 | } |
91 | } | 86 | } |
92 | 87 | ||
88 | static void __kprobes do_trap(struct pt_regs *regs, int si_signo, int si_code, | ||
89 | char *str) | ||
90 | { | ||
91 | if (notify_die(DIE_TRAP, str, regs, 0, | ||
92 | regs->int_code, si_signo) == NOTIFY_STOP) | ||
93 | return; | ||
94 | do_report_trap(regs, si_signo, si_code, str); | ||
95 | } | ||
96 | |||
93 | void __kprobes do_per_trap(struct pt_regs *regs) | 97 | void __kprobes do_per_trap(struct pt_regs *regs) |
94 | { | 98 | { |
95 | siginfo_t info; | 99 | siginfo_t info; |
@@ -178,6 +182,7 @@ void __kprobes illegal_op(struct pt_regs *regs) | |||
178 | siginfo_t info; | 182 | siginfo_t info; |
179 | __u8 opcode[6]; | 183 | __u8 opcode[6]; |
180 | __u16 __user *location; | 184 | __u16 __user *location; |
185 | int is_uprobe_insn = 0; | ||
181 | int signal = 0; | 186 | int signal = 0; |
182 | 187 | ||
183 | location = get_trap_ip(regs); | 188 | location = get_trap_ip(regs); |
@@ -194,6 +199,10 @@ void __kprobes illegal_op(struct pt_regs *regs) | |||
194 | force_sig_info(SIGTRAP, &info, current); | 199 | force_sig_info(SIGTRAP, &info, current); |
195 | } else | 200 | } else |
196 | signal = SIGILL; | 201 | signal = SIGILL; |
202 | #ifdef CONFIG_UPROBES | ||
203 | } else if (*((__u16 *) opcode) == UPROBE_SWBP_INSN) { | ||
204 | is_uprobe_insn = 1; | ||
205 | #endif | ||
197 | #ifdef CONFIG_MATHEMU | 206 | #ifdef CONFIG_MATHEMU |
198 | } else if (opcode[0] == 0xb3) { | 207 | } else if (opcode[0] == 0xb3) { |
199 | if (get_user(*((__u16 *) (opcode+2)), location+1)) | 208 | if (get_user(*((__u16 *) (opcode+2)), location+1)) |
@@ -219,11 +228,13 @@ void __kprobes illegal_op(struct pt_regs *regs) | |||
219 | #endif | 228 | #endif |
220 | } else | 229 | } else |
221 | signal = SIGILL; | 230 | signal = SIGILL; |
222 | } else { | 231 | } |
223 | /* | 232 | /* |
224 | * If we get an illegal op in kernel mode, send it through the | 233 | * We got either an illegal op in kernel mode, or user space trapped |
225 | * kprobes notifier. If kprobes doesn't pick it up, SIGILL | 234 | * on a uprobes illegal instruction. See if kprobes or uprobes picks |
226 | */ | 235 | * it up. If not, SIGILL. |
236 | */ | ||
237 | if (is_uprobe_insn || !user_mode(regs)) { | ||
227 | if (notify_die(DIE_BPT, "bpt", regs, 0, | 238 | if (notify_die(DIE_BPT, "bpt", regs, 0, |
228 | 3, SIGTRAP) != NOTIFY_STOP) | 239 | 3, SIGTRAP) != NOTIFY_STOP) |
229 | signal = SIGILL; | 240 | signal = SIGILL; |
diff --git a/arch/s390/kernel/uprobes.c b/arch/s390/kernel/uprobes.c new file mode 100644 index 000000000000..956f4f7a591c --- /dev/null +++ b/arch/s390/kernel/uprobes.c | |||
@@ -0,0 +1,332 @@ | |||
1 | /* | ||
2 | * User-space Probes (UProbes) for s390 | ||
3 | * | ||
4 | * Copyright IBM Corp. 2014 | ||
5 | * Author(s): Jan Willeke, | ||
6 | */ | ||
7 | |||
8 | #include <linux/kprobes.h> | ||
9 | #include <linux/uaccess.h> | ||
10 | #include <linux/uprobes.h> | ||
11 | #include <linux/compat.h> | ||
12 | #include <linux/kdebug.h> | ||
13 | #include <asm/switch_to.h> | ||
14 | #include <asm/facility.h> | ||
15 | #include <asm/dis.h> | ||
16 | #include "entry.h" | ||
17 | |||
18 | #define UPROBE_TRAP_NR UINT_MAX | ||
19 | |||
20 | int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, | ||
21 | unsigned long addr) | ||
22 | { | ||
23 | return probe_is_prohibited_opcode(auprobe->insn); | ||
24 | } | ||
25 | |||
26 | int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | ||
27 | { | ||
28 | if (psw_bits(regs->psw).eaba == PSW_AMODE_24BIT) | ||
29 | return -EINVAL; | ||
30 | if (!is_compat_task() && psw_bits(regs->psw).eaba == PSW_AMODE_31BIT) | ||
31 | return -EINVAL; | ||
32 | clear_pt_regs_flag(regs, PIF_PER_TRAP); | ||
33 | auprobe->saved_per = psw_bits(regs->psw).r; | ||
34 | auprobe->saved_int_code = regs->int_code; | ||
35 | regs->int_code = UPROBE_TRAP_NR; | ||
36 | regs->psw.addr = current->utask->xol_vaddr; | ||
37 | set_tsk_thread_flag(current, TIF_UPROBE_SINGLESTEP); | ||
38 | update_cr_regs(current); | ||
39 | return 0; | ||
40 | } | ||
41 | |||
42 | bool arch_uprobe_xol_was_trapped(struct task_struct *tsk) | ||
43 | { | ||
44 | struct pt_regs *regs = task_pt_regs(tsk); | ||
45 | |||
46 | if (regs->int_code != UPROBE_TRAP_NR) | ||
47 | return true; | ||
48 | return false; | ||
49 | } | ||
50 | |||
51 | int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | ||
52 | { | ||
53 | int fixup = probe_get_fixup_type(auprobe->insn); | ||
54 | struct uprobe_task *utask = current->utask; | ||
55 | |||
56 | clear_tsk_thread_flag(current, TIF_UPROBE_SINGLESTEP); | ||
57 | update_cr_regs(current); | ||
58 | psw_bits(regs->psw).r = auprobe->saved_per; | ||
59 | regs->int_code = auprobe->saved_int_code; | ||
60 | |||
61 | if (fixup & FIXUP_PSW_NORMAL) | ||
62 | regs->psw.addr += utask->vaddr - utask->xol_vaddr; | ||
63 | if (fixup & FIXUP_RETURN_REGISTER) { | ||
64 | int reg = (auprobe->insn[0] & 0xf0) >> 4; | ||
65 | |||
66 | regs->gprs[reg] += utask->vaddr - utask->xol_vaddr; | ||
67 | } | ||
68 | if (fixup & FIXUP_BRANCH_NOT_TAKEN) { | ||
69 | int ilen = insn_length(auprobe->insn[0] >> 8); | ||
70 | |||
71 | if (regs->psw.addr - utask->xol_vaddr == ilen) | ||
72 | regs->psw.addr = utask->vaddr + ilen; | ||
73 | } | ||
74 | /* If per tracing was active generate trap */ | ||
75 | if (regs->psw.mask & PSW_MASK_PER) | ||
76 | do_per_trap(regs); | ||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, | ||
81 | void *data) | ||
82 | { | ||
83 | struct die_args *args = data; | ||
84 | struct pt_regs *regs = args->regs; | ||
85 | |||
86 | if (!user_mode(regs)) | ||
87 | return NOTIFY_DONE; | ||
88 | if (regs->int_code & 0x200) /* Trap during transaction */ | ||
89 | return NOTIFY_DONE; | ||
90 | switch (val) { | ||
91 | case DIE_BPT: | ||
92 | if (uprobe_pre_sstep_notifier(regs)) | ||
93 | return NOTIFY_STOP; | ||
94 | break; | ||
95 | case DIE_SSTEP: | ||
96 | if (uprobe_post_sstep_notifier(regs)) | ||
97 | return NOTIFY_STOP; | ||
98 | default: | ||
99 | break; | ||
100 | } | ||
101 | return NOTIFY_DONE; | ||
102 | } | ||
103 | |||
104 | void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | ||
105 | { | ||
106 | clear_thread_flag(TIF_UPROBE_SINGLESTEP); | ||
107 | regs->int_code = auprobe->saved_int_code; | ||
108 | regs->psw.addr = current->utask->vaddr; | ||
109 | } | ||
110 | |||
111 | unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline, | ||
112 | struct pt_regs *regs) | ||
113 | { | ||
114 | unsigned long orig; | ||
115 | |||
116 | orig = regs->gprs[14]; | ||
117 | regs->gprs[14] = trampoline; | ||
118 | return orig; | ||
119 | } | ||
120 | |||
121 | /* Instruction Emulation */ | ||
122 | |||
123 | static void adjust_psw_addr(psw_t *psw, unsigned long len) | ||
124 | { | ||
125 | psw->addr = __rewind_psw(*psw, -len); | ||
126 | } | ||
127 | |||
128 | #define EMU_ILLEGAL_OP 1 | ||
129 | #define EMU_SPECIFICATION 2 | ||
130 | #define EMU_ADDRESSING 3 | ||
131 | |||
132 | #define emu_load_ril(ptr, output) \ | ||
133 | ({ \ | ||
134 | unsigned int mask = sizeof(*(ptr)) - 1; \ | ||
135 | __typeof__(*(ptr)) input; \ | ||
136 | int __rc = 0; \ | ||
137 | \ | ||
138 | if (!test_facility(34)) \ | ||
139 | __rc = EMU_ILLEGAL_OP; \ | ||
140 | else if ((u64 __force)ptr & mask) \ | ||
141 | __rc = EMU_SPECIFICATION; \ | ||
142 | else if (get_user(input, ptr)) \ | ||
143 | __rc = EMU_ADDRESSING; \ | ||
144 | else \ | ||
145 | *(output) = input; \ | ||
146 | __rc; \ | ||
147 | }) | ||
148 | |||
149 | #define emu_store_ril(ptr, input) \ | ||
150 | ({ \ | ||
151 | unsigned int mask = sizeof(*(ptr)) - 1; \ | ||
152 | int __rc = 0; \ | ||
153 | \ | ||
154 | if (!test_facility(34)) \ | ||
155 | __rc = EMU_ILLEGAL_OP; \ | ||
156 | else if ((u64 __force)ptr & mask) \ | ||
157 | __rc = EMU_SPECIFICATION; \ | ||
158 | else if (put_user(*(input), ptr)) \ | ||
159 | __rc = EMU_ADDRESSING; \ | ||
160 | __rc; \ | ||
161 | }) | ||
162 | |||
163 | #define emu_cmp_ril(regs, ptr, cmp) \ | ||
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 if (input > *(cmp)) \ | ||
176 | psw_bits((regs)->psw).cc = 1; \ | ||
177 | else if (input < *(cmp)) \ | ||
178 | psw_bits((regs)->psw).cc = 2; \ | ||
179 | else \ | ||
180 | psw_bits((regs)->psw).cc = 0; \ | ||
181 | __rc; \ | ||
182 | }) | ||
183 | |||
184 | struct insn_ril { | ||
185 | u8 opc0; | ||
186 | u8 reg : 4; | ||
187 | u8 opc1 : 4; | ||
188 | s32 disp; | ||
189 | } __packed; | ||
190 | |||
191 | union split_register { | ||
192 | u64 u64; | ||
193 | u32 u32[2]; | ||
194 | u16 u16[4]; | ||
195 | s64 s64; | ||
196 | s32 s32[2]; | ||
197 | s16 s16[4]; | ||
198 | }; | ||
199 | |||
200 | /* | ||
201 | * pc relative instructions are emulated, since parameters may not be | ||
202 | * accessible from the xol area due to range limitations. | ||
203 | */ | ||
204 | static void handle_insn_ril(struct arch_uprobe *auprobe, struct pt_regs *regs) | ||
205 | { | ||
206 | union split_register *rx; | ||
207 | struct insn_ril *insn; | ||
208 | unsigned int ilen; | ||
209 | void *uptr; | ||
210 | int rc = 0; | ||
211 | |||
212 | insn = (struct insn_ril *) &auprobe->insn; | ||
213 | rx = (union split_register *) ®s->gprs[insn->reg]; | ||
214 | uptr = (void *)(regs->psw.addr + (insn->disp * 2)); | ||
215 | ilen = insn_length(insn->opc0); | ||
216 | |||
217 | switch (insn->opc0) { | ||
218 | case 0xc0: | ||
219 | switch (insn->opc1) { | ||
220 | case 0x00: /* larl */ | ||
221 | rx->u64 = (unsigned long)uptr; | ||
222 | break; | ||
223 | } | ||
224 | break; | ||
225 | case 0xc4: | ||
226 | switch (insn->opc1) { | ||
227 | case 0x02: /* llhrl */ | ||
228 | rc = emu_load_ril((u16 __user *)uptr, &rx->u32[1]); | ||
229 | break; | ||
230 | case 0x04: /* lghrl */ | ||
231 | rc = emu_load_ril((s16 __user *)uptr, &rx->u64); | ||
232 | break; | ||
233 | case 0x05: /* lhrl */ | ||
234 | rc = emu_load_ril((s16 __user *)uptr, &rx->u32[1]); | ||
235 | break; | ||
236 | case 0x06: /* llghrl */ | ||
237 | rc = emu_load_ril((u16 __user *)uptr, &rx->u64); | ||
238 | break; | ||
239 | case 0x08: /* lgrl */ | ||
240 | rc = emu_load_ril((u64 __user *)uptr, &rx->u64); | ||
241 | break; | ||
242 | case 0x0c: /* lgfrl */ | ||
243 | rc = emu_load_ril((s32 __user *)uptr, &rx->u64); | ||
244 | break; | ||
245 | case 0x0d: /* lrl */ | ||
246 | rc = emu_load_ril((u32 __user *)uptr, &rx->u32[1]); | ||
247 | break; | ||
248 | case 0x0e: /* llgfrl */ | ||
249 | rc = emu_load_ril((u32 __user *)uptr, &rx->u64); | ||
250 | break; | ||
251 | case 0x07: /* sthrl */ | ||
252 | rc = emu_store_ril((u16 __user *)uptr, &rx->u16[3]); | ||
253 | break; | ||
254 | case 0x0b: /* stgrl */ | ||
255 | rc = emu_store_ril((u64 __user *)uptr, &rx->u64); | ||
256 | break; | ||
257 | case 0x0f: /* strl */ | ||
258 | rc = emu_store_ril((u32 __user *)uptr, &rx->u32[1]); | ||
259 | break; | ||
260 | } | ||
261 | break; | ||
262 | case 0xc6: | ||
263 | switch (insn->opc1) { | ||
264 | case 0x02: /* pfdrl */ | ||
265 | if (!test_facility(34)) | ||
266 | rc = EMU_ILLEGAL_OP; | ||
267 | break; | ||
268 | case 0x04: /* cghrl */ | ||
269 | rc = emu_cmp_ril(regs, (s16 __user *)uptr, &rx->s64); | ||
270 | break; | ||
271 | case 0x05: /* chrl */ | ||
272 | rc = emu_cmp_ril(regs, (s16 __user *)uptr, &rx->s32[1]); | ||
273 | break; | ||
274 | case 0x06: /* clghrl */ | ||
275 | rc = emu_cmp_ril(regs, (u16 __user *)uptr, &rx->u64); | ||
276 | break; | ||
277 | case 0x07: /* clhrl */ | ||
278 | rc = emu_cmp_ril(regs, (u16 __user *)uptr, &rx->u32[1]); | ||
279 | break; | ||
280 | case 0x08: /* cgrl */ | ||
281 | rc = emu_cmp_ril(regs, (s64 __user *)uptr, &rx->s64); | ||
282 | break; | ||
283 | case 0x0a: /* clgrl */ | ||
284 | rc = emu_cmp_ril(regs, (u64 __user *)uptr, &rx->u64); | ||
285 | break; | ||
286 | case 0x0c: /* cgfrl */ | ||
287 | rc = emu_cmp_ril(regs, (s32 __user *)uptr, &rx->s64); | ||
288 | break; | ||
289 | case 0x0d: /* crl */ | ||
290 | rc = emu_cmp_ril(regs, (s32 __user *)uptr, &rx->s32[1]); | ||
291 | break; | ||
292 | case 0x0e: /* clgfrl */ | ||
293 | rc = emu_cmp_ril(regs, (u32 __user *)uptr, &rx->u64); | ||
294 | break; | ||
295 | case 0x0f: /* clrl */ | ||
296 | rc = emu_cmp_ril(regs, (u32 __user *)uptr, &rx->u32[1]); | ||
297 | break; | ||
298 | } | ||
299 | break; | ||
300 | } | ||
301 | adjust_psw_addr(®s->psw, ilen); | ||
302 | switch (rc) { | ||
303 | case EMU_ILLEGAL_OP: | ||
304 | regs->int_code = ilen << 16 | 0x0001; | ||
305 | do_report_trap(regs, SIGILL, ILL_ILLOPC, NULL); | ||
306 | break; | ||
307 | case EMU_SPECIFICATION: | ||
308 | regs->int_code = ilen << 16 | 0x0006; | ||
309 | do_report_trap(regs, SIGILL, ILL_ILLOPC , NULL); | ||
310 | break; | ||
311 | case EMU_ADDRESSING: | ||
312 | regs->int_code = ilen << 16 | 0x0005; | ||
313 | do_report_trap(regs, SIGSEGV, SEGV_MAPERR, NULL); | ||
314 | break; | ||
315 | } | ||
316 | } | ||
317 | |||
318 | bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) | ||
319 | { | ||
320 | if ((psw_bits(regs->psw).eaba == PSW_AMODE_24BIT) || | ||
321 | ((psw_bits(regs->psw).eaba == PSW_AMODE_31BIT) && | ||
322 | !is_compat_task())) { | ||
323 | regs->psw.addr = __rewind_psw(regs->psw, UPROBE_SWBP_INSN_SIZE); | ||
324 | do_report_trap(regs, SIGILL, ILL_ILLADR, NULL); | ||
325 | return true; | ||
326 | } | ||
327 | if (probe_is_insn_relative_long(auprobe->insn)) { | ||
328 | handle_insn_ril(auprobe, regs); | ||
329 | return true; | ||
330 | } | ||
331 | return false; | ||
332 | } | ||