diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2014-10-06 11:53:53 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2014-10-09 03:14:13 -0400 |
commit | 8070361799ae1e3f4ef347bd10f0a508ac10acfb (patch) | |
tree | 9846c0633cd28a86dc62eca55a4268fed513fcfd /arch | |
parent | 42f4dd613fe808676126472bbe1283e452201148 (diff) |
s390: add support for vector extension
The vector extension introduces 32 128-bit vector registers and a set of
instruction to operate on the vector registers.
The kernel can control the use of vector registers for the problem state
program with a bit in control register 0. Once enabled for a process the
kernel needs to retain the content of the vector registers on context
switch. The signal frame is extended to include the vector registers.
Two new register sets NT_S390_VXRS_LOW and NT_S390_VXRS_HIGH are added
to the regset interface for the debugger and core dumps.
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/s390/include/asm/elf.h | 1 | ||||
-rw-r--r-- | arch/s390/include/asm/lowcore.h | 10 | ||||
-rw-r--r-- | arch/s390/include/asm/nmi.h | 2 | ||||
-rw-r--r-- | arch/s390/include/asm/processor.h | 1 | ||||
-rw-r--r-- | arch/s390/include/asm/setup.h | 3 | ||||
-rw-r--r-- | arch/s390/include/asm/switch_to.h | 48 | ||||
-rw-r--r-- | arch/s390/include/uapi/asm/sigcontext.h | 20 | ||||
-rw-r--r-- | arch/s390/include/uapi/asm/types.h | 4 | ||||
-rw-r--r-- | arch/s390/include/uapi/asm/ucontext.h | 15 | ||||
-rw-r--r-- | arch/s390/kernel/compat_linux.h | 9 | ||||
-rw-r--r-- | arch/s390/kernel/compat_signal.c | 212 | ||||
-rw-r--r-- | arch/s390/kernel/early.c | 2 | ||||
-rw-r--r-- | arch/s390/kernel/entry.h | 3 | ||||
-rw-r--r-- | arch/s390/kernel/nmi.c | 16 | ||||
-rw-r--r-- | arch/s390/kernel/pgm_check.S | 2 | ||||
-rw-r--r-- | arch/s390/kernel/processor.c | 2 | ||||
-rw-r--r-- | arch/s390/kernel/ptrace.c | 249 | ||||
-rw-r--r-- | arch/s390/kernel/setup.c | 9 | ||||
-rw-r--r-- | arch/s390/kernel/signal.c | 296 | ||||
-rw-r--r-- | arch/s390/kernel/smp.c | 3 | ||||
-rw-r--r-- | arch/s390/kernel/traps.c | 82 |
21 files changed, 802 insertions, 187 deletions
diff --git a/arch/s390/include/asm/elf.h b/arch/s390/include/asm/elf.h index 78f4f8711d58..27735ae06a78 100644 --- a/arch/s390/include/asm/elf.h +++ b/arch/s390/include/asm/elf.h | |||
@@ -102,6 +102,7 @@ | |||
102 | #define HWCAP_S390_ETF3EH 256 | 102 | #define HWCAP_S390_ETF3EH 256 |
103 | #define HWCAP_S390_HIGH_GPRS 512 | 103 | #define HWCAP_S390_HIGH_GPRS 512 |
104 | #define HWCAP_S390_TE 1024 | 104 | #define HWCAP_S390_TE 1024 |
105 | #define HWCAP_S390_VXRS 2048 | ||
105 | 106 | ||
106 | /* | 107 | /* |
107 | * These are used to set parameters in the core dumps. | 108 | * These are used to set parameters in the core dumps. |
diff --git a/arch/s390/include/asm/lowcore.h b/arch/s390/include/asm/lowcore.h index 4349197ab9df..d812cf1a8177 100644 --- a/arch/s390/include/asm/lowcore.h +++ b/arch/s390/include/asm/lowcore.h | |||
@@ -310,7 +310,10 @@ struct _lowcore { | |||
310 | 310 | ||
311 | /* Extended facility list */ | 311 | /* Extended facility list */ |
312 | __u64 stfle_fac_list[32]; /* 0x0f00 */ | 312 | __u64 stfle_fac_list[32]; /* 0x0f00 */ |
313 | __u8 pad_0x1000[0x11b8-0x1000]; /* 0x1000 */ | 313 | __u8 pad_0x1000[0x11b0-0x1000]; /* 0x1000 */ |
314 | |||
315 | /* Pointer to vector register save area */ | ||
316 | __u64 vector_save_area_addr; /* 0x11b0 */ | ||
314 | 317 | ||
315 | /* 64 bit extparam used for pfault/diag 250: defined by architecture */ | 318 | /* 64 bit extparam used for pfault/diag 250: defined by architecture */ |
316 | __u64 ext_params2; /* 0x11B8 */ | 319 | __u64 ext_params2; /* 0x11B8 */ |
@@ -334,9 +337,10 @@ struct _lowcore { | |||
334 | 337 | ||
335 | /* Transaction abort diagnostic block */ | 338 | /* Transaction abort diagnostic block */ |
336 | __u8 pgm_tdb[256]; /* 0x1800 */ | 339 | __u8 pgm_tdb[256]; /* 0x1800 */ |
340 | __u8 pad_0x1900[0x1c00-0x1900]; /* 0x1900 */ | ||
337 | 341 | ||
338 | /* align to the top of the prefix area */ | 342 | /* Software defined save area for vector registers */ |
339 | __u8 pad_0x1900[0x2000-0x1900]; /* 0x1900 */ | 343 | __u8 vector_save_area[1024]; /* 0x1c00 */ |
340 | } __packed; | 344 | } __packed; |
341 | 345 | ||
342 | #endif /* CONFIG_32BIT */ | 346 | #endif /* CONFIG_32BIT */ |
diff --git a/arch/s390/include/asm/nmi.h b/arch/s390/include/asm/nmi.h index 35f8ec185616..3027a5a72b74 100644 --- a/arch/s390/include/asm/nmi.h +++ b/arch/s390/include/asm/nmi.h | |||
@@ -38,7 +38,7 @@ struct mci { | |||
38 | __u32 pm : 1; /* 22 psw program mask and cc validity */ | 38 | __u32 pm : 1; /* 22 psw program mask and cc validity */ |
39 | __u32 ia : 1; /* 23 psw instruction address validity */ | 39 | __u32 ia : 1; /* 23 psw instruction address validity */ |
40 | __u32 fa : 1; /* 24 failing storage address validity */ | 40 | __u32 fa : 1; /* 24 failing storage address validity */ |
41 | __u32 : 1; /* 25 */ | 41 | __u32 vr : 1; /* 25 vector register validity */ |
42 | __u32 ec : 1; /* 26 external damage code validity */ | 42 | __u32 ec : 1; /* 26 external damage code validity */ |
43 | __u32 fp : 1; /* 27 floating point register validity */ | 43 | __u32 fp : 1; /* 27 floating point register validity */ |
44 | __u32 gr : 1; /* 28 general register validity */ | 44 | __u32 gr : 1; /* 28 general register validity */ |
diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h index 3d0871058306..d559bdb03d18 100644 --- a/arch/s390/include/asm/processor.h +++ b/arch/s390/include/asm/processor.h | |||
@@ -117,6 +117,7 @@ struct thread_struct { | |||
117 | int ri_signum; | 117 | int ri_signum; |
118 | #ifdef CONFIG_64BIT | 118 | #ifdef CONFIG_64BIT |
119 | unsigned char trap_tdb[256]; /* Transaction abort diagnose block */ | 119 | unsigned char trap_tdb[256]; /* Transaction abort diagnose block */ |
120 | __vector128 *vxrs; /* Vector register save area */ | ||
120 | #endif | 121 | #endif |
121 | }; | 122 | }; |
122 | 123 | ||
diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h index dbde7c236577..7736fdd72595 100644 --- a/arch/s390/include/asm/setup.h +++ b/arch/s390/include/asm/setup.h | |||
@@ -56,6 +56,7 @@ extern void detect_memory_memblock(void); | |||
56 | #define MACHINE_FLAG_TOPOLOGY (1UL << 14) | 56 | #define MACHINE_FLAG_TOPOLOGY (1UL << 14) |
57 | #define MACHINE_FLAG_TE (1UL << 15) | 57 | #define MACHINE_FLAG_TE (1UL << 15) |
58 | #define MACHINE_FLAG_TLB_LC (1UL << 17) | 58 | #define MACHINE_FLAG_TLB_LC (1UL << 17) |
59 | #define MACHINE_FLAG_VX (1UL << 18) | ||
59 | 60 | ||
60 | #define MACHINE_IS_VM (S390_lowcore.machine_flags & MACHINE_FLAG_VM) | 61 | #define MACHINE_IS_VM (S390_lowcore.machine_flags & MACHINE_FLAG_VM) |
61 | #define MACHINE_IS_KVM (S390_lowcore.machine_flags & MACHINE_FLAG_KVM) | 62 | #define MACHINE_IS_KVM (S390_lowcore.machine_flags & MACHINE_FLAG_KVM) |
@@ -78,6 +79,7 @@ extern void detect_memory_memblock(void); | |||
78 | #define MACHINE_HAS_TOPOLOGY (0) | 79 | #define MACHINE_HAS_TOPOLOGY (0) |
79 | #define MACHINE_HAS_TE (0) | 80 | #define MACHINE_HAS_TE (0) |
80 | #define MACHINE_HAS_TLB_LC (0) | 81 | #define MACHINE_HAS_TLB_LC (0) |
82 | #define MACHINE_HAS_VX (0) | ||
81 | #else /* CONFIG_64BIT */ | 83 | #else /* CONFIG_64BIT */ |
82 | #define MACHINE_HAS_IEEE (1) | 84 | #define MACHINE_HAS_IEEE (1) |
83 | #define MACHINE_HAS_CSP (1) | 85 | #define MACHINE_HAS_CSP (1) |
@@ -90,6 +92,7 @@ extern void detect_memory_memblock(void); | |||
90 | #define MACHINE_HAS_TOPOLOGY (S390_lowcore.machine_flags & MACHINE_FLAG_TOPOLOGY) | 92 | #define MACHINE_HAS_TOPOLOGY (S390_lowcore.machine_flags & MACHINE_FLAG_TOPOLOGY) |
91 | #define MACHINE_HAS_TE (S390_lowcore.machine_flags & MACHINE_FLAG_TE) | 93 | #define MACHINE_HAS_TE (S390_lowcore.machine_flags & MACHINE_FLAG_TE) |
92 | #define MACHINE_HAS_TLB_LC (S390_lowcore.machine_flags & MACHINE_FLAG_TLB_LC) | 94 | #define MACHINE_HAS_TLB_LC (S390_lowcore.machine_flags & MACHINE_FLAG_TLB_LC) |
95 | #define MACHINE_HAS_VX (S390_lowcore.machine_flags & MACHINE_FLAG_VX) | ||
93 | #endif /* CONFIG_64BIT */ | 96 | #endif /* CONFIG_64BIT */ |
94 | 97 | ||
95 | /* | 98 | /* |
diff --git a/arch/s390/include/asm/switch_to.h b/arch/s390/include/asm/switch_to.h index 18ea9e3f8142..0e0109578021 100644 --- a/arch/s390/include/asm/switch_to.h +++ b/arch/s390/include/asm/switch_to.h | |||
@@ -103,6 +103,48 @@ static inline void restore_fp_regs(freg_t *fprs) | |||
103 | asm volatile("ld 15,%0" : : "Q" (fprs[15])); | 103 | asm volatile("ld 15,%0" : : "Q" (fprs[15])); |
104 | } | 104 | } |
105 | 105 | ||
106 | static inline void save_vx_regs(__vector128 *vxrs) | ||
107 | { | ||
108 | typedef struct { __vector128 _[__NUM_VXRS]; } addrtype; | ||
109 | |||
110 | asm volatile( | ||
111 | " la 1,%0\n" | ||
112 | " .word 0xe70f,0x1000,0x003e\n" /* vstm 0,15,0(1) */ | ||
113 | " .word 0xe70f,0x1100,0x0c3e\n" /* vstm 16,31,256(1) */ | ||
114 | : "=Q" (*(addrtype *) vxrs) : : "1"); | ||
115 | } | ||
116 | |||
117 | static inline void restore_vx_regs(__vector128 *vxrs) | ||
118 | { | ||
119 | typedef struct { __vector128 _[__NUM_VXRS]; } addrtype; | ||
120 | |||
121 | asm volatile( | ||
122 | " la 1,%0\n" | ||
123 | " .word 0xe70f,0x1000,0x0036\n" /* vlm 0,15,0(1) */ | ||
124 | " .word 0xe70f,0x1100,0x0c36\n" /* vlm 16,31,256(1) */ | ||
125 | : : "Q" (*(addrtype *) vxrs) : "1"); | ||
126 | } | ||
127 | |||
128 | static inline void save_fp_vx_regs(struct task_struct *task) | ||
129 | { | ||
130 | #ifdef CONFIG_64BIT | ||
131 | if (task->thread.vxrs) | ||
132 | save_vx_regs(task->thread.vxrs); | ||
133 | else | ||
134 | #endif | ||
135 | save_fp_regs(task->thread.fp_regs.fprs); | ||
136 | } | ||
137 | |||
138 | static inline void restore_fp_vx_regs(struct task_struct *task) | ||
139 | { | ||
140 | #ifdef CONFIG_64BIT | ||
141 | if (task->thread.vxrs) | ||
142 | restore_vx_regs(task->thread.vxrs); | ||
143 | else | ||
144 | #endif | ||
145 | restore_fp_regs(task->thread.fp_regs.fprs); | ||
146 | } | ||
147 | |||
106 | static inline void save_access_regs(unsigned int *acrs) | 148 | static inline void save_access_regs(unsigned int *acrs) |
107 | { | 149 | { |
108 | typedef struct { int _[NUM_ACRS]; } acrstype; | 150 | typedef struct { int _[NUM_ACRS]; } acrstype; |
@@ -120,16 +162,16 @@ static inline void restore_access_regs(unsigned int *acrs) | |||
120 | #define switch_to(prev,next,last) do { \ | 162 | #define switch_to(prev,next,last) do { \ |
121 | if (prev->mm) { \ | 163 | if (prev->mm) { \ |
122 | save_fp_ctl(&prev->thread.fp_regs.fpc); \ | 164 | save_fp_ctl(&prev->thread.fp_regs.fpc); \ |
123 | save_fp_regs(prev->thread.fp_regs.fprs); \ | 165 | save_fp_vx_regs(prev); \ |
124 | save_access_regs(&prev->thread.acrs[0]); \ | 166 | save_access_regs(&prev->thread.acrs[0]); \ |
125 | save_ri_cb(prev->thread.ri_cb); \ | 167 | save_ri_cb(prev->thread.ri_cb); \ |
126 | } \ | 168 | } \ |
127 | if (next->mm) { \ | 169 | if (next->mm) { \ |
170 | update_cr_regs(next); \ | ||
128 | restore_fp_ctl(&next->thread.fp_regs.fpc); \ | 171 | restore_fp_ctl(&next->thread.fp_regs.fpc); \ |
129 | restore_fp_regs(next->thread.fp_regs.fprs); \ | 172 | restore_fp_vx_regs(next); \ |
130 | restore_access_regs(&next->thread.acrs[0]); \ | 173 | restore_access_regs(&next->thread.acrs[0]); \ |
131 | restore_ri_cb(next->thread.ri_cb, prev->thread.ri_cb); \ | 174 | restore_ri_cb(next->thread.ri_cb, prev->thread.ri_cb); \ |
132 | update_cr_regs(next); \ | ||
133 | } \ | 175 | } \ |
134 | prev = __switch_to(prev,next); \ | 176 | prev = __switch_to(prev,next); \ |
135 | } while (0) | 177 | } while (0) |
diff --git a/arch/s390/include/uapi/asm/sigcontext.h b/arch/s390/include/uapi/asm/sigcontext.h index b30de9c01bbe..5f0b8d7ddb0b 100644 --- a/arch/s390/include/uapi/asm/sigcontext.h +++ b/arch/s390/include/uapi/asm/sigcontext.h | |||
@@ -7,10 +7,14 @@ | |||
7 | #define _ASM_S390_SIGCONTEXT_H | 7 | #define _ASM_S390_SIGCONTEXT_H |
8 | 8 | ||
9 | #include <linux/compiler.h> | 9 | #include <linux/compiler.h> |
10 | #include <linux/types.h> | ||
10 | 11 | ||
11 | #define __NUM_GPRS 16 | 12 | #define __NUM_GPRS 16 |
12 | #define __NUM_FPRS 16 | 13 | #define __NUM_FPRS 16 |
13 | #define __NUM_ACRS 16 | 14 | #define __NUM_ACRS 16 |
15 | #define __NUM_VXRS 32 | ||
16 | #define __NUM_VXRS_LOW 16 | ||
17 | #define __NUM_VXRS_HIGH 16 | ||
14 | 18 | ||
15 | #ifndef __s390x__ | 19 | #ifndef __s390x__ |
16 | 20 | ||
@@ -59,6 +63,16 @@ typedef struct | |||
59 | _s390_fp_regs fpregs; | 63 | _s390_fp_regs fpregs; |
60 | } _sigregs; | 64 | } _sigregs; |
61 | 65 | ||
66 | typedef struct | ||
67 | { | ||
68 | #ifndef __s390x__ | ||
69 | unsigned long gprs_high[__NUM_GPRS]; | ||
70 | #endif | ||
71 | unsigned long long vxrs_low[__NUM_VXRS_LOW]; | ||
72 | __vector128 vxrs_high[__NUM_VXRS_HIGH]; | ||
73 | unsigned char __reserved[128]; | ||
74 | } _sigregs_ext; | ||
75 | |||
62 | struct sigcontext | 76 | struct sigcontext |
63 | { | 77 | { |
64 | unsigned long oldmask[_SIGCONTEXT_NSIG_WORDS]; | 78 | unsigned long oldmask[_SIGCONTEXT_NSIG_WORDS]; |
diff --git a/arch/s390/include/uapi/asm/types.h b/arch/s390/include/uapi/asm/types.h index 038f2b9178a4..3c3951e3415b 100644 --- a/arch/s390/include/uapi/asm/types.h +++ b/arch/s390/include/uapi/asm/types.h | |||
@@ -17,6 +17,10 @@ | |||
17 | typedef unsigned long addr_t; | 17 | typedef unsigned long addr_t; |
18 | typedef __signed__ long saddr_t; | 18 | typedef __signed__ long saddr_t; |
19 | 19 | ||
20 | typedef struct { | ||
21 | __u32 u[4]; | ||
22 | } __vector128; | ||
23 | |||
20 | #endif /* __ASSEMBLY__ */ | 24 | #endif /* __ASSEMBLY__ */ |
21 | 25 | ||
22 | #endif /* _UAPI_S390_TYPES_H */ | 26 | #endif /* _UAPI_S390_TYPES_H */ |
diff --git a/arch/s390/include/uapi/asm/ucontext.h b/arch/s390/include/uapi/asm/ucontext.h index 3e077b2a4705..64a69aa5dde0 100644 --- a/arch/s390/include/uapi/asm/ucontext.h +++ b/arch/s390/include/uapi/asm/ucontext.h | |||
@@ -7,10 +7,15 @@ | |||
7 | #ifndef _ASM_S390_UCONTEXT_H | 7 | #ifndef _ASM_S390_UCONTEXT_H |
8 | #define _ASM_S390_UCONTEXT_H | 8 | #define _ASM_S390_UCONTEXT_H |
9 | 9 | ||
10 | #define UC_EXTENDED 0x00000001 | 10 | #define UC_GPRS_HIGH 1 /* uc_mcontext_ext has valid high gprs */ |
11 | 11 | #define UC_VXRS 2 /* uc_mcontext_ext has valid vector regs */ | |
12 | #ifndef __s390x__ | ||
13 | 12 | ||
13 | /* | ||
14 | * The struct ucontext_extended describes how the registers are stored | ||
15 | * on a rt signal frame. Please note that the structure is not fixed, | ||
16 | * if new CPU registers are added to the user state the size of the | ||
17 | * struct ucontext_extended will increase. | ||
18 | */ | ||
14 | struct ucontext_extended { | 19 | struct ucontext_extended { |
15 | unsigned long uc_flags; | 20 | unsigned long uc_flags; |
16 | struct ucontext *uc_link; | 21 | struct ucontext *uc_link; |
@@ -19,11 +24,9 @@ struct ucontext_extended { | |||
19 | sigset_t uc_sigmask; | 24 | sigset_t uc_sigmask; |
20 | /* Allow for uc_sigmask growth. Glibc uses a 1024-bit sigset_t. */ | 25 | /* Allow for uc_sigmask growth. Glibc uses a 1024-bit sigset_t. */ |
21 | unsigned char __unused[128 - sizeof(sigset_t)]; | 26 | unsigned char __unused[128 - sizeof(sigset_t)]; |
22 | unsigned long uc_gprs_high[16]; | 27 | _sigregs_ext uc_mcontext_ext; |
23 | }; | 28 | }; |
24 | 29 | ||
25 | #endif | ||
26 | |||
27 | struct ucontext { | 30 | struct ucontext { |
28 | unsigned long uc_flags; | 31 | unsigned long uc_flags; |
29 | struct ucontext *uc_link; | 32 | struct ucontext *uc_link; |
diff --git a/arch/s390/kernel/compat_linux.h b/arch/s390/kernel/compat_linux.h index 70d4b7c4beaa..a0a886c04977 100644 --- a/arch/s390/kernel/compat_linux.h +++ b/arch/s390/kernel/compat_linux.h | |||
@@ -50,6 +50,14 @@ typedef struct | |||
50 | _s390_fp_regs32 fpregs; | 50 | _s390_fp_regs32 fpregs; |
51 | } _sigregs32; | 51 | } _sigregs32; |
52 | 52 | ||
53 | typedef struct | ||
54 | { | ||
55 | __u32 gprs_high[__NUM_GPRS]; | ||
56 | __u64 vxrs_low[__NUM_VXRS_LOW]; | ||
57 | __vector128 vxrs_high[__NUM_VXRS_HIGH]; | ||
58 | __u8 __reserved[128]; | ||
59 | } _sigregs_ext32; | ||
60 | |||
53 | #define _SIGCONTEXT_NSIG32 64 | 61 | #define _SIGCONTEXT_NSIG32 64 |
54 | #define _SIGCONTEXT_NSIG_BPW32 32 | 62 | #define _SIGCONTEXT_NSIG_BPW32 32 |
55 | #define __SIGNAL_FRAMESIZE32 96 | 63 | #define __SIGNAL_FRAMESIZE32 96 |
@@ -72,6 +80,7 @@ struct ucontext32 { | |||
72 | compat_sigset_t uc_sigmask; | 80 | compat_sigset_t uc_sigmask; |
73 | /* Allow for uc_sigmask growth. Glibc uses a 1024-bit sigset_t. */ | 81 | /* Allow for uc_sigmask growth. Glibc uses a 1024-bit sigset_t. */ |
74 | unsigned char __unused[128 - sizeof(compat_sigset_t)]; | 82 | unsigned char __unused[128 - sizeof(compat_sigset_t)]; |
83 | _sigregs_ext32 uc_mcontext_ext; | ||
75 | }; | 84 | }; |
76 | 85 | ||
77 | struct stat64_emu31; | 86 | struct stat64_emu31; |
diff --git a/arch/s390/kernel/compat_signal.c b/arch/s390/kernel/compat_signal.c index 598b0b42668b..009f5eb11125 100644 --- a/arch/s390/kernel/compat_signal.c +++ b/arch/s390/kernel/compat_signal.c | |||
@@ -36,17 +36,16 @@ typedef struct | |||
36 | struct sigcontext32 sc; | 36 | struct sigcontext32 sc; |
37 | _sigregs32 sregs; | 37 | _sigregs32 sregs; |
38 | int signo; | 38 | int signo; |
39 | __u32 gprs_high[NUM_GPRS]; | 39 | _sigregs_ext32 sregs_ext; |
40 | __u8 retcode[S390_SYSCALL_SIZE]; | 40 | __u16 svc_insn; /* Offset of svc_insn is NOT fixed! */ |
41 | } sigframe32; | 41 | } sigframe32; |
42 | 42 | ||
43 | typedef struct | 43 | typedef struct |
44 | { | 44 | { |
45 | __u8 callee_used_stack[__SIGNAL_FRAMESIZE32]; | 45 | __u8 callee_used_stack[__SIGNAL_FRAMESIZE32]; |
46 | __u8 retcode[S390_SYSCALL_SIZE]; | 46 | __u16 svc_insn; |
47 | compat_siginfo_t info; | 47 | compat_siginfo_t info; |
48 | struct ucontext32 uc; | 48 | struct ucontext32 uc; |
49 | __u32 gprs_high[NUM_GPRS]; | ||
50 | } rt_sigframe32; | 49 | } rt_sigframe32; |
51 | 50 | ||
52 | int copy_siginfo_to_user32(compat_siginfo_t __user *to, const siginfo_t *from) | 51 | int copy_siginfo_to_user32(compat_siginfo_t __user *to, const siginfo_t *from) |
@@ -151,6 +150,38 @@ int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from) | |||
151 | return err ? -EFAULT : 0; | 150 | return err ? -EFAULT : 0; |
152 | } | 151 | } |
153 | 152 | ||
153 | /* Store registers needed to create the signal frame */ | ||
154 | static void store_sigregs(void) | ||
155 | { | ||
156 | int i; | ||
157 | |||
158 | save_access_regs(current->thread.acrs); | ||
159 | save_fp_ctl(¤t->thread.fp_regs.fpc); | ||
160 | if (current->thread.vxrs) { | ||
161 | save_vx_regs(current->thread.vxrs); | ||
162 | for (i = 0; i < __NUM_FPRS; i++) | ||
163 | current->thread.fp_regs.fprs[i] = | ||
164 | *(freg_t *)(current->thread.vxrs + i); | ||
165 | } else | ||
166 | save_fp_regs(current->thread.fp_regs.fprs); | ||
167 | } | ||
168 | |||
169 | /* Load registers after signal return */ | ||
170 | static void load_sigregs(void) | ||
171 | { | ||
172 | int i; | ||
173 | |||
174 | restore_access_regs(current->thread.acrs); | ||
175 | /* restore_fp_ctl is done in restore_sigregs */ | ||
176 | if (current->thread.vxrs) { | ||
177 | for (i = 0; i < __NUM_FPRS; i++) | ||
178 | *(freg_t *)(current->thread.vxrs + i) = | ||
179 | current->thread.fp_regs.fprs[i]; | ||
180 | restore_vx_regs(current->thread.vxrs); | ||
181 | } else | ||
182 | restore_fp_regs(current->thread.fp_regs.fprs); | ||
183 | } | ||
184 | |||
154 | static int save_sigregs32(struct pt_regs *regs, _sigregs32 __user *sregs) | 185 | static int save_sigregs32(struct pt_regs *regs, _sigregs32 __user *sregs) |
155 | { | 186 | { |
156 | _sigregs32 user_sregs; | 187 | _sigregs32 user_sregs; |
@@ -163,11 +194,8 @@ static int save_sigregs32(struct pt_regs *regs, _sigregs32 __user *sregs) | |||
163 | (__u32)(regs->psw.mask & PSW_MASK_BA); | 194 | (__u32)(regs->psw.mask & PSW_MASK_BA); |
164 | for (i = 0; i < NUM_GPRS; i++) | 195 | for (i = 0; i < NUM_GPRS; i++) |
165 | user_sregs.regs.gprs[i] = (__u32) regs->gprs[i]; | 196 | user_sregs.regs.gprs[i] = (__u32) regs->gprs[i]; |
166 | save_access_regs(current->thread.acrs); | ||
167 | memcpy(&user_sregs.regs.acrs, current->thread.acrs, | 197 | memcpy(&user_sregs.regs.acrs, current->thread.acrs, |
168 | sizeof(user_sregs.regs.acrs)); | 198 | sizeof(user_sregs.regs.acrs)); |
169 | save_fp_ctl(¤t->thread.fp_regs.fpc); | ||
170 | save_fp_regs(current->thread.fp_regs.fprs); | ||
171 | memcpy(&user_sregs.fpregs, ¤t->thread.fp_regs, | 199 | memcpy(&user_sregs.fpregs, ¤t->thread.fp_regs, |
172 | sizeof(user_sregs.fpregs)); | 200 | sizeof(user_sregs.fpregs)); |
173 | if (__copy_to_user(sregs, &user_sregs, sizeof(_sigregs32))) | 201 | if (__copy_to_user(sregs, &user_sregs, sizeof(_sigregs32))) |
@@ -207,37 +235,67 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs) | |||
207 | regs->gprs[i] = (__u64) user_sregs.regs.gprs[i]; | 235 | regs->gprs[i] = (__u64) user_sregs.regs.gprs[i]; |
208 | memcpy(¤t->thread.acrs, &user_sregs.regs.acrs, | 236 | memcpy(¤t->thread.acrs, &user_sregs.regs.acrs, |
209 | sizeof(current->thread.acrs)); | 237 | sizeof(current->thread.acrs)); |
210 | restore_access_regs(current->thread.acrs); | ||
211 | 238 | ||
212 | memcpy(¤t->thread.fp_regs, &user_sregs.fpregs, | 239 | memcpy(¤t->thread.fp_regs, &user_sregs.fpregs, |
213 | sizeof(current->thread.fp_regs)); | 240 | sizeof(current->thread.fp_regs)); |
214 | 241 | ||
215 | restore_fp_regs(current->thread.fp_regs.fprs); | ||
216 | clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */ | 242 | clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */ |
217 | return 0; | 243 | return 0; |
218 | } | 244 | } |
219 | 245 | ||
220 | static int save_sigregs_gprs_high(struct pt_regs *regs, __u32 __user *uregs) | 246 | static int save_sigregs_ext32(struct pt_regs *regs, |
247 | _sigregs_ext32 __user *sregs_ext) | ||
221 | { | 248 | { |
222 | __u32 gprs_high[NUM_GPRS]; | 249 | __u32 gprs_high[NUM_GPRS]; |
250 | __u64 vxrs[__NUM_VXRS_LOW]; | ||
223 | int i; | 251 | int i; |
224 | 252 | ||
253 | /* Save high gprs to signal stack */ | ||
225 | for (i = 0; i < NUM_GPRS; i++) | 254 | for (i = 0; i < NUM_GPRS; i++) |
226 | gprs_high[i] = regs->gprs[i] >> 32; | 255 | gprs_high[i] = regs->gprs[i] >> 32; |
227 | if (__copy_to_user(uregs, &gprs_high, sizeof(gprs_high))) | 256 | if (__copy_to_user(&sregs_ext->gprs_high, &gprs_high, |
257 | sizeof(sregs_ext->gprs_high))) | ||
228 | return -EFAULT; | 258 | return -EFAULT; |
259 | |||
260 | /* Save vector registers to signal stack */ | ||
261 | if (current->thread.vxrs) { | ||
262 | for (i = 0; i < __NUM_VXRS_LOW; i++) | ||
263 | vxrs[i] = *((__u64 *)(current->thread.vxrs + i) + 1); | ||
264 | if (__copy_to_user(&sregs_ext->vxrs_low, vxrs, | ||
265 | sizeof(sregs_ext->vxrs_low)) || | ||
266 | __copy_to_user(&sregs_ext->vxrs_high, | ||
267 | current->thread.vxrs + __NUM_VXRS_LOW, | ||
268 | sizeof(sregs_ext->vxrs_high))) | ||
269 | return -EFAULT; | ||
270 | } | ||
229 | return 0; | 271 | return 0; |
230 | } | 272 | } |
231 | 273 | ||
232 | static int restore_sigregs_gprs_high(struct pt_regs *regs, __u32 __user *uregs) | 274 | static int restore_sigregs_ext32(struct pt_regs *regs, |
275 | _sigregs_ext32 __user *sregs_ext) | ||
233 | { | 276 | { |
234 | __u32 gprs_high[NUM_GPRS]; | 277 | __u32 gprs_high[NUM_GPRS]; |
278 | __u64 vxrs[__NUM_VXRS_LOW]; | ||
235 | int i; | 279 | int i; |
236 | 280 | ||
237 | if (__copy_from_user(&gprs_high, uregs, sizeof(gprs_high))) | 281 | /* Restore high gprs from signal stack */ |
282 | if (__copy_from_user(&gprs_high, &sregs_ext->gprs_high, | ||
283 | sizeof(&sregs_ext->gprs_high))) | ||
238 | return -EFAULT; | 284 | return -EFAULT; |
239 | for (i = 0; i < NUM_GPRS; i++) | 285 | for (i = 0; i < NUM_GPRS; i++) |
240 | *(__u32 *)®s->gprs[i] = gprs_high[i]; | 286 | *(__u32 *)®s->gprs[i] = gprs_high[i]; |
287 | |||
288 | /* Restore vector registers from signal stack */ | ||
289 | if (current->thread.vxrs) { | ||
290 | if (__copy_from_user(vxrs, &sregs_ext->vxrs_low, | ||
291 | sizeof(sregs_ext->vxrs_low)) || | ||
292 | __copy_from_user(current->thread.vxrs + __NUM_VXRS_LOW, | ||
293 | &sregs_ext->vxrs_high, | ||
294 | sizeof(sregs_ext->vxrs_high))) | ||
295 | return -EFAULT; | ||
296 | for (i = 0; i < __NUM_VXRS_LOW; i++) | ||
297 | *((__u64 *)(current->thread.vxrs + i) + 1) = vxrs[i]; | ||
298 | } | ||
241 | return 0; | 299 | return 0; |
242 | } | 300 | } |
243 | 301 | ||
@@ -252,8 +310,9 @@ COMPAT_SYSCALL_DEFINE0(sigreturn) | |||
252 | set_current_blocked(&set); | 310 | set_current_blocked(&set); |
253 | if (restore_sigregs32(regs, &frame->sregs)) | 311 | if (restore_sigregs32(regs, &frame->sregs)) |
254 | goto badframe; | 312 | goto badframe; |
255 | if (restore_sigregs_gprs_high(regs, frame->gprs_high)) | 313 | if (restore_sigregs_ext32(regs, &frame->sregs_ext)) |
256 | goto badframe; | 314 | goto badframe; |
315 | load_sigregs(); | ||
257 | return regs->gprs[2]; | 316 | return regs->gprs[2]; |
258 | badframe: | 317 | badframe: |
259 | force_sig(SIGSEGV, current); | 318 | force_sig(SIGSEGV, current); |
@@ -269,12 +328,13 @@ COMPAT_SYSCALL_DEFINE0(rt_sigreturn) | |||
269 | if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) | 328 | if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) |
270 | goto badframe; | 329 | goto badframe; |
271 | set_current_blocked(&set); | 330 | set_current_blocked(&set); |
331 | if (compat_restore_altstack(&frame->uc.uc_stack)) | ||
332 | goto badframe; | ||
272 | if (restore_sigregs32(regs, &frame->uc.uc_mcontext)) | 333 | if (restore_sigregs32(regs, &frame->uc.uc_mcontext)) |
273 | goto badframe; | 334 | goto badframe; |
274 | if (restore_sigregs_gprs_high(regs, frame->gprs_high)) | 335 | if (restore_sigregs_ext32(regs, &frame->uc.uc_mcontext_ext)) |
275 | goto badframe; | 336 | goto badframe; |
276 | if (compat_restore_altstack(&frame->uc.uc_stack)) | 337 | load_sigregs(); |
277 | goto badframe; | ||
278 | return regs->gprs[2]; | 338 | return regs->gprs[2]; |
279 | badframe: | 339 | badframe: |
280 | force_sig(SIGSEGV, current); | 340 | force_sig(SIGSEGV, current); |
@@ -324,37 +384,64 @@ static int setup_frame32(struct ksignal *ksig, sigset_t *set, | |||
324 | struct pt_regs *regs) | 384 | struct pt_regs *regs) |
325 | { | 385 | { |
326 | int sig = ksig->sig; | 386 | int sig = ksig->sig; |
327 | sigframe32 __user *frame = get_sigframe(&ksig->ka, regs, sizeof(sigframe32)); | 387 | sigframe32 __user *frame; |
328 | 388 | struct sigcontext32 sc; | |
389 | unsigned long restorer; | ||
390 | size_t frame_size; | ||
391 | |||
392 | /* | ||
393 | * gprs_high are always present for 31-bit compat tasks. | ||
394 | * The space for vector registers is only allocated if | ||
395 | * the machine supports it | ||
396 | */ | ||
397 | frame_size = sizeof(*frame) - sizeof(frame->sregs_ext.__reserved); | ||
398 | if (!MACHINE_HAS_VX) | ||
399 | frame_size -= sizeof(frame->sregs_ext.vxrs_low) + | ||
400 | sizeof(frame->sregs_ext.vxrs_high); | ||
401 | frame = get_sigframe(&ksig->ka, regs, frame_size); | ||
329 | if (frame == (void __user *) -1UL) | 402 | if (frame == (void __user *) -1UL) |
330 | return -EFAULT; | 403 | return -EFAULT; |
331 | 404 | ||
332 | if (__copy_to_user(&frame->sc.oldmask, &set->sig, _SIGMASK_COPY_SIZE32)) | 405 | /* Set up backchain. */ |
406 | if (__put_user(regs->gprs[15], (unsigned int __user *) frame)) | ||
407 | return -EFAULT; | ||
408 | |||
409 | /* Create struct sigcontext32 on the signal stack */ | ||
410 | memcpy(&sc.oldmask, &set->sig, _SIGMASK_COPY_SIZE32); | ||
411 | sc.sregs = (__u32)(unsigned long __force) &frame->sregs; | ||
412 | if (__copy_to_user(&frame->sc, &sc, sizeof(frame->sc))) | ||
333 | return -EFAULT; | 413 | return -EFAULT; |
334 | 414 | ||
415 | /* Store registers needed to create the signal frame */ | ||
416 | store_sigregs(); | ||
417 | |||
418 | /* Create _sigregs32 on the signal stack */ | ||
335 | if (save_sigregs32(regs, &frame->sregs)) | 419 | if (save_sigregs32(regs, &frame->sregs)) |
336 | return -EFAULT; | 420 | return -EFAULT; |
337 | if (save_sigregs_gprs_high(regs, frame->gprs_high)) | 421 | |
422 | /* Place signal number on stack to allow backtrace from handler. */ | ||
423 | if (__put_user(regs->gprs[2], (int __force __user *) &frame->signo)) | ||
338 | return -EFAULT; | 424 | return -EFAULT; |
339 | if (__put_user((unsigned long) &frame->sregs, &frame->sc.sregs)) | 425 | |
426 | /* Create _sigregs_ext32 on the signal stack */ | ||
427 | if (save_sigregs_ext32(regs, &frame->sregs_ext)) | ||
340 | return -EFAULT; | 428 | return -EFAULT; |
341 | 429 | ||
342 | /* Set up to return from userspace. If provided, use a stub | 430 | /* Set up to return from userspace. If provided, use a stub |
343 | already in userspace. */ | 431 | already in userspace. */ |
344 | if (ksig->ka.sa.sa_flags & SA_RESTORER) { | 432 | if (ksig->ka.sa.sa_flags & SA_RESTORER) { |
345 | regs->gprs[14] = (__u64 __force) ksig->ka.sa.sa_restorer | PSW32_ADDR_AMODE; | 433 | restorer = (unsigned long __force) |
434 | ksig->ka.sa.sa_restorer | PSW32_ADDR_AMODE; | ||
346 | } else { | 435 | } else { |
347 | regs->gprs[14] = (__u64 __force) frame->retcode | PSW32_ADDR_AMODE; | 436 | /* Signal frames without vectors registers are short ! */ |
348 | if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, | 437 | __u16 __user *svc = (void *) frame + frame_size - 2; |
349 | (u16 __force __user *)(frame->retcode))) | 438 | if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, svc)) |
350 | return -EFAULT; | 439 | return -EFAULT; |
440 | restorer = (unsigned long __force) svc | PSW32_ADDR_AMODE; | ||
351 | } | 441 | } |
352 | 442 | ||
353 | /* Set up backchain. */ | ||
354 | if (__put_user(regs->gprs[15], (unsigned int __user *) frame)) | ||
355 | return -EFAULT; | ||
356 | |||
357 | /* Set up registers for signal handler */ | 443 | /* Set up registers for signal handler */ |
444 | regs->gprs[14] = restorer; | ||
358 | regs->gprs[15] = (__force __u64) frame; | 445 | regs->gprs[15] = (__force __u64) frame; |
359 | /* Force 31 bit amode and default user address space control. */ | 446 | /* Force 31 bit amode and default user address space control. */ |
360 | regs->psw.mask = PSW_MASK_BA | | 447 | regs->psw.mask = PSW_MASK_BA | |
@@ -375,50 +462,69 @@ static int setup_frame32(struct ksignal *ksig, sigset_t *set, | |||
375 | regs->gprs[6] = task_thread_info(current)->last_break; | 462 | regs->gprs[6] = task_thread_info(current)->last_break; |
376 | } | 463 | } |
377 | 464 | ||
378 | /* Place signal number on stack to allow backtrace from handler. */ | ||
379 | if (__put_user(regs->gprs[2], (int __force __user *) &frame->signo)) | ||
380 | return -EFAULT; | ||
381 | return 0; | 465 | return 0; |
382 | } | 466 | } |
383 | 467 | ||
384 | static int setup_rt_frame32(struct ksignal *ksig, sigset_t *set, | 468 | static int setup_rt_frame32(struct ksignal *ksig, sigset_t *set, |
385 | struct pt_regs *regs) | 469 | struct pt_regs *regs) |
386 | { | 470 | { |
387 | int err = 0; | 471 | rt_sigframe32 __user *frame; |
388 | rt_sigframe32 __user *frame = get_sigframe(&ksig->ka, regs, sizeof(rt_sigframe32)); | 472 | unsigned long restorer; |
389 | 473 | size_t frame_size; | |
474 | u32 uc_flags; | ||
475 | |||
476 | frame_size = sizeof(*frame) - | ||
477 | sizeof(frame->uc.uc_mcontext_ext.__reserved); | ||
478 | /* | ||
479 | * gprs_high are always present for 31-bit compat tasks. | ||
480 | * The space for vector registers is only allocated if | ||
481 | * the machine supports it | ||
482 | */ | ||
483 | uc_flags = UC_GPRS_HIGH; | ||
484 | if (MACHINE_HAS_VX) { | ||
485 | if (current->thread.vxrs) | ||
486 | uc_flags |= UC_VXRS; | ||
487 | } else | ||
488 | frame_size -= sizeof(frame->uc.uc_mcontext_ext.vxrs_low) + | ||
489 | sizeof(frame->uc.uc_mcontext_ext.vxrs_high); | ||
490 | frame = get_sigframe(&ksig->ka, regs, frame_size); | ||
390 | if (frame == (void __user *) -1UL) | 491 | if (frame == (void __user *) -1UL) |
391 | return -EFAULT; | 492 | return -EFAULT; |
392 | 493 | ||
393 | if (copy_siginfo_to_user32(&frame->info, &ksig->info)) | 494 | /* Set up backchain. */ |
394 | return -EFAULT; | 495 | if (__put_user(regs->gprs[15], (unsigned int __force __user *) frame)) |
395 | |||
396 | /* Create the ucontext. */ | ||
397 | err |= __put_user(UC_EXTENDED, &frame->uc.uc_flags); | ||
398 | err |= __put_user(0, &frame->uc.uc_link); | ||
399 | err |= __compat_save_altstack(&frame->uc.uc_stack, regs->gprs[15]); | ||
400 | err |= save_sigregs32(regs, &frame->uc.uc_mcontext); | ||
401 | err |= save_sigregs_gprs_high(regs, frame->gprs_high); | ||
402 | err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); | ||
403 | if (err) | ||
404 | return -EFAULT; | 496 | return -EFAULT; |
405 | 497 | ||
406 | /* Set up to return from userspace. If provided, use a stub | 498 | /* Set up to return from userspace. If provided, use a stub |
407 | already in userspace. */ | 499 | already in userspace. */ |
408 | if (ksig->ka.sa.sa_flags & SA_RESTORER) { | 500 | if (ksig->ka.sa.sa_flags & SA_RESTORER) { |
409 | regs->gprs[14] = (__u64 __force) ksig->ka.sa.sa_restorer | PSW32_ADDR_AMODE; | 501 | restorer = (unsigned long __force) |
502 | ksig->ka.sa.sa_restorer | PSW32_ADDR_AMODE; | ||
410 | } else { | 503 | } else { |
411 | regs->gprs[14] = (__u64 __force) frame->retcode | PSW32_ADDR_AMODE; | 504 | __u16 __user *svc = &frame->svc_insn; |
412 | if (__put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, | 505 | if (__put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, svc)) |
413 | (u16 __force __user *)(frame->retcode))) | ||
414 | return -EFAULT; | 506 | return -EFAULT; |
507 | restorer = (unsigned long __force) svc | PSW32_ADDR_AMODE; | ||
415 | } | 508 | } |
416 | 509 | ||
417 | /* Set up backchain. */ | 510 | /* Create siginfo on the signal stack */ |
418 | if (__put_user(regs->gprs[15], (unsigned int __force __user *) frame)) | 511 | if (copy_siginfo_to_user32(&frame->info, &ksig->info)) |
512 | return -EFAULT; | ||
513 | |||
514 | /* Store registers needed to create the signal frame */ | ||
515 | store_sigregs(); | ||
516 | |||
517 | /* Create ucontext on the signal stack. */ | ||
518 | if (__put_user(uc_flags, &frame->uc.uc_flags) || | ||
519 | __put_user(0, &frame->uc.uc_link) || | ||
520 | __compat_save_altstack(&frame->uc.uc_stack, regs->gprs[15]) || | ||
521 | save_sigregs32(regs, &frame->uc.uc_mcontext) || | ||
522 | __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)) || | ||
523 | save_sigregs_ext32(regs, &frame->uc.uc_mcontext_ext)) | ||
419 | return -EFAULT; | 524 | return -EFAULT; |
420 | 525 | ||
421 | /* Set up registers for signal handler */ | 526 | /* Set up registers for signal handler */ |
527 | regs->gprs[14] = restorer; | ||
422 | regs->gprs[15] = (__force __u64) frame; | 528 | regs->gprs[15] = (__force __u64) frame; |
423 | /* Force 31 bit amode and default user address space control. */ | 529 | /* Force 31 bit amode and default user address space control. */ |
424 | regs->psw.mask = PSW_MASK_BA | | 530 | regs->psw.mask = PSW_MASK_BA | |
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index f6c66b5d0cfa..cef2879edff3 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c | |||
@@ -392,6 +392,8 @@ static __init void detect_machine_facilities(void) | |||
392 | S390_lowcore.machine_flags |= MACHINE_FLAG_TE; | 392 | S390_lowcore.machine_flags |= MACHINE_FLAG_TE; |
393 | if (test_facility(51)) | 393 | if (test_facility(51)) |
394 | S390_lowcore.machine_flags |= MACHINE_FLAG_TLB_LC; | 394 | S390_lowcore.machine_flags |= MACHINE_FLAG_TLB_LC; |
395 | if (test_facility(129)) | ||
396 | S390_lowcore.machine_flags |= MACHINE_FLAG_VX; | ||
395 | #endif | 397 | #endif |
396 | } | 398 | } |
397 | 399 | ||
diff --git a/arch/s390/kernel/entry.h b/arch/s390/kernel/entry.h index cd68869f9504..0554b9771c9f 100644 --- a/arch/s390/kernel/entry.h +++ b/arch/s390/kernel/entry.h | |||
@@ -21,6 +21,8 @@ void psw_idle(struct s390_idle_data *, unsigned long); | |||
21 | asmlinkage long do_syscall_trace_enter(struct pt_regs *regs); | 21 | asmlinkage long do_syscall_trace_enter(struct pt_regs *regs); |
22 | asmlinkage void do_syscall_trace_exit(struct pt_regs *regs); | 22 | asmlinkage void do_syscall_trace_exit(struct pt_regs *regs); |
23 | 23 | ||
24 | int alloc_vector_registers(struct task_struct *tsk); | ||
25 | |||
24 | void do_protection_exception(struct pt_regs *regs); | 26 | void do_protection_exception(struct pt_regs *regs); |
25 | void do_dat_exception(struct pt_regs *regs); | 27 | void do_dat_exception(struct pt_regs *regs); |
26 | 28 | ||
@@ -43,6 +45,7 @@ void special_op_exception(struct pt_regs *regs); | |||
43 | void specification_exception(struct pt_regs *regs); | 45 | void specification_exception(struct pt_regs *regs); |
44 | void transaction_exception(struct pt_regs *regs); | 46 | void transaction_exception(struct pt_regs *regs); |
45 | void translation_exception(struct pt_regs *regs); | 47 | void translation_exception(struct pt_regs *regs); |
48 | void vector_exception(struct pt_regs *regs); | ||
46 | 49 | ||
47 | void do_per_trap(struct pt_regs *regs); | 50 | 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); | 51 | void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str); |
diff --git a/arch/s390/kernel/nmi.c b/arch/s390/kernel/nmi.c index 210e1285f75a..db96b418160a 100644 --- a/arch/s390/kernel/nmi.c +++ b/arch/s390/kernel/nmi.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <asm/cputime.h> | 20 | #include <asm/cputime.h> |
21 | #include <asm/nmi.h> | 21 | #include <asm/nmi.h> |
22 | #include <asm/crw.h> | 22 | #include <asm/crw.h> |
23 | #include <asm/switch_to.h> | ||
23 | 24 | ||
24 | struct mcck_struct { | 25 | struct mcck_struct { |
25 | int kill_task; | 26 | int kill_task; |
@@ -163,6 +164,21 @@ static int notrace s390_revalidate_registers(struct mci *mci) | |||
163 | " ld 15,120(%0)\n" | 164 | " ld 15,120(%0)\n" |
164 | : : "a" (fpt_save_area)); | 165 | : : "a" (fpt_save_area)); |
165 | } | 166 | } |
167 | |||
168 | #ifdef CONFIG_64BIT | ||
169 | /* Revalidate vector registers */ | ||
170 | if (MACHINE_HAS_VX && current->thread.vxrs) { | ||
171 | if (!mci->vr) { | ||
172 | /* | ||
173 | * Vector registers can't be restored and therefore | ||
174 | * the process needs to be terminated. | ||
175 | */ | ||
176 | kill_task = 1; | ||
177 | } | ||
178 | restore_vx_regs((__vector128 *) | ||
179 | S390_lowcore.vector_save_area_addr); | ||
180 | } | ||
181 | #endif | ||
166 | /* Revalidate access registers */ | 182 | /* Revalidate access registers */ |
167 | asm volatile( | 183 | asm volatile( |
168 | " lam 0,15,0(%0)" | 184 | " lam 0,15,0(%0)" |
diff --git a/arch/s390/kernel/pgm_check.S b/arch/s390/kernel/pgm_check.S index 813ec7260878..f6f8886399f6 100644 --- a/arch/s390/kernel/pgm_check.S +++ b/arch/s390/kernel/pgm_check.S | |||
@@ -49,7 +49,7 @@ PGM_CHECK_DEFAULT /* 17 */ | |||
49 | PGM_CHECK_64BIT(transaction_exception) /* 18 */ | 49 | PGM_CHECK_64BIT(transaction_exception) /* 18 */ |
50 | PGM_CHECK_DEFAULT /* 19 */ | 50 | PGM_CHECK_DEFAULT /* 19 */ |
51 | PGM_CHECK_DEFAULT /* 1a */ | 51 | PGM_CHECK_DEFAULT /* 1a */ |
52 | PGM_CHECK_DEFAULT /* 1b */ | 52 | PGM_CHECK_64BIT(vector_exception) /* 1b */ |
53 | PGM_CHECK(space_switch_exception) /* 1c */ | 53 | PGM_CHECK(space_switch_exception) /* 1c */ |
54 | PGM_CHECK(hfp_sqrt_exception) /* 1d */ | 54 | PGM_CHECK(hfp_sqrt_exception) /* 1d */ |
55 | PGM_CHECK_DEFAULT /* 1e */ | 55 | PGM_CHECK_DEFAULT /* 1e */ |
diff --git a/arch/s390/kernel/processor.c b/arch/s390/kernel/processor.c index 32587cc76306..edefead3b43a 100644 --- a/arch/s390/kernel/processor.c +++ b/arch/s390/kernel/processor.c | |||
@@ -39,7 +39,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) | |||
39 | { | 39 | { |
40 | static const char *hwcap_str[] = { | 40 | static const char *hwcap_str[] = { |
41 | "esan3", "zarch", "stfle", "msa", "ldisp", "eimm", "dfp", | 41 | "esan3", "zarch", "stfle", "msa", "ldisp", "eimm", "dfp", |
42 | "edat", "etf3eh", "highgprs", "te" | 42 | "edat", "etf3eh", "highgprs", "te", "vx" |
43 | }; | 43 | }; |
44 | unsigned long n = (unsigned long) v - 1; | 44 | unsigned long n = (unsigned long) v - 1; |
45 | int i; | 45 | int i; |
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index fe99d6b3f185..0ecfdb3c6f8e 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c | |||
@@ -38,15 +38,6 @@ | |||
38 | #define CREATE_TRACE_POINTS | 38 | #define CREATE_TRACE_POINTS |
39 | #include <trace/events/syscalls.h> | 39 | #include <trace/events/syscalls.h> |
40 | 40 | ||
41 | enum s390_regset { | ||
42 | REGSET_GENERAL, | ||
43 | REGSET_FP, | ||
44 | REGSET_LAST_BREAK, | ||
45 | REGSET_TDB, | ||
46 | REGSET_SYSTEM_CALL, | ||
47 | REGSET_GENERAL_EXTENDED, | ||
48 | }; | ||
49 | |||
50 | void update_cr_regs(struct task_struct *task) | 41 | void update_cr_regs(struct task_struct *task) |
51 | { | 42 | { |
52 | struct pt_regs *regs = task_pt_regs(task); | 43 | struct pt_regs *regs = task_pt_regs(task); |
@@ -55,27 +46,39 @@ void update_cr_regs(struct task_struct *task) | |||
55 | 46 | ||
56 | #ifdef CONFIG_64BIT | 47 | #ifdef CONFIG_64BIT |
57 | /* Take care of the enable/disable of transactional execution. */ | 48 | /* Take care of the enable/disable of transactional execution. */ |
58 | if (MACHINE_HAS_TE) { | 49 | if (MACHINE_HAS_TE || MACHINE_HAS_VX) { |
59 | unsigned long cr, cr_new; | 50 | unsigned long cr, cr_new; |
60 | 51 | ||
61 | __ctl_store(cr, 0, 0); | 52 | __ctl_store(cr, 0, 0); |
62 | /* Set or clear transaction execution TXC bit 8. */ | 53 | cr_new = cr; |
63 | cr_new = cr | (1UL << 55); | 54 | if (MACHINE_HAS_TE) { |
64 | if (task->thread.per_flags & PER_FLAG_NO_TE) | 55 | /* Set or clear transaction execution TXC bit 8. */ |
65 | cr_new &= ~(1UL << 55); | 56 | cr_new |= (1UL << 55); |
57 | if (task->thread.per_flags & PER_FLAG_NO_TE) | ||
58 | cr_new &= ~(1UL << 55); | ||
59 | } | ||
60 | if (MACHINE_HAS_VX) { | ||
61 | /* Enable/disable of vector extension */ | ||
62 | cr_new &= ~(1UL << 17); | ||
63 | if (task->thread.vxrs) | ||
64 | cr_new |= (1UL << 17); | ||
65 | } | ||
66 | if (cr_new != cr) | 66 | if (cr_new != cr) |
67 | __ctl_load(cr_new, 0, 0); | 67 | __ctl_load(cr_new, 0, 0); |
68 | /* Set or clear transaction execution TDC bits 62 and 63. */ | 68 | if (MACHINE_HAS_TE) { |
69 | __ctl_store(cr, 2, 2); | 69 | /* Set/clear transaction execution TDC bits 62/63. */ |
70 | cr_new = cr & ~3UL; | 70 | __ctl_store(cr, 2, 2); |
71 | if (task->thread.per_flags & PER_FLAG_TE_ABORT_RAND) { | 71 | cr_new = cr & ~3UL; |
72 | if (task->thread.per_flags & PER_FLAG_TE_ABORT_RAND_TEND) | 72 | if (task->thread.per_flags & PER_FLAG_TE_ABORT_RAND) { |
73 | cr_new |= 1UL; | 73 | if (task->thread.per_flags & |
74 | else | 74 | PER_FLAG_TE_ABORT_RAND_TEND) |
75 | cr_new |= 2UL; | 75 | cr_new |= 1UL; |
76 | else | ||
77 | cr_new |= 2UL; | ||
78 | } | ||
79 | if (cr_new != cr) | ||
80 | __ctl_load(cr_new, 2, 2); | ||
76 | } | 81 | } |
77 | if (cr_new != cr) | ||
78 | __ctl_load(cr_new, 2, 2); | ||
79 | } | 82 | } |
80 | #endif | 83 | #endif |
81 | /* Copy user specified PER registers */ | 84 | /* Copy user specified PER registers */ |
@@ -926,7 +929,15 @@ static int s390_fpregs_get(struct task_struct *target, | |||
926 | save_fp_ctl(&target->thread.fp_regs.fpc); | 929 | save_fp_ctl(&target->thread.fp_regs.fpc); |
927 | save_fp_regs(target->thread.fp_regs.fprs); | 930 | save_fp_regs(target->thread.fp_regs.fprs); |
928 | } | 931 | } |
932 | #ifdef CONFIG_64BIT | ||
933 | else if (target->thread.vxrs) { | ||
934 | int i; | ||
929 | 935 | ||
936 | for (i = 0; i < __NUM_VXRS_LOW; i++) | ||
937 | target->thread.fp_regs.fprs[i] = | ||
938 | *(freg_t *)(target->thread.vxrs + i); | ||
939 | } | ||
940 | #endif | ||
930 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, | 941 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
931 | &target->thread.fp_regs, 0, -1); | 942 | &target->thread.fp_regs, 0, -1); |
932 | } | 943 | } |
@@ -960,9 +971,20 @@ static int s390_fpregs_set(struct task_struct *target, | |||
960 | target->thread.fp_regs.fprs, | 971 | target->thread.fp_regs.fprs, |
961 | offsetof(s390_fp_regs, fprs), -1); | 972 | offsetof(s390_fp_regs, fprs), -1); |
962 | 973 | ||
963 | if (rc == 0 && target == current) { | 974 | if (rc == 0) { |
964 | restore_fp_ctl(&target->thread.fp_regs.fpc); | 975 | if (target == current) { |
965 | restore_fp_regs(target->thread.fp_regs.fprs); | 976 | restore_fp_ctl(&target->thread.fp_regs.fpc); |
977 | restore_fp_regs(target->thread.fp_regs.fprs); | ||
978 | } | ||
979 | #ifdef CONFIG_64BIT | ||
980 | else if (target->thread.vxrs) { | ||
981 | int i; | ||
982 | |||
983 | for (i = 0; i < __NUM_VXRS_LOW; i++) | ||
984 | *(freg_t *)(target->thread.vxrs + i) = | ||
985 | target->thread.fp_regs.fprs[i]; | ||
986 | } | ||
987 | #endif | ||
966 | } | 988 | } |
967 | 989 | ||
968 | return rc; | 990 | return rc; |
@@ -1018,6 +1040,95 @@ static int s390_tdb_set(struct task_struct *target, | |||
1018 | return 0; | 1040 | return 0; |
1019 | } | 1041 | } |
1020 | 1042 | ||
1043 | static int s390_vxrs_active(struct task_struct *target, | ||
1044 | const struct user_regset *regset) | ||
1045 | { | ||
1046 | return !!target->thread.vxrs; | ||
1047 | } | ||
1048 | |||
1049 | static int s390_vxrs_low_get(struct task_struct *target, | ||
1050 | const struct user_regset *regset, | ||
1051 | unsigned int pos, unsigned int count, | ||
1052 | void *kbuf, void __user *ubuf) | ||
1053 | { | ||
1054 | __u64 vxrs[__NUM_VXRS_LOW]; | ||
1055 | int i; | ||
1056 | |||
1057 | if (target->thread.vxrs) { | ||
1058 | if (target == current) | ||
1059 | save_vx_regs(target->thread.vxrs); | ||
1060 | for (i = 0; i < __NUM_VXRS_LOW; i++) | ||
1061 | vxrs[i] = *((__u64 *)(target->thread.vxrs + i) + 1); | ||
1062 | } else | ||
1063 | memset(vxrs, 0, sizeof(vxrs)); | ||
1064 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, vxrs, 0, -1); | ||
1065 | } | ||
1066 | |||
1067 | static int s390_vxrs_low_set(struct task_struct *target, | ||
1068 | const struct user_regset *regset, | ||
1069 | unsigned int pos, unsigned int count, | ||
1070 | const void *kbuf, const void __user *ubuf) | ||
1071 | { | ||
1072 | __u64 vxrs[__NUM_VXRS_LOW]; | ||
1073 | int i, rc; | ||
1074 | |||
1075 | if (!target->thread.vxrs) { | ||
1076 | rc = alloc_vector_registers(target); | ||
1077 | if (rc) | ||
1078 | return rc; | ||
1079 | } else if (target == current) | ||
1080 | save_vx_regs(target->thread.vxrs); | ||
1081 | |||
1082 | rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, vxrs, 0, -1); | ||
1083 | if (rc == 0) { | ||
1084 | for (i = 0; i < __NUM_VXRS_LOW; i++) | ||
1085 | *((__u64 *)(target->thread.vxrs + i) + 1) = vxrs[i]; | ||
1086 | if (target == current) | ||
1087 | restore_vx_regs(target->thread.vxrs); | ||
1088 | } | ||
1089 | |||
1090 | return rc; | ||
1091 | } | ||
1092 | |||
1093 | static int s390_vxrs_high_get(struct task_struct *target, | ||
1094 | const struct user_regset *regset, | ||
1095 | unsigned int pos, unsigned int count, | ||
1096 | void *kbuf, void __user *ubuf) | ||
1097 | { | ||
1098 | __vector128 vxrs[__NUM_VXRS_HIGH]; | ||
1099 | |||
1100 | if (target->thread.vxrs) { | ||
1101 | if (target == current) | ||
1102 | save_vx_regs(target->thread.vxrs); | ||
1103 | memcpy(vxrs, target->thread.vxrs + __NUM_VXRS_LOW, | ||
1104 | sizeof(vxrs)); | ||
1105 | } else | ||
1106 | memset(vxrs, 0, sizeof(vxrs)); | ||
1107 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, vxrs, 0, -1); | ||
1108 | } | ||
1109 | |||
1110 | static int s390_vxrs_high_set(struct task_struct *target, | ||
1111 | const struct user_regset *regset, | ||
1112 | unsigned int pos, unsigned int count, | ||
1113 | const void *kbuf, const void __user *ubuf) | ||
1114 | { | ||
1115 | int rc; | ||
1116 | |||
1117 | if (!target->thread.vxrs) { | ||
1118 | rc = alloc_vector_registers(target); | ||
1119 | if (rc) | ||
1120 | return rc; | ||
1121 | } else if (target == current) | ||
1122 | save_vx_regs(target->thread.vxrs); | ||
1123 | |||
1124 | rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
1125 | target->thread.vxrs + __NUM_VXRS_LOW, 0, -1); | ||
1126 | if (rc == 0 && target == current) | ||
1127 | restore_vx_regs(target->thread.vxrs); | ||
1128 | |||
1129 | return rc; | ||
1130 | } | ||
1131 | |||
1021 | #endif | 1132 | #endif |
1022 | 1133 | ||
1023 | static int s390_system_call_get(struct task_struct *target, | 1134 | static int s390_system_call_get(struct task_struct *target, |
@@ -1041,7 +1152,7 @@ static int s390_system_call_set(struct task_struct *target, | |||
1041 | } | 1152 | } |
1042 | 1153 | ||
1043 | static const struct user_regset s390_regsets[] = { | 1154 | static const struct user_regset s390_regsets[] = { |
1044 | [REGSET_GENERAL] = { | 1155 | { |
1045 | .core_note_type = NT_PRSTATUS, | 1156 | .core_note_type = NT_PRSTATUS, |
1046 | .n = sizeof(s390_regs) / sizeof(long), | 1157 | .n = sizeof(s390_regs) / sizeof(long), |
1047 | .size = sizeof(long), | 1158 | .size = sizeof(long), |
@@ -1049,7 +1160,7 @@ static const struct user_regset s390_regsets[] = { | |||
1049 | .get = s390_regs_get, | 1160 | .get = s390_regs_get, |
1050 | .set = s390_regs_set, | 1161 | .set = s390_regs_set, |
1051 | }, | 1162 | }, |
1052 | [REGSET_FP] = { | 1163 | { |
1053 | .core_note_type = NT_PRFPREG, | 1164 | .core_note_type = NT_PRFPREG, |
1054 | .n = sizeof(s390_fp_regs) / sizeof(long), | 1165 | .n = sizeof(s390_fp_regs) / sizeof(long), |
1055 | .size = sizeof(long), | 1166 | .size = sizeof(long), |
@@ -1057,8 +1168,16 @@ static const struct user_regset s390_regsets[] = { | |||
1057 | .get = s390_fpregs_get, | 1168 | .get = s390_fpregs_get, |
1058 | .set = s390_fpregs_set, | 1169 | .set = s390_fpregs_set, |
1059 | }, | 1170 | }, |
1171 | { | ||
1172 | .core_note_type = NT_S390_SYSTEM_CALL, | ||
1173 | .n = 1, | ||
1174 | .size = sizeof(unsigned int), | ||
1175 | .align = sizeof(unsigned int), | ||
1176 | .get = s390_system_call_get, | ||
1177 | .set = s390_system_call_set, | ||
1178 | }, | ||
1060 | #ifdef CONFIG_64BIT | 1179 | #ifdef CONFIG_64BIT |
1061 | [REGSET_LAST_BREAK] = { | 1180 | { |
1062 | .core_note_type = NT_S390_LAST_BREAK, | 1181 | .core_note_type = NT_S390_LAST_BREAK, |
1063 | .n = 1, | 1182 | .n = 1, |
1064 | .size = sizeof(long), | 1183 | .size = sizeof(long), |
@@ -1066,7 +1185,7 @@ static const struct user_regset s390_regsets[] = { | |||
1066 | .get = s390_last_break_get, | 1185 | .get = s390_last_break_get, |
1067 | .set = s390_last_break_set, | 1186 | .set = s390_last_break_set, |
1068 | }, | 1187 | }, |
1069 | [REGSET_TDB] = { | 1188 | { |
1070 | .core_note_type = NT_S390_TDB, | 1189 | .core_note_type = NT_S390_TDB, |
1071 | .n = 1, | 1190 | .n = 1, |
1072 | .size = 256, | 1191 | .size = 256, |
@@ -1074,15 +1193,25 @@ static const struct user_regset s390_regsets[] = { | |||
1074 | .get = s390_tdb_get, | 1193 | .get = s390_tdb_get, |
1075 | .set = s390_tdb_set, | 1194 | .set = s390_tdb_set, |
1076 | }, | 1195 | }, |
1077 | #endif | 1196 | { |
1078 | [REGSET_SYSTEM_CALL] = { | 1197 | .core_note_type = NT_S390_VXRS_LOW, |
1079 | .core_note_type = NT_S390_SYSTEM_CALL, | 1198 | .n = __NUM_VXRS_LOW, |
1080 | .n = 1, | 1199 | .size = sizeof(__u64), |
1081 | .size = sizeof(unsigned int), | 1200 | .align = sizeof(__u64), |
1082 | .align = sizeof(unsigned int), | 1201 | .active = s390_vxrs_active, |
1083 | .get = s390_system_call_get, | 1202 | .get = s390_vxrs_low_get, |
1084 | .set = s390_system_call_set, | 1203 | .set = s390_vxrs_low_set, |
1204 | }, | ||
1205 | { | ||
1206 | .core_note_type = NT_S390_VXRS_HIGH, | ||
1207 | .n = __NUM_VXRS_HIGH, | ||
1208 | .size = sizeof(__vector128), | ||
1209 | .align = sizeof(__vector128), | ||
1210 | .active = s390_vxrs_active, | ||
1211 | .get = s390_vxrs_high_get, | ||
1212 | .set = s390_vxrs_high_set, | ||
1085 | }, | 1213 | }, |
1214 | #endif | ||
1086 | }; | 1215 | }; |
1087 | 1216 | ||
1088 | static const struct user_regset_view user_s390_view = { | 1217 | static const struct user_regset_view user_s390_view = { |
@@ -1247,7 +1376,7 @@ static int s390_compat_last_break_set(struct task_struct *target, | |||
1247 | } | 1376 | } |
1248 | 1377 | ||
1249 | static const struct user_regset s390_compat_regsets[] = { | 1378 | static const struct user_regset s390_compat_regsets[] = { |
1250 | [REGSET_GENERAL] = { | 1379 | { |
1251 | .core_note_type = NT_PRSTATUS, | 1380 | .core_note_type = NT_PRSTATUS, |
1252 | .n = sizeof(s390_compat_regs) / sizeof(compat_long_t), | 1381 | .n = sizeof(s390_compat_regs) / sizeof(compat_long_t), |
1253 | .size = sizeof(compat_long_t), | 1382 | .size = sizeof(compat_long_t), |
@@ -1255,7 +1384,7 @@ static const struct user_regset s390_compat_regsets[] = { | |||
1255 | .get = s390_compat_regs_get, | 1384 | .get = s390_compat_regs_get, |
1256 | .set = s390_compat_regs_set, | 1385 | .set = s390_compat_regs_set, |
1257 | }, | 1386 | }, |
1258 | [REGSET_FP] = { | 1387 | { |
1259 | .core_note_type = NT_PRFPREG, | 1388 | .core_note_type = NT_PRFPREG, |
1260 | .n = sizeof(s390_fp_regs) / sizeof(compat_long_t), | 1389 | .n = sizeof(s390_fp_regs) / sizeof(compat_long_t), |
1261 | .size = sizeof(compat_long_t), | 1390 | .size = sizeof(compat_long_t), |
@@ -1263,7 +1392,15 @@ static const struct user_regset s390_compat_regsets[] = { | |||
1263 | .get = s390_fpregs_get, | 1392 | .get = s390_fpregs_get, |
1264 | .set = s390_fpregs_set, | 1393 | .set = s390_fpregs_set, |
1265 | }, | 1394 | }, |
1266 | [REGSET_LAST_BREAK] = { | 1395 | { |
1396 | .core_note_type = NT_S390_SYSTEM_CALL, | ||
1397 | .n = 1, | ||
1398 | .size = sizeof(compat_uint_t), | ||
1399 | .align = sizeof(compat_uint_t), | ||
1400 | .get = s390_system_call_get, | ||
1401 | .set = s390_system_call_set, | ||
1402 | }, | ||
1403 | { | ||
1267 | .core_note_type = NT_S390_LAST_BREAK, | 1404 | .core_note_type = NT_S390_LAST_BREAK, |
1268 | .n = 1, | 1405 | .n = 1, |
1269 | .size = sizeof(long), | 1406 | .size = sizeof(long), |
@@ -1271,7 +1408,7 @@ static const struct user_regset s390_compat_regsets[] = { | |||
1271 | .get = s390_compat_last_break_get, | 1408 | .get = s390_compat_last_break_get, |
1272 | .set = s390_compat_last_break_set, | 1409 | .set = s390_compat_last_break_set, |
1273 | }, | 1410 | }, |
1274 | [REGSET_TDB] = { | 1411 | { |
1275 | .core_note_type = NT_S390_TDB, | 1412 | .core_note_type = NT_S390_TDB, |
1276 | .n = 1, | 1413 | .n = 1, |
1277 | .size = 256, | 1414 | .size = 256, |
@@ -1279,15 +1416,25 @@ static const struct user_regset s390_compat_regsets[] = { | |||
1279 | .get = s390_tdb_get, | 1416 | .get = s390_tdb_get, |
1280 | .set = s390_tdb_set, | 1417 | .set = s390_tdb_set, |
1281 | }, | 1418 | }, |
1282 | [REGSET_SYSTEM_CALL] = { | 1419 | { |
1283 | .core_note_type = NT_S390_SYSTEM_CALL, | 1420 | .core_note_type = NT_S390_VXRS_LOW, |
1284 | .n = 1, | 1421 | .n = __NUM_VXRS_LOW, |
1285 | .size = sizeof(compat_uint_t), | 1422 | .size = sizeof(__u64), |
1286 | .align = sizeof(compat_uint_t), | 1423 | .align = sizeof(__u64), |
1287 | .get = s390_system_call_get, | 1424 | .active = s390_vxrs_active, |
1288 | .set = s390_system_call_set, | 1425 | .get = s390_vxrs_low_get, |
1426 | .set = s390_vxrs_low_set, | ||
1427 | }, | ||
1428 | { | ||
1429 | .core_note_type = NT_S390_VXRS_HIGH, | ||
1430 | .n = __NUM_VXRS_HIGH, | ||
1431 | .size = sizeof(__vector128), | ||
1432 | .align = sizeof(__vector128), | ||
1433 | .active = s390_vxrs_active, | ||
1434 | .get = s390_vxrs_high_get, | ||
1435 | .set = s390_vxrs_high_set, | ||
1289 | }, | 1436 | }, |
1290 | [REGSET_GENERAL_EXTENDED] = { | 1437 | { |
1291 | .core_note_type = NT_S390_HIGH_GPRS, | 1438 | .core_note_type = NT_S390_HIGH_GPRS, |
1292 | .n = sizeof(s390_compat_regs_high) / sizeof(compat_long_t), | 1439 | .n = sizeof(s390_compat_regs_high) / sizeof(compat_long_t), |
1293 | .size = sizeof(compat_long_t), | 1440 | .size = sizeof(compat_long_t), |
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index cdfc060dd319..e80d9ff9a56d 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c | |||
@@ -343,6 +343,9 @@ static void __init setup_lowcore(void) | |||
343 | __ctl_set_bit(14, 29); | 343 | __ctl_set_bit(14, 29); |
344 | } | 344 | } |
345 | #else | 345 | #else |
346 | if (MACHINE_HAS_VX) | ||
347 | lc->vector_save_area_addr = | ||
348 | (unsigned long) &lc->vector_save_area; | ||
346 | lc->vdso_per_cpu_data = (unsigned long) &lc->paste[0]; | 349 | lc->vdso_per_cpu_data = (unsigned long) &lc->paste[0]; |
347 | #endif | 350 | #endif |
348 | lc->sync_enter_timer = S390_lowcore.sync_enter_timer; | 351 | lc->sync_enter_timer = S390_lowcore.sync_enter_timer; |
@@ -765,6 +768,12 @@ static void __init setup_hwcaps(void) | |||
765 | */ | 768 | */ |
766 | if (test_facility(50) && test_facility(73)) | 769 | if (test_facility(50) && test_facility(73)) |
767 | elf_hwcap |= HWCAP_S390_TE; | 770 | elf_hwcap |= HWCAP_S390_TE; |
771 | |||
772 | /* | ||
773 | * Vector extension HWCAP_S390_VXRS is bit 11. | ||
774 | */ | ||
775 | if (test_facility(129)) | ||
776 | elf_hwcap |= HWCAP_S390_VXRS; | ||
768 | #endif | 777 | #endif |
769 | 778 | ||
770 | get_cpu_id(&cpu_id); | 779 | get_cpu_id(&cpu_id); |
diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c index 469c4c6d9182..0c1a0ff0a558 100644 --- a/arch/s390/kernel/signal.c +++ b/arch/s390/kernel/signal.c | |||
@@ -31,30 +31,117 @@ | |||
31 | #include <asm/switch_to.h> | 31 | #include <asm/switch_to.h> |
32 | #include "entry.h" | 32 | #include "entry.h" |
33 | 33 | ||
34 | typedef struct | 34 | /* |
35 | * Layout of an old-style signal-frame: | ||
36 | * ----------------------------------------- | ||
37 | * | save area (_SIGNAL_FRAMESIZE) | | ||
38 | * ----------------------------------------- | ||
39 | * | struct sigcontext | | ||
40 | * | oldmask | | ||
41 | * | _sigregs * | | ||
42 | * ----------------------------------------- | ||
43 | * | _sigregs with | | ||
44 | * | _s390_regs_common | | ||
45 | * | _s390_fp_regs | | ||
46 | * ----------------------------------------- | ||
47 | * | int signo | | ||
48 | * ----------------------------------------- | ||
49 | * | _sigregs_ext with | | ||
50 | * | gprs_high 64 byte (opt) | | ||
51 | * | vxrs_low 128 byte (opt) | | ||
52 | * | vxrs_high 256 byte (opt) | | ||
53 | * | reserved 128 byte (opt) | | ||
54 | * ----------------------------------------- | ||
55 | * | __u16 svc_insn | | ||
56 | * ----------------------------------------- | ||
57 | * The svc_insn entry with the sigreturn system call opcode does not | ||
58 | * have a fixed position and moves if gprs_high or vxrs exist. | ||
59 | * Future extensions will be added to _sigregs_ext. | ||
60 | */ | ||
61 | struct sigframe | ||
35 | { | 62 | { |
36 | __u8 callee_used_stack[__SIGNAL_FRAMESIZE]; | 63 | __u8 callee_used_stack[__SIGNAL_FRAMESIZE]; |
37 | struct sigcontext sc; | 64 | struct sigcontext sc; |
38 | _sigregs sregs; | 65 | _sigregs sregs; |
39 | int signo; | 66 | int signo; |
40 | __u8 retcode[S390_SYSCALL_SIZE]; | 67 | _sigregs_ext sregs_ext; |
41 | } sigframe; | 68 | __u16 svc_insn; /* Offset of svc_insn is NOT fixed! */ |
69 | }; | ||
42 | 70 | ||
43 | typedef struct | 71 | /* |
72 | * Layout of an rt signal-frame: | ||
73 | * ----------------------------------------- | ||
74 | * | save area (_SIGNAL_FRAMESIZE) | | ||
75 | * ----------------------------------------- | ||
76 | * | svc __NR_rt_sigreturn 2 byte | | ||
77 | * ----------------------------------------- | ||
78 | * | struct siginfo | | ||
79 | * ----------------------------------------- | ||
80 | * | struct ucontext_extended with | | ||
81 | * | unsigned long uc_flags | | ||
82 | * | struct ucontext *uc_link | | ||
83 | * | stack_t uc_stack | | ||
84 | * | _sigregs uc_mcontext with | | ||
85 | * | _s390_regs_common | | ||
86 | * | _s390_fp_regs | | ||
87 | * | sigset_t uc_sigmask | | ||
88 | * | _sigregs_ext uc_mcontext_ext | | ||
89 | * | gprs_high 64 byte (opt) | | ||
90 | * | vxrs_low 128 byte (opt) | | ||
91 | * | vxrs_high 256 byte (opt)| | ||
92 | * | reserved 128 byte (opt) | | ||
93 | * ----------------------------------------- | ||
94 | * Future extensions will be added to _sigregs_ext. | ||
95 | */ | ||
96 | struct rt_sigframe | ||
44 | { | 97 | { |
45 | __u8 callee_used_stack[__SIGNAL_FRAMESIZE]; | 98 | __u8 callee_used_stack[__SIGNAL_FRAMESIZE]; |
46 | __u8 retcode[S390_SYSCALL_SIZE]; | 99 | __u16 svc_insn; |
47 | struct siginfo info; | 100 | struct siginfo info; |
48 | struct ucontext uc; | 101 | struct ucontext_extended uc; |
49 | } rt_sigframe; | 102 | }; |
103 | |||
104 | /* Store registers needed to create the signal frame */ | ||
105 | static void store_sigregs(void) | ||
106 | { | ||
107 | save_access_regs(current->thread.acrs); | ||
108 | save_fp_ctl(¤t->thread.fp_regs.fpc); | ||
109 | #ifdef CONFIG_64BIT | ||
110 | if (current->thread.vxrs) { | ||
111 | int i; | ||
112 | |||
113 | save_vx_regs(current->thread.vxrs); | ||
114 | for (i = 0; i < __NUM_FPRS; i++) | ||
115 | current->thread.fp_regs.fprs[i] = | ||
116 | *(freg_t *)(current->thread.vxrs + i); | ||
117 | } else | ||
118 | #endif | ||
119 | save_fp_regs(current->thread.fp_regs.fprs); | ||
120 | } | ||
121 | |||
122 | /* Load registers after signal return */ | ||
123 | static void load_sigregs(void) | ||
124 | { | ||
125 | restore_access_regs(current->thread.acrs); | ||
126 | /* restore_fp_ctl is done in restore_sigregs */ | ||
127 | #ifdef CONFIG_64BIT | ||
128 | if (current->thread.vxrs) { | ||
129 | int i; | ||
130 | |||
131 | for (i = 0; i < __NUM_FPRS; i++) | ||
132 | *(freg_t *)(current->thread.vxrs + i) = | ||
133 | current->thread.fp_regs.fprs[i]; | ||
134 | restore_vx_regs(current->thread.vxrs); | ||
135 | } else | ||
136 | #endif | ||
137 | restore_fp_regs(current->thread.fp_regs.fprs); | ||
138 | } | ||
50 | 139 | ||
51 | /* Returns non-zero on fault. */ | 140 | /* Returns non-zero on fault. */ |
52 | static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs) | 141 | static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs) |
53 | { | 142 | { |
54 | _sigregs user_sregs; | 143 | _sigregs user_sregs; |
55 | 144 | ||
56 | save_access_regs(current->thread.acrs); | ||
57 | |||
58 | /* Copy a 'clean' PSW mask to the user to avoid leaking | 145 | /* Copy a 'clean' PSW mask to the user to avoid leaking |
59 | information about whether PER is currently on. */ | 146 | information about whether PER is currently on. */ |
60 | user_sregs.regs.psw.mask = PSW_USER_BITS | | 147 | user_sregs.regs.psw.mask = PSW_USER_BITS | |
@@ -63,12 +150,6 @@ static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs) | |||
63 | memcpy(&user_sregs.regs.gprs, ®s->gprs, sizeof(sregs->regs.gprs)); | 150 | memcpy(&user_sregs.regs.gprs, ®s->gprs, sizeof(sregs->regs.gprs)); |
64 | memcpy(&user_sregs.regs.acrs, current->thread.acrs, | 151 | memcpy(&user_sregs.regs.acrs, current->thread.acrs, |
65 | sizeof(user_sregs.regs.acrs)); | 152 | sizeof(user_sregs.regs.acrs)); |
66 | /* | ||
67 | * We have to store the fp registers to current->thread.fp_regs | ||
68 | * to merge them with the emulated registers. | ||
69 | */ | ||
70 | save_fp_ctl(¤t->thread.fp_regs.fpc); | ||
71 | save_fp_regs(current->thread.fp_regs.fprs); | ||
72 | memcpy(&user_sregs.fpregs, ¤t->thread.fp_regs, | 153 | memcpy(&user_sregs.fpregs, ¤t->thread.fp_regs, |
73 | sizeof(user_sregs.fpregs)); | 154 | sizeof(user_sregs.fpregs)); |
74 | if (__copy_to_user(sregs, &user_sregs, sizeof(_sigregs))) | 155 | if (__copy_to_user(sregs, &user_sregs, sizeof(_sigregs))) |
@@ -107,20 +188,64 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs) | |||
107 | memcpy(®s->gprs, &user_sregs.regs.gprs, sizeof(sregs->regs.gprs)); | 188 | memcpy(®s->gprs, &user_sregs.regs.gprs, sizeof(sregs->regs.gprs)); |
108 | memcpy(¤t->thread.acrs, &user_sregs.regs.acrs, | 189 | memcpy(¤t->thread.acrs, &user_sregs.regs.acrs, |
109 | sizeof(current->thread.acrs)); | 190 | sizeof(current->thread.acrs)); |
110 | restore_access_regs(current->thread.acrs); | ||
111 | 191 | ||
112 | memcpy(¤t->thread.fp_regs, &user_sregs.fpregs, | 192 | memcpy(¤t->thread.fp_regs, &user_sregs.fpregs, |
113 | sizeof(current->thread.fp_regs)); | 193 | sizeof(current->thread.fp_regs)); |
114 | 194 | ||
115 | restore_fp_regs(current->thread.fp_regs.fprs); | ||
116 | clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */ | 195 | clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */ |
117 | return 0; | 196 | return 0; |
118 | } | 197 | } |
119 | 198 | ||
199 | /* Returns non-zero on fault. */ | ||
200 | static int save_sigregs_ext(struct pt_regs *regs, | ||
201 | _sigregs_ext __user *sregs_ext) | ||
202 | { | ||
203 | #ifdef CONFIG_64BIT | ||
204 | __u64 vxrs[__NUM_VXRS_LOW]; | ||
205 | int i; | ||
206 | |||
207 | /* Save vector registers to signal stack */ | ||
208 | if (current->thread.vxrs) { | ||
209 | for (i = 0; i < __NUM_VXRS_LOW; i++) | ||
210 | vxrs[i] = *((__u64 *)(current->thread.vxrs + i) + 1); | ||
211 | if (__copy_to_user(&sregs_ext->vxrs_low, vxrs, | ||
212 | sizeof(sregs_ext->vxrs_low)) || | ||
213 | __copy_to_user(&sregs_ext->vxrs_high, | ||
214 | current->thread.vxrs + __NUM_VXRS_LOW, | ||
215 | sizeof(sregs_ext->vxrs_high))) | ||
216 | return -EFAULT; | ||
217 | } | ||
218 | #endif | ||
219 | return 0; | ||
220 | } | ||
221 | |||
222 | static int restore_sigregs_ext(struct pt_regs *regs, | ||
223 | _sigregs_ext __user *sregs_ext) | ||
224 | { | ||
225 | #ifdef CONFIG_64BIT | ||
226 | __u64 vxrs[__NUM_VXRS_LOW]; | ||
227 | int i; | ||
228 | |||
229 | /* Restore vector registers from signal stack */ | ||
230 | if (current->thread.vxrs) { | ||
231 | if (__copy_from_user(vxrs, &sregs_ext->vxrs_low, | ||
232 | sizeof(sregs_ext->vxrs_low)) || | ||
233 | __copy_from_user(current->thread.vxrs + __NUM_VXRS_LOW, | ||
234 | &sregs_ext->vxrs_high, | ||
235 | sizeof(sregs_ext->vxrs_high))) | ||
236 | return -EFAULT; | ||
237 | for (i = 0; i < __NUM_VXRS_LOW; i++) | ||
238 | *((__u64 *)(current->thread.vxrs + i) + 1) = vxrs[i]; | ||
239 | } | ||
240 | #endif | ||
241 | return 0; | ||
242 | } | ||
243 | |||
120 | SYSCALL_DEFINE0(sigreturn) | 244 | SYSCALL_DEFINE0(sigreturn) |
121 | { | 245 | { |
122 | struct pt_regs *regs = task_pt_regs(current); | 246 | struct pt_regs *regs = task_pt_regs(current); |
123 | sigframe __user *frame = (sigframe __user *)regs->gprs[15]; | 247 | struct sigframe __user *frame = |
248 | (struct sigframe __user *) regs->gprs[15]; | ||
124 | sigset_t set; | 249 | sigset_t set; |
125 | 250 | ||
126 | if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE)) | 251 | if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE)) |
@@ -128,6 +253,9 @@ SYSCALL_DEFINE0(sigreturn) | |||
128 | set_current_blocked(&set); | 253 | set_current_blocked(&set); |
129 | if (restore_sigregs(regs, &frame->sregs)) | 254 | if (restore_sigregs(regs, &frame->sregs)) |
130 | goto badframe; | 255 | goto badframe; |
256 | if (restore_sigregs_ext(regs, &frame->sregs_ext)) | ||
257 | goto badframe; | ||
258 | load_sigregs(); | ||
131 | return regs->gprs[2]; | 259 | return regs->gprs[2]; |
132 | badframe: | 260 | badframe: |
133 | force_sig(SIGSEGV, current); | 261 | force_sig(SIGSEGV, current); |
@@ -137,16 +265,20 @@ badframe: | |||
137 | SYSCALL_DEFINE0(rt_sigreturn) | 265 | SYSCALL_DEFINE0(rt_sigreturn) |
138 | { | 266 | { |
139 | struct pt_regs *regs = task_pt_regs(current); | 267 | struct pt_regs *regs = task_pt_regs(current); |
140 | rt_sigframe __user *frame = (rt_sigframe __user *)regs->gprs[15]; | 268 | struct rt_sigframe __user *frame = |
269 | (struct rt_sigframe __user *)regs->gprs[15]; | ||
141 | sigset_t set; | 270 | sigset_t set; |
142 | 271 | ||
143 | if (__copy_from_user(&set.sig, &frame->uc.uc_sigmask, sizeof(set))) | 272 | if (__copy_from_user(&set.sig, &frame->uc.uc_sigmask, sizeof(set))) |
144 | goto badframe; | 273 | goto badframe; |
145 | set_current_blocked(&set); | 274 | set_current_blocked(&set); |
275 | if (restore_altstack(&frame->uc.uc_stack)) | ||
276 | goto badframe; | ||
146 | if (restore_sigregs(regs, &frame->uc.uc_mcontext)) | 277 | if (restore_sigregs(regs, &frame->uc.uc_mcontext)) |
147 | goto badframe; | 278 | goto badframe; |
148 | if (restore_altstack(&frame->uc.uc_stack)) | 279 | if (restore_sigregs_ext(regs, &frame->uc.uc_mcontext_ext)) |
149 | goto badframe; | 280 | goto badframe; |
281 | load_sigregs(); | ||
150 | return regs->gprs[2]; | 282 | return regs->gprs[2]; |
151 | badframe: | 283 | badframe: |
152 | force_sig(SIGSEGV, current); | 284 | force_sig(SIGSEGV, current); |
@@ -154,11 +286,6 @@ badframe: | |||
154 | } | 286 | } |
155 | 287 | ||
156 | /* | 288 | /* |
157 | * Set up a signal frame. | ||
158 | */ | ||
159 | |||
160 | |||
161 | /* | ||
162 | * Determine which stack to use.. | 289 | * Determine which stack to use.. |
163 | */ | 290 | */ |
164 | static inline void __user * | 291 | static inline void __user * |
@@ -195,39 +322,63 @@ static inline int map_signal(int sig) | |||
195 | static int setup_frame(int sig, struct k_sigaction *ka, | 322 | static int setup_frame(int sig, struct k_sigaction *ka, |
196 | sigset_t *set, struct pt_regs * regs) | 323 | sigset_t *set, struct pt_regs * regs) |
197 | { | 324 | { |
198 | sigframe __user *frame; | 325 | struct sigframe __user *frame; |
199 | 326 | struct sigcontext sc; | |
200 | frame = get_sigframe(ka, regs, sizeof(sigframe)); | 327 | unsigned long restorer; |
328 | size_t frame_size; | ||
201 | 329 | ||
330 | /* | ||
331 | * gprs_high are only present for a 31-bit task running on | ||
332 | * a 64-bit kernel (see compat_signal.c) but the space for | ||
333 | * gprs_high need to be allocated if vector registers are | ||
334 | * included in the signal frame on a 31-bit system. | ||
335 | */ | ||
336 | frame_size = sizeof(*frame) - sizeof(frame->sregs_ext); | ||
337 | if (MACHINE_HAS_VX) | ||
338 | frame_size += sizeof(frame->sregs_ext); | ||
339 | frame = get_sigframe(ka, regs, frame_size); | ||
202 | if (frame == (void __user *) -1UL) | 340 | if (frame == (void __user *) -1UL) |
203 | return -EFAULT; | 341 | return -EFAULT; |
204 | 342 | ||
205 | if (__copy_to_user(&frame->sc.oldmask, &set->sig, _SIGMASK_COPY_SIZE)) | 343 | /* Set up backchain. */ |
344 | if (__put_user(regs->gprs[15], (addr_t __user *) frame)) | ||
206 | return -EFAULT; | 345 | return -EFAULT; |
207 | 346 | ||
347 | /* Create struct sigcontext on the signal stack */ | ||
348 | memcpy(&sc.oldmask, &set->sig, _SIGMASK_COPY_SIZE); | ||
349 | sc.sregs = (_sigregs __user __force *) &frame->sregs; | ||
350 | if (__copy_to_user(&frame->sc, &sc, sizeof(frame->sc))) | ||
351 | return -EFAULT; | ||
352 | |||
353 | /* Store registers needed to create the signal frame */ | ||
354 | store_sigregs(); | ||
355 | |||
356 | /* Create _sigregs on the signal stack */ | ||
208 | if (save_sigregs(regs, &frame->sregs)) | 357 | if (save_sigregs(regs, &frame->sregs)) |
209 | return -EFAULT; | 358 | return -EFAULT; |
210 | if (__put_user(&frame->sregs, &frame->sc.sregs)) | 359 | |
360 | /* Place signal number on stack to allow backtrace from handler. */ | ||
361 | if (__put_user(regs->gprs[2], (int __user *) &frame->signo)) | ||
362 | return -EFAULT; | ||
363 | |||
364 | /* Create _sigregs_ext on the signal stack */ | ||
365 | if (save_sigregs_ext(regs, &frame->sregs_ext)) | ||
211 | return -EFAULT; | 366 | return -EFAULT; |
212 | 367 | ||
213 | /* Set up to return from userspace. If provided, use a stub | 368 | /* Set up to return from userspace. If provided, use a stub |
214 | already in userspace. */ | 369 | already in userspace. */ |
215 | if (ka->sa.sa_flags & SA_RESTORER) { | 370 | if (ka->sa.sa_flags & SA_RESTORER) { |
216 | regs->gprs[14] = (unsigned long) | 371 | restorer = (unsigned long) ka->sa.sa_restorer | PSW_ADDR_AMODE; |
217 | ka->sa.sa_restorer | PSW_ADDR_AMODE; | ||
218 | } else { | 372 | } else { |
219 | regs->gprs[14] = (unsigned long) | 373 | /* Signal frame without vector registers are short ! */ |
220 | frame->retcode | PSW_ADDR_AMODE; | 374 | __u16 __user *svc = (void *) frame + frame_size - 2; |
221 | if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, | 375 | if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, svc)) |
222 | (u16 __user *)(frame->retcode))) | ||
223 | return -EFAULT; | 376 | return -EFAULT; |
377 | restorer = (unsigned long) svc | PSW_ADDR_AMODE; | ||
224 | } | 378 | } |
225 | 379 | ||
226 | /* Set up backchain. */ | ||
227 | if (__put_user(regs->gprs[15], (addr_t __user *) frame)) | ||
228 | return -EFAULT; | ||
229 | |||
230 | /* Set up registers for signal handler */ | 380 | /* Set up registers for signal handler */ |
381 | regs->gprs[14] = restorer; | ||
231 | regs->gprs[15] = (unsigned long) frame; | 382 | regs->gprs[15] = (unsigned long) frame; |
232 | /* Force default amode and default user address space control. */ | 383 | /* Force default amode and default user address space control. */ |
233 | regs->psw.mask = PSW_MASK_EA | PSW_MASK_BA | | 384 | regs->psw.mask = PSW_MASK_EA | PSW_MASK_BA | |
@@ -247,54 +398,69 @@ static int setup_frame(int sig, struct k_sigaction *ka, | |||
247 | regs->gprs[5] = regs->int_parm_long; | 398 | regs->gprs[5] = regs->int_parm_long; |
248 | regs->gprs[6] = task_thread_info(current)->last_break; | 399 | regs->gprs[6] = task_thread_info(current)->last_break; |
249 | } | 400 | } |
250 | |||
251 | /* Place signal number on stack to allow backtrace from handler. */ | ||
252 | if (__put_user(regs->gprs[2], (int __user *) &frame->signo)) | ||
253 | return -EFAULT; | ||
254 | return 0; | 401 | return 0; |
255 | } | 402 | } |
256 | 403 | ||
257 | static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, | 404 | static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, |
258 | struct pt_regs *regs) | 405 | struct pt_regs *regs) |
259 | { | 406 | { |
260 | int err = 0; | 407 | struct rt_sigframe __user *frame; |
261 | rt_sigframe __user *frame; | 408 | unsigned long uc_flags, restorer; |
262 | 409 | size_t frame_size; | |
263 | frame = get_sigframe(&ksig->ka, regs, sizeof(rt_sigframe)); | ||
264 | 410 | ||
411 | frame_size = sizeof(struct rt_sigframe) - sizeof(_sigregs_ext); | ||
412 | /* | ||
413 | * gprs_high are only present for a 31-bit task running on | ||
414 | * a 64-bit kernel (see compat_signal.c) but the space for | ||
415 | * gprs_high need to be allocated if vector registers are | ||
416 | * included in the signal frame on a 31-bit system. | ||
417 | */ | ||
418 | uc_flags = 0; | ||
419 | #ifdef CONFIG_64BIT | ||
420 | if (MACHINE_HAS_VX) { | ||
421 | frame_size += sizeof(_sigregs_ext); | ||
422 | if (current->thread.vxrs) | ||
423 | uc_flags |= UC_VXRS; | ||
424 | } | ||
425 | #endif | ||
426 | frame = get_sigframe(&ksig->ka, regs, frame_size); | ||
265 | if (frame == (void __user *) -1UL) | 427 | if (frame == (void __user *) -1UL) |
266 | return -EFAULT; | 428 | return -EFAULT; |
267 | 429 | ||
268 | if (copy_siginfo_to_user(&frame->info, &ksig->info)) | 430 | /* Set up backchain. */ |
269 | return -EFAULT; | 431 | if (__put_user(regs->gprs[15], (addr_t __user *) frame)) |
270 | |||
271 | /* Create the ucontext. */ | ||
272 | err |= __put_user(0, &frame->uc.uc_flags); | ||
273 | err |= __put_user(NULL, &frame->uc.uc_link); | ||
274 | err |= __save_altstack(&frame->uc.uc_stack, regs->gprs[15]); | ||
275 | err |= save_sigregs(regs, &frame->uc.uc_mcontext); | ||
276 | err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); | ||
277 | if (err) | ||
278 | return -EFAULT; | 432 | return -EFAULT; |
279 | 433 | ||
280 | /* Set up to return from userspace. If provided, use a stub | 434 | /* Set up to return from userspace. If provided, use a stub |
281 | already in userspace. */ | 435 | already in userspace. */ |
282 | if (ksig->ka.sa.sa_flags & SA_RESTORER) { | 436 | if (ksig->ka.sa.sa_flags & SA_RESTORER) { |
283 | regs->gprs[14] = (unsigned long) | 437 | restorer = (unsigned long) |
284 | ksig->ka.sa.sa_restorer | PSW_ADDR_AMODE; | 438 | ksig->ka.sa.sa_restorer | PSW_ADDR_AMODE; |
285 | } else { | 439 | } else { |
286 | regs->gprs[14] = (unsigned long) | 440 | __u16 __user *svc = &frame->svc_insn; |
287 | frame->retcode | PSW_ADDR_AMODE; | 441 | if (__put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, svc)) |
288 | if (__put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, | ||
289 | (u16 __user *)(frame->retcode))) | ||
290 | return -EFAULT; | 442 | return -EFAULT; |
443 | restorer = (unsigned long) svc | PSW_ADDR_AMODE; | ||
291 | } | 444 | } |
292 | 445 | ||
293 | /* Set up backchain. */ | 446 | /* Create siginfo on the signal stack */ |
294 | if (__put_user(regs->gprs[15], (addr_t __user *) frame)) | 447 | if (copy_siginfo_to_user(&frame->info, &ksig->info)) |
448 | return -EFAULT; | ||
449 | |||
450 | /* Store registers needed to create the signal frame */ | ||
451 | store_sigregs(); | ||
452 | |||
453 | /* Create ucontext on the signal stack. */ | ||
454 | if (__put_user(uc_flags, &frame->uc.uc_flags) || | ||
455 | __put_user(NULL, &frame->uc.uc_link) || | ||
456 | __save_altstack(&frame->uc.uc_stack, regs->gprs[15]) || | ||
457 | save_sigregs(regs, &frame->uc.uc_mcontext) || | ||
458 | __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)) || | ||
459 | save_sigregs_ext(regs, &frame->uc.uc_mcontext_ext)) | ||
295 | return -EFAULT; | 460 | return -EFAULT; |
296 | 461 | ||
297 | /* Set up registers for signal handler */ | 462 | /* Set up registers for signal handler */ |
463 | regs->gprs[14] = restorer; | ||
298 | regs->gprs[15] = (unsigned long) frame; | 464 | regs->gprs[15] = (unsigned long) frame; |
299 | /* Force default amode and default user address space control. */ | 465 | /* Force default amode and default user address space control. */ |
300 | regs->psw.mask = PSW_MASK_EA | PSW_MASK_BA | | 466 | regs->psw.mask = PSW_MASK_EA | PSW_MASK_BA | |
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index bba0e2469254..13cae5b152d7 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c | |||
@@ -179,6 +179,9 @@ static int pcpu_alloc_lowcore(struct pcpu *pcpu, int cpu) | |||
179 | goto out; | 179 | goto out; |
180 | } | 180 | } |
181 | #else | 181 | #else |
182 | if (MACHINE_HAS_VX) | ||
183 | lc->vector_save_area_addr = | ||
184 | (unsigned long) &lc->vector_save_area; | ||
182 | if (vdso_alloc_per_cpu(lc)) | 185 | if (vdso_alloc_per_cpu(lc)) |
183 | goto out; | 186 | goto out; |
184 | #endif | 187 | #endif |
diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index e3e06a4fdfce..9ff5ecba26ab 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c | |||
@@ -18,6 +18,8 @@ | |||
18 | #include <linux/ptrace.h> | 18 | #include <linux/ptrace.h> |
19 | #include <linux/sched.h> | 19 | #include <linux/sched.h> |
20 | #include <linux/mm.h> | 20 | #include <linux/mm.h> |
21 | #include <linux/slab.h> | ||
22 | #include <asm/switch_to.h> | ||
21 | #include "entry.h" | 23 | #include "entry.h" |
22 | 24 | ||
23 | int show_unhandled_signals = 1; | 25 | int show_unhandled_signals = 1; |
@@ -303,6 +305,74 @@ DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN, | |||
303 | "specification exception"); | 305 | "specification exception"); |
304 | #endif | 306 | #endif |
305 | 307 | ||
308 | #ifdef CONFIG_64BIT | ||
309 | int alloc_vector_registers(struct task_struct *tsk) | ||
310 | { | ||
311 | __vector128 *vxrs; | ||
312 | int i; | ||
313 | |||
314 | /* Allocate vector register save area. */ | ||
315 | vxrs = kzalloc(sizeof(__vector128) * __NUM_VXRS, | ||
316 | GFP_KERNEL|__GFP_REPEAT); | ||
317 | if (!vxrs) | ||
318 | return -ENOMEM; | ||
319 | preempt_disable(); | ||
320 | if (tsk == current) | ||
321 | save_fp_regs(tsk->thread.fp_regs.fprs); | ||
322 | /* Copy the 16 floating point registers */ | ||
323 | for (i = 0; i < 16; i++) | ||
324 | *(freg_t *) &vxrs[i] = tsk->thread.fp_regs.fprs[i]; | ||
325 | tsk->thread.vxrs = vxrs; | ||
326 | if (tsk == current) { | ||
327 | __ctl_set_bit(0, 17); | ||
328 | restore_vx_regs(vxrs); | ||
329 | } | ||
330 | preempt_enable(); | ||
331 | return 0; | ||
332 | } | ||
333 | |||
334 | void vector_exception(struct pt_regs *regs) | ||
335 | { | ||
336 | int si_code, vic; | ||
337 | |||
338 | if (!MACHINE_HAS_VX) { | ||
339 | do_trap(regs, SIGILL, ILL_ILLOPN, "illegal operation"); | ||
340 | return; | ||
341 | } | ||
342 | |||
343 | /* get vector interrupt code from fpc */ | ||
344 | asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc)); | ||
345 | vic = (current->thread.fp_regs.fpc & 0xf00) >> 8; | ||
346 | switch (vic) { | ||
347 | case 1: /* invalid vector operation */ | ||
348 | si_code = FPE_FLTINV; | ||
349 | break; | ||
350 | case 2: /* division by zero */ | ||
351 | si_code = FPE_FLTDIV; | ||
352 | break; | ||
353 | case 3: /* overflow */ | ||
354 | si_code = FPE_FLTOVF; | ||
355 | break; | ||
356 | case 4: /* underflow */ | ||
357 | si_code = FPE_FLTUND; | ||
358 | break; | ||
359 | case 5: /* inexact */ | ||
360 | si_code = FPE_FLTRES; | ||
361 | break; | ||
362 | default: /* unknown cause */ | ||
363 | si_code = 0; | ||
364 | } | ||
365 | do_trap(regs, SIGFPE, si_code, "vector exception"); | ||
366 | } | ||
367 | |||
368 | static int __init disable_vector_extension(char *str) | ||
369 | { | ||
370 | S390_lowcore.machine_flags &= ~MACHINE_FLAG_VX; | ||
371 | return 1; | ||
372 | } | ||
373 | __setup("novx", disable_vector_extension); | ||
374 | #endif | ||
375 | |||
306 | void data_exception(struct pt_regs *regs) | 376 | void data_exception(struct pt_regs *regs) |
307 | { | 377 | { |
308 | __u16 __user *location; | 378 | __u16 __user *location; |
@@ -368,6 +438,18 @@ void data_exception(struct pt_regs *regs) | |||
368 | } | 438 | } |
369 | } | 439 | } |
370 | #endif | 440 | #endif |
441 | #ifdef CONFIG_64BIT | ||
442 | /* Check for vector register enablement */ | ||
443 | if (MACHINE_HAS_VX && !current->thread.vxrs && | ||
444 | (current->thread.fp_regs.fpc & FPC_DXC_MASK) == 0xfe00) { | ||
445 | alloc_vector_registers(current); | ||
446 | /* Vector data exception is suppressing, rewind psw. */ | ||
447 | regs->psw.addr = __rewind_psw(regs->psw, regs->int_code >> 16); | ||
448 | clear_pt_regs_flag(regs, PIF_PER_TRAP); | ||
449 | return; | ||
450 | } | ||
451 | #endif | ||
452 | |||
371 | if (current->thread.fp_regs.fpc & FPC_DXC_MASK) | 453 | if (current->thread.fp_regs.fpc & FPC_DXC_MASK) |
372 | signal = SIGFPE; | 454 | signal = SIGFPE; |
373 | else | 455 | else |