aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Willeke <willeke@de.ibm.com>2014-09-22 10:39:06 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2014-09-25 04:52:17 -0400
commit2a0a5b2299b9bef76123fac91e68d39cb361c33e (patch)
treefa8872070bf295dbbc57e7063db6e6baf739f543
parent975fab17399a2b29985166181ad80e5f50fa42e9 (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/Kconfig3
-rw-r--r--arch/s390/include/asm/ptrace.h6
-rw-r--r--arch/s390/include/asm/thread_info.h3
-rw-r--r--arch/s390/include/asm/uprobes.h42
-rw-r--r--arch/s390/kernel/Makefile1
-rw-r--r--arch/s390/kernel/entry.h1
-rw-r--r--arch/s390/kernel/entry64.S17
-rw-r--r--arch/s390/kernel/ptrace.c5
-rw-r--r--arch/s390/kernel/traps.c33
-rw-r--r--arch/s390/kernel/uprobes.c332
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
58config PCI_QUIRKS 58config PCI_QUIRKS
59 def_bool n 59 def_bool n
60 60
61config ARCH_SUPPORTS_UPROBES
62 def_bool 64BIT
63
61config S390 64config 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
164static inline void instruction_pointer_set(struct pt_regs *regs,
165 unsigned long val)
166{
167 regs->psw.addr = val | PSW_ADDR_AMODE;
168}
169
164int regs_query_register_offset(const char *name); 170int regs_query_register_offset(const char *name);
165const char *regs_query_register_name(unsigned int offset); 171const char *regs_query_register_name(unsigned int offset);
166unsigned long regs_get_register(struct pt_regs *regs, unsigned int offset); 172unsigned 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
13typedef 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
20struct 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
29struct arch_uprobe_task {
30};
31
32int arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm,
33 unsigned long addr);
34int arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs);
35int arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs);
36bool arch_uprobe_xol_was_trapped(struct task_struct *tsk);
37int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val,
38 void *data);
39void arch_uprobe_abort_xol(struct arch_uprobe *ap, struct pt_regs *regs);
40unsigned 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
55obj-$(CONFIG_FUNCTION_TRACER) += $(if $(CONFIG_64BIT),mcount64.o,mcount.o) 55obj-$(CONFIG_FUNCTION_TRACER) += $(if $(CONFIG_64BIT),mcount64.o,mcount.o)
56obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o 56obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o
57obj-$(CONFIG_CRASH_DUMP) += crash_dump.o 57obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
58obj-$(CONFIG_UPROBES) += uprobes.o
58 59
59ifdef CONFIG_64BIT 60ifdef CONFIG_64BIT
60obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_cpum_cf.o perf_cpum_sf.o \ 61obj-$(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);
45void translation_exception(struct pt_regs *regs); 45void translation_exception(struct pt_regs *regs);
46 46
47void do_per_trap(struct pt_regs *regs); 47void do_per_trap(struct pt_regs *regs);
48void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str);
48void syscall_trace(struct pt_regs *regs, int entryexit); 49void syscall_trace(struct pt_regs *regs, int entryexit);
49void kernel_stack_overflow(struct pt_regs * regs); 50void kernel_stack_overflow(struct pt_regs * regs);
50void do_signal(struct pt_regs *regs); 51void 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
42STACK_SIZE = 1 << STACK_SHIFT 42STACK_SIZE = 1 << STACK_SHIFT
43STACK_INIT = STACK_SIZE - STACK_FRAME_OVERHEAD - __PT_SIZE 43STACK_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
334sysc_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#
328sysc_singlestep: 343sysc_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
61static void __kprobes do_trap(struct pt_regs *regs, 61void 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
88static 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
93void __kprobes do_per_trap(struct pt_regs *regs) 97void __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
20int 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
26int 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
42bool 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
51int 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
80int 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
104void 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
111unsigned 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
123static 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
184struct insn_ril {
185 u8 opc0;
186 u8 reg : 4;
187 u8 opc1 : 4;
188 s32 disp;
189} __packed;
190
191union 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 */
204static 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 *) &regs->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(&regs->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
318bool 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}