#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: sig_machdep.c,v 1.55 2023/12/20 15:29:06 thorpej Exp $");
#ifdef _KERNEL_OPT
#include "opt_altivec.h"
#include "opt_ppcarch.h"
#endif
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/syscallargs.h>
#include <sys/systm.h>
#include <sys/ucontext.h>
#include <sys/cpu.h>
#include <uvm/uvm_extern.h>
#include <powerpc/fpu.h>
#include <powerpc/altivec.h>
#include <powerpc/pcb.h>
#include <powerpc/psl.h>
CTASSERT((sizeof(siginfo_t) & (CALLFRAMELEN-1)) == 0);
CTASSERT((sizeof(ucontext_t) & (CALLFRAMELEN-1)) == 0);
void
sendsig_siginfo(const ksiginfo_t *ksi, const sigset_t *mask)
{
struct lwp * const l = curlwp;
struct proc * const p = l->l_proc;
struct trapframe * const tf = l->l_md.md_utf;
stack_t * const ss = &l->l_sigstk;
const struct sigact_sigdesc * const sd =
&p->p_sigacts->sa_sigdesc[ksi->ksi_signo];
const void * const handler = sd->sd_sigact.sa_handler;
ucontext_t uc;
vaddr_t sp, sip, ucp;
int onstack, error;
onstack = (ss->ss_flags & (SS_DISABLE | SS_ONSTACK)) == 0 &&
(sd->sd_sigact.sa_flags & SA_ONSTACK) != 0;
sp = (onstack ? (vaddr_t)ss->ss_sp + ss->ss_size : tf->tf_fixreg[1]);
sp &= ~(CALLFRAMELEN-1);
sp -= sizeof(ucontext_t);
sp -= sizeof(siginfo_t);
#if 0
sp &= ~(CALLFRAMELEN-1);
#endif
sip = sp;
ucp = sp + sizeof(siginfo_t);
KASSERT((sip & (CALLFRAMELEN-1)) == 0);
KASSERT((ucp & (CALLFRAMELEN-1)) == 0);
sp -= CALLFRAMELEN;
memset(&uc, 0, sizeof(uc));
uc.uc_flags = _UC_SIGMASK;
uc.uc_flags |= (ss->ss_flags & SS_ONSTACK) ?
_UC_SETSTACK : _UC_CLRSTACK;
uc.uc_sigmask = *mask;
uc.uc_link = l->l_ctxlink;
sendsig_reset(l, ksi->ksi_signo);
mutex_exit(p->p_lock);
cpu_getmcontext(l, &uc.uc_mcontext, &uc.uc_flags);
error = (copyout(&ksi->ksi_info, (void *)sip, sizeof(ksi->ksi_info)) != 0 ||
copyout(&uc, (void *)ucp, sizeof(uc)) != 0);
mutex_enter(p->p_lock);
if (error) {
sigexit(l, SIGILL);
}
switch (sd->sd_vers) {
case __SIGTRAMP_SIGINFO_VERSION:
tf->tf_fixreg[1] = (register_t)sp;
tf->tf_fixreg[3] = (register_t)ksi->ksi_signo;
tf->tf_fixreg[4] = (register_t)sip;
tf->tf_fixreg[5] = (register_t)ucp;
tf->tf_fixreg[30] = (register_t)ucp;
tf->tf_lr = (register_t)sd->sd_tramp;
tf->tf_srr0 = (register_t)handler;
break;
default:
goto nosupport;
}
if (onstack)
ss->ss_flags |= SS_ONSTACK;
return;
nosupport:
printf("sendsig_siginfo(sig %d): bad version %d\n",
ksi->ksi_signo, sd->sd_vers);
sigexit(l, SIGILL);
}
void
cpu_getmcontext(struct lwp *l, mcontext_t *mcp, unsigned int *flagp)
{
const struct trapframe * const tf = l->l_md.md_utf;
__greg_t * const gr = mcp->__gregs;
#if defined(PPC_HAVE_FPU)
struct pcb * const pcb = lwp_getpcb(l);
#endif
(void)memcpy(gr, &tf->tf_fixreg, 32 * sizeof (gr[0]));
gr[_REG_CR] = tf->tf_cr;
gr[_REG_LR] = tf->tf_lr;
gr[_REG_PC] = tf->tf_srr0;
gr[_REG_MSR] = tf->tf_srr1 & PSL_USERSRR1;
#ifdef PPC_HAVE_FPU
gr[_REG_MSR] |= pcb->pcb_flags & (PCB_FE0|PCB_FE1);
#endif
gr[_REG_CTR] = tf->tf_ctr;
gr[_REG_XER] = tf->tf_xer;
#ifdef PPC_OEA
gr[_REG_MQ] = tf->tf_mq;
#else
gr[_REG_MQ] = 0;
#endif
*flagp |= _UC_CPU;
*flagp |= _UC_TLSBASE;
#ifdef PPC_HAVE_FPU
if (!fpu_save_to_mcontext(l, mcp, flagp))
#endif
memset(&mcp->__fpregs, 0, sizeof(mcp->__fpregs));
#if defined(ALTIVEC) || defined(PPC_HAVE_SPE)
if (!vec_save_to_mcontext(l, mcp, flagp))
#endif
memset(&mcp->__vrf, 0, sizeof (mcp->__vrf));
}
int
cpu_mcontext_validate(struct lwp *l, const mcontext_t *mcp)
{
return 0;
}
int
cpu_setmcontext(struct lwp *l, const mcontext_t *mcp, unsigned int flags)
{
struct trapframe * const tf = l->l_md.md_utf;
const __greg_t * const gr = mcp->__gregs;
struct proc * const p = l->l_proc;
int error;
if (flags & _UC_CPU) {
error = cpu_mcontext_validate(l, mcp);
if (error)
return error;
#ifdef PPC_HAVE_FPU
struct pcb * const pcb = lwp_getpcb(l);
pcb->pcb_flags &= ~(PCB_FE0|PCB_FE1);
pcb->pcb_flags |= gr[_REG_MSR] & (PCB_FE0|PCB_FE1);
#endif
__greg_t save_r2 = tf->tf_fixreg[_REG_R2];
(void)memcpy(&tf->tf_fixreg, gr, 32 * sizeof (gr[0]));
tf->tf_fixreg[_REG_R2] = save_r2;
tf->tf_cr = gr[_REG_CR];
tf->tf_lr = gr[_REG_LR];
tf->tf_srr0 = gr[_REG_PC];
tf->tf_srr1 = (gr[_REG_MSR] & PSL_USERMOD) | PSL_USERSET;
tf->tf_ctr = gr[_REG_CTR];
tf->tf_xer = gr[_REG_XER];
#ifdef PPC_OEA
tf->tf_mq = gr[_REG_MQ];
#endif
}
if (flags & _UC_TLSBASE)
lwp_setprivate(l, (void *)(uintptr_t)gr[_REG_R2]);
#ifdef PPC_HAVE_FPU
if (flags & _UC_FPU)
fpu_restore_from_mcontext(l, mcp);
#endif
#ifdef ALTIVEC
if (flags & _UC_POWERPC_VEC)
vec_restore_from_mcontext(l, mcp);
#endif
#ifdef PPC_HAVE_SPE
if (flags & _UC_POWERPC_SPE)
vec_restore_from_mcontext(l, mcp);
#endif
mutex_enter(p->p_lock);
if (flags & _UC_SETSTACK)
l->l_sigstk.ss_flags |= SS_ONSTACK;
if (flags & _UC_CLRSTACK)
l->l_sigstk.ss_flags &= ~SS_ONSTACK;
mutex_exit(p->p_lock);
return (0);
}
int
cpu_lwp_setprivate(lwp_t *l, void *addr)
{
struct trapframe * const tf = l->l_md.md_utf;
tf->tf_fixreg[_REG_R2] = (register_t)addr;
return 0;
}