unix_signal.c   [plain text]


/*
 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 * 
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */
/* 
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 */

#include <mach/mach_types.h>
#include <mach/exception_types.h>

#include <sys/param.h>
#include <sys/proc_internal.h>
#include <sys/user.h>
#include <sys/ucontext.h>
#include <sys/sysproto.h>
#include <sys/systm.h>
#include <sys/ux_exception.h>

#include <ppc/signal.h>
#include <sys/signalvar.h>
#include <sys/kdebug.h>
#include <sys/wait.h>
#include <kern/thread.h>
#include <mach/ppc/thread_status.h>
#include <ppc/proc_reg.h>

#include <sys/sdt.h>

// #include <machine/thread.h> XXX include path messed up for some reason...

/* XXX functions not in a Mach headers */
extern kern_return_t thread_getstatus(register thread_t act, int flavor,
			thread_state_t tstate, mach_msg_type_number_t *count);
extern unsigned int get_msr_exportmask(void);
extern kern_return_t thread_setstatus(thread_t thread, int flavor,
			thread_state_t tstate, mach_msg_type_number_t count);
extern void ppc_checkthreadstate(void *, int);
extern struct savearea_vec *find_user_vec_curr(void);
extern int thread_enable_fpe(thread_t act, int onoff);



#define	C_32_REDZONE_LEN	224
#define	C_32_STK_ALIGN		16
#define C_32_PARAMSAVE_LEN	64
#define	C_32_LINKAGE_LEN	48

#define	C_64_REDZONE_LEN	320
#define	C_64_STK_ALIGN		32
#define	C_64_PARAMSAVE_LEN	64
#define	C_64_LINKAGE_LEN	48

#define TRUNC_DOWN32(a,b,c)	((((uint32_t)a)-(b)) & ((uint32_t)(-(c))))
#define TRUNC_DOWN64(a,b,c)	((((uint64_t)a)-(b)) & ((uint64_t)(-(c))))

/*
 * The stack layout possibilities (info style); This needs to mach with signal trampoline code
 *
 * Traditional:			1
 * Traditional64:		20
 * Traditional64with vec:	25
 * 32bit context		30
 * 32bit context with vector	35
 * 64bit context		40
 * 64bit context with vector	45
 * Dual context			50
 * Dual context with vector	55
 *
 */
 
#define UC_TRAD			1
#define UC_TRAD_VEC		6
#define UC_TRAD64		20
#define UC_TRAD64_VEC		25
#define UC_FLAVOR		30
#define UC_FLAVOR_VEC		35
#define UC_FLAVOR64		40
#define UC_FLAVOR64_VEC		45
#define UC_DUAL			50
#define UC_DUAL_VEC		55
#define	UC_SET_ALT_STACK	0x40000000
#define UC_RESET_ALT_STACK	0x80000000

 /* The following are valid mcontext sizes */
#define UC_FLAVOR_SIZE ((PPC_THREAD_STATE_COUNT + PPC_EXCEPTION_STATE_COUNT + PPC_FLOAT_STATE_COUNT) * sizeof(int))

#define UC_FLAVOR_VEC_SIZE ((PPC_THREAD_STATE_COUNT + PPC_EXCEPTION_STATE_COUNT + PPC_FLOAT_STATE_COUNT + PPC_VECTOR_STATE_COUNT) * sizeof(int))

#define UC_FLAVOR64_SIZE ((PPC_THREAD_STATE64_COUNT + PPC_EXCEPTION_STATE64_COUNT + PPC_FLOAT_STATE_COUNT) * sizeof(int))

#define UC_FLAVOR64_VEC_SIZE ((PPC_THREAD_STATE64_COUNT + PPC_EXCEPTION_STATE64_COUNT + PPC_FLOAT_STATE_COUNT + PPC_VECTOR_STATE_COUNT) * sizeof(int))


/*
 * NOTE: Source and target may *NOT* overlap!
 */
static void
ucontext_32to64(struct ucontext64 *in, struct user_ucontext64 *out)
{
	out->uc_onstack		= in->uc_onstack;
	out->uc_sigmask		= in->uc_sigmask;

	/* internal "structure assign" */
	out->uc_stack.ss_sp	= CAST_USER_ADDR_T(in->uc_stack.ss_sp);
	out->uc_stack.ss_size	= in->uc_stack.ss_size;
	out->uc_stack.ss_flags	= in->uc_stack.ss_flags;

	out->uc_link		= CAST_USER_ADDR_T(in->uc_link);
	out->uc_mcsize		= in->uc_mcsize;
	out->uc_mcontext64	= CAST_USER_ADDR_T(in->uc_mcontext64);
}

