s390, s390x: use PTRACE_GETREGSET to fetch registers

* linux/s390/arch_regs.h: New file.
* linux/s390x/arch_regs.h: New file.
* Makefile.am (EXTRA_DIST): Add them.
* signal.c (sys_sigreturn) [S390 || S390X]: Use s390_frame_ptr.
* syscall.c [S390 || S390X] (s390_regset, s390_frame_ptr): New variable.
[S390 || S390X] (ARCH_REGS_FOR_GETREGSET): New macro.
(print_pc) [S390 || S390X]: Use s390_regset.
(get_scno) [S390 || S390X]: Likewise.
(get_syscall_args) [S390 || S390X]: Likewise.
(get_error) [S390 || S390X]: Likewise.
(get_syscall_result) [S390 || S390X]: Remove.
diff --git a/Makefile.am b/Makefile.am
index 587dd7a..589e61b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -264,12 +264,14 @@
 	linux/powerpc64/syscallent1.h	\
 	linux/powerpc64/userent.h	\
 	linux/ptp_clock.h		\
+	linux/s390/arch_regs.h		\
 	linux/s390/ioctls_arch0.h	\
 	linux/s390/ioctls_inc0.h	\
 	linux/s390/syscallent.h		\
 	linux/s390/userent.h		\
 	linux/s390/userent0.h		\
 	linux/s390/userent1.h		\
+	linux/s390x/arch_regs.h		\
 	linux/s390x/ioctls_arch0.h	\
 	linux/s390x/ioctls_inc0.h	\
 	linux/s390x/syscallent.h	\
