]>
Commit | Line | Data |
---|---|---|
befb7447 LV |
1 | /* |
2 | * Emulation of Linux signals | |
3 | * | |
4 | * Copyright (c) 2003 Fabrice Bellard | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
4c4c73e3 LV |
19 | #include "qemu/osdep.h" |
20 | #include "qemu.h" | |
21 | #include "target_signal.h" | |
22 | #include "signal-common.h" | |
23 | #include "linux-user/trace.h" | |
24 | ||
25 | #define __NUM_GPRS 16 | |
26 | #define __NUM_FPRS 16 | |
27 | #define __NUM_ACRS 16 | |
28 | ||
29 | #define S390_SYSCALL_SIZE 2 | |
30 | #define __SIGNAL_FRAMESIZE 160 /* FIXME: 31-bit mode -> 96 */ | |
31 | ||
32 | #define _SIGCONTEXT_NSIG 64 | |
33 | #define _SIGCONTEXT_NSIG_BPW 64 /* FIXME: 31-bit mode -> 32 */ | |
34 | #define _SIGCONTEXT_NSIG_WORDS (_SIGCONTEXT_NSIG / _SIGCONTEXT_NSIG_BPW) | |
35 | #define _SIGMASK_COPY_SIZE (sizeof(unsigned long)*_SIGCONTEXT_NSIG_WORDS) | |
36 | #define PSW_ADDR_AMODE 0x0000000000000000UL /* 0x80000000UL for 31-bit */ | |
37 | #define S390_SYSCALL_OPCODE ((uint16_t)0x0a00) | |
38 | ||
39 | typedef struct { | |
40 | target_psw_t psw; | |
41 | target_ulong gprs[__NUM_GPRS]; | |
42 | unsigned int acrs[__NUM_ACRS]; | |
43 | } target_s390_regs_common; | |
44 | ||
45 | typedef struct { | |
46 | unsigned int fpc; | |
47 | double fprs[__NUM_FPRS]; | |
48 | } target_s390_fp_regs; | |
49 | ||
50 | typedef struct { | |
51 | target_s390_regs_common regs; | |
52 | target_s390_fp_regs fpregs; | |
53 | } target_sigregs; | |
54 | ||
55 | struct target_sigcontext { | |
56 | target_ulong oldmask[_SIGCONTEXT_NSIG_WORDS]; | |
57 | target_sigregs *sregs; | |
58 | }; | |
59 | ||
60 | typedef struct { | |
61 | uint8_t callee_used_stack[__SIGNAL_FRAMESIZE]; | |
62 | struct target_sigcontext sc; | |
63 | target_sigregs sregs; | |
64 | int signo; | |
65 | uint8_t retcode[S390_SYSCALL_SIZE]; | |
66 | } sigframe; | |
67 | ||
68 | struct target_ucontext { | |
69 | target_ulong tuc_flags; | |
70 | struct target_ucontext *tuc_link; | |
71 | target_stack_t tuc_stack; | |
72 | target_sigregs tuc_mcontext; | |
73 | target_sigset_t tuc_sigmask; /* mask last for extensibility */ | |
74 | }; | |
75 | ||
76 | typedef struct { | |
77 | uint8_t callee_used_stack[__SIGNAL_FRAMESIZE]; | |
78 | uint8_t retcode[S390_SYSCALL_SIZE]; | |
79 | struct target_siginfo info; | |
80 | struct target_ucontext uc; | |
81 | } rt_sigframe; | |
82 | ||
83 | static inline abi_ulong | |
84 | get_sigframe(struct target_sigaction *ka, CPUS390XState *env, size_t frame_size) | |
85 | { | |
86 | abi_ulong sp; | |
87 | ||
88 | /* Default to using normal stack */ | |
89 | sp = env->regs[15]; | |
90 | ||
91 | /* This is the X/Open sanctioned signal stack switching. */ | |
92 | if (ka->sa_flags & TARGET_SA_ONSTACK) { | |
93 | if (!sas_ss_flags(sp)) { | |
94 | sp = target_sigaltstack_used.ss_sp + | |
95 | target_sigaltstack_used.ss_size; | |
96 | } | |
97 | } | |
98 | ||
99 | /* This is the legacy signal stack switching. */ | |
100 | else if (/* FIXME !user_mode(regs) */ 0 && | |
101 | !(ka->sa_flags & TARGET_SA_RESTORER) && | |
102 | ka->sa_restorer) { | |
103 | sp = (abi_ulong) ka->sa_restorer; | |
104 | } | |
105 | ||
106 | return (sp - frame_size) & -8ul; | |
107 | } | |
108 | ||
109 | static void save_sigregs(CPUS390XState *env, target_sigregs *sregs) | |
110 | { | |
111 | int i; | |
112 | //save_access_regs(current->thread.acrs); FIXME | |
113 | ||
114 | /* Copy a 'clean' PSW mask to the user to avoid leaking | |
115 | information about whether PER is currently on. */ | |
116 | __put_user(env->psw.mask, &sregs->regs.psw.mask); | |
117 | __put_user(env->psw.addr, &sregs->regs.psw.addr); | |
118 | for (i = 0; i < 16; i++) { | |
119 | __put_user(env->regs[i], &sregs->regs.gprs[i]); | |
120 | } | |
121 | for (i = 0; i < 16; i++) { | |
122 | __put_user(env->aregs[i], &sregs->regs.acrs[i]); | |
123 | } | |
124 | /* | |
125 | * We have to store the fp registers to current->thread.fp_regs | |
126 | * to merge them with the emulated registers. | |
127 | */ | |
128 | //save_fp_regs(¤t->thread.fp_regs); FIXME | |
129 | for (i = 0; i < 16; i++) { | |
130 | __put_user(get_freg(env, i)->ll, &sregs->fpregs.fprs[i]); | |
131 | } | |
132 | } | |
133 | ||
134 | void setup_frame(int sig, struct target_sigaction *ka, | |
135 | target_sigset_t *set, CPUS390XState *env) | |
136 | { | |
137 | sigframe *frame; | |
138 | abi_ulong frame_addr; | |
139 | ||
140 | frame_addr = get_sigframe(ka, env, sizeof(*frame)); | |
141 | trace_user_setup_frame(env, frame_addr); | |
142 | if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { | |
143 | goto give_sigsegv; | |
144 | } | |
145 | ||
146 | __put_user(set->sig[0], &frame->sc.oldmask[0]); | |
147 | ||
148 | save_sigregs(env, &frame->sregs); | |
149 | ||
150 | __put_user((abi_ulong)(unsigned long)&frame->sregs, | |
151 | (abi_ulong *)&frame->sc.sregs); | |
152 | ||
153 | /* Set up to return from userspace. If provided, use a stub | |
154 | already in userspace. */ | |
155 | if (ka->sa_flags & TARGET_SA_RESTORER) { | |
156 | env->regs[14] = (unsigned long) | |
157 | ka->sa_restorer | PSW_ADDR_AMODE; | |
158 | } else { | |
159 | env->regs[14] = (frame_addr + offsetof(sigframe, retcode)) | |
160 | | PSW_ADDR_AMODE; | |
161 | __put_user(S390_SYSCALL_OPCODE | TARGET_NR_sigreturn, | |
162 | (uint16_t *)(frame->retcode)); | |
163 | } | |
164 | ||
165 | /* Set up backchain. */ | |
166 | __put_user(env->regs[15], (abi_ulong *) frame); | |
167 | ||
168 | /* Set up registers for signal handler */ | |
169 | env->regs[15] = frame_addr; | |
170 | env->psw.addr = (target_ulong) ka->_sa_handler | PSW_ADDR_AMODE; | |
171 | ||
172 | env->regs[2] = sig; //map_signal(sig); | |
173 | env->regs[3] = frame_addr += offsetof(typeof(*frame), sc); | |
174 | ||
175 | /* We forgot to include these in the sigcontext. | |
176 | To avoid breaking binary compatibility, they are passed as args. */ | |
177 | env->regs[4] = 0; // FIXME: no clue... current->thread.trap_no; | |
178 | env->regs[5] = 0; // FIXME: no clue... current->thread.prot_addr; | |
179 | ||
180 | /* Place signal number on stack to allow backtrace from handler. */ | |
181 | __put_user(env->regs[2], &frame->signo); | |
182 | unlock_user_struct(frame, frame_addr, 1); | |
183 | return; | |
184 | ||
185 | give_sigsegv: | |
186 | force_sigsegv(sig); | |
187 | } | |
188 | ||
189 | void setup_rt_frame(int sig, struct target_sigaction *ka, | |
190 | target_siginfo_t *info, | |
191 | target_sigset_t *set, CPUS390XState *env) | |
192 | { | |
193 | int i; | |
194 | rt_sigframe *frame; | |
195 | abi_ulong frame_addr; | |
196 | ||
197 | frame_addr = get_sigframe(ka, env, sizeof *frame); | |
198 | trace_user_setup_rt_frame(env, frame_addr); | |
199 | if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { | |
200 | goto give_sigsegv; | |
201 | } | |
202 | ||
203 | tswap_siginfo(&frame->info, info); | |
204 | ||
205 | /* Create the ucontext. */ | |
206 | __put_user(0, &frame->uc.tuc_flags); | |
207 | __put_user((abi_ulong)0, (abi_ulong *)&frame->uc.tuc_link); | |
208 | __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); | |
209 | __put_user(sas_ss_flags(get_sp_from_cpustate(env)), | |
210 | &frame->uc.tuc_stack.ss_flags); | |
211 | __put_user(target_sigaltstack_used.ss_size, &frame->uc.tuc_stack.ss_size); | |
212 | save_sigregs(env, &frame->uc.tuc_mcontext); | |
213 | for (i = 0; i < TARGET_NSIG_WORDS; i++) { | |
214 | __put_user((abi_ulong)set->sig[i], | |
215 | (abi_ulong *)&frame->uc.tuc_sigmask.sig[i]); | |
216 | } | |
217 | ||
218 | /* Set up to return from userspace. If provided, use a stub | |
219 | already in userspace. */ | |
220 | if (ka->sa_flags & TARGET_SA_RESTORER) { | |
221 | env->regs[14] = (unsigned long) ka->sa_restorer | PSW_ADDR_AMODE; | |
222 | } else { | |
223 | env->regs[14] = (unsigned long) frame->retcode | PSW_ADDR_AMODE; | |
224 | __put_user(S390_SYSCALL_OPCODE | TARGET_NR_rt_sigreturn, | |
225 | (uint16_t *)(frame->retcode)); | |
226 | } | |
227 | ||
228 | /* Set up backchain. */ | |
229 | __put_user(env->regs[15], (abi_ulong *) frame); | |
230 | ||
231 | /* Set up registers for signal handler */ | |
232 | env->regs[15] = frame_addr; | |
233 | env->psw.addr = (target_ulong) ka->_sa_handler | PSW_ADDR_AMODE; | |
234 | ||
235 | env->regs[2] = sig; //map_signal(sig); | |
236 | env->regs[3] = frame_addr + offsetof(typeof(*frame), info); | |
237 | env->regs[4] = frame_addr + offsetof(typeof(*frame), uc); | |
238 | return; | |
239 | ||
240 | give_sigsegv: | |
241 | force_sigsegv(sig); | |
242 | } | |
243 | ||
244 | static int | |
245 | restore_sigregs(CPUS390XState *env, target_sigregs *sc) | |
246 | { | |
247 | int err = 0; | |
248 | int i; | |
249 | ||
250 | for (i = 0; i < 16; i++) { | |
251 | __get_user(env->regs[i], &sc->regs.gprs[i]); | |
252 | } | |
253 | ||
254 | __get_user(env->psw.mask, &sc->regs.psw.mask); | |
255 | trace_user_s390x_restore_sigregs(env, (unsigned long long)sc->regs.psw.addr, | |
256 | (unsigned long long)env->psw.addr); | |
257 | __get_user(env->psw.addr, &sc->regs.psw.addr); | |
258 | /* FIXME: 31-bit -> | PSW_ADDR_AMODE */ | |
259 | ||
260 | for (i = 0; i < 16; i++) { | |
261 | __get_user(env->aregs[i], &sc->regs.acrs[i]); | |
262 | } | |
263 | for (i = 0; i < 16; i++) { | |
264 | __get_user(get_freg(env, i)->ll, &sc->fpregs.fprs[i]); | |
265 | } | |
266 | ||
267 | return err; | |
268 | } | |
269 | ||
270 | long do_sigreturn(CPUS390XState *env) | |
271 | { | |
272 | sigframe *frame; | |
273 | abi_ulong frame_addr = env->regs[15]; | |
274 | target_sigset_t target_set; | |
275 | sigset_t set; | |
276 | ||
277 | trace_user_do_sigreturn(env, frame_addr); | |
278 | if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { | |
279 | goto badframe; | |
280 | } | |
281 | __get_user(target_set.sig[0], &frame->sc.oldmask[0]); | |
282 | ||
283 | target_to_host_sigset_internal(&set, &target_set); | |
284 | set_sigmask(&set); /* ~_BLOCKABLE? */ | |
285 | ||
286 | if (restore_sigregs(env, &frame->sregs)) { | |
287 | goto badframe; | |
288 | } | |
289 | ||
290 | unlock_user_struct(frame, frame_addr, 0); | |
291 | return -TARGET_QEMU_ESIGRETURN; | |
292 | ||
293 | badframe: | |
294 | force_sig(TARGET_SIGSEGV); | |
295 | return -TARGET_QEMU_ESIGRETURN; | |
296 | } | |
297 | ||
298 | long do_rt_sigreturn(CPUS390XState *env) | |
299 | { | |
300 | rt_sigframe *frame; | |
301 | abi_ulong frame_addr = env->regs[15]; | |
302 | sigset_t set; | |
303 | ||
304 | trace_user_do_rt_sigreturn(env, frame_addr); | |
305 | if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { | |
306 | goto badframe; | |
307 | } | |
308 | target_to_host_sigset(&set, &frame->uc.tuc_sigmask); | |
309 | ||
310 | set_sigmask(&set); /* ~_BLOCKABLE? */ | |
311 | ||
312 | if (restore_sigregs(env, &frame->uc.tuc_mcontext)) { | |
313 | goto badframe; | |
314 | } | |
315 | ||
316 | if (do_sigaltstack(frame_addr + offsetof(rt_sigframe, uc.tuc_stack), 0, | |
317 | get_sp_from_cpustate(env)) == -EFAULT) { | |
318 | goto badframe; | |
319 | } | |
320 | unlock_user_struct(frame, frame_addr, 0); | |
321 | return -TARGET_QEMU_ESIGRETURN; | |
322 | ||
323 | badframe: | |
324 | unlock_user_struct(frame, frame_addr, 0); | |
325 | force_sig(TARGET_SIGSEGV); | |
326 | return -TARGET_QEMU_ESIGRETURN; | |
327 | } |