/*
 * This conversion is safe, since if we are converting for a 32 bit process,
 * then it's values of uc-stack.ss_size and uc_mcsize will never exceed 4G.
 *
 * NOTE: Source and target may *NOT* overlap!
 */
static void
ucontext_64to32(struct user_ucontext64 *in, struct ucontext64 *out)
{
	out->uc_onstack		= in->uc_onstack;
	out->uc_sigmask		= in->uc_sigmask;

	/* internal "structure assign" */
	out->uc_stack.ss_sp	= CAST_DOWN(void *,in->uc_stack.ss_sp);
	out->uc_stack.ss_size	= in->uc_stack.ss_size;	/* range reduction */
	out->uc_stack.ss_flags	= in->uc_stack.ss_flags;

	out->uc_link		= CAST_DOWN(void *,in->uc_link);
	out->uc_mcsize		= in->uc_mcsize;	/* range reduction */
	out->uc_mcontext64	= CAST_DOWN(void *,in->uc_mcontext64);
}

/*
 * NOTE: Source and target may *NOT* overlap!
 */
static void
siginfo_user_to_user32(user_siginfo_t *in, user32_siginfo_t *out)
{
	out->si_signo	= in->si_signo;
	out->si_errno	= in->si_errno;
	out->si_code	= in->si_code;
	out->si_pid	= in->si_pid;
	out->si_uid	= in->si_uid;
	out->si_status	= in->si_status;
	out->si_addr	= CAST_DOWN_EXPLICIT(user32_addr_t,in->si_addr);
	/* following cast works for sival_int because of padding */
	out->si_value.sival_ptr	= CAST_DOWN_EXPLICIT(user32_addr_t,in->si_value.sival_ptr);
	out->si_band	= in->si_band;			/* range reduction */
	out->__pad[0]	= in->pad[0];			/* mcontext.ss.r1 */
}

static void
siginfo_user_to_user64(user_siginfo_t *in, user64_siginfo_t *out)
{
	out->si_signo	= in->si_signo;
	out->si_errno	= in->si_errno;
	out->si_code	= in->si_code;
	out->si_pid	= in->si_pid;
	out->si_uid	= in->si_uid;
	out->si_status	= in->si_status;
	out->si_addr	= in->si_addr;
	out->si_value.sival_ptr	= in->si_value.sival_ptr;
	out->si_band	= in->si_band;			/* range reduction */
	out->__pad[0]	= in->pad[0];			/* mcontext.ss.r1 */
}


/*
 * Arrange for this process to run a signal handler
 */