diff --git a/NEWS b/NEWS
index 4ef4d48..8ff0249 100644
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,7 @@
   * Implemented full 32-bit decoding of ioctl commands
     (addresses Fedora bug #902788).
   * Implemented PTRACE_GETREGS API support on MIPS.
+  * Implemented PTRACE_GETREGSET API support on S390/S390x.
   * Implemented decoding of getrandom and seccomp syscalls.
   * Implemented full decoding of 64-bit capability sets.
   * Implemented decoding of all prctl commands.
diff --git a/linux/s390/arch_regs.h b/linux/s390/arch_regs.h
new file mode 100644
index 0000000..586326f
--- /dev/null
+++ b/linux/s390/arch_regs.h
@@ -0,0 +1 @@
+extern unsigned long *const s390_frame_ptr;
diff --git a/linux/s390x/arch_regs.h b/linux/s390x/arch_regs.h
new file mode 100644
index 0000000..14fced4
--- /dev/null
+++ b/linux/s390x/arch_regs.h
@@ -0,0 +1 @@
+#include "s390/arch_regs.h"
diff --git a/signal.c b/signal.c
index 6aab54f..69a47bb 100644
--- a/signal.c
+++ b/signal.c
@@ -717,19 +717,16 @@
 	}
 #elif defined(S390) || defined(S390X)
 	if (entering(tcp)) {
-		long usp;
 		long mask[NSIG / 8 / sizeof(long)];
-		if (upeek(tcp->pid, PT_GPR15, &usp) < 0)
-			return 0;
 		tprints("{mask=");
-		const long addr = usp + __SIGNAL_FRAMESIZE;
+		const long addr = *s390_frame_ptr + __SIGNAL_FRAMESIZE;
 		if (umove(tcp, addr, &mask) < 0) {
 			tprintf("%#lx", addr);
 		} else {
 # ifdef S390
-			usp = mask[0];
+			long v = mask[0];
 			mask[0] = mask[1];
-			mask[1] = usp;
+			mask[1] = v;
 # endif
 			tprintsigmask_addr("", mask);
 		}
diff --git a/syscall.c b/syscall.c
index 2e536fc..854a785 100644
--- a/syscall.c
+++ b/syscall.c
@@ -764,7 +764,10 @@
 /* PTRACE_GETREGS on MIPS is available since linux v2.6.15. */
 # define ARCH_REGS_FOR_GETREGS mips_regs
 #elif defined(S390) || defined(S390X)
-static long s390_gpr2;
+/* PTRACE_GETREGSET on S390 is available since linux v2.6.27. */
+static struct user_regs_struct s390_regset;
+unsigned long *const s390_frame_ptr = &s390_regset.gprs[15];
+# define ARCH_REGS_FOR_GETREGSET s390_regset
 #elif defined(HPPA)
 static long hppa_r28;
 #elif defined(SH)
@@ -829,12 +832,7 @@
 	else
 		tprintf(fmt, (unsigned long) x86_64_regs.rip);
 #elif defined(S390) || defined(S390X)
-	long psw;
-	if (upeek(tcp->pid, PT_PSWADDR, &psw) < 0) {
-		PRINTBADPC;
-		return;
-	}
-	tprintf(fmt, psw);
+	tprintf(fmt, s390_regset.psw.addr);
 #elif defined(IA64)
 	long ip;
 	if (upeek(tcp->pid, PT_B0, &ip) < 0) {
@@ -1134,87 +1132,7 @@
 	long scno = 0;
 
 #if defined(S390) || defined(S390X)
-	if (upeek(tcp->pid, PT_GPR2, &s390_gpr2) < 0)
-		return -1;
-
-	if (s390_gpr2 != -ENOSYS) {
-		/*
-		 * Since kernel version 2.5.44 the scno gets passed in gpr2.
-		 */
-		scno = s390_gpr2;
-	} else {
-		/*
-		 * Old style of "passing" the scno via the SVC instruction.
-		 */
-		long psw;
-		long opcode, offset_reg, tmp;
-		void *svc_addr;
-		static const int gpr_offset[16] = {
-				PT_GPR0,  PT_GPR1,  PT_ORIGGPR2, PT_GPR3,
-				PT_GPR4,  PT_GPR5,  PT_GPR6,     PT_GPR7,
-				PT_GPR8,  PT_GPR9,  PT_GPR10,    PT_GPR11,
-				PT_GPR12, PT_GPR13, PT_GPR14,    PT_GPR15
-		};
-
-		if (upeek(tcp->pid, PT_PSWADDR, &psw) < 0)
-			return -1;
-		errno = 0;
-		opcode = ptrace(PTRACE_PEEKTEXT, tcp->pid, (char *)(psw - sizeof(long)), 0);
-		if (errno) {
-			perror_msg("peektext(psw-oneword)");
-			return -1;
-		}
-
-		/*
-		 *  We have to check if the SVC got executed directly or via an
-		 *  EXECUTE instruction. In case of EXECUTE it is necessary to do
-		 *  instruction decoding to derive the system call number.
-		 *  Unfortunately the opcode sizes of EXECUTE and SVC are differently,
-		 *  so that this doesn't work if a SVC opcode is part of an EXECUTE
-		 *  opcode. Since there is no way to find out the opcode size this
-		 *  is the best we can do...
-		 */
-		if ((opcode & 0xff00) == 0x0a00) {
-			/* SVC opcode */
-			scno = opcode & 0xff;
-		}
-		else {
-			/* SVC got executed by EXECUTE instruction */
-
-			/*
-			 *  Do instruction decoding of EXECUTE. If you really want to
-			 *  understand this, read the Principles of Operations.
-			 */
-			svc_addr = (void *) (opcode & 0xfff);
-
-			tmp = 0;
-			offset_reg = (opcode & 0x000f0000) >> 16;
-			if (offset_reg && (upeek(tcp->pid, gpr_offset[offset_reg], &tmp) < 0))
-				return -1;
-			svc_addr += tmp;
-
-			tmp = 0;
-			offset_reg = (opcode & 0x0000f000) >> 12;
-			if (offset_reg && (upeek(tcp->pid, gpr_offset[offset_reg], &tmp) < 0))
-				return -1;
-			svc_addr += tmp;
-
-			scno = ptrace(PTRACE_PEEKTEXT, tcp->pid, svc_addr, 0);
-			if (errno)
-				return -1;
-# if defined(S390X)
-			scno >>= 48;
-# else
-			scno >>= 16;
-# endif
-			tmp = 0;
-			offset_reg = (opcode & 0x00f00000) >> 20;
-			if (offset_reg && (upeek(tcp->pid, gpr_offset[offset_reg], &tmp) < 0))
-				return -1;
-
-			scno = (scno | tmp) & 0xff;
-		}
-	}
+	scno = s390_regset.gprs[2];
 #elif defined(POWERPC)
 	scno = ppc_regs.gpr[0];
 # ifdef POWERPC64
@@ -1624,9 +1542,14 @@
 	nargs = tcp->s_ent->nargs;
 
 #if defined(S390) || defined(S390X)
-	for (i = 0; i < nargs; ++i)
-		if (upeek(tcp->pid, i==0 ? PT_ORIGGPR2 : PT_GPR2 + i*sizeof(long), &tcp->u_arg[i]) < 0)
-			return -1;
+	(void)i;
+	(void)nargs;
+	tcp->u_arg[0] = s390_regset.orig_gpr2;
+	tcp->u_arg[1] = s390_regset.gprs[3];
+	tcp->u_arg[2] = s390_regset.gprs[4];
+	tcp->u_arg[3] = s390_regset.gprs[5];
+	tcp->u_arg[4] = s390_regset.gprs[6];
+	tcp->u_arg[5] = s390_regset.gprs[7];
 #elif defined(ALPHA)
 	for (i = 0; i < nargs; ++i)
 		if (upeek(tcp->pid, REG_A0+i, &tcp->u_arg[i]) < 0)
@@ -1946,9 +1869,6 @@
 {
 #if defined ARCH_REGS_FOR_GETREGSET || defined ARCH_REGS_FOR_GETREGS
 	/* already done by get_regs */
-#elif defined(S390) || defined(S390X)
-	if (upeek(tcp->pid, PT_GPR2, &s390_gpr2) < 0)
-		return -1;
 #elif defined(BFIN)
 	if (upeek(tcp->pid, PT_R0, &bfin_r0) < 0)
 		return -1;
@@ -2009,12 +1929,12 @@
 		check_errno = 0;
 	}
 #if defined(S390) || defined(S390X)
-	if (check_errno && is_negated_errno(s390_gpr2)) {
+	if (check_errno && is_negated_errno(s390_regset.gprs[2])) {
 		tcp->u_rval = -1;
-		u_error = -s390_gpr2;
+		u_error = -s390_regset.gprs[2];
 	}
 	else {
-		tcp->u_rval = s390_gpr2;
+		tcp->u_rval = s390_regset.gprs[2];
 	}
 #elif defined(I386)
 	if (check_errno && is_negated_errno(i386_regs.eax)) {