#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: netbsd32_machdep.c,v 1.25 2024/06/18 13:29:56 rin Exp $");
#if defined(_KERNEL_OPT)
#include "opt_compat_netbsd.h"
#endif
#include <sys/param.h>
#include <sys/core.h>
#include <sys/exec.h>
#include <sys/lwp.h>
#include <sys/ptrace.h>
#include <sys/ras.h>
#include <sys/signalvar.h>
#include <sys/syscallargs.h>
#include <sys/compat_stub.h>
#include <uvm/uvm_extern.h>
#include <compat/netbsd32/netbsd32.h>
#include <compat/netbsd32/netbsd32_exec.h>
#include <compat/netbsd32/netbsd32_syscallargs.h>
#include <aarch64/armreg.h>
#include <aarch64/frame.h>
#include <aarch64/machdep.h>
#include <aarch64/userret.h>
#include <arm/cpufunc.h>
const char machine32[] = MACHINE;
const char machine_arch32[] = MACHINE32_ARCH;
void
netbsd32_setregs(struct lwp *l, struct exec_package *pack, vaddr_t stack)
{
struct proc * const p = l->l_proc;
struct trapframe * const tf = lwp_trapframe(l);
netbsd32_adjust_limits(p);
aarch64_setregs_ptrauth(l, false);
p->p_flag |= PK_32;
memset(tf, 0, sizeof(*tf));
tf->tf_reg[0] = (uint32_t)p->p_psstrp;
tf->tf_reg[12] = stack;
tf->tf_reg[13] = stack;
tf->tf_reg[14] = pack->ep_entry;
tf->tf_reg[18] = 0x77777777;
tf->tf_pc = pack->ep_entry;
#ifdef __AARCH64EB__
tf->tf_spsr = SPSR_M_USR32 | SPSR_A32_E;
#else
tf->tf_spsr = SPSR_M_USR32;
#endif
if (pack->ep_entry & 1)
tf->tf_spsr |= SPSR_A32_T;
}
int
netbsd32_ptrace_translate_request(int req)
{
switch (req) {
case 0 ... PT_FIRSTMACH - 1:
return req;
case PT32_GETREGS:
return PT_GETREGS;
case PT32_SETREGS:
return PT_SETREGS;
case PT32_GETFPREGS:
return PT_GETFPREGS;
case PT32_SETFPREGS:
return PT_SETFPREGS;
case PT32_STEP:
case PT32_SETSTEP:
case PT32_CLEARSTEP:
default:
return -1;
}
}
#define FPSR_BITS \
(FPSR_N32|FPSR_Z32|FPSR_C32|FPSR_V32|FPSR_QC| \
FPSR_IDC|FPSR_IXC|FPSR_UFC|FPSR_OFC|FPSR_DZC|FPSR_IOC)
#define FPCR_BITS \
(FPCR_AHP|FPCR_DN|FPCR_FZ|FPCR_RMODE|FPCR_STRIDE|FPCR_LEN| \
FPCR_IDE|FPCR_IXE|FPCR_UFE|FPCR_OFE|FPCR_DZE|FPCR_IOE)
int
netbsd32_process_read_regs(struct lwp *l, struct reg32 *regs)
{
struct proc * const p = l->l_proc;
struct trapframe *tf = lwp_trapframe(l);
int i;
if ((p->p_flag & PK_32) == 0)
return EINVAL;
for (i = 0; i < 13; i++)
regs->r[i] = tf->tf_reg[i];
regs->r_sp = tf->tf_reg[13];
regs->r_lr = tf->tf_reg[14];
regs->r_pc = tf->tf_pc;
regs->r_cpsr = tf->tf_spsr;
if (tf->tf_spsr & SPSR_A32_T)
regs->r_pc |= 1;
return 0;
}
int
netbsd32_process_read_fpregs(struct lwp *l, struct fpreg32 *fpregs,
size_t *lenp)
{
struct proc * const p = l->l_proc;
struct pcb * const pcb = lwp_getpcb(l);
int i, j;
if ((p->p_flag & PK_32) == 0)
return EINVAL;
KASSERT(*lenp <= sizeof(*fpregs));
fpu_save(l);
#define VFP_FPEXC_EN 0x40000000
#define VFP_FPEXC_VECITR 0x00000700
fpregs->fpr_vfp.vfp_fpexc = VFP_FPEXC_EN | VFP_FPEXC_VECITR;
fpregs->fpr_vfp.vfp_fpscr =
(pcb->pcb_fpregs.fpsr & FPSR_BITS) |
(pcb->pcb_fpregs.fpcr & FPCR_BITS);
fpregs->fpr_vfp.vfp_fpinst = 0;
fpregs->fpr_vfp.vfp_fpinst2 = 0;
for (i = j = 0; i < 16; i++) {
#ifdef __AARCH64EB__
fpregs->fpr_vfp.vfp_regs[j++] =
pcb->pcb_fpregs.fp_reg[i].u64[1];
fpregs->fpr_vfp.vfp_regs[j++] =
pcb->pcb_fpregs.fp_reg[i].u64[0];
#else
fpregs->fpr_vfp.vfp_regs[j++] =
pcb->pcb_fpregs.fp_reg[i].u64[0];
fpregs->fpr_vfp.vfp_regs[j++] =
pcb->pcb_fpregs.fp_reg[i].u64[1];
#endif
}
return 0;
}
int
netbsd32_process_write_regs(struct lwp *l, const struct reg32 *regs)
{
struct proc * const p = l->l_proc;
struct trapframe *tf = lwp_trapframe(l);
int i;
if ((p->p_flag & PK_32) == 0)
return EINVAL;
if (regs->r_pc >= VM_MAXUSER_ADDRESS32 ||
regs->r_sp >= VM_MAXUSER_ADDRESS32)
return EINVAL;
for (i = 0; i < 13; i++)
tf->tf_reg[i] = regs->r[i];
tf->tf_reg[13] = regs->r_sp;
tf->tf_reg[14] = regs->r_lr;
tf->tf_pc = regs->r_pc;
tf->tf_spsr &= ~(SPSR_NZCV | SPSR_A32_T);
tf->tf_spsr |= regs->r_cpsr & (SPSR_NZCV | SPSR_A32_T);
if (regs->r_pc & 1)
tf->tf_spsr |= SPSR_A32_T;
return 0;
}
int
netbsd32_process_write_fpregs(struct lwp *l, const struct fpreg32 *fpregs,
size_t len)
{
struct proc * const p = l->l_proc;
struct pcb * const pcb = lwp_getpcb(l);
int i, j;
if ((p->p_flag & PK_32) == 0)
return EINVAL;
KASSERT(len <= sizeof(*fpregs));
fpu_discard(l, true);
pcb->pcb_fpregs.fpsr = fpregs->fpr_vfp.vfp_fpscr & FPSR_BITS;
pcb->pcb_fpregs.fpcr = fpregs->fpr_vfp.vfp_fpscr & FPCR_BITS;
for (i = j = 0; i < 16; i++) {
#ifdef __AARCH64EB__
pcb->pcb_fpregs.fp_reg[i].u64[1] =
fpregs->fpr_vfp.vfp_regs[j++];
pcb->pcb_fpregs.fp_reg[i].u64[0] =
fpregs->fpr_vfp.vfp_regs[j++];
#else
pcb->pcb_fpregs.fp_reg[i].u64[0] =
fpregs->fpr_vfp.vfp_regs[j++];
pcb->pcb_fpregs.fp_reg[i].u64[1] =
fpregs->fpr_vfp.vfp_regs[j++];
#endif
}
return 0;
}
int
cpu_coredump32(struct lwp *l, struct coredump_iostate *iocookie,
struct core32 *chdr)
{
struct netbsd32_cpustate md_core32;
struct coreseg32 cseg;
int error;
if (iocookie == NULL) {
CORE_SETMAGIC(*chdr, COREMAGIC, MID_ARM6, 0);
chdr->c_hdrsize = ALIGN32(sizeof(*chdr));
chdr->c_seghdrsize = ALIGN32(sizeof(cseg));
chdr->c_cpusize = sizeof(md_core32);
chdr->c_nseg++;
return 0;
}
error = netbsd32_process_read_regs(l, &md_core32.regs);
if (error)
return error;
error = netbsd32_process_read_fpregs(l, &md_core32.fpregs, NULL);
if (error)
return error;
CORE_SETMAGIC(cseg, CORESEGMAGIC, MID_ARM6, CORE_CPU);
cseg.c_addr = 0;
cseg.c_size = chdr->c_cpusize;
MODULE_HOOK_CALL(coredump_write_hook, (iocookie, UIO_SYSSPACE,
&cseg, chdr->c_seghdrsize), ENOSYS, error);
if (error)
return error;
MODULE_HOOK_CALL(coredump_write_hook, (iocookie, UIO_SYSSPACE,
&md_core32, sizeof(md_core32)), ENOSYS, error);
return error;
}
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 trapframe * const tf = lwp_trapframe(l);
stack_t * const ss = &l->l_sigstk;
const int signo = ksi->ksi_signo;
const struct sigaction * const sa = &SIGACTION(p, signo);
const struct sigact_sigdesc * const sdesc =
&p->p_sigacts->sa_sigdesc[signo];
const sig_t handler = sa->sa_handler;
struct netbsd32_sigframe_siginfo *fp, frame;
int error;
const bool onstack_p =
(ss->ss_flags & (SS_DISABLE | SS_ONSTACK)) == 0 &&
(sa->sa_flags & SA_ONSTACK);
vaddr_t sp = onstack_p ?
((vaddr_t)ss->ss_sp + ss->ss_size) :
tf->tf_reg[13];
fp = (struct netbsd32_sigframe_siginfo *)sp;
fp = (struct netbsd32_sigframe_siginfo *)STACK_ALIGN(fp - 1, (8 - 1));
memset(&frame, 0, sizeof(frame));
netbsd32_si_to_si32(&frame.sf_si, (const siginfo_t *)&ksi->ksi_info);
frame.sf_uc.uc_flags = _UC_SIGMASK;
frame.sf_uc.uc_sigmask = *mask;
frame.sf_uc.uc_link = (uint32_t)(uintptr_t)l->l_ctxlink;
frame.sf_uc.uc_flags |= (l->l_sigstk.ss_flags & SS_ONSTACK) ?
_UC_SETSTACK : _UC_CLRSTACK;
sendsig_reset(l, signo);
mutex_exit(p->p_lock);
cpu_getmcontext32(l, &frame.sf_uc.uc_mcontext, &frame.sf_uc.uc_flags);
error = copyout(&frame, fp, sizeof(frame));
mutex_enter(p->p_lock);
if (error != 0) {
sigexit(l, SIGILL);
}
tf->tf_reg[0] = signo;
tf->tf_reg[1] = (uint32_t)(uintptr_t)&fp->sf_si;
tf->tf_reg[2] = (uint32_t)(uintptr_t)&fp->sf_uc;
tf->tf_reg[5] = (uint32_t)(uintptr_t)&fp->sf_uc;
tf->tf_pc = (uint32_t)(uintptr_t)handler;
if (((uintptr_t)handler) & 1)
tf->tf_spsr |= SPSR_A32_T;
else
tf->tf_spsr &= ~SPSR_A32_T;
tf->tf_reg[13] = (uint32_t)(uintptr_t)fp;
tf->tf_reg[14] = (uint32_t)(uintptr_t)sdesc->sd_tramp;
if (onstack_p)
ss->ss_flags |= SS_ONSTACK;
}
void
startlwp32(void *arg)
{
ucontext32_t *uc = arg;
lwp_t *l = curlwp;
int error __diagused;
CTASSERT(sizeof(ucontext_t) >= sizeof(ucontext32_t));
error = cpu_setmcontext32(l, &uc->uc_mcontext, uc->uc_flags);
KASSERT(error == 0);
kmem_free(uc, sizeof(ucontext_t));
userret(l);
}
int
cpu_mcontext32_validate(struct lwp *l, const mcontext32_t *mcp)
{
struct proc * const p __diagused = l->l_proc;
const uint32_t spsr = mcp->__gregs[_REG_CPSR];
KASSERT(p->p_flag & PK_32);
if (__SHIFTOUT(spsr, SPSR_M) != SPSR_M_USR32)
return EINVAL;
#ifdef __AARCH64EB__
if ((spsr & SPSR_A32_E) == 0)
return EINVAL;
#else
if ((spsr & SPSR_A32_E) != 0)
return EINVAL;
#endif
if ((spsr & (SPSR_A|SPSR_I|SPSR_F)) != 0)
return EINVAL;
return 0;
}
void
cpu_getmcontext32(struct lwp *l, mcontext32_t *mcp, unsigned int *flagsp)
{
struct trapframe * const tf = lwp_trapframe(l);
__greg32_t *gr = mcp->__gregs;
void *ras_pc;
gr[_REG_R0] = tf->tf_reg[0];
gr[_REG_R1] = tf->tf_reg[1];
gr[_REG_R2] = tf->tf_reg[2];
gr[_REG_R3] = tf->tf_reg[3];
gr[_REG_R4] = tf->tf_reg[4];
gr[_REG_R5] = tf->tf_reg[5];
gr[_REG_R6] = tf->tf_reg[6];
gr[_REG_R7] = tf->tf_reg[7];
gr[_REG_R8] = tf->tf_reg[8];
gr[_REG_R9] = tf->tf_reg[9];
gr[_REG_R10] = tf->tf_reg[10];
gr[_REG_R11] = tf->tf_reg[11];
gr[_REG_R12] = tf->tf_reg[12];
gr[_REG_R13] = tf->tf_reg[13];
gr[_REG_R14] = tf->tf_reg[14];
gr[_REG_R15] = tf->tf_pc;
gr[_REG_CPSR] = tf->tf_spsr;
ras_pc = ras_lookup(l->l_proc, (void *)(uintptr_t)gr[_REG_R15]);
if (ras_pc != (void *)-1) {
gr[_REG_R15] = (__greg32_t)(uintptr_t)ras_pc;
}
*flagsp |= _UC_CPU;
if (fpu_used_p(l)) {
const struct pcb * const pcb = lwp_getpcb(l);
int i, j;
fpu_save(l);
for (i = j = 0; i < 16; i++) {
#ifdef __AARCH64EB__
mcp->__vfpregs.__vfp_fstmx[j++] =
pcb->pcb_fpregs.fp_reg[i].u64[1];
mcp->__vfpregs.__vfp_fstmx[j++] =
pcb->pcb_fpregs.fp_reg[i].u64[0];
#else
mcp->__vfpregs.__vfp_fstmx[j++] =
pcb->pcb_fpregs.fp_reg[i].u64[0];
mcp->__vfpregs.__vfp_fstmx[j++] =
pcb->pcb_fpregs.fp_reg[i].u64[1];
#endif
}
mcp->__vfpregs.__vfp_fpscr =
(pcb->pcb_fpregs.fpsr & FPSR_BITS) |
(pcb->pcb_fpregs.fpcr & FPCR_BITS);
mcp->__vfpregs.__vfp_fpsid = 0;
*flagsp |= _UC_FPU;
}
mcp->_mc_tlsbase = (uint32_t)(uintptr_t)l->l_private;
*flagsp |= _UC_TLSBASE;
}
int
cpu_setmcontext32(struct lwp *l, const mcontext32_t *mcp, unsigned int flags)
{
struct trapframe * const tf = lwp_trapframe(l);
const __greg32_t * const gr = mcp->__gregs;
struct proc * const p = l->l_proc;
int error, i, j;
if (flags & _UC_CPU) {
error = cpu_mcontext32_validate(l, mcp);
if (error != 0)
return error;
tf->tf_reg[0] = gr[_REG_R0];
tf->tf_reg[1] = gr[_REG_R1];
tf->tf_reg[2] = gr[_REG_R2];
tf->tf_reg[3] = gr[_REG_R3];
tf->tf_reg[4] = gr[_REG_R4];
tf->tf_reg[5] = gr[_REG_R5];
tf->tf_reg[6] = gr[_REG_R6];
tf->tf_reg[7] = gr[_REG_R7];
tf->tf_reg[8] = gr[_REG_R8];
tf->tf_reg[9] = gr[_REG_R9];
tf->tf_reg[10] = gr[_REG_R10];
tf->tf_reg[11] = gr[_REG_R11];
tf->tf_reg[12] = gr[_REG_R12];
tf->tf_reg[13] = gr[_REG_R13];
tf->tf_reg[14] = gr[_REG_R14];
tf->tf_pc = gr[_REG_R15];
tf->tf_spsr = gr[_REG_CPSR];
}
if (flags & _UC_FPU) {
struct pcb * const pcb = lwp_getpcb(l);
fpu_discard(l, true);
for (i = j = 0; i < 16; i++) {
#ifdef __AARCH64EB__
pcb->pcb_fpregs.fp_reg[i].u64[1] =
mcp->__vfpregs.__vfp_fstmx[j++];
pcb->pcb_fpregs.fp_reg[i].u64[0] =
mcp->__vfpregs.__vfp_fstmx[j++];
#else
pcb->pcb_fpregs.fp_reg[i].u64[0] =
mcp->__vfpregs.__vfp_fstmx[j++];
pcb->pcb_fpregs.fp_reg[i].u64[1] =
mcp->__vfpregs.__vfp_fstmx[j++];
#endif
}
pcb->pcb_fpregs.fpsr =
mcp->__vfpregs.__vfp_fpscr & FPSR_BITS;
pcb->pcb_fpregs.fpcr =
mcp->__vfpregs.__vfp_fpscr & FPCR_BITS;
}
if (flags &_UC_TLSBASE)
l->l_private = (void *)(uintptr_t)mcp->_mc_tlsbase;
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;
}
static int
arm32_sync_icache(struct lwp *l, const void *args, register_t *retval)
{
struct netbsd32_arm_sync_icache_args ua;
struct faultbuf fb;
int error;
error = copyin(args, &ua, sizeof(ua));
if (error != 0)
return error;
if ((vaddr_t)ua.addr + ua.len > VM_MAXUSER_ADDRESS32)
return EINVAL;
if ((error = cpu_set_onfault(&fb)) == 0) {
pmap_icache_sync_range(
vm_map_pmap(&l->l_proc->p_vmspace->vm_map),
(vaddr_t)ua.addr, (vaddr_t)ua.addr + ua.len);
cpu_unset_onfault();
}
*retval = 0;
return error;
}
static int
arm32_drain_writebuf(struct lwp *l, const void *args, register_t *retval)
{
aarch64_drain_writebuf();
*retval = 0;
return 0;
}
int
netbsd32_sysarch(struct lwp *l, const struct netbsd32_sysarch_args *uap,
register_t *retval)
{
int error;
switch (SCARG(uap, op)) {
case ARM_SYNC_ICACHE:
error = arm32_sync_icache(l,
NETBSD32PTR64(SCARG(uap, parms)), retval);
break;
case ARM_DRAIN_WRITEBUF:
error = arm32_drain_writebuf(l,
NETBSD32PTR64(SCARG(uap, parms)), retval);
break;
case ARM_VFP_FPSCR:
printf("%s: ARM_VFP_FPSCR not implemented\n", __func__);
error = EINVAL;
break;
case ARM_FPU_USED:
printf("%s: ARM_FPU_USED not implemented\n", __func__);
error = EINVAL;
break;
default:
printf("%s: op=%d: not implemented\n", __func__,
SCARG(uap, op));
error = EINVAL;
break;
}
return error;
}
vaddr_t
netbsd32_vm_default_addr(struct proc *p, vaddr_t base, vsize_t sz,
int topdown)
{
if (topdown)
return VM_DEFAULT_ADDRESS32_TOPDOWN(base, sz);
else
return VM_DEFAULT_ADDRESS32_BOTTOMUP(base, sz);
}
void
netbsd32_machdep_md_init(void)
{
}
void
netbsd32_machdep_md_fini(void)
{
}