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