linux_signal.c revision 50477
1/*-
2 * Copyright (c) 1994-1995 S�ren Schmidt
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer
10 *    in this position and unchanged.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software withough specific prior written permission
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * $FreeBSD: head/sys/compat/linux/linux_signal.c 50477 1999-08-28 01:08:13Z peter $
29 */
30
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <sys/sysproto.h>
34#include <sys/proc.h>
35#include <sys/signalvar.h>
36
37#include <i386/linux/linux.h>
38#include <i386/linux/linux_proto.h>
39#include <i386/linux/linux_util.h>
40
41static sigset_t
42linux_to_bsd_sigset(linux_sigset_t mask) {
43    int b, l;
44    sigset_t new = 0;
45
46    for (l = 1; l < LINUX_NSIG; l++) {
47	if (mask & (1 << (l - 1))) {
48	    if ((b = linux_to_bsd_signal[l]))
49		new |= (1 << (b - 1));
50	}
51    }
52    return new;
53}
54
55static linux_sigset_t
56bsd_to_linux_sigset(sigset_t mask) {
57    int b, l;
58    sigset_t new = 0;
59
60    for (b = 1; b < NSIG; b++) {
61	if (mask & (1 << (b - 1))) {
62	    if ((l = bsd_to_linux_signal[b]))
63		new |= (1 << (l - 1));
64	}
65    }
66    return new;
67}
68
69static void
70linux_to_bsd_sigaction(linux_sigaction_t *lsa, struct sigaction *bsa)
71{
72    bsa->sa_mask = linux_to_bsd_sigset(lsa->lsa_mask);
73    bsa->sa_handler = lsa->lsa_handler;
74    bsa->sa_flags = 0;
75    if (lsa->lsa_flags & LINUX_SA_NOCLDSTOP)
76	bsa->sa_flags |= SA_NOCLDSTOP;
77    if (lsa->lsa_flags & LINUX_SA_NOCLDWAIT)
78	bsa->sa_flags |= SA_NOCLDWAIT;
79    if (lsa->lsa_flags & LINUX_SA_SIGINFO)
80	bsa->sa_flags |= SA_SIGINFO;
81    if (lsa->lsa_flags & LINUX_SA_ONSTACK)
82	bsa->sa_flags |= SA_ONSTACK;
83    if (lsa->lsa_flags & LINUX_SA_RESTART)
84	bsa->sa_flags |= SA_RESTART;
85    if (lsa->lsa_flags & LINUX_SA_ONESHOT)
86	bsa->sa_flags |= SA_RESETHAND;
87    if (lsa->lsa_flags & LINUX_SA_NOMASK)
88	bsa->sa_flags |= SA_NODEFER;
89}
90
91static void
92bsd_to_linux_sigaction(struct sigaction *bsa, linux_sigaction_t *lsa)
93{
94    lsa->lsa_handler = bsa->sa_handler;
95    lsa->lsa_restorer = NULL;	/* unsupported */
96    lsa->lsa_mask = bsd_to_linux_sigset(bsa->sa_mask);
97    lsa->lsa_flags = 0;
98    if (bsa->sa_flags & SA_NOCLDSTOP)
99	lsa->lsa_flags |= LINUX_SA_NOCLDSTOP;
100    if (bsa->sa_flags & SA_NOCLDWAIT)
101	lsa->lsa_flags |= LINUX_SA_NOCLDWAIT;
102    if (bsa->sa_flags & SA_SIGINFO)
103	lsa->lsa_flags |= LINUX_SA_SIGINFO;
104    if (bsa->sa_flags & SA_ONSTACK)
105	lsa->lsa_flags |= LINUX_SA_ONSTACK;
106    if (bsa->sa_flags & SA_RESTART)
107	lsa->lsa_flags |= LINUX_SA_RESTART;
108    if (bsa->sa_flags & SA_RESETHAND)
109	lsa->lsa_flags |= LINUX_SA_ONESHOT;
110    if (bsa->sa_flags & SA_NODEFER)
111	lsa->lsa_flags |= LINUX_SA_NOMASK;
112}
113
114static int
115linux_do_sigaction(struct proc *p, int linux_sig, linux_sigaction_t *linux_nsa,
116		   linux_sigaction_t *linux_osa)
117{
118    struct sigaction *nsa, *osa, sa;
119    struct sigaction_args sa_args;
120    int error;
121    caddr_t sg = stackgap_init();
122
123    if (linux_sig <= 0 || linux_sig >= LINUX_NSIG)
124	return EINVAL;
125
126    if (linux_osa)
127	osa = stackgap_alloc(&sg, sizeof(struct sigaction));
128    else
129	osa = NULL;
130
131    if (linux_nsa) {
132	nsa = stackgap_alloc(&sg, sizeof(struct sigaction));
133	linux_to_bsd_sigaction(linux_nsa, &sa);
134	error = copyout(&sa, nsa, sizeof(struct sigaction));
135	if (error)
136	    return error;
137    }
138    else
139	nsa = NULL;
140
141    sa_args.signum = linux_to_bsd_signal[linux_sig];
142    sa_args.nsa = nsa;
143    sa_args.osa = osa;
144    error = sigaction(p, &sa_args);
145    if (error)
146	return error;
147
148    if (linux_osa) {
149	error = copyin(osa, &sa, sizeof(struct sigaction));
150	if (error)
151	    return error;
152	bsd_to_linux_sigaction(&sa, linux_osa);
153    }
154
155    return 0;
156}
157
158int
159linux_sigaction(struct proc *p, struct linux_sigaction_args *args)
160{
161    linux_sigaction_t nsa, osa;
162    int error;
163
164#ifdef DEBUG
165    printf("Linux-emul(%ld): sigaction(%d, %p, %p)\n",
166	   (long)p->p_pid, args->sig, (void *)args->nsa, (void *)args->osa);
167#endif
168
169    if (args->nsa) {
170	error = copyin(args->nsa, &nsa, sizeof(linux_sigaction_t));
171	if (error)
172	    return error;
173    }
174
175    error = linux_do_sigaction(p, args->sig,
176			       args->nsa ? &nsa : NULL,
177			       args->osa ? &osa : NULL);
178    if (error)
179	return error;
180
181    if (args->osa) {
182	error = copyout(&osa, args->osa, sizeof(linux_sigaction_t));
183	if (error)
184	    return error;
185    }
186
187    return 0;
188}
189
190int
191linux_signal(struct proc *p, struct linux_signal_args *args)
192{
193    linux_sigaction_t nsa, osa;
194    int error;
195
196#ifdef DEBUG
197    printf("Linux-emul(%ld): signal(%d, %p)\n",
198	   (long)p->p_pid, args->sig, (void *)args->handler);
199#endif
200
201    nsa.lsa_handler = args->handler;
202    nsa.lsa_flags = LINUX_SA_ONESHOT | LINUX_SA_NOMASK;
203    nsa.lsa_mask = NULL;
204
205    error = linux_do_sigaction(p, args->sig, &nsa, &osa);
206
207    p->p_retval[0] = (int)osa.lsa_handler;
208
209    return 0;
210}
211
212int
213linux_rt_sigaction(struct proc *p, struct linux_rt_sigaction_args *args)
214{
215    linux_sigaction_t nsa, osa;
216    linux_new_sigaction_t new_sa;
217    int error;
218
219#ifdef DEBUG
220    printf("Linux-emul(%ld): rt_sigaction(%d, %p, %p, %d)\n",
221	   (long)p->p_pid, args->sig, (void *)args->act,
222	   (void *)args->oact, args->sigsetsize);
223#endif
224
225    if (args->sigsetsize != sizeof(linux_new_sigset_t))
226	return EINVAL;
227
228#ifdef DEBUG
229    if (args->sig >= LINUX_NSIG) {
230	printf("LINUX(%ld): rt_sigaction: 64-bit signal (%d)\n",
231	       (long)p->p_pid, args->sig);
232    }
233#endif
234
235    if (args->act) {
236	error = copyin(args->act, &new_sa, sizeof(linux_new_sigaction_t));
237	if (error)
238	    return error;
239
240	nsa.lsa_handler = new_sa.lsa_handler;
241	nsa.lsa_mask = new_sa.lsa_mask.sig[0];
242	nsa.lsa_flags = new_sa.lsa_flags;
243	nsa.lsa_restorer = new_sa.lsa_restorer;
244
245#ifdef DEBUG
246	if (new_sa.lsa_mask.sig[1] != 0)
247	    printf("LINUX(%ld): rt_sigaction: sig[1] = 0x%08lx\n",
248		   (long)p->p_pid, new_sa.lsa_mask.sig[1]);
249#endif
250    }
251
252    error = linux_do_sigaction(p, args->sig,
253			       args->act ? &nsa : NULL,
254			       args->oact ? &osa : NULL);
255    if (error)
256	return error;
257
258    if (args->oact) {
259	new_sa.lsa_handler = osa.lsa_handler;
260	new_sa.lsa_flags = osa.lsa_flags;
261	new_sa.lsa_restorer = osa.lsa_restorer;
262	new_sa.lsa_mask.sig[0] = osa.lsa_mask;
263	new_sa.lsa_mask.sig[1] = 0;
264	error = copyout(&osa, args->oact, sizeof(linux_new_sigaction_t));
265	if (error)
266	    return error;
267    }
268
269    return 0;
270}
271
272static int
273linux_do_sigprocmask(struct proc *p, int how, linux_sigset_t *new,
274		     linux_sigset_t *old)
275{
276    int error = 0, s;
277    sigset_t mask;
278
279    p->p_retval[0] = 0;
280
281    if (old != NULL)
282	*old = bsd_to_linux_sigset(p->p_sigmask);
283
284    if (new != NULL) {
285	mask = linux_to_bsd_sigset(*new);
286
287	s = splhigh();
288
289	switch (how) {
290	case LINUX_SIG_BLOCK:
291	    p->p_sigmask |= (mask & ~sigcantmask);
292	    break;
293	case LINUX_SIG_UNBLOCK:
294	    p->p_sigmask &= ~mask;
295	    break;
296	case LINUX_SIG_SETMASK:
297	    p->p_sigmask = (mask & ~sigcantmask);
298	    break;
299	default:
300	    error = EINVAL;
301	    break;
302	}
303
304	splx(s);
305    }
306
307    return error;
308}
309
310int
311linux_sigprocmask(struct proc *p, struct linux_sigprocmask_args *args)
312{
313    linux_sigset_t mask;
314    linux_sigset_t omask;
315    int error;
316
317#ifdef DEBUG
318    printf("Linux-emul(%d): sigprocmask(%d, *, *)\n", p->p_pid, args->how);
319#endif
320
321    if (args->mask != NULL) {
322	error = copyin(args->mask, &mask, sizeof(linux_sigset_t));
323	if (error)
324	    return error;
325    }
326
327    error = linux_do_sigprocmask(p, args->how,
328				 args->mask ? &mask : NULL,
329				 args->omask ? &omask : NULL);
330
331    if (!error && args->omask != NULL) {
332	error = copyout(&omask, args->omask, sizeof(linux_sigset_t));
333    }
334
335    return error;
336}
337
338int
339linux_rt_sigprocmask(struct proc *p, struct linux_rt_sigprocmask_args *args)
340{
341    linux_new_sigset_t new_mask;
342    linux_sigset_t old_mask;
343    int error;
344
345#ifdef DEBUG
346    printf("Linux-emul(%ld): rt_sigprocmask(%d, %p, %p, %d)\n",
347	   (long)p->p_pid, args->how, (void *)args->mask,
348	   (void *)args->omask, args->sigsetsize);
349#endif
350
351    if (args->sigsetsize != sizeof(linux_new_sigset_t))
352	return EINVAL;
353
354    if (args->mask != NULL) {
355	error = copyin(args->mask, &new_mask, sizeof(linux_new_sigset_t));
356	if (error)
357	    return error;
358
359#ifdef DEBUG
360	if (new_mask.sig[1] != 0)
361	    printf("LINUX(%ld): rt_sigprocmask: sig[1] = 0x%08lx\n",
362		   (long)p->p_pid, new_mask.sig[1]);
363#endif
364    }
365
366    error = linux_do_sigprocmask(p, args->how,
367				 args->mask ? new_mask.sig : NULL,
368				 args->omask ? &old_mask : NULL);
369
370    if (!error && args->omask != NULL) {
371	new_mask.sig[0] = old_mask;
372	error = copyout(&new_mask, args->omask, sizeof(linux_new_sigset_t));
373    }
374
375    return error;
376}
377
378int
379linux_siggetmask(struct proc *p, struct linux_siggetmask_args *args)
380{
381#ifdef DEBUG
382    printf("Linux-emul(%d): siggetmask()\n", p->p_pid);
383#endif
384    p->p_retval[0] = bsd_to_linux_sigset(p->p_sigmask);
385    return 0;
386}
387
388int
389linux_sigsetmask(struct proc *p, struct linux_sigsetmask_args *args)
390{
391    int s;
392    sigset_t mask;
393
394#ifdef DEBUG
395    printf("Linux-emul(%ld): sigsetmask(%08lx)\n",
396	(long)p->p_pid, (unsigned long)args->mask);
397#endif
398    p->p_retval[0] = bsd_to_linux_sigset(p->p_sigmask);
399
400    mask = linux_to_bsd_sigset(args->mask);
401    s = splhigh();
402    p->p_sigmask = mask & ~sigcantmask;
403    splx(s);
404    return 0;
405}
406
407int
408linux_sigpending(struct proc *p, struct linux_sigpending_args *args)
409{
410    linux_sigset_t linux_sig;
411
412#ifdef DEBUG
413    printf("Linux-emul(%d): sigpending(*)\n", p->p_pid);
414#endif
415    linux_sig = bsd_to_linux_sigset(p->p_siglist & p->p_sigmask);
416    return copyout(&linux_sig, args->mask, sizeof(linux_sig));
417}
418
419/*
420 * Linux has two extra args, restart and oldmask.  We dont use these,
421 * but it seems that "restart" is actually a context pointer that
422 * enables the signal to happen with a different register set.
423 */
424int
425linux_sigsuspend(struct proc *p, struct linux_sigsuspend_args *args)
426{
427    struct sigsuspend_args tmp;
428
429#ifdef DEBUG
430    printf("Linux-emul(%ld): sigsuspend(%08lx)\n",
431	(long)p->p_pid, (unsigned long)args->mask);
432#endif
433    tmp.mask = linux_to_bsd_sigset(args->mask);
434    return sigsuspend(p, &tmp);
435}
436
437int
438linux_pause(struct proc *p, struct linux_pause_args *args)
439{
440    struct sigsuspend_args tmp;
441
442#ifdef DEBUG
443    printf("Linux-emul(%d): pause()\n", p->p_pid);
444#endif
445    tmp.mask = p->p_sigmask;
446    return sigsuspend(p, &tmp);
447}
448
449int
450linux_kill(struct proc *p, struct linux_kill_args *args)
451{
452    struct kill_args /* {
453	int pid;
454	int signum;
455    } */ tmp;
456
457#ifdef DEBUG
458    printf("Linux-emul(%d): kill(%d, %d)\n",
459	   p->p_pid, args->pid, args->signum);
460#endif
461    if (args->signum < 0 || args->signum >= LINUX_NSIG)
462	return EINVAL;
463    tmp.pid = args->pid;
464    tmp.signum = linux_to_bsd_signal[args->signum];
465    return kill(p, &tmp);
466}
467