#include <sys/param.h>
#include <sys/exec.h>
#include <sys/proc.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/syscallsubr.h>
#include <sys/ktr.h>
#include <sys/sysctl.h>
#include <sys/sysent.h>
#include <sys/sysproto.h>
#include <machine/armreg.h>
#include <machine/pcb.h>
#ifdef VFP
#include <machine/vfp.h>
#endif
#include <compat/freebsd32/freebsd32_proto.h>
#include <compat/freebsd32/freebsd32_signal.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/pmap.h>
#include <vm/vm_map.h>
_Static_assert(sizeof(mcontext32_t) == 208, "mcontext32_t size incorrect");
_Static_assert(sizeof(ucontext32_t) == 260, "ucontext32_t size incorrect");
_Static_assert(sizeof(struct __siginfo32) == 64,
"struct __siginfo32 size incorrect");
extern void freebsd32_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask);
SYSCTL_NODE(_compat, OID_AUTO, arm, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
"32-bit mode");
#define UC32_COPY_SIZE offsetof(ucontext32_t, uc_link)
int
freebsd32_sysarch(struct thread *td, struct freebsd32_sysarch_args *uap)
{
int error;
#define ARM_SYNC_ICACHE 0
#define ARM_DRAIN_WRITEBUF 1
#define ARM_SET_TP 2
#define ARM_GET_TP 3
#define ARM_GET_VFPSTATE 4
switch(uap->op) {
case ARM_SET_TP:
WRITE_SPECIALREG(tpidr_el0, uap->parms);
WRITE_SPECIALREG(tpidrro_el0, uap->parms);
return 0;
case ARM_SYNC_ICACHE:
{
struct {
uint32_t addr;
uint32_t size;
} args;
if ((error = copyin(uap->parms, &args, sizeof(args))) != 0)
return (error);
if ((uint64_t)args.addr + (uint64_t)args.size > 0xffffffff)
return (EINVAL);
cpu_icache_sync_range_checked(
(void *)(uintptr_t)args.addr, args.size);
return 0;
}
case ARM_GET_VFPSTATE:
{
mcontext32_vfp_t mcontext_vfp;
struct {
uint32_t mc_vfp_size;
uint32_t mc_vfp;
} args;
if ((error = copyin(uap->parms, &args, sizeof(args))) != 0)
return (error);
if (args.mc_vfp_size != sizeof(mcontext_vfp))
return (EINVAL);
#ifdef VFP
get_fpcontext32(td, &mcontext_vfp);
#else
bzero(&mcontext_vfp, sizeof(mcontext_vfp));
#endif
error = copyout(&mcontext_vfp,
(void *)(uintptr_t)args.mc_vfp,
sizeof(mcontext_vfp));
return error;
}
}
return (EINVAL);
}
#ifdef VFP
void
get_fpcontext32(struct thread *td, mcontext32_vfp_t *mcp)
{
struct pcb *pcb;
int i;
KASSERT(td == curthread || TD_IS_SUSPENDED(td) ||
P_SHOULDSTOP(td->td_proc),
("not suspended thread %p", td));
memset(mcp, 0, sizeof(*mcp));
pcb = td->td_pcb;
if ((pcb->pcb_fpflags & PCB_FP_STARTED) != 0) {
if (td == curthread)
vfp_save_state(td, pcb);
KASSERT(pcb->pcb_fpusaved == &pcb->pcb_fpustate,
("Called get_fpcontext32 while the kernel is using the VFP"));
KASSERT((pcb->pcb_fpflags & ~PCB_FP_USERMASK) == 0,
("Non-userspace FPU flags set in get_fpcontext32"));
for (i = 0; i < 16; i++) {
uint64_t *tmpreg = (uint64_t *)&pcb->pcb_fpustate.vfp_regs[i];
mcp->mcv_reg[i * 2] = tmpreg[0];
mcp->mcv_reg[i * 2 + 1] = tmpreg[1];
}
mcp->mcv_fpscr = VFP_FPSCR_FROM_SRCR(pcb->pcb_fpustate.vfp_fpcr,
pcb->pcb_fpustate.vfp_fpsr);
}
}
void
set_fpcontext32(struct thread *td, mcontext32_vfp_t *mcp)
{
struct pcb *pcb;
int i;
critical_enter();
pcb = td->td_pcb;
if (td == curthread)
vfp_discard(td);
for (i = 0; i < 16; i++) {
uint64_t *tmpreg = (uint64_t *)&pcb->pcb_fpustate.vfp_regs[i];
tmpreg[0] = mcp->mcv_reg[i * 2];
tmpreg[1] = mcp->mcv_reg[i * 2 + 1];
}
pcb->pcb_fpustate.vfp_fpsr = VFP_FPSR_FROM_FPSCR(mcp->mcv_fpscr);
pcb->pcb_fpustate.vfp_fpcr = VFP_FPSR_FROM_FPSCR(mcp->mcv_fpscr);
critical_exit();
}
#endif
static void
get_mcontext32(struct thread *td, mcontext32_t *mcp, int flags)
{
struct trapframe *tf;
int i;
tf = td->td_frame;
if ((flags & GET_MC_CLEAR_RET) != 0) {
mcp->mc_gregset[0] = 0;
mcp->mc_gregset[16] = tf->tf_spsr & ~PSR_C;
} else {
mcp->mc_gregset[0] = tf->tf_x[0];
mcp->mc_gregset[16] = tf->tf_spsr;
}
for (i = 1; i < 15; i++)
mcp->mc_gregset[i] = tf->tf_x[i];
mcp->mc_gregset[15] = tf->tf_elr;
mcp->mc_vfp_size = 0;
mcp->mc_vfp_ptr = 0;
memset(mcp->mc_spare, 0, sizeof(mcp->mc_spare));
}
static int
set_mcontext32(struct thread *td, mcontext32_t *mcp)
{
struct trapframe *tf;
mcontext32_vfp_t mc_vfp;
uint32_t spsr;
int i;
tf = td->td_frame;
spsr = mcp->mc_gregset[16];
if ((spsr & ~(PSR_SETTABLE_32 | PSR_SS)) !=
(tf->tf_spsr & ~(PSR_SETTABLE_32 | PSR_SS)))
return (EINVAL);
spsr &= PSR_SETTABLE_32;
spsr |= tf->tf_spsr & ~PSR_SETTABLE_32;
if ((td->td_dbgflags & TDB_STEP) != 0) {
spsr |= PSR_SS;
td->td_pcb->pcb_flags |= PCB_SINGLE_STEP;
WRITE_SPECIALREG(mdscr_el1,
READ_SPECIALREG(mdscr_el1) | MDSCR_SS);
}
for (i = 0; i < 15; i++)
tf->tf_x[i] = mcp->mc_gregset[i];
tf->tf_elr = mcp->mc_gregset[15];
tf->tf_spsr = spsr;
#ifdef VFP
if (mcp->mc_vfp_size == sizeof(mc_vfp) && mcp->mc_vfp_ptr != 0) {
if (copyin((void *)(uintptr_t)mcp->mc_vfp_ptr, &mc_vfp,
sizeof(mc_vfp)) != 0)
return (EFAULT);
set_fpcontext32(td, &mc_vfp);
}
#endif
return (0);
}
#define UC_COPY_SIZE offsetof(ucontext32_t, uc_link)
int
freebsd32_getcontext(struct thread *td, struct freebsd32_getcontext_args *uap)
{
ucontext32_t uc;
int ret;
if (uap->ucp == NULL)
ret = EINVAL;
else {
memset(&uc, 0, sizeof(uc));
get_mcontext32(td, &uc.uc_mcontext, GET_MC_CLEAR_RET);
PROC_LOCK(td->td_proc);
uc.uc_sigmask = td->td_sigmask;
PROC_UNLOCK(td->td_proc);
ret = copyout(&uc, uap->ucp, UC_COPY_SIZE);
}
return (ret);
}
int
freebsd32_setcontext(struct thread *td, struct freebsd32_setcontext_args *uap)
{
ucontext32_t uc;
int ret;
if (uap->ucp == NULL)
ret = EINVAL;
else {
ret = copyin(uap->ucp, &uc, UC_COPY_SIZE);
if (ret == 0) {
ret = set_mcontext32(td, &uc.uc_mcontext);
if (ret == 0)
kern_sigprocmask(td, SIG_SETMASK, &uc.uc_sigmask,
NULL, 0);
}
}
return (ret);
}
int
freebsd32_sigreturn(struct thread *td, struct freebsd32_sigreturn_args *uap)
{
ucontext32_t uc;
int error;
if (uap == NULL)
return (EFAULT);
if (copyin(uap->sigcntxp, &uc, sizeof(uc)))
return (EFAULT);
error = set_mcontext32(td, &uc.uc_mcontext);
if (error != 0)
return (0);
kern_sigprocmask(td, SIG_SETMASK, &uc.uc_sigmask, NULL, 0);
return (EJUSTRETURN);
}
int
freebsd32_swapcontext(struct thread *td, struct freebsd32_swapcontext_args *uap)
{
ucontext32_t uc;
int ret;
if (uap->oucp == NULL || uap->ucp == NULL)
ret = EINVAL;
else {
bzero(&uc, sizeof(uc));
get_mcontext32(td, &uc.uc_mcontext, GET_MC_CLEAR_RET);
PROC_LOCK(td->td_proc);
uc.uc_sigmask = td->td_sigmask;
PROC_UNLOCK(td->td_proc);
ret = copyout(&uc, uap->oucp, UC32_COPY_SIZE);
if (ret == 0) {
ret = copyin(uap->ucp, &uc, UC32_COPY_SIZE);
if (ret == 0) {
ret = set_mcontext32(td, &uc.uc_mcontext);
kern_sigprocmask(td, SIG_SETMASK,
&uc.uc_sigmask, NULL, 0);
}
}
}
return (ret);
}
void
freebsd32_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
{
struct thread *td;
struct proc *p;
struct trapframe *tf;
struct sigframe32 *fp, frame;
struct sigacts *psp;
struct __siginfo32 siginfo;
struct sysentvec *sysent;
int onstack;
int sig;
siginfo_to_siginfo32(&ksi->ksi_info, &siginfo);
td = curthread;
p = td->td_proc;
PROC_LOCK_ASSERT(p, MA_OWNED);
sig = ksi->ksi_signo;
psp = p->p_sigacts;
mtx_assert(&psp->ps_mtx, MA_OWNED);
tf = td->td_frame;
onstack = sigonstack(tf->tf_x[13]);
CTR4(KTR_SIG, "sendsig: td=%p (%s) catcher=%p sig=%d", td, p->p_comm,
catcher, sig);
if ((td->td_pflags & TDP_ALTSTACK) != 0 && !(onstack) &&
SIGISMEMBER(psp->ps_sigonstack, sig)) {
fp = (struct sigframe32 *)((uintptr_t)td->td_sigstk.ss_sp +
td->td_sigstk.ss_size);
#if defined(COMPAT_43)
td->td_sigstk.ss_flags |= SS_ONSTACK;
#endif
} else
fp = (struct sigframe32 *)td->td_frame->tf_x[13];
fp--;
fp = (struct sigframe32 *)((unsigned long)(fp) &~ (8 - 1));
get_mcontext32(td, &frame.sf_uc.uc_mcontext, 0);
#ifdef VFP
get_fpcontext32(td, &frame.sf_vfp);
frame.sf_uc.uc_mcontext.mc_vfp_size = sizeof(fp->sf_vfp);
frame.sf_uc.uc_mcontext.mc_vfp_ptr = (uint32_t)(uintptr_t)&fp->sf_vfp;
#else
frame.sf_uc.uc_mcontext.mc_vfp_size = 0;
frame.sf_uc.uc_mcontext.mc_vfp_ptr = (uint32_t)NULL;
#endif
frame.sf_si = siginfo;
frame.sf_uc.uc_sigmask = *mask;
frame.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK )
? ((onstack) ? SS_ONSTACK : 0) : SS_DISABLE;
frame.sf_uc.uc_stack.ss_sp = (uintptr_t)td->td_sigstk.ss_sp;
frame.sf_uc.uc_stack.ss_size = td->td_sigstk.ss_size;
mtx_unlock(&psp->ps_mtx);
PROC_UNLOCK(td->td_proc);
if (copyout(&frame, fp, sizeof(*fp)) != 0) {
CTR2(KTR_SIG, "sendsig: sigexit td=%p fp=%p", td, fp);
PROC_LOCK(p);
sigexit(td, SIGILL);
}
tf->tf_x[0] = sig;
tf->tf_x[1] = (register_t)&fp->sf_si;
tf->tf_x[2] = (register_t)&fp->sf_uc;
tf->tf_x[5] = (register_t)&fp->sf_uc;
tf->tf_elr = (register_t)catcher;
tf->tf_x[13] = (register_t)fp;
sysent = p->p_sysent;
if (PROC_HAS_SHP(p))
tf->tf_x[14] = (register_t)PROC_SIGCODE(p);
else
tf->tf_x[14] = (register_t)(PROC_PS_STRINGS(p) -
*(sysent->sv_szsigcode));
if ((register_t)catcher & 1)
tf->tf_spsr |= PSR_T;
else
tf->tf_spsr &= ~PSR_T;
if ((td->td_pcb->pcb_flags & PCB_SINGLE_STEP) != 0) {
td->td_pcb->pcb_flags &= ~PCB_SINGLE_STEP;
WRITE_SPECIALREG(mdscr_el1,
READ_SPECIALREG(mdscr_el1) & ~MDSCR_SS);
isb();
}
CTR3(KTR_SIG, "sendsig: return td=%p pc=%#x sp=%#x", td, tf->tf_x[14],
tf->tf_x[13]);
PROC_LOCK(p);
mtx_lock(&psp->ps_mtx);
}
#ifdef COMPAT_43
int
ofreebsd32_sigreturn(struct thread *td, struct ofreebsd32_sigreturn_args *uap)
{
return (nosys(td, (struct nosys_args *)uap));
}
#endif