void
sendsig(struct proc *p, user_addr_t catcher, int sig, int mask, __unused uint32_t code)
{
	kern_return_t kretn;
	struct mcontext mctx;
	user_addr_t p_mctx = USER_ADDR_NULL;		/* mcontext dest. */
	struct mcontext64 mctx64;
	user_addr_t p_mctx64 = USER_ADDR_NULL;		/* mcontext dest. */
	struct user_ucontext64 uctx;
	user_addr_t p_uctx;		/* user stack addr top copy ucontext */
	user_siginfo_t sinfo;
	user_addr_t p_sinfo;		/* user stack addr top copy siginfo */
	struct sigacts *ps = p->p_sigacts;
	int oonstack;
	user_addr_t sp;
	mach_msg_type_number_t state_count;
	thread_t th_act;
	struct uthread *ut;
	int infostyle = UC_TRAD;
	int dualcontext =0;
	user_addr_t trampact;
	int vec_used = 0;
	int stack_size = 0;
	void * tstate;
	int flavor;
        int ctx32 = 1;

	th_act = current_thread();
	ut = get_bsdthread_info(th_act);

	/*
	 * XXX We conditionalize type passed here based on SA_SIGINFO, but
	 * XXX we always send up all the information, regardless; perhaps
	 * XXX this should not be conditionalized?  Defer making this change
	 * XXX now, due to possible tools impact.
	 */
	if (p->p_sigacts->ps_siginfo & sigmask(sig)) {
		/*
		 * If SA_SIGINFO is set, then we must provide the user
		 * process both a siginfo_t and a context argument.  We call
		 * this "FLAVORED", as opposed to "TRADITIONAL", which doesn't
		 * expect a context.  "DUAL" is a type of "FLAVORED".
		 */
		if (is_64signalregset()) {
			/*
			 * If this is a 64 bit CPU, we must include a 64 bit
			 * context in the data we pass to user space; we may
			 * or may not also include a 32 bit context at the
			 * same time, for non-leaf functions.
			 *
			 * The user may also explicitly choose to not receive
			 * a 32 bit context, at their option; we only allow
			 * this to happen on 64 bit processors, for obvious
			 * reasons.
			 */
			if (IS_64BIT_PROCESS(p) ||
			    (p->p_sigacts->ps_64regset & sigmask(sig))) {
				 /*
				  * For a 64 bit process, there is no 32 bit
				  * context.
				  */
				ctx32 = 0;
				infostyle = UC_FLAVOR64;
			} else {
				/*
				 * For a 32 bit process on a 64 bit CPU, we
				 * may have 64 bit leaf functions, so we need
				 * both contexts.
				 */
				dualcontext = 1;
				infostyle = UC_DUAL;
			}
		} else {
			/*
			 * If this is a 32 bit CPU, then we only have a 32 bit
			 * context to contend with.
			 */
			infostyle = UC_FLAVOR;
		}
        } else {
		/*
		 * If SA_SIGINFO is not set, then we have a traditional style
		 * call which does not need additional context passed.  The
		 * default is 32 bit traditional.
		 *
		 * XXX The second check is redundant on PPC32; keep it anyway.
		 */
		if (is_64signalregset() || IS_64BIT_PROCESS(p)) {
			/*
			 * However, if this is a 64 bit CPU, we need to change
			 * this to 64 bit traditional, and drop the 32 bit
			 * context.
			 */
			ctx32 = 0;
			infostyle = UC_TRAD64;
		}
	}

	proc_unlock(p);

	/* I need this for SIGINFO anyway */
	flavor = PPC_THREAD_STATE;
	tstate = (void *)&mctx.ss;
	state_count = PPC_THREAD_STATE_COUNT;
	if (thread_getstatus(th_act, flavor, (thread_state_t)tstate, &state_count)  != KERN_SUCCESS)
		goto bad;

	if ((ctx32 == 0) || dualcontext) {
		flavor = PPC_THREAD_STATE64;
		tstate = (void *)&mctx64.ss;
		state_count = PPC_THREAD_STATE64_COUNT;
                if (thread_getstatus(th_act, flavor, (thread_state_t)tstate, &state_count)  != KERN_SUCCESS)
                    goto bad;
	}

        if ((ctx32 == 1) || dualcontext) {
                flavor = PPC_EXCEPTION_STATE;
		tstate = (void *)&mctx.es;
		state_count = PPC_EXCEPTION_STATE_COUNT;
                if (thread_getstatus(th_act, flavor, (thread_state_t)tstate, &state_count)  != KERN_SUCCESS)
                    goto bad;
       } 
       
	if ((ctx32 == 0) || dualcontext) {
 		flavor = PPC_EXCEPTION_STATE64;
		tstate = (void *)&mctx64.es;
		state_count = PPC_EXCEPTION_STATE64_COUNT;
       
                if (thread_getstatus(th_act, flavor, (thread_state_t)tstate, &state_count)  != KERN_SUCCESS)
                    goto bad;
                
        }
       

        if ((ctx32 == 1) || dualcontext) {
                flavor = PPC_FLOAT_STATE;
		tstate = (void *)&mctx.fs;
		state_count = PPC_FLOAT_STATE_COUNT;
                if (thread_getstatus(th_act, flavor, (thread_state_t)tstate, &state_count)  != KERN_SUCCESS)
                    goto bad;
       } 
       
	if ((ctx32 == 0) || dualcontext) {
 		flavor = PPC_FLOAT_STATE;
		tstate = (void *)&mctx64.fs;
		state_count = PPC_FLOAT_STATE_COUNT;
                       if (thread_getstatus(th_act, flavor, (thread_state_t)tstate, &state_count)  != KERN_SUCCESS)
                    goto bad;
                
        }


	if (find_user_vec_curr()) {
		vec_used = 1;

                if ((ctx32 == 1) || dualcontext) {
                    flavor = PPC_VECTOR_STATE;
                    tstate = (void *)&mctx.vs;
                    state_count = PPC_VECTOR_STATE_COUNT;
                    if (thread_getstatus(th_act, flavor, (thread_state_t)tstate, &state_count)  != KERN_SUCCESS)
                    goto bad;
                    infostyle += 5;
            } 
       
            if ((ctx32 == 0) || dualcontext) {
                    flavor = PPC_VECTOR_STATE;
                    tstate = (void *)&mctx64.vs;
                    state_count = PPC_VECTOR_STATE_COUNT;
                    if (thread_getstatus(th_act, flavor, (thread_state_t)tstate, &state_count)  != KERN_SUCCESS)
                        goto bad;
                    infostyle += 5;
           }
	}  

	trampact = ps->ps_trampact[sig];
	oonstack = ut->uu_sigstk.ss_flags & SA_ONSTACK;

	/* figure out where our new stack lives */
	if ((ut->uu_flag & UT_ALTSTACK) && !oonstack &&
		(ps->ps_sigonstack & sigmask(sig))) {
		sp = ut->uu_sigstk.ss_sp;
		sp += ut->uu_sigstk.ss_size;
		stack_size = ut->uu_sigstk.ss_size;
		ut->uu_sigstk.ss_flags |= SA_ONSTACK;
	}
	else {
		if (ctx32 == 0)
			sp = mctx64.ss.r1;
		else
			sp = CAST_USER_ADDR_T(mctx.ss.r1);
	}

	
	/* put siginfo on top */
        
	/* preserve RED ZONE area */
	if (IS_64BIT_PROCESS(p))
		sp = TRUNC_DOWN64(sp, C_64_REDZONE_LEN, C_64_STK_ALIGN);
	else
		sp = TRUNC_DOWN32(sp, C_32_REDZONE_LEN, C_32_STK_ALIGN);

        /* next are the saved registers */
        if ((ctx32 == 0) || dualcontext) {
            sp -= sizeof(struct mcontext64);
            p_mctx64 = sp;
        }
        if ((ctx32 == 1) || dualcontext) {
            sp -= sizeof(struct mcontext);
            p_mctx = sp;
        }    
        
	if (IS_64BIT_PROCESS(p)) {
		/* context goes first on stack */
		sp -= sizeof(struct user_ucontext64);
		p_uctx = sp;

		/* this is where siginfo goes on stack */
		sp -= sizeof(user64_siginfo_t);
		p_sinfo = sp;
		
		sp = TRUNC_DOWN64(sp, C_64_PARAMSAVE_LEN+C_64_LINKAGE_LEN, C_64_STK_ALIGN);
	} else {
		/*
		 * struct ucontext and struct ucontext64 are identical in
		 * size and content; the only difference is the internal
		 * pointer type for the last element, which makes no
		 * difference for the copyout().
		 */

		/* context goes first on stack */
		sp -= sizeof(struct ucontext64);
		p_uctx = sp;

		/* this is where siginfo goes on stack */
		sp -= sizeof(user32_siginfo_t);
		p_sinfo = sp;

		sp = TRUNC_DOWN32(sp, C_32_PARAMSAVE_LEN+C_32_LINKAGE_LEN, C_32_STK_ALIGN);
	}

	uctx.uc_onstack = oonstack;
	uctx.uc_sigmask = mask;
	uctx.uc_stack.ss_sp = sp;
	uctx.uc_stack.ss_size = stack_size;
	if (oonstack)
		uctx.uc_stack.ss_flags |= SS_ONSTACK;
		
	uctx.uc_link = 0;
	if (ctx32 == 0)
		uctx.uc_mcsize = (size_t)((PPC_EXCEPTION_STATE64_COUNT + PPC_THREAD_STATE64_COUNT + PPC_FLOAT_STATE_COUNT) * sizeof(int));
	else
		uctx.uc_mcsize = (size_t)((PPC_EXCEPTION_STATE_COUNT + PPC_THREAD_STATE_COUNT + PPC_FLOAT_STATE_COUNT) * sizeof(int));
	
	if (vec_used) 
		uctx.uc_mcsize += (size_t)(PPC_VECTOR_STATE_COUNT * sizeof(int));
        
	if (ctx32 == 0)
             uctx.uc_mcontext64 = p_mctx64;
       else
            uctx.uc_mcontext64 = p_mctx;

	/* setup siginfo */
	bzero((caddr_t)&sinfo, sizeof(sinfo));
	sinfo.si_signo = sig;
	if (ctx32 == 0) {
		sinfo.si_addr = mctx64.ss.srr0;
		sinfo.pad[0] = mctx64.ss.r1;
	} else {
		sinfo.si_addr = CAST_USER_ADDR_T(mctx.ss.srr0);
		sinfo.pad[0] = CAST_USER_ADDR_T(mctx.ss.r1);
	}

	switch (sig) {
		case SIGILL:
			/*
			 * If it's 64 bit and not a dual context, mctx will
			 * contain uninitialized data, so we have to use
			 * mctx64 here.
			 */
			if(ctx32 == 0) {
				if (mctx64.ss.srr1 & (1 << (31 - SRR1_PRG_ILL_INS_BIT)))
					sinfo.si_code = ILL_ILLOPC;
				else if (mctx64.ss.srr1 & (1 << (31 - SRR1_PRG_PRV_INS_BIT)))
					sinfo.si_code = ILL_PRVOPC;
				else if (mctx64.ss.srr1 & (1 << (31 - SRR1_PRG_TRAP_BIT)))
					sinfo.si_code = ILL_ILLTRP;
				else
					sinfo.si_code = ILL_NOOP;
			} else {
				if (mctx.ss.srr1 & (1 << (31 - SRR1_PRG_ILL_INS_BIT)))
					sinfo.si_code = ILL_ILLOPC;
				else if (mctx.ss.srr1 & (1 << (31 - SRR1_PRG_PRV_INS_BIT)))
					sinfo.si_code = ILL_PRVOPC;
				else if (mctx.ss.srr1 & (1 << (31 - SRR1_PRG_TRAP_BIT)))
					sinfo.si_code = ILL_ILLTRP;
				else
					sinfo.si_code = ILL_NOOP;
			}
			break;
		case SIGFPE:
#define FPSCR_VX	2
#define FPSCR_OX	3
#define FPSCR_UX	4
#define FPSCR_ZX	5
#define FPSCR_XX	6
			/*
			 * If it's 64 bit and not a dual context, mctx will
			 * contain uninitialized data, so we have to use
			 * mctx64 here.
			 */
			if(ctx32 == 0) {
				if (mctx64.fs.fpscr & (1 << (31 - FPSCR_VX)))
					sinfo.si_code = FPE_FLTINV;
				else if (mctx64.fs.fpscr & (1 << (31 - FPSCR_OX)))
					sinfo.si_code = FPE_FLTOVF;
				else if (mctx64.fs.fpscr & (1 << (31 - FPSCR_UX)))
					sinfo.si_code = FPE_FLTUND;
				else if (mctx64.fs.fpscr & (1 << (31 - FPSCR_ZX)))
					sinfo.si_code = FPE_FLTDIV;
				else if (mctx64.fs.fpscr & (1 << (31 - FPSCR_XX)))
					sinfo.si_code = FPE_FLTRES;
				else
					sinfo.si_code = FPE_NOOP;
			} else {
				if (mctx.fs.fpscr & (1 << (31 - FPSCR_VX)))
					sinfo.si_code = FPE_FLTINV;
				else if (mctx.fs.fpscr & (1 << (31 - FPSCR_OX)))
					sinfo.si_code = FPE_FLTOVF;
				else if (mctx.fs.fpscr & (1 << (31 - FPSCR_UX)))
					sinfo.si_code = FPE_FLTUND;
				else if (mctx.fs.fpscr & (1 << (31 - FPSCR_ZX)))
					sinfo.si_code = FPE_FLTDIV;
				else if (mctx.fs.fpscr & (1 << (31 - FPSCR_XX)))
					sinfo.si_code = FPE_FLTRES;
				else
					sinfo.si_code = FPE_NOOP;
			}
			break;

		case SIGBUS:
			if (ctx32 == 0) {
				sinfo.si_addr = mctx64.es.dar;
			} else {
				sinfo.si_addr = CAST_USER_ADDR_T(mctx.es.dar);
			}
			/* on ppc we generate only if EXC_PPC_UNALIGNED */
			sinfo.si_code = BUS_ADRALN;
			break;

		case SIGSEGV:
			/*
			 * If it's 64 bit and not a dual context, mctx will
			 * contain uninitialized data, so we have to use
			 * mctx64 here.
			 */
			if (ctx32 == 0) {
				sinfo.si_addr = mctx64.es.dar;
				/* First check in srr1 and then in dsisr */
				if (mctx64.ss.srr1 & (1 << (31 - DSISR_PROT_BIT)))
					sinfo.si_code = SEGV_ACCERR;
				else if (mctx64.es.dsisr & (1 << (31 - DSISR_PROT_BIT)))
					sinfo.si_code = SEGV_ACCERR;
				else
					sinfo.si_code = SEGV_MAPERR;
			} else {
				sinfo.si_addr = CAST_USER_ADDR_T(mctx.es.dar);
				/* First check in srr1 and then in dsisr */
				if (mctx.ss.srr1 & (1 << (31 - DSISR_PROT_BIT)))
					sinfo.si_code = SEGV_ACCERR;
				else if (mctx.es.dsisr & (1 << (31 - DSISR_PROT_BIT)))
					sinfo.si_code = SEGV_ACCERR;
				else
					sinfo.si_code = SEGV_MAPERR;
			}
			break;
		default:
		{
			int status_and_exitcode;

			/*
			 * All other signals need to fill out a minimum set of
			 * information for the siginfo structure passed into
			 * the signal handler, if SA_SIGINFO was specified.
			 *
			 * p->si_status actually contains both the status and
			 * the exit code; we save it off in its own variable
			 * for later breakdown.
			 */
			proc_lock(p);
			sinfo.si_pid = p->si_pid;
			p->si_pid = 0;
			status_and_exitcode = p->si_status;
			p->si_status = 0;
			sinfo.si_uid = p->si_uid;
			p->si_uid = 0;
			sinfo.si_code = p->si_code;
			p->si_code = 0;
			proc_unlock(p);
			if (sinfo.si_code == CLD_EXITED) {
				if (WIFEXITED(status_and_exitcode)) 
					sinfo.si_code = CLD_EXITED;
				else if (WIFSIGNALED(status_and_exitcode)) {
					if (WCOREDUMP(status_and_exitcode)) {
						sinfo.si_code = CLD_DUMPED;
						status_and_exitcode = W_EXITCODE(status_and_exitcode,status_and_exitcode);
					} else {
						sinfo.si_code = CLD_KILLED;
						status_and_exitcode = W_EXITCODE(status_and_exitcode,status_and_exitcode);
					}
				}
			}
			/*
			 * The recorded status contains the exit code and the
			 * signal information, but the information to be passed
			 * in the siginfo to the handler is supposed to only
			 * contain the status, so we have to shift it out.
			 */
			sinfo.si_status = WEXITSTATUS(status_and_exitcode);
			break;
		}
	}


	/* copy info out to user space */
	if (IS_64BIT_PROCESS(p)) {
		user64_siginfo_t sinfo64;

		siginfo_user_to_user64(&sinfo,&sinfo64);

#if CONFIG_DTRACE		
        bzero((caddr_t)&(ut->t_dtrace_siginfo), sizeof(ut->t_dtrace_siginfo));

        ut->t_dtrace_siginfo.si_signo = sinfo.si_signo;
        ut->t_dtrace_siginfo.si_code = sinfo.si_code;
        ut->t_dtrace_siginfo.si_pid = sinfo.si_pid;
        ut->t_dtrace_siginfo.si_uid = sinfo.si_uid;
        ut->t_dtrace_siginfo.si_status = sinfo.si_status;
		/* XXX truncates faulting address to void * on K32  */
        ut->t_dtrace_siginfo.si_addr = CAST_DOWN(void *, sinfo.si_addr);


        /* Fire DTrace proc:::fault probe when signal is generated by hardware. */
        switch (sig) {
        case SIGILL: case SIGBUS: case SIGSEGV: case SIGFPE: case SIGTRAP:
            DTRACE_PROC2(fault, int, (int)(ut->uu_code), siginfo_t *, &(ut->t_dtrace_siginfo));
            break;
        default:
            break;
        }

		/* XXX truncates catcher address to uintptr_t */
		DTRACE_PROC3(signal__handle, int, sig, siginfo_t *, &(ut->t_dtrace_siginfo),
			void (*)(void), CAST_DOWN(sig_t, catcher));
#endif /* CONFIG_DTRACE */

		if (copyout(&uctx, p_uctx, sizeof(struct user_ucontext64)))
			goto bad;
		if (copyout(&sinfo64, p_sinfo, sizeof(sinfo64)))
			goto bad;
	} else {
		struct ucontext64 uctx32;
		user32_siginfo_t sinfo32;

		ucontext_64to32(&uctx, &uctx32);
		siginfo_user_to_user32(&sinfo,&sinfo32);

#if CONFIG_DTRACE
        bzero((caddr_t)&(ut->t_dtrace_siginfo), sizeof(ut->t_dtrace_siginfo));

        ut->t_dtrace_siginfo.si_signo = sinfo.si_signo;
        ut->t_dtrace_siginfo.si_code = sinfo.si_code;
        ut->t_dtrace_siginfo.si_pid = sinfo.si_pid;
        ut->t_dtrace_siginfo.si_uid = sinfo.si_uid;
        ut->t_dtrace_siginfo.si_status = sinfo.si_status;
        ut->t_dtrace_siginfo.si_addr = CAST_DOWN(void *, sinfo.si_addr);


        /* Fire DTrace proc:::fault probe when signal is generated by hardware. */
        switch (sig) {
        case SIGILL: case SIGBUS: case SIGSEGV: case SIGFPE: case SIGTRAP:
            DTRACE_PROC2(fault, int, (int)(ut->uu_code), siginfo_t *, &(ut->t_dtrace_siginfo));
            break;
        default:
            break;
        }

		DTRACE_PROC3(signal__handle, int, sig, siginfo_t *, &(ut->t_dtrace_siginfo),
			void (*)(void), CAST_DOWN(sig_t, catcher));
#endif /* CONFIG_DTRACE */

		if (copyout(&uctx32, p_uctx, sizeof(struct ucontext64)))
			goto bad;

		if (copyout(&sinfo32, p_sinfo, sizeof(sinfo32)))
			goto bad;
	}
        if ((ctx32 == 0) || dualcontext) {
	    /*
	     * NOTE: Size of mcontext is not variant between 64bit and
	     * 32bit programs usng 64bit registers.
	     */
            if (copyout(&mctx64, p_mctx64, (vec_used? UC_FLAVOR64_VEC_SIZE: UC_FLAVOR64_SIZE)))
		goto bad;
        }
        if ((ctx32 == 1) || dualcontext) {
            if (copyout(&mctx, p_mctx, uctx.uc_mcsize))
		goto bad;
        }    


	/* Place our arguments in arg registers: rtm dependent */
	if(IS_64BIT_PROCESS(p)) {
		mctx64.ss.r3 = catcher;
		mctx64.ss.r4 = CAST_USER_ADDR_T(infostyle);
		mctx64.ss.r5 = CAST_USER_ADDR_T(sig);
		mctx64.ss.r6 = p_sinfo;
		mctx64.ss.r7 = p_uctx;

		mctx64.ss.srr0 = trampact;
		/* MSR_EXPORT_MASK_SET */
		mctx64.ss.srr1 = CAST_USER_ADDR_T(get_msr_exportmask());
		mctx64.ss.r1 = sp;
		state_count = PPC_THREAD_STATE64_COUNT;
		if ((kretn = thread_setstatus(th_act, PPC_THREAD_STATE64, (void *)&mctx64.ss, state_count))  != KERN_SUCCESS) {
			panic("sendsig: thread_setstatus failed, ret = %08X\n", kretn);
		}	
	} else {
		mctx.ss.r3 = CAST_DOWN(uint32_t,catcher);
		mctx.ss.r4 = (uint32_t)infostyle;
		mctx.ss.r5 = (uint32_t)sig;
		mctx.ss.r6 = CAST_DOWN(uint32_t,p_sinfo);
		mctx.ss.r7 = CAST_DOWN(uint32_t,p_uctx);

		mctx.ss.srr0 = CAST_DOWN(uint32_t,trampact);
		/* MSR_EXPORT_MASK_SET */
		mctx.ss.srr1 = get_msr_exportmask();
		mctx.ss.r1 = CAST_DOWN(uint32_t,sp);
		state_count = PPC_THREAD_STATE_COUNT;
		if ((kretn = thread_setstatus(th_act, PPC_THREAD_STATE, (void *)&mctx.ss, state_count))  != KERN_SUCCESS) {
			panic("sendsig: thread_setstatus failed, ret = %08X\n", kretn);
		}	
	}

	proc_lock(p);
	return;

bad:
	proc_lock(p);
	SIGACTION(p, SIGILL) = SIG_DFL;
	sig = sigmask(SIGILL);
	p->p_sigignore &= ~sig;
	p->p_sigcatch &= ~sig;
	ut->uu_sigmask &= ~sig;
	/* sendsig is called with signal lock held */
	proc_unlock(p);
	psignal_locked(p, SIGILL);
	proc_lock(p);
	return;
}

