freebsd-dev/sys/alpha/osf1/osf1_signal.c
David Xu 9104847f21 1. Change prototype of trapsignal and sendsig to use ksiginfo_t *, most
changes in MD code are trivial, before this change, trapsignal and
   sendsig use discrete parameters, now they uses member fields of
   ksiginfo_t structure. For sendsig, this change allows us to pass
   POSIX realtime signal value to user code.

2. Remove cpu_thread_siginfo, it is no longer needed because we now always
   generate ksiginfo_t data and feed it to libpthread.

3. Add p_sigqueue to proc structure to hold shared signals which were
   blocked by all threads in the proc.

4. Add td_sigqueue to thread structure to hold all signals delivered to
   thread.

5. i386 and amd64 now return POSIX standard si_code, other arches will
   be fixed.

6. In this sigqueue implementation, pending signal set is kept as before,
   an extra siginfo list holds additional siginfo_t data for signals.
   kernel code uses psignal() still behavior as before, it won't be failed
   even under memory pressure, only exception is when deleting a signal,
   we should call sigqueue_delete to remove signal from sigqueue but
   not SIGDELSET. Current there is no kernel code will deliver a signal
   with additional data, so kernel should be as stable as before,
   a ksiginfo can carry more information, for example, allow signal to
   be delivered but throw away siginfo data if memory is not enough.
   SIGKILL and SIGSTOP have fast path in sigqueue_add, because they can
   not be caught or masked.
   The sigqueue() syscall allows user code to queue a signal to target
   process, if resource is unavailable, EAGAIN will be returned as
   specification said.
   Just before thread exits, signal queue memory will be freed by
   sigqueue_flush.
   Current, all signals are allowed to be queued, not only realtime signals.

Earlier patch reviewed by: jhb, deischen
Tested on: i386, amd64
2005-10-14 12:43:47 +00:00

638 lines
16 KiB
C

/* $NetBSD: osf1_signal.c,v 1.4 1998/05/20 16:35:01 chs Exp $
*/
/*-
* Copyright (c) 1998-1999 Andrew Gallatin
*
* Taken from NetBSD's sys/compat/osf1/osf1_signal.c, which at the
* time *had no copyright*!
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_compat.h"
#ifndef COMPAT_43
#error "COMPAT_OSF1 requires COMPAT_43"
#endif
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysproto.h>
#include <sys/signalvar.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/reboot.h>
#include <sys/bio.h>
#include <sys/buf.h>
#include <sys/bus.h>
#include <sys/mbuf.h>
#include <sys/vmmeter.h>
#include <sys/msgbuf.h>
#include <sys/exec.h>
#include <sys/syscallsubr.h>
#include <sys/sysctl.h>
#include <sys/uio.h>
#include <net/netisr.h>
#include <vm/vm.h>
#include <vm/vm_kern.h>
#include <vm/vm_page.h>
#include <vm/vm_map.h>
#include <vm/vm_extern.h>
#include <vm/vm_object.h>
#include <vm/vm_pager.h>
#include <sys/ptrace.h>
#include <sys/cons.h>
#include <machine/clock.h>
#include <machine/md_var.h>
#include <machine/reg.h>
#include <machine/pal.h>
#include <machine/cpuconf.h>
#include <machine/bootinfo.h>
#include <machine/rpb.h>
#include <machine/prom.h>
#include <machine/chipset.h>
#include <machine/vmparam.h>
#include <machine/elf.h>
#include <ddb/ddb.h>
#include <alpha/alpha/db_instruction.h>
#include <sys/vnode.h>
#include <machine/pcb.h>
#include <alpha/osf1/osf1_signal.h>
#include <alpha/osf1/osf1_proto.h>
#include <alpha/osf1/osf1_syscall.h>
#include <alpha/osf1/osf1_util.h>
#include <alpha/osf1/osf1.h>
#include <sys/sysproto.h>
#define DPRINTF uprintf
int osf1_sigdbg = 0;
static void bsd_to_osf1_sigaction(const struct sigaction *bsa,
struct osf1_sigaction *osa);
static void osf1_to_bsd_sigaction(const struct osf1_sigaction *osa,
struct sigaction *bsa);
#define sigemptyset(s) SIGEMPTYSET(*(s))
#define sigismember(s, n) SIGISMEMBER(*(s), n)
#define sigaddset(s, n) SIGADDSET(*(s), n)
#define osf1_sigmask(n) (1 << ((n) - 1))
#define osf1_sigemptyset(s) memset((s), 0, sizeof(*(s)))
#define osf1_sigfillset(s) memset((s), 0xffffffff, sizeof(*(s)))
#define osf1_sigismember(s, n) (*(s) & sigmask(n))
#define osf1_sigaddset(s, n) (*(s) |= sigmask(n))
void
osf1_to_bsd_sigset(oss, bss)
const osf1_sigset_t *oss;
sigset_t *bss;
{
const u_int32_t *obits;
SIGEMPTYSET(*bss);
obits = (const u_int32_t *)oss;
bss->__bits[0] = obits[0];
bss->__bits[1] = obits[1];
}
void
bsd_to_osf1_sigset(bss, oss)
const sigset_t *bss;
osf1_sigset_t *oss;
{
u_int32_t *obits;
osf1_sigemptyset(oss);
obits = (u_int32_t *)oss;
obits[0] = bss->__bits[0];
obits[1] = bss->__bits[1];
}
/*
* XXX: Only a subset of the flags is currently implemented.
*/
void
osf1_to_bsd_sigaction(osa, bsa)
const struct osf1_sigaction *osa;
struct sigaction *bsa;
{
bsa->sa_handler = osa->osa_handler;
if (osf1_sigdbg)
uprintf("%s(%d): handler @0x%lx \n", __FILE__, __LINE__,
(unsigned long)osa->osa_handler);
osf1_to_bsd_sigset(&osa->osa_mask, &bsa->sa_mask);
bsa->sa_flags = 0;
if ((osa->osa_flags & OSF1_SA_ONSTACK) != 0)
bsa->sa_flags |= SA_ONSTACK;
if ((osa->osa_flags & OSF1_SA_RESTART) != 0)
bsa->sa_flags |= SA_RESTART;
if ((osa->osa_flags & OSF1_SA_RESETHAND) != 0)
bsa->sa_flags |= SA_RESETHAND;
if ((osa->osa_flags & OSF1_SA_NOCLDSTOP) != 0)
bsa->sa_flags |= SA_NOCLDSTOP;
if ((osa->osa_flags & OSF1_SA_NODEFER) != 0)
bsa->sa_flags |= SA_NODEFER;
}
void
bsd_to_osf1_sigaction(bsa, osa)
const struct sigaction *bsa;
struct osf1_sigaction *osa;
{
osa->osa_handler = bsa->sa_handler;
bsd_to_osf1_sigset(&bsa->sa_mask, &osa->osa_mask);
osa->osa_flags = 0;
if ((bsa->sa_flags & SA_ONSTACK) != 0)
osa->osa_flags |= SA_ONSTACK;
if ((bsa->sa_flags & SA_RESTART) != 0)
osa->osa_flags |= SA_RESTART;
if ((bsa->sa_flags & SA_NOCLDSTOP) != 0)
osa->osa_flags |= SA_NOCLDSTOP;
if ((bsa->sa_flags & SA_NODEFER) != 0)
osa->osa_flags |= SA_NODEFER;
if ((bsa->sa_flags & SA_RESETHAND) != 0)
osa->osa_flags |= SA_RESETHAND;
}
void
osf1_to_bsd_sigaltstack(oss, bss)
const struct osf1_sigaltstack *oss;
struct sigaltstack *bss;
{
bss->ss_sp = oss->ss_sp;
bss->ss_size = oss->ss_size;
bss->ss_flags = 0;
if ((oss->ss_flags & OSF1_SS_DISABLE) != 0)
bss->ss_flags |= SS_DISABLE;
if ((oss->ss_flags & OSF1_SS_ONSTACK) != 0)
bss->ss_flags |= SS_ONSTACK;
}
void
bsd_to_osf1_sigaltstack(bss, oss)
const struct sigaltstack *bss;
struct osf1_sigaltstack *oss;
{
oss->ss_sp = bss->ss_sp;
oss->ss_size = bss->ss_size;
oss->ss_flags = 0;
if ((bss->ss_flags & SS_DISABLE) != 0)
oss->ss_flags |= OSF1_SS_DISABLE;
if ((bss->ss_flags & SS_ONSTACK) != 0)
oss->ss_flags |= OSF1_SS_ONSTACK;
}
int
osf1_sigaction(td, uap)
struct thread *td;
struct osf1_sigaction_args *uap;
{
struct osf1_sigaction osa;
struct sigaction nbsa, obsa;
struct sigaction *nbsap;
int error;
if (osf1_sigdbg && uap->sigtramp)
uprintf("osf1_sigaction: trampoline handler at %p\n",
uap->sigtramp);
td->td_md.osf_sigtramp = uap->sigtramp;
if (uap->nsa != NULL) {
if ((error = copyin(uap->nsa, &osa, sizeof(osa))) != 0)
return (error);
osf1_to_bsd_sigaction(&osa, &nbsa);
nbsap = &nbsa;
} else
nbsap = NULL;
error = kern_sigaction(td, uap->signum, &nbsa, &obsa, 0);
if (error == 0 && uap->osa != NULL) {
bsd_to_osf1_sigaction(&obsa, &osa);
error = copyout(&osa, uap->osa, sizeof(osa));
}
return (error);
}
int
osf1_sigaltstack(td, uap)
register struct thread *td;
struct osf1_sigaltstack_args *uap;
{
struct osf1_sigaltstack oss;
struct sigaltstack nbss, obss, *nbssp;
int error;
if (uap->nss != NULL) {
if ((error = copyin(uap->nss, &oss, sizeof(oss))) != 0)
return (error);
osf1_to_bsd_sigaltstack(&oss, &nbss);
nbssp = &nbss;
} else
nbssp = NULL;
error = kern_sigaltstack(td, nbssp, &obss);
if (error == 0 && uap->oss != NULL) {
bsd_to_osf1_sigaltstack(&obss, &oss);
error = copyout(&oss, uap->oss, sizeof(oss));
}
return (error);
}
int
osf1_signal(td, uap)
register struct thread *td;
struct osf1_signal_args *uap;
{
struct proc *p;
int error, signum;
signum = OSF1_SIGNO(uap->signum);
if (signum <= 0 || signum > OSF1_NSIG) {
if (OSF1_SIGCALL(uap->signum) == OSF1_SIGNAL_MASK ||
OSF1_SIGCALL(uap->signum) == OSF1_SIGDEFER_MASK)
td->td_retval[0] = -1;
return EINVAL;
}
switch (OSF1_SIGCALL(uap->signum)) {
case OSF1_SIGDEFER_MASK:
/*
* sigset is identical to signal() except
* that SIG_HOLD is allowed as
* an action.
*/
if ((u_long)uap->handler == OSF1_SIG_HOLD) {
sigset_t mask;
SIGEMPTYSET(mask);
SIGADDSET(mask, signum);
return (kern_sigprocmask(td, SIG_BLOCK, &mask, NULL,
0));
}
/* FALLTHROUGH */
case OSF1_SIGNAL_MASK:
{
struct sigaction nbsa, obsa;
nbsa.sa_handler = uap->handler;
SIGEMPTYSET(nbsa.sa_mask);
nbsa.sa_flags = 0;
#if 0
if (signum != SIGALRM)
nbsa.sa_flags = SA_RESTART;
#endif
error = kern_sigaction(td, signum, &nbsa, &obsa, 0);
if (error != 0) {
DPRINTF("signal: sigaction failed: %d\n",
error);
td->td_retval[0] = -1;
return (error);
}
td->td_retval[0] = (long)obsa.sa_handler;
return 0;
}
case OSF1_SIGHOLD_MASK:
{
sigset_t set;
SIGEMPTYSET(set);
SIGADDSET(set, signum);
return (kern_sigprocmask(td, SIG_BLOCK, &set, NULL, 0));
}
case OSF1_SIGRELSE_MASK:
{
sigset_t set;
SIGEMPTYSET(set);
SIGADDSET(set, signum);
return (kern_sigprocmask(td, SIG_UNBLOCK, &set, NULL,
0));
}
case OSF1_SIGIGNORE_MASK:
{
struct sigaction sa;
sa.sa_handler = SIG_IGN;
SIGEMPTYSET(sa.sa_mask);
sa.sa_flags = 0;
error = kern_sigaction(td, signum, &sa, NULL, 0);
if (error != 0)
DPRINTF(("sigignore: sigaction failed\n"));
return (error);
}
case OSF1_SIGPAUSE_MASK:
{
sigset_t mask;
p = td->td_proc;
PROC_LOCK(p);
mask = td->td_sigmask;
PROC_UNLOCK(p);
SIGDELSET(mask, signum);
return kern_sigsuspend(td, mask);
}
default:
return ENOSYS;
}
}
int
osf1_sigprocmask(td, uap)
register struct thread *td;
struct osf1_sigprocmask_args /* {
syscallarg(int) how;
syscallarg(osf1_sigset_t *) set;
} */ *uap;
{
osf1_sigset_t oss;
sigset_t obss, nbss;
int error;
/* OSF/1 sigprocmask flag values match FreeBSD flag values. */
osf1_to_bsd_sigset(&uap->mask, &nbss);
error = kern_sigprocmask(td, uap->how, &nbss, &obss, 0);
if (error == 0) {
bsd_to_osf1_sigset(&obss, &oss);
td->td_retval[0] = oss;
}
return (error);
}
int
osf1_sigpending(td, uap)
register struct thread *td;
struct osf1_sigpending_args /* {
syscallarg(osf1_sigset_t *) mask;
} */ *uap;
{
struct proc *p;
osf1_sigset_t oss;
sigset_t bss;
p = td->td_proc;
PROC_LOCK(p);
bss = td->td_siglist;
SIGSETOR(bss, p->p_siglist);
SIGSETAND(bss, td->td_sigmask);
PROC_UNLOCK(p);
bsd_to_osf1_sigset(&bss, &oss);
return copyout(&oss, uap->mask, sizeof(oss));
}
int
osf1_sigsuspend(td, uap)
register struct thread *td;
struct osf1_sigsuspend_args /* {
syscallarg(osf1_sigset_t *) ss;
} */ *uap;
{
osf1_sigset_t oss;
sigset_t bss;
oss = uap->ss;
osf1_to_bsd_sigset(&oss, &bss);
return kern_sigsuspend(td, bss);
}
int
osf1_kill(td, uap)
register struct thread *td;
struct osf1_kill_args /* {
syscallarg(int) pid;
syscallarg(int) signum;
} */ *uap;
{
struct kill_args ka;
ka.pid = uap->pid;
ka.signum = uap->signum;
return kill(td, &ka);
}
/*
* Send an interrupt to process.
*
* Stack is set up to allow sigcode stored at top to call routine,
* followed by kcall to sigreturn routine below. After sigreturn resets
* the signal mask, the stack, and the frame pointer, it returns to the
* user specified pc, psl.
*/
void
osf1_sendsig(sig_t catcher, ksiginfo_t *kp, sigset_t *mask)
{
int fsize, oonstack, rndfsize;
struct thread *td;
struct proc *p;
osiginfo_t *sip, ksi;
struct trapframe *frame;
struct sigacts *psp;
int sig;
int code;
td = curthread;
p = td->td_proc;
PROC_LOCK_ASSERT(p, MA_OWNED);
sig = kp->ksi_signo;
code = kp->ksi_code;
psp = p->p_sigacts;
mtx_assert(&psp->ps_mtx, MA_OWNED);
frame = td->td_frame;
oonstack = sigonstack(alpha_pal_rdusp());
fsize = sizeof ksi;
rndfsize = ((fsize + 15) / 16) * 16;
/*
* Allocate and validate space for the signal handler context.
* Note that if the stack is in P0 space, the call to grow() is a nop,
* and the useracc() check will fail if the process has not already
* allocated the space with a `brk'.
*/
if ((td->td_pflags & TDP_ALTSTACK) && !oonstack &&
SIGISMEMBER(psp->ps_sigonstack, sig)) {
sip = (osiginfo_t *)((caddr_t)td->td_sigstk.ss_sp +
td->td_sigstk.ss_size - rndfsize);
td->td_sigstk.ss_flags |= SS_ONSTACK;
} else
sip = (osiginfo_t *)(alpha_pal_rdusp() - rndfsize);
mtx_unlock(&psp->ps_mtx);
PROC_UNLOCK(p);
/*
* Build the signal context to be used by sigreturn.
*/
ksi.si_sc.sc_onstack = (oonstack) ? 1 : 0;
bsd_to_osf1_sigset(mask, &ksi.si_sc.sc_mask);
ksi.si_sc.sc_pc = frame->tf_regs[FRAME_PC];
ksi.si_sc.sc_ps = frame->tf_regs[FRAME_PS];
/* copy the registers. */
fill_regs(td, (struct reg *)ksi.si_sc.sc_regs);
ksi.si_sc.sc_regs[R_ZERO] = 0xACEDBADE; /* magic number */
ksi.si_sc.sc_regs[R_SP] = alpha_pal_rdusp();
/* save the floating-point state, if necessary, then copy it. */
alpha_fpstate_save(td, 1); /* XXX maybe write=0 */
ksi.si_sc.sc_ownedfp = td->td_md.md_flags & MDTD_FPUSED;
bcopy(&td->td_pcb->pcb_fp, (struct fpreg *)ksi.si_sc.sc_fpregs,
sizeof(struct fpreg));
ksi.si_sc.sc_fp_control = td->td_pcb->pcb_fp_control;
bzero(ksi.si_sc.sc_reserved, sizeof ksi.si_sc.sc_reserved); /* XXX */
ksi.si_sc.sc_xxx1[0] = 0; /* XXX */
ksi.si_sc.sc_xxx1[1] = 0; /* XXX */
ksi.si_sc.sc_traparg_a0 = frame->tf_regs[FRAME_TRAPARG_A0];
ksi.si_sc.sc_traparg_a1 = frame->tf_regs[FRAME_TRAPARG_A1];
ksi.si_sc.sc_traparg_a2 = frame->tf_regs[FRAME_TRAPARG_A2];
ksi.si_sc.sc_xxx2[0] = 0; /* XXX */
ksi.si_sc.sc_xxx2[1] = 0; /* XXX */
ksi.si_sc.sc_xxx2[2] = 0; /* XXX */
/* Fill in POSIX parts */
ksi.si_signo = sig;
ksi.si_code = code;
ksi.si_value = kp->ksi_value;
/*
* copy the frame out to userland.
*/
if (copyout((caddr_t)&ksi, (caddr_t)sip, fsize) != 0) {
/*
* Process has trashed its stack; give it an illegal
* instruction to halt it in its tracks.
*/
PROC_LOCK(p);
sigexit(td, SIGILL);
return;
}
/*
* Set up the registers to return to sigcode.
*/
if (osf1_sigdbg)
uprintf("attempting to call osf1 sigtramp\n");
frame->tf_regs[FRAME_PC] = (u_int64_t)td->td_md.osf_sigtramp;
frame->tf_regs[FRAME_A0] = sig;
frame->tf_regs[FRAME_A1] = code;
frame->tf_regs[FRAME_A2] = (u_int64_t)sip;
frame->tf_regs[FRAME_A3] = (u_int64_t)catcher; /* a3 is pv */
frame->tf_regs[FRAME_FLAGS] = 0; /* full restore */
alpha_pal_wrusp((unsigned long)sip);
PROC_LOCK(p);
mtx_lock(&psp->ps_mtx);
}
/*
* System call to cleanup state after a signal has been taken. Reset signal
* mask and stack state from context left by sendsig (above). Return to
* previous pc and psl as specified by context left by sendsig. Check
* carefully to make sure that the user has not modified the state to gain
* improper privileges.
*/
int
osf1_sigreturn(struct thread *td,
struct osf1_sigreturn_args /* {
struct osigcontext *sigcntxp;
} */ *uap)
{
struct osigcontext ksc, *scp;
struct proc *p;
p = td->td_proc;
scp = uap->sigcntxp;
/*
* Fetch the entire context structure at once for speed.
*/
if (copyin((caddr_t)scp, (caddr_t)&ksc, sizeof ksc))
return (EFAULT);
/*
* Restore the user-supplied information.
*/
PROC_LOCK(p);
if (ksc.sc_onstack)
td->td_sigstk.ss_flags |= SS_ONSTACK;
else
td->td_sigstk.ss_flags &= ~SS_ONSTACK;
/*
* longjmp is still implemented by calling osigreturn. The new
* sigmask is stored in sc_reserved, sc_mask is only used for
* backward compatibility.
*/
osf1_to_bsd_sigset(&ksc.sc_mask, &td->td_sigmask);
SIG_CANTMASK(td->td_sigmask);
signotify(td);
PROC_UNLOCK(p);
set_regs(td, (struct reg *)ksc.sc_regs);
td->td_frame->tf_regs[FRAME_PC] = ksc.sc_pc;
td->td_frame->tf_regs[FRAME_PS] =
(ksc.sc_ps | ALPHA_PSL_USERSET) & ~ALPHA_PSL_USERCLR;
td->td_frame->tf_regs[FRAME_FLAGS] = 0; /* full restore */
alpha_pal_wrusp(ksc.sc_regs[R_SP]);
/* XXX ksc.sc_ownedfp ? */
alpha_fpstate_drop(td);
bcopy((struct fpreg *)ksc.sc_fpregs, &td->td_pcb->pcb_fp,
sizeof(struct fpreg));
td->td_pcb->pcb_fp_control = ksc.sc_fp_control;
return (EJUSTRETURN);
}
int
osf1_osigstack(td, uap)
register struct thread *td;
struct osf1_osigstack_args /* {
struct sigstack *nss;
struct sigstack *oss;
} */ *uap;
{
/* uprintf("osf1_osigstack: oss = %p, nss = %p",uap->oss, uap->nss);
uprintf(" stack ptr = %p\n",p->p_sigacts->ps_sigstk.ss_sp);*/
return(osigstack(td, (struct osigstack_args *)uap));
}