diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2008-07-14 03:58:54 -0400 |
---|---|---|
committer | Heiko Carstens <heiko.carstens@de.ibm.com> | 2008-07-14 04:02:09 -0400 |
commit | 63506c41986c4af9d4fd6f3490e98e335f3dc8f5 (patch) | |
tree | 6d817aedf37a004c12713a0b594387b9dadd59ea /arch/s390 | |
parent | ae437a452ed20f9d13c1f17b0356201d54394efa (diff) |
[S390] Introduce user_regset accessors for s390
Add the user_regset definitions for normal and compat processes, replace
the dump_regs core dump cruft with the generic CORE_DUMP_USER_REGSET and
replace binfmt_elf32.c with the generic compat_binfmt_elf.c implementation.
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Diffstat (limited to 'arch/s390')
-rw-r--r-- | arch/s390/Kconfig | 1 | ||||
-rw-r--r-- | arch/s390/kernel/Makefile | 7 | ||||
-rw-r--r-- | arch/s390/kernel/binfmt_elf32.c | 214 | ||||
-rw-r--r-- | arch/s390/kernel/compat_ptrace.h | 4 | ||||
-rw-r--r-- | arch/s390/kernel/ptrace.c | 363 |
5 files changed, 327 insertions, 262 deletions
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 107e492cb47e..a79820c3ab08 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig | |||
@@ -146,6 +146,7 @@ config MATHEMU | |||
146 | config COMPAT | 146 | config COMPAT |
147 | bool "Kernel support for 31 bit emulation" | 147 | bool "Kernel support for 31 bit emulation" |
148 | depends on 64BIT | 148 | depends on 64BIT |
149 | select COMPAT_BINFMT_ELF | ||
149 | help | 150 | help |
150 | Select this option if you want to enable your system kernel to | 151 | Select this option if you want to enable your system kernel to |
151 | handle system-calls from ELF binaries for 31 bit ESA. This option | 152 | handle system-calls from ELF binaries for 31 bit ESA. This option |
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index 6302f5082588..0fed81d91e03 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile | |||
@@ -7,6 +7,11 @@ | |||
7 | # | 7 | # |
8 | CFLAGS_smp.o := -Wno-nonnull | 8 | CFLAGS_smp.o := -Wno-nonnull |
9 | 9 | ||
10 | # | ||
11 | # Pass UTS_MACHINE for user_regset definition | ||
12 | # | ||
13 | CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"' | ||
14 | |||
10 | obj-y := bitmap.o traps.o time.o process.o base.o early.o \ | 15 | obj-y := bitmap.o traps.o time.o process.o base.o early.o \ |
11 | setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ | 16 | setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ |
12 | s390_ext.o debug.o irq.o ipl.o dis.o diag.o | 17 | s390_ext.o debug.o irq.o ipl.o dis.o diag.o |
@@ -23,7 +28,7 @@ obj-$(CONFIG_AUDIT) += audit.o | |||
23 | compat-obj-$(CONFIG_AUDIT) += compat_audit.o | 28 | compat-obj-$(CONFIG_AUDIT) += compat_audit.o |
24 | obj-$(CONFIG_COMPAT) += compat_linux.o compat_signal.o \ | 29 | obj-$(CONFIG_COMPAT) += compat_linux.o compat_signal.o \ |
25 | compat_wrapper.o compat_exec_domain.o \ | 30 | compat_wrapper.o compat_exec_domain.o \ |
26 | binfmt_elf32.o $(compat-obj-y) | 31 | $(compat-obj-y) |
27 | 32 | ||
28 | obj-$(CONFIG_VIRT_TIMER) += vtime.o | 33 | obj-$(CONFIG_VIRT_TIMER) += vtime.o |
29 | obj-$(CONFIG_STACKTRACE) += stacktrace.o | 34 | obj-$(CONFIG_STACKTRACE) += stacktrace.o |
diff --git a/arch/s390/kernel/binfmt_elf32.c b/arch/s390/kernel/binfmt_elf32.c deleted file mode 100644 index 3e1c315b736d..000000000000 --- a/arch/s390/kernel/binfmt_elf32.c +++ /dev/null | |||
@@ -1,214 +0,0 @@ | |||
1 | /* | ||
2 | * Support for 32-bit Linux for S390 ELF binaries. | ||
3 | * | ||
4 | * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation | ||
5 | * Author(s): Gerhard Tonn (ton@de.ibm.com) | ||
6 | * | ||
7 | * Heavily inspired by the 32-bit Sparc compat code which is | ||
8 | * Copyright (C) 1995, 1996, 1997, 1998 David S. Miller (davem@redhat.com) | ||
9 | * Copyright (C) 1995, 1996, 1997, 1998 Jakub Jelinek (jj@ultra.linux.cz) | ||
10 | */ | ||
11 | |||
12 | #define __ASMS390_ELF_H | ||
13 | |||
14 | #include <linux/time.h> | ||
15 | |||
16 | /* | ||
17 | * These are used to set parameters in the core dumps. | ||
18 | */ | ||
19 | #define ELF_CLASS ELFCLASS32 | ||
20 | #define ELF_DATA ELFDATA2MSB | ||
21 | #define ELF_ARCH EM_S390 | ||
22 | |||
23 | /* | ||
24 | * This is used to ensure we don't load something for the wrong architecture. | ||
25 | */ | ||
26 | #define elf_check_arch(x) \ | ||
27 | (((x)->e_machine == EM_S390 || (x)->e_machine == EM_S390_OLD) \ | ||
28 | && (x)->e_ident[EI_CLASS] == ELF_CLASS) | ||
29 | |||
30 | /* ELF register definitions */ | ||
31 | #define NUM_GPRS 16 | ||
32 | #define NUM_FPRS 16 | ||
33 | #define NUM_ACRS 16 | ||
34 | |||
35 | /* For SVR4/S390 the function pointer to be registered with `atexit` is | ||
36 | passed in R14. */ | ||
37 | #define ELF_PLAT_INIT(_r, load_addr) \ | ||
38 | do { \ | ||
39 | _r->gprs[14] = 0; \ | ||
40 | } while(0) | ||
41 | |||
42 | #define USE_ELF_CORE_DUMP | ||
43 | #define ELF_EXEC_PAGESIZE 4096 | ||
44 | |||
45 | /* This is the location that an ET_DYN program is loaded if exec'ed. Typical | ||
46 | use of this is to invoke "./ld.so someprog" to test out a new version of | ||
47 | the loader. We need to make sure that it is out of the way of the program | ||
48 | that it will "exec", and that there is sufficient room for the brk. */ | ||
49 | |||
50 | #define ELF_ET_DYN_BASE (TASK_SIZE / 3 * 2) | ||
51 | |||
52 | /* Wow, the "main" arch needs arch dependent functions too.. :) */ | ||
53 | |||
54 | /* regs is struct pt_regs, pr_reg is elf_gregset_t (which is | ||
55 | now struct_user_regs, they are different) */ | ||
56 | |||
57 | #define ELF_CORE_COPY_REGS(pr_reg, regs) dump_regs32(regs, &pr_reg); | ||
58 | |||
59 | #define ELF_CORE_COPY_TASK_REGS(tsk, regs) dump_task_regs32(tsk, regs) | ||
60 | |||
61 | #define ELF_CORE_COPY_FPREGS(tsk, fpregs) dump_task_fpu(tsk, fpregs) | ||
62 | |||
63 | /* This yields a mask that user programs can use to figure out what | ||
64 | instruction set this CPU supports. */ | ||
65 | |||
66 | #define ELF_HWCAP (0) | ||
67 | |||
68 | /* This yields a string that ld.so will use to load implementation | ||
69 | specific libraries for optimization. This is more specific in | ||
70 | intent than poking at uname or /proc/cpuinfo. | ||
71 | |||
72 | For the moment, we have only optimizations for the Intel generations, | ||
73 | but that could change... */ | ||
74 | |||
75 | #define ELF_PLATFORM (NULL) | ||
76 | |||
77 | #define SET_PERSONALITY(ex, ibcs2) \ | ||
78 | do { \ | ||
79 | if (ibcs2) \ | ||
80 | set_personality(PER_SVR4); \ | ||
81 | else if (current->personality != PER_LINUX32) \ | ||
82 | set_personality(PER_LINUX); \ | ||
83 | set_thread_flag(TIF_31BIT); \ | ||
84 | } while (0) | ||
85 | |||
86 | #include "compat_linux.h" | ||
87 | |||
88 | typedef _s390_fp_regs32 elf_fpregset_t; | ||
89 | |||
90 | typedef struct | ||
91 | { | ||
92 | |||
93 | _psw_t32 psw; | ||
94 | __u32 gprs[__NUM_GPRS]; | ||
95 | __u32 acrs[__NUM_ACRS]; | ||
96 | __u32 orig_gpr2; | ||
97 | } s390_regs32; | ||
98 | typedef s390_regs32 elf_gregset_t; | ||
99 | |||
100 | static inline int dump_regs32(struct pt_regs *ptregs, elf_gregset_t *regs) | ||
101 | { | ||
102 | int i; | ||
103 | |||
104 | memcpy(®s->psw.mask, &ptregs->psw.mask, 4); | ||
105 | memcpy(®s->psw.addr, (char *)&ptregs->psw.addr + 4, 4); | ||
106 | for (i = 0; i < NUM_GPRS; i++) | ||
107 | regs->gprs[i] = ptregs->gprs[i]; | ||
108 | save_access_regs(regs->acrs); | ||
109 | regs->orig_gpr2 = ptregs->orig_gpr2; | ||
110 | return 1; | ||
111 | } | ||
112 | |||
113 | static inline int dump_task_regs32(struct task_struct *tsk, elf_gregset_t *regs) | ||
114 | { | ||
115 | struct pt_regs *ptregs = task_pt_regs(tsk); | ||
116 | int i; | ||
117 | |||
118 | memcpy(®s->psw.mask, &ptregs->psw.mask, 4); | ||
119 | memcpy(®s->psw.addr, (char *)&ptregs->psw.addr + 4, 4); | ||
120 | for (i = 0; i < NUM_GPRS; i++) | ||
121 | regs->gprs[i] = ptregs->gprs[i]; | ||
122 | memcpy(regs->acrs, tsk->thread.acrs, sizeof(regs->acrs)); | ||
123 | regs->orig_gpr2 = ptregs->orig_gpr2; | ||
124 | return 1; | ||
125 | } | ||
126 | |||
127 | static inline int dump_task_fpu(struct task_struct *tsk, elf_fpregset_t *fpregs) | ||
128 | { | ||
129 | if (tsk == current) | ||
130 | save_fp_regs((s390_fp_regs *) fpregs); | ||
131 | else | ||
132 | memcpy(fpregs, &tsk->thread.fp_regs, sizeof(elf_fpregset_t)); | ||
133 | return 1; | ||
134 | } | ||
135 | |||
136 | #include <asm/processor.h> | ||
137 | #include <asm/pgalloc.h> | ||
138 | #include <linux/module.h> | ||
139 | #include <linux/elfcore.h> | ||
140 | #include <linux/binfmts.h> | ||
141 | #include <linux/compat.h> | ||
142 | |||
143 | #define elf_prstatus elf_prstatus32 | ||
144 | struct elf_prstatus32 | ||
145 | { | ||
146 | struct elf_siginfo pr_info; /* Info associated with signal */ | ||
147 | short pr_cursig; /* Current signal */ | ||
148 | u32 pr_sigpend; /* Set of pending signals */ | ||
149 | u32 pr_sighold; /* Set of held signals */ | ||
150 | pid_t pr_pid; | ||
151 | pid_t pr_ppid; | ||
152 | pid_t pr_pgrp; | ||
153 | pid_t pr_sid; | ||
154 | struct compat_timeval pr_utime; /* User time */ | ||
155 | struct compat_timeval pr_stime; /* System time */ | ||
156 | struct compat_timeval pr_cutime; /* Cumulative user time */ | ||
157 | struct compat_timeval pr_cstime; /* Cumulative system time */ | ||
158 | elf_gregset_t pr_reg; /* GP registers */ | ||
159 | int pr_fpvalid; /* True if math co-processor being used. */ | ||
160 | }; | ||
161 | |||
162 | #define elf_prpsinfo elf_prpsinfo32 | ||
163 | struct elf_prpsinfo32 | ||
164 | { | ||
165 | char pr_state; /* numeric process state */ | ||
166 | char pr_sname; /* char for pr_state */ | ||
167 | char pr_zomb; /* zombie */ | ||
168 | char pr_nice; /* nice val */ | ||
169 | u32 pr_flag; /* flags */ | ||
170 | u16 pr_uid; | ||
171 | u16 pr_gid; | ||
172 | pid_t pr_pid, pr_ppid, pr_pgrp, pr_sid; | ||
173 | /* Lots missing */ | ||
174 | char pr_fname[16]; /* filename of executable */ | ||
175 | char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ | ||
176 | }; | ||
177 | |||
178 | #include <linux/highuid.h> | ||
179 | |||
180 | /* | ||
181 | #define init_elf_binfmt init_elf32_binfmt | ||
182 | */ | ||
183 | |||
184 | #undef start_thread | ||
185 | #define start_thread start_thread31 | ||
186 | |||
187 | static inline void start_thread31(struct pt_regs *regs, unsigned long new_psw, | ||
188 | unsigned long new_stackp) | ||
189 | { | ||
190 | set_fs(USER_DS); | ||
191 | regs->psw.mask = psw_user32_bits; | ||
192 | regs->psw.addr = new_psw; | ||
193 | regs->gprs[15] = new_stackp; | ||
194 | crst_table_downgrade(current->mm, 1UL << 31); | ||
195 | } | ||
196 | |||
197 | MODULE_DESCRIPTION("Binary format loader for compatibility with 32bit Linux for S390 binaries," | ||
198 | " Copyright 2000 IBM Corporation"); | ||
199 | MODULE_AUTHOR("Gerhard Tonn <ton@de.ibm.com>"); | ||
200 | |||
201 | #undef MODULE_DESCRIPTION | ||
202 | #undef MODULE_AUTHOR | ||
203 | |||
204 | #undef cputime_to_timeval | ||
205 | #define cputime_to_timeval cputime_to_compat_timeval | ||
206 | static inline void | ||
207 | cputime_to_compat_timeval(const cputime_t cputime, struct compat_timeval *value) | ||
208 | { | ||
209 | value->tv_usec = cputime % 1000000; | ||
210 | value->tv_sec = cputime / 1000000; | ||
211 | } | ||
212 | |||
213 | #include "../../../fs/binfmt_elf.c" | ||
214 | |||
diff --git a/arch/s390/kernel/compat_ptrace.h b/arch/s390/kernel/compat_ptrace.h index 419aef913ee1..cde81fa64f89 100644 --- a/arch/s390/kernel/compat_ptrace.h +++ b/arch/s390/kernel/compat_ptrace.h | |||
@@ -1,7 +1,7 @@ | |||
1 | #ifndef _PTRACE32_H | 1 | #ifndef _PTRACE32_H |
2 | #define _PTRACE32_H | 2 | #define _PTRACE32_H |
3 | 3 | ||
4 | #include "compat_linux.h" /* needed for _psw_t32 */ | 4 | #include "compat_linux.h" /* needed for psw_compat_t */ |
5 | 5 | ||
6 | typedef struct { | 6 | typedef struct { |
7 | __u32 cr[3]; | 7 | __u32 cr[3]; |
@@ -38,7 +38,7 @@ typedef struct { | |||
38 | 38 | ||
39 | struct user_regs_struct32 | 39 | struct user_regs_struct32 |
40 | { | 40 | { |
41 | _psw_t32 psw; | 41 | psw_compat_t psw; |
42 | u32 gprs[NUM_GPRS]; | 42 | u32 gprs[NUM_GPRS]; |
43 | u32 acrs[NUM_ACRS]; | 43 | u32 acrs[NUM_ACRS]; |
44 | u32 orig_gpr2; | 44 | u32 orig_gpr2; |
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index 35827b9bd4d1..2815bfe348a6 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c | |||
@@ -33,6 +33,8 @@ | |||
33 | #include <linux/security.h> | 33 | #include <linux/security.h> |
34 | #include <linux/audit.h> | 34 | #include <linux/audit.h> |
35 | #include <linux/signal.h> | 35 | #include <linux/signal.h> |
36 | #include <linux/elf.h> | ||
37 | #include <linux/regset.h> | ||
36 | 38 | ||
37 | #include <asm/segment.h> | 39 | #include <asm/segment.h> |
38 | #include <asm/page.h> | 40 | #include <asm/page.h> |
@@ -47,6 +49,11 @@ | |||
47 | #include "compat_ptrace.h" | 49 | #include "compat_ptrace.h" |
48 | #endif | 50 | #endif |
49 | 51 | ||
52 | enum s390_regset { | ||
53 | REGSET_GENERAL, | ||
54 | REGSET_FP, | ||
55 | }; | ||
56 | |||
50 | static void | 57 | static void |
51 | FixPerRegisters(struct task_struct *task) | 58 | FixPerRegisters(struct task_struct *task) |
52 | { | 59 | { |
@@ -126,24 +133,10 @@ ptrace_disable(struct task_struct *child) | |||
126 | * struct user contain pad bytes that should be read as zeroes. | 133 | * struct user contain pad bytes that should be read as zeroes. |
127 | * Lovely... | 134 | * Lovely... |
128 | */ | 135 | */ |
129 | static int | 136 | static unsigned long __peek_user(struct task_struct *child, addr_t addr) |
130 | peek_user(struct task_struct *child, addr_t addr, addr_t data) | ||
131 | { | 137 | { |
132 | struct user *dummy = NULL; | 138 | struct user *dummy = NULL; |
133 | addr_t offset, tmp, mask; | 139 | addr_t offset, tmp; |
134 | |||
135 | /* | ||
136 | * Stupid gdb peeks/pokes the access registers in 64 bit with | ||
137 | * an alignment of 4. Programmers from hell... | ||
138 | */ | ||
139 | mask = __ADDR_MASK; | ||
140 | #ifdef CONFIG_64BIT | ||
141 | if (addr >= (addr_t) &dummy->regs.acrs && | ||
142 | addr < (addr_t) &dummy->regs.orig_gpr2) | ||
143 | mask = 3; | ||
144 | #endif | ||
145 | if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK) | ||
146 | return -EIO; | ||
147 | 140 | ||
148 | if (addr < (addr_t) &dummy->regs.acrs) { | 141 | if (addr < (addr_t) &dummy->regs.acrs) { |
149 | /* | 142 | /* |
@@ -197,24 +190,18 @@ peek_user(struct task_struct *child, addr_t addr, addr_t data) | |||
197 | } else | 190 | } else |
198 | tmp = 0; | 191 | tmp = 0; |
199 | 192 | ||
200 | return put_user(tmp, (addr_t __user *) data); | 193 | return tmp; |
201 | } | 194 | } |
202 | 195 | ||
203 | /* | ||
204 | * Write a word to the user area of a process at location addr. This | ||
205 | * operation does have an additional problem compared to peek_user. | ||
206 | * Stores to the program status word and on the floating point | ||
207 | * control register needs to get checked for validity. | ||
208 | */ | ||
209 | static int | 196 | static int |
210 | poke_user(struct task_struct *child, addr_t addr, addr_t data) | 197 | peek_user(struct task_struct *child, addr_t addr, addr_t data) |
211 | { | 198 | { |
212 | struct user *dummy = NULL; | 199 | struct user *dummy = NULL; |
213 | addr_t offset, mask; | 200 | addr_t tmp, mask; |
214 | 201 | ||
215 | /* | 202 | /* |
216 | * Stupid gdb peeks/pokes the access registers in 64 bit with | 203 | * Stupid gdb peeks/pokes the access registers in 64 bit with |
217 | * an alignment of 4. Programmers from hell indeed... | 204 | * an alignment of 4. Programmers from hell... |
218 | */ | 205 | */ |
219 | mask = __ADDR_MASK; | 206 | mask = __ADDR_MASK; |
220 | #ifdef CONFIG_64BIT | 207 | #ifdef CONFIG_64BIT |
@@ -225,6 +212,21 @@ poke_user(struct task_struct *child, addr_t addr, addr_t data) | |||
225 | if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK) | 212 | if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK) |
226 | return -EIO; | 213 | return -EIO; |
227 | 214 | ||
215 | tmp = __peek_user(child, addr); | ||
216 | return put_user(tmp, (addr_t __user *) data); | ||
217 | } | ||
218 | |||
219 | /* | ||
220 | * Write a word to the user area of a process at location addr. This | ||
221 | * operation does have an additional problem compared to peek_user. | ||
222 | * Stores to the program status word and on the floating point | ||
223 | * control register needs to get checked for validity. | ||
224 | */ | ||
225 | static int __poke_user(struct task_struct *child, addr_t addr, addr_t data) | ||
226 | { | ||
227 | struct user *dummy = NULL; | ||
228 | addr_t offset; | ||
229 | |||
228 | if (addr < (addr_t) &dummy->regs.acrs) { | 230 | if (addr < (addr_t) &dummy->regs.acrs) { |
229 | /* | 231 | /* |
230 | * psw and gprs are stored on the stack | 232 | * psw and gprs are stored on the stack |
@@ -292,6 +294,28 @@ poke_user(struct task_struct *child, addr_t addr, addr_t data) | |||
292 | return 0; | 294 | return 0; |
293 | } | 295 | } |
294 | 296 | ||
297 | static int | ||
298 | poke_user(struct task_struct *child, addr_t addr, addr_t data) | ||
299 | { | ||
300 | struct user *dummy = NULL; | ||
301 | addr_t mask; | ||
302 | |||
303 | /* | ||
304 | * Stupid gdb peeks/pokes the access registers in 64 bit with | ||
305 | * an alignment of 4. Programmers from hell indeed... | ||
306 | */ | ||
307 | mask = __ADDR_MASK; | ||
308 | #ifdef CONFIG_64BIT | ||
309 | if (addr >= (addr_t) &dummy->regs.acrs && | ||
310 | addr < (addr_t) &dummy->regs.orig_gpr2) | ||
311 | mask = 3; | ||
312 | #endif | ||
313 | if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK) | ||
314 | return -EIO; | ||
315 | |||
316 | return __poke_user(child, addr, data); | ||
317 | } | ||
318 | |||
295 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) | 319 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) |
296 | { | 320 | { |
297 | ptrace_area parea; | 321 | ptrace_area parea; |
@@ -367,18 +391,13 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
367 | /* | 391 | /* |
368 | * Same as peek_user but for a 31 bit program. | 392 | * Same as peek_user but for a 31 bit program. |
369 | */ | 393 | */ |
370 | static int | 394 | static u32 __peek_user_compat(struct task_struct *child, addr_t addr) |
371 | peek_user_emu31(struct task_struct *child, addr_t addr, addr_t data) | ||
372 | { | 395 | { |
373 | struct user32 *dummy32 = NULL; | 396 | struct user32 *dummy32 = NULL; |
374 | per_struct32 *dummy_per32 = NULL; | 397 | per_struct32 *dummy_per32 = NULL; |
375 | addr_t offset; | 398 | addr_t offset; |
376 | __u32 tmp; | 399 | __u32 tmp; |
377 | 400 | ||
378 | if (!test_thread_flag(TIF_31BIT) || | ||
379 | (addr & 3) || addr > sizeof(struct user) - 3) | ||
380 | return -EIO; | ||
381 | |||
382 | if (addr < (addr_t) &dummy32->regs.acrs) { | 401 | if (addr < (addr_t) &dummy32->regs.acrs) { |
383 | /* | 402 | /* |
384 | * psw and gprs are stored on the stack | 403 | * psw and gprs are stored on the stack |
@@ -435,25 +454,32 @@ peek_user_emu31(struct task_struct *child, addr_t addr, addr_t data) | |||
435 | } else | 454 | } else |
436 | tmp = 0; | 455 | tmp = 0; |
437 | 456 | ||
457 | return tmp; | ||
458 | } | ||
459 | |||
460 | static int peek_user_compat(struct task_struct *child, | ||
461 | addr_t addr, addr_t data) | ||
462 | { | ||
463 | __u32 tmp; | ||
464 | |||
465 | if (!test_thread_flag(TIF_31BIT) || | ||
466 | (addr & 3) || addr > sizeof(struct user) - 3) | ||
467 | return -EIO; | ||
468 | |||
469 | tmp = __peek_user_compat(child, addr); | ||
438 | return put_user(tmp, (__u32 __user *) data); | 470 | return put_user(tmp, (__u32 __user *) data); |
439 | } | 471 | } |
440 | 472 | ||
441 | /* | 473 | /* |
442 | * Same as poke_user but for a 31 bit program. | 474 | * Same as poke_user but for a 31 bit program. |
443 | */ | 475 | */ |
444 | static int | 476 | static int __poke_user_compat(struct task_struct *child, |
445 | poke_user_emu31(struct task_struct *child, addr_t addr, addr_t data) | 477 | addr_t addr, addr_t data) |
446 | { | 478 | { |
447 | struct user32 *dummy32 = NULL; | 479 | struct user32 *dummy32 = NULL; |
448 | per_struct32 *dummy_per32 = NULL; | 480 | per_struct32 *dummy_per32 = NULL; |
481 | __u32 tmp = (__u32) data; | ||
449 | addr_t offset; | 482 | addr_t offset; |
450 | __u32 tmp; | ||
451 | |||
452 | if (!test_thread_flag(TIF_31BIT) || | ||
453 | (addr & 3) || addr > sizeof(struct user32) - 3) | ||
454 | return -EIO; | ||
455 | |||
456 | tmp = (__u32) data; | ||
457 | 483 | ||
458 | if (addr < (addr_t) &dummy32->regs.acrs) { | 484 | if (addr < (addr_t) &dummy32->regs.acrs) { |
459 | /* | 485 | /* |
@@ -528,6 +554,16 @@ poke_user_emu31(struct task_struct *child, addr_t addr, addr_t data) | |||
528 | return 0; | 554 | return 0; |
529 | } | 555 | } |
530 | 556 | ||
557 | static int poke_user_compat(struct task_struct *child, | ||
558 | addr_t addr, addr_t data) | ||
559 | { | ||
560 | if (!test_thread_flag(TIF_31BIT) || | ||
561 | (addr & 3) || addr > sizeof(struct user32) - 3) | ||
562 | return -EIO; | ||
563 | |||
564 | return __poke_user_compat(child, addr, data); | ||
565 | } | ||
566 | |||
531 | long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | 567 | long compat_arch_ptrace(struct task_struct *child, compat_long_t request, |
532 | compat_ulong_t caddr, compat_ulong_t cdata) | 568 | compat_ulong_t caddr, compat_ulong_t cdata) |
533 | { | 569 | { |
@@ -539,11 +575,11 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | |||
539 | switch (request) { | 575 | switch (request) { |
540 | case PTRACE_PEEKUSR: | 576 | case PTRACE_PEEKUSR: |
541 | /* read the word at location addr in the USER area. */ | 577 | /* read the word at location addr in the USER area. */ |
542 | return peek_user_emu31(child, addr, data); | 578 | return peek_user_compat(child, addr, data); |
543 | 579 | ||
544 | case PTRACE_POKEUSR: | 580 | case PTRACE_POKEUSR: |
545 | /* write the word at location addr in the USER area */ | 581 | /* write the word at location addr in the USER area */ |
546 | return poke_user_emu31(child, addr, data); | 582 | return poke_user_compat(child, addr, data); |
547 | 583 | ||
548 | case PTRACE_PEEKUSR_AREA: | 584 | case PTRACE_PEEKUSR_AREA: |
549 | case PTRACE_POKEUSR_AREA: | 585 | case PTRACE_POKEUSR_AREA: |
@@ -555,13 +591,13 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | |||
555 | copied = 0; | 591 | copied = 0; |
556 | while (copied < parea.len) { | 592 | while (copied < parea.len) { |
557 | if (request == PTRACE_PEEKUSR_AREA) | 593 | if (request == PTRACE_PEEKUSR_AREA) |
558 | ret = peek_user_emu31(child, addr, data); | 594 | ret = peek_user_compat(child, addr, data); |
559 | else { | 595 | else { |
560 | __u32 utmp; | 596 | __u32 utmp; |
561 | if (get_user(utmp, | 597 | if (get_user(utmp, |
562 | (__u32 __force __user *) data)) | 598 | (__u32 __force __user *) data)) |
563 | return -EFAULT; | 599 | return -EFAULT; |
564 | ret = poke_user_emu31(child, addr, utmp); | 600 | ret = poke_user_compat(child, addr, utmp); |
565 | } | 601 | } |
566 | if (ret) | 602 | if (ret) |
567 | return ret; | 603 | return ret; |
@@ -610,3 +646,240 @@ syscall_trace(struct pt_regs *regs, int entryexit) | |||
610 | regs->gprs[2], regs->orig_gpr2, regs->gprs[3], | 646 | regs->gprs[2], regs->orig_gpr2, regs->gprs[3], |
611 | regs->gprs[4], regs->gprs[5]); | 647 | regs->gprs[4], regs->gprs[5]); |
612 | } | 648 | } |
649 | |||
650 | /* | ||
651 | * user_regset definitions. | ||
652 | */ | ||
653 | |||
654 | static int s390_regs_get(struct task_struct *target, | ||
655 | const struct user_regset *regset, | ||
656 | unsigned int pos, unsigned int count, | ||
657 | void *kbuf, void __user *ubuf) | ||
658 | { | ||
659 | if (target == current) | ||
660 | save_access_regs(target->thread.acrs); | ||
661 | |||
662 | if (kbuf) { | ||
663 | unsigned long *k = kbuf; | ||
664 | while (count > 0) { | ||
665 | *k++ = __peek_user(target, pos); | ||
666 | count -= sizeof(*k); | ||
667 | pos += sizeof(*k); | ||
668 | } | ||
669 | } else { | ||
670 | unsigned long __user *u = ubuf; | ||
671 | while (count > 0) { | ||
672 | if (__put_user(__peek_user(target, pos), u++)) | ||
673 | return -EFAULT; | ||
674 | count -= sizeof(*u); | ||
675 | pos += sizeof(*u); | ||
676 | } | ||
677 | } | ||
678 | return 0; | ||
679 | } | ||
680 | |||
681 | static int s390_regs_set(struct task_struct *target, | ||
682 | const struct user_regset *regset, | ||
683 | unsigned int pos, unsigned int count, | ||
684 | const void *kbuf, const void __user *ubuf) | ||
685 | { | ||
686 | int rc = 0; | ||
687 | |||
688 | if (target == current) | ||
689 | save_access_regs(target->thread.acrs); | ||
690 | |||
691 | if (kbuf) { | ||
692 | const unsigned long *k = kbuf; | ||
693 | while (count > 0 && !rc) { | ||
694 | rc = __poke_user(target, pos, *k++); | ||
695 | count -= sizeof(*k); | ||
696 | pos += sizeof(*k); | ||
697 | } | ||
698 | } else { | ||
699 | const unsigned long __user *u = ubuf; | ||
700 | while (count > 0 && !rc) { | ||
701 | unsigned long word; | ||
702 | rc = __get_user(word, u++); | ||
703 | if (rc) | ||
704 | break; | ||
705 | rc = __poke_user(target, pos, word); | ||
706 | count -= sizeof(*u); | ||
707 | pos += sizeof(*u); | ||
708 | } | ||
709 | } | ||
710 | |||
711 | if (rc == 0 && target == current) | ||
712 | restore_access_regs(target->thread.acrs); | ||
713 | |||
714 | return rc; | ||
715 | } | ||
716 | |||
717 | static int s390_fpregs_get(struct task_struct *target, | ||
718 | const struct user_regset *regset, unsigned int pos, | ||
719 | unsigned int count, void *kbuf, void __user *ubuf) | ||
720 | { | ||
721 | if (target == current) | ||
722 | save_fp_regs(&target->thread.fp_regs); | ||
723 | |||
724 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
725 | &target->thread.fp_regs, 0, -1); | ||
726 | } | ||
727 | |||
728 | static int s390_fpregs_set(struct task_struct *target, | ||
729 | const struct user_regset *regset, unsigned int pos, | ||
730 | unsigned int count, const void *kbuf, | ||
731 | const void __user *ubuf) | ||
732 | { | ||
733 | int rc = 0; | ||
734 | |||
735 | if (target == current) | ||
736 | save_fp_regs(&target->thread.fp_regs); | ||
737 | |||
738 | /* If setting FPC, must validate it first. */ | ||
739 | if (count > 0 && pos < offsetof(s390_fp_regs, fprs)) { | ||
740 | u32 fpc[2] = { target->thread.fp_regs.fpc, 0 }; | ||
741 | rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &fpc, | ||
742 | 0, offsetof(s390_fp_regs, fprs)); | ||
743 | if (rc) | ||
744 | return rc; | ||
745 | if ((fpc[0] & ~FPC_VALID_MASK) != 0 || fpc[1] != 0) | ||
746 | return -EINVAL; | ||
747 | target->thread.fp_regs.fpc = fpc[0]; | ||
748 | } | ||
749 | |||
750 | if (rc == 0 && count > 0) | ||
751 | rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
752 | target->thread.fp_regs.fprs, | ||
753 | offsetof(s390_fp_regs, fprs), -1); | ||
754 | |||
755 | if (rc == 0 && target == current) | ||
756 | restore_fp_regs(&target->thread.fp_regs); | ||
757 | |||
758 | return rc; | ||
759 | } | ||
760 | |||
761 | static const struct user_regset s390_regsets[] = { | ||
762 | [REGSET_GENERAL] = { | ||
763 | .core_note_type = NT_PRSTATUS, | ||
764 | .n = sizeof(s390_regs) / sizeof(long), | ||
765 | .size = sizeof(long), | ||
766 | .align = sizeof(long), | ||
767 | .get = s390_regs_get, | ||
768 | .set = s390_regs_set, | ||
769 | }, | ||
770 | [REGSET_FP] = { | ||
771 | .core_note_type = NT_PRFPREG, | ||
772 | .n = sizeof(s390_fp_regs) / sizeof(long), | ||
773 | .size = sizeof(long), | ||
774 | .align = sizeof(long), | ||
775 | .get = s390_fpregs_get, | ||
776 | .set = s390_fpregs_set, | ||
777 | }, | ||
778 | }; | ||
779 | |||
780 | static const struct user_regset_view user_s390_view = { | ||
781 | .name = UTS_MACHINE, | ||
782 | .e_machine = EM_S390, | ||
783 | .regsets = s390_regsets, | ||
784 | .n = ARRAY_SIZE(s390_regsets) | ||
785 | }; | ||
786 | |||
787 | #ifdef CONFIG_COMPAT | ||
788 | static int s390_compat_regs_get(struct task_struct *target, | ||
789 | const struct user_regset *regset, | ||
790 | unsigned int pos, unsigned int count, | ||
791 | void *kbuf, void __user *ubuf) | ||
792 | { | ||
793 | if (target == current) | ||
794 | save_access_regs(target->thread.acrs); | ||
795 | |||
796 | if (kbuf) { | ||
797 | compat_ulong_t *k = kbuf; | ||
798 | while (count > 0) { | ||
799 | *k++ = __peek_user_compat(target, pos); | ||
800 | count -= sizeof(*k); | ||
801 | pos += sizeof(*k); | ||
802 | } | ||
803 | } else { | ||
804 | compat_ulong_t __user *u = ubuf; | ||
805 | while (count > 0) { | ||
806 | if (__put_user(__peek_user_compat(target, pos), u++)) | ||
807 | return -EFAULT; | ||
808 | count -= sizeof(*u); | ||
809 | pos += sizeof(*u); | ||
810 | } | ||
811 | } | ||
812 | return 0; | ||
813 | } | ||
814 | |||
815 | static int s390_compat_regs_set(struct task_struct *target, | ||
816 | const struct user_regset *regset, | ||
817 | unsigned int pos, unsigned int count, | ||
818 | const void *kbuf, const void __user *ubuf) | ||
819 | { | ||
820 | int rc = 0; | ||
821 | |||
822 | if (target == current) | ||
823 | save_access_regs(target->thread.acrs); | ||
824 | |||
825 | if (kbuf) { | ||
826 | const compat_ulong_t *k = kbuf; | ||
827 | while (count > 0 && !rc) { | ||
828 | rc = __poke_user_compat(target, pos, *k++); | ||
829 | count -= sizeof(*k); | ||
830 | pos += sizeof(*k); | ||
831 | } | ||
832 | } else { | ||
833 | const compat_ulong_t __user *u = ubuf; | ||
834 | while (count > 0 && !rc) { | ||
835 | compat_ulong_t word; | ||
836 | rc = __get_user(word, u++); | ||
837 | if (rc) | ||
838 | break; | ||
839 | rc = __poke_user_compat(target, pos, word); | ||
840 | count -= sizeof(*u); | ||
841 | pos += sizeof(*u); | ||
842 | } | ||
843 | } | ||
844 | |||
845 | if (rc == 0 && target == current) | ||
846 | restore_access_regs(target->thread.acrs); | ||
847 | |||
848 | return rc; | ||
849 | } | ||
850 | |||
851 | static const struct user_regset s390_compat_regsets[] = { | ||
852 | [REGSET_GENERAL] = { | ||
853 | .core_note_type = NT_PRSTATUS, | ||
854 | .n = sizeof(s390_compat_regs) / sizeof(compat_long_t), | ||
855 | .size = sizeof(compat_long_t), | ||
856 | .align = sizeof(compat_long_t), | ||
857 | .get = s390_compat_regs_get, | ||
858 | .set = s390_compat_regs_set, | ||
859 | }, | ||
860 | [REGSET_FP] = { | ||
861 | .core_note_type = NT_PRFPREG, | ||
862 | .n = sizeof(s390_fp_regs) / sizeof(compat_long_t), | ||
863 | .size = sizeof(compat_long_t), | ||
864 | .align = sizeof(compat_long_t), | ||
865 | .get = s390_fpregs_get, | ||
866 | .set = s390_fpregs_set, | ||
867 | }, | ||
868 | }; | ||
869 | |||
870 | static const struct user_regset_view user_s390_compat_view = { | ||
871 | .name = "s390", | ||
872 | .e_machine = EM_S390, | ||
873 | .regsets = s390_compat_regsets, | ||
874 | .n = ARRAY_SIZE(s390_compat_regsets) | ||
875 | }; | ||
876 | #endif | ||
877 | |||
878 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | ||
879 | { | ||
880 | #ifdef CONFIG_COMPAT | ||
881 | if (test_tsk_thread_flag(task, TIF_31BIT)) | ||
882 | return &user_s390_compat_view; | ||
883 | #endif | ||
884 | return &user_s390_view; | ||
885 | } | ||