/*
 * 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
 * psl to gain improper priviledges or to cause
 * a machine fault.
 */

/* ARGSUSED */
int
sigreturn(struct proc *p, struct sigreturn_args *uap, __unused int *retval)
{
	struct user_ucontext64 uctx;

	char mactx[sizeof(struct mcontext64)];
	struct mcontext *p_mctx;
	struct mcontext64 *p_64mctx;
	int error;
	thread_t th_act;
	struct sigacts *ps = p->p_sigacts;
	sigset_t mask;	
	user_addr_t action;
	uint32_t state_count;
	unsigned int state_flavor;
	struct uthread * ut;
	int vec_used = 0;
	void *tsptr, *fptr, *vptr;
        int infostyle = uap->infostyle;

	th_act = current_thread();

	ut = (struct uthread *)get_bsdthread_info(th_act);

	/*
	 * If we are being asked to change the altstack flag on the thread, we
	 * just rest it and return (the uap->uctx is not used).
	 */
	if (infostyle == UC_SET_ALT_STACK) {
		ut->uu_sigstk.ss_flags |= SA_ONSTACK;
		return (0);
	} else if ((unsigned int)infostyle == UC_RESET_ALT_STACK) {
		ut->uu_sigstk.ss_flags &= ~SA_ONSTACK;
		return (0);
	}

	if (IS_64BIT_PROCESS(p)) {
		error = copyin(uap->uctx, &uctx, sizeof(struct user_ucontext64));
		if (error)
			return(error);
	} else {
		struct ucontext64 uctx32;

		/*
		 * struct ucontext and struct ucontext64 are identical in
		 * size and content; the only difference is the internal
		 * pointer type for the last element, which makes no
		 * difference for the copyin().
		 */
		error = copyin(uap->uctx, &uctx32, sizeof(struct ucontext));
		if (error)
			return(error);
		ucontext_32to64(&uctx32, &uctx);
	}
        

	/* validate the machine context size */
	switch (uctx.uc_mcsize) {
		case UC_FLAVOR64_VEC_SIZE:
		case UC_FLAVOR64_SIZE:
		case UC_FLAVOR_VEC_SIZE:
		case UC_FLAVOR_SIZE:
			break;
		default:
			return(EINVAL);
	}

	/*
	 * The 64 bit process mcontext is identical to the mcontext64, so
	 * there is no conversion necessary.
	 */
	error = copyin(uctx.uc_mcontext64, mactx, uctx.uc_mcsize);
	if (error)
		return(error);
	
	if ((uctx.uc_onstack & 01))
			ut->uu_sigstk.ss_flags |= SA_ONSTACK;
	else
			ut->uu_sigstk.ss_flags &= ~SA_ONSTACK;

	ut->uu_sigmask = uctx.uc_sigmask & ~sigcantmask;
	if (ut->uu_siglist & ~ut->uu_sigmask)
		signal_setast(current_thread());	

	vec_used = 0;
	switch (infostyle)  {
                case UC_FLAVOR64_VEC:
                case UC_TRAD64_VEC:
                                vec_used = 1;
                case UC_TRAD64:
		case UC_FLAVOR64:  {
                            p_64mctx = (struct mcontext64 *)mactx;	
                            tsptr = (void *)&p_64mctx->ss;
                            fptr = (void *)&p_64mctx->fs;
                            vptr = (void *)&p_64mctx->vs;
                            state_flavor = PPC_THREAD_STATE64;
                            state_count = PPC_THREAD_STATE64_COUNT;
                    } 
                    break;
		case UC_FLAVOR_VEC :
		case UC_TRAD_VEC :
                                vec_used = 1;
		case UC_FLAVOR :
		case UC_TRAD :
		default: {
			p_mctx = (struct mcontext *)mactx;	
			tsptr = (void *)&p_mctx->ss;
			fptr = (void *)&p_mctx->fs;
			vptr = (void *)&p_mctx->vs;
			state_flavor = PPC_THREAD_STATE;
			state_count = PPC_THREAD_STATE_COUNT;
		}
		break;
	} /* switch () */

	/* validate the thread state, set/reset appropriate mode bits in srr1 */
	(void)ppc_checkthreadstate(tsptr, state_flavor);

	if (thread_setstatus(th_act, state_flavor, tsptr, state_count)  != KERN_SUCCESS) {
		return(EINVAL);
	}	

	state_count = PPC_FLOAT_STATE_COUNT;
	if (thread_setstatus(th_act, PPC_FLOAT_STATE, fptr, state_count)  != KERN_SUCCESS) {
		return(EINVAL);
	}	

	mask = sigmask(SIGFPE);
	if (((ut->uu_sigmask & mask) == 0) && (p->p_sigcatch & mask) && ((p->p_sigignore & mask) == 0)) {
		action = ps->ps_sigact[SIGFPE];
		if((action != SIG_DFL) && (action != SIG_IGN)) {
			thread_enable_fpe(th_act, 1);
		}
	}

	if (vec_used) {
		state_count = PPC_VECTOR_STATE_COUNT;
		if (thread_setstatus(th_act, PPC_VECTOR_STATE, vptr, state_count)  != KERN_SUCCESS) {
			return(EINVAL);
		}	
	}
	return (EJUSTRETURN);
}

/*
 * machine_exception() performs MD translation
 * of a mach exception to a unix signal and code.
 */

boolean_t
machine_exception(
		int				exception,
		mach_exception_code_t		code,
		__unused mach_exception_subcode_t subcode,
		int				*unix_signal,
		mach_exception_code_t		*unix_code)
{
    switch(exception) {

    case EXC_BAD_INSTRUCTION:
	*unix_signal = SIGILL;
	*unix_code = code;
	break;

    case EXC_ARITHMETIC:
	*unix_signal = SIGFPE;
	*unix_code = code;
	break;

    case EXC_SOFTWARE:
	if (code == EXC_PPC_TRAP) {
		*unix_signal = SIGTRAP;
		*unix_code = code;
		break;
	} else
		return(FALSE);

    default:
	return(FALSE);
    }
   
    return(TRUE);
}