netbsd/sys/arch/mips/mips/netbsd32_machdep.c
2015-10-15 10:25:28 +02:00

357 lines
9.4 KiB
C

/* $NetBSD: netbsd32_machdep.c,v 1.12 2015/05/17 18:52:37 matt Exp $ */
/*-
* Copyright (c) 2009 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Matt Thomas <matt@3am-software.com>.
*
* 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.
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``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 FOUNDATION OR CONTRIBUTORS
* 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>
__KERNEL_RCSID(0, "$NetBSD: netbsd32_machdep.c,v 1.12 2015/05/17 18:52:37 matt Exp $");
#include "opt_compat_netbsd.h"
#include "opt_coredump.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/ioctl.h>
#include <sys/exec.h>
#include <sys/cpu.h>
#include <sys/core.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/proc.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/buf.h>
#include <sys/signal.h>
#include <sys/signalvar.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
#include <compat/netbsd32/netbsd32.h>
#include <compat/netbsd32/netbsd32_exec.h>
#include <compat/netbsd32/netbsd32_syscallargs.h>
#include <mips/cache.h>
#include <mips/sysarch.h>
#include <mips/cachectl.h>
#include <mips/locore.h>
#include <mips/frame.h>
#include <mips/regnum.h>
#include <mips/pcb.h>
#include <uvm/uvm_extern.h>
const char machine32[] = MACHINE;
const char machine_arch32[] = MACHINE32_ARCH;
#if 0
cpu_coredump32
netbsd32_cpu_upcall
netbsd32_vm_default_addr
#endif
int
netbsd32_sysarch(struct lwp *l, const struct netbsd32_sysarch_args *uap,
register_t *retval)
{
/* {
syscallarg(int) op;
syscallarg(netbsd32_voidp) parms;
} */
struct proc *p = l->l_proc;
void *parms = SCARG_P32(uap, parms);
int error = 0;
switch(SCARG(uap, op)) {
case MIPS_CACHEFLUSH: {
struct mips_cacheflush_args32 cfua;
error = copyin(parms, &cfua, sizeof(cfua));
if (error != 0)
return (error);
error = mips_user_cacheflush(p, cfua.va, cfua.nbytes,
cfua.whichcache);
break;
}
case MIPS_CACHECTL: {
struct mips_cachectl_args32 ccua;
error = copyin(parms, &ccua, sizeof(ccua));
if (error != 0)
return (error);
error = mips_user_cachectl(p, ccua.va, ccua.nbytes, ccua.ctl);
break;
}
default:
error = ENOSYS;
break;
}
return (error);
}
#ifdef COMPAT_13
int
compat_13_netbsd32_sigreturn(struct lwp *l,
const struct compat_13_netbsd32_sigreturn_args *uap,
register_t *retval)
{
struct compat_13_sys_sigreturn_args ua;
NETBSD32TOP_UAP(sigcntxp, struct sigcontext13 *);
return compat_13_sys_sigreturn(l, &ua, retval);
}
#endif
#ifdef COMPAT_16
int
compat_16_netbsd32___sigreturn14(struct lwp *l,
const struct compat_16_netbsd32___sigreturn14_args *uap,
register_t *retval)
{
struct compat_16_sys___sigreturn14_args ua;
NETBSD32TOP_UAP(sigcntxp, struct sigcontext *);
return compat_16_sys___sigreturn14(l, &ua, retval);
}
#endif
vaddr_t
netbsd32_vm_default_addr(struct proc *p, vaddr_t base, vsize_t size)
{
if (p->p_vmspace->vm_map.flags & VM_MAP_TOPDOWN)
return VM_DEFAULT_ADDRESS32_TOPDOWN(base, size);
else
return VM_DEFAULT_ADDRESS32_BOTTOMUP(base, size);
}
struct sigframe_siginfo32 {
siginfo32_t sf_si;
ucontext32_t sf_uc;
};
/*
* Send a signal to process.
*/
static void
netbsd32_sendsig_siginfo(const ksiginfo_t *ksi, const sigset_t *mask)
{
struct lwp * const l = curlwp;
struct proc * const p = l->l_proc;
struct sigacts * const ps = p->p_sigacts;
int onstack, error;
int sig = ksi->ksi_signo;
struct sigframe_siginfo32 *sfp = getframe(l, sig, &onstack);
struct sigframe_siginfo32 sf;
struct trapframe * const tf = l->l_md.md_utf;
size_t sfsz;
sig_t catcher = SIGACTION(p, sig).sa_handler;
sfp--;
netbsd32_si_to_si32(&sf.sf_si, (const siginfo_t *)&ksi->ksi_info);
/* Build stack frame for signal trampoline. */
switch (ps->sa_sigdesc[sig].sd_vers) {
case 0: /* handled by sendsig_sigcontext */
case 1: /* handled by sendsig_sigcontext */
default: /* unknown version */
printf("sendsig_siginfo: bad version %d\n",
ps->sa_sigdesc[sig].sd_vers);
sigexit(l, SIGILL);
case 2:
break;
}
sf.sf_uc.uc_flags = _UC_SIGMASK
| ((l->l_sigstk.ss_flags & SS_ONSTACK)
? _UC_SETSTACK : _UC_CLRSTACK);
sf.sf_uc.uc_sigmask = *mask;
sf.sf_uc.uc_link = (intptr_t)l->l_ctxlink;
memset(&sf.sf_uc.uc_stack, 0, sizeof(sf.sf_uc.uc_stack));
sfsz = offsetof(struct sigframe_siginfo32, sf_uc.uc_mcontext);
if (p->p_md.md_abi == _MIPS_BSD_API_O32)
sfsz += sizeof(mcontext_o32_t);
else
sfsz += sizeof(mcontext32_t);
sendsig_reset(l, sig);
mutex_exit(p->p_lock);
cpu_getmcontext32(l, &sf.sf_uc.uc_mcontext, &sf.sf_uc.uc_flags);
error = copyout(&sf, sfp, sfsz);
mutex_enter(p->p_lock);
if (error != 0) {
/*
* Process has trashed its stack; give it an illegal
* instruction to halt it in its tracks.
*/
sigexit(l, SIGILL);
/* NOTREACHED */
}
/*
* Set up the registers to directly invoke the signal
* handler. The return address will be set up to point
* to the signal trampoline to bounce us back.
*/
tf->tf_regs[_R_A0] = sig;
tf->tf_regs[_R_A1] = (intptr_t)&sfp->sf_si;
tf->tf_regs[_R_A2] = (intptr_t)&sfp->sf_uc;
tf->tf_regs[_R_PC] = (intptr_t)catcher;
tf->tf_regs[_R_T9] = (intptr_t)catcher;
tf->tf_regs[_R_SP] = (intptr_t)sfp;
tf->tf_regs[_R_RA] = (intptr_t)ps->sa_sigdesc[sig].sd_tramp;
/* Remember that we're now on the signal stack. */
if (onstack)
l->l_sigstk.ss_flags |= SS_ONSTACK;
}
void
netbsd32_sendsig(const ksiginfo_t *ksi, const sigset_t *mask)
{
#ifdef COMPAT_16
if (curproc->p_sigacts->sa_sigdesc[ksi->ksi_signo].sd_vers < 2)
sendsig_sigcontext(ksi, mask);
else
#endif
netbsd32_sendsig_siginfo(ksi, mask);
}
void
cpu_getmcontext32(struct lwp *l, mcontext32_t *mc32, unsigned int *flagsp)
{
mcontext_o32_t * const mco32 = (mcontext_o32_t *)mc32;
mcontext_t mc;
size_t i;
if (l->l_proc->p_md.md_abi == _MIPS_BSD_API_N32) {
cpu_getmcontext(l, (mcontext_t *)mc32, flagsp);
return;
}
cpu_getmcontext(l, &mc, flagsp);
for (i = 1; i < __arraycount(mc.__gregs); i++)
mco32->__gregs[i] = mc.__gregs[i];
if (*flagsp & _UC_FPU)
memcpy(&mco32->__fpregs, &mc.__fpregs,
sizeof(struct fpreg_oabi));
mco32->_mc_tlsbase = mc._mc_tlsbase;
*flagsp |= _UC_TLSBASE;
}
int
cpu_mcontext32_validate(struct lwp *l, const mcontext32_t *mc32)
{
return 0;
}
int
cpu_setmcontext32(struct lwp *l, const mcontext32_t *mc32, unsigned int flags)
{
const mcontext_o32_t * const mco32 = (const mcontext_o32_t *)mc32;
mcontext_t mc;
size_t i, error;
if (flags & _UC_CPU) {
error = cpu_mcontext32_validate(l, mc32);
if (error)
return error;
}
if (l->l_proc->p_md.md_abi == _MIPS_BSD_API_N32)
return cpu_setmcontext(l, (const mcontext_t *)mc32, flags);
for (i = 0; i < __arraycount(mc.__gregs); i++)
mc.__gregs[i] = mco32->__gregs[i];
if (flags & _UC_FPU)
memcpy(&mc.__fpregs, &mco32->__fpregs,
sizeof(struct fpreg_oabi));
mc._mc_tlsbase = mco32->_mc_tlsbase;
return cpu_setmcontext(l, &mc, flags);
}
#ifdef COREDUMP
/*
* Dump the machine specific segment at the start of a core dump.
*/
int
cpu_coredump32(struct lwp *l, struct coredump_iostate *iocookie,
struct core32 *chdr)
{
int error;
struct coreseg cseg;
struct cpustate {
struct trapframe frame;
struct fpreg fpregs;
} cpustate;
if (iocookie == NULL) {
CORE_SETMAGIC(*chdr, COREMAGIC, MID_MACHINE, 0);
chdr->c_hdrsize = ALIGN(sizeof(struct core));
chdr->c_seghdrsize = ALIGN(sizeof(struct coreseg));
chdr->c_cpusize = sizeof(struct cpustate);
chdr->c_nseg++;
return 0;
}
KASSERT(l == curlwp);
fpu_save();
struct pcb * const pcb = lwp_getpcb(l);
cpustate.frame = *l->l_md.md_utf;
cpustate.fpregs = pcb->pcb_fpregs;
CORE_SETMAGIC(cseg, CORESEGMAGIC, MID_MACHINE, CORE_CPU);
cseg.c_addr = 0;
cseg.c_size = chdr->c_cpusize;
error = coredump_write(iocookie, UIO_SYSSPACE, &cseg,
chdr->c_seghdrsize);
if (error)
return error;
return coredump_write(iocookie, UIO_SYSSPACE, &cpustate,
chdr->c_cpusize);
}
#endif
int
cpu_machinearch32(SYSCTLFN_ARGS)
{
struct sysctlnode node = *rnode;
const char *march = l->l_proc->p_md.md_abi == _MIPS_BSD_API_O32
? machine_arch32 : machine_arch;
node.sysctl_data = __UNCONST(march);
node.sysctl_size = strlen(march) + 1;
return sysctl_lookup(SYSCTLFN_CALL(&node));
}