#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: syscall.c,v 1.33 2023/10/05 19:41:05 ad Exp $");
#include "opt_sparc_arch.h"
#include "opt_multiprocessor.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/signal.h>
#include <sys/syscall.h>
#include <sys/syscallvar.h>
#include <sys/ktrace.h>
#include <uvm/uvm_extern.h>
#include <sparc/sparc/asm.h>
#include <machine/cpu.h>
#include <machine/ctlreg.h>
#include <machine/trap.h>
#include <machine/instr.h>
#include <machine/pmap.h>
#include <machine/userret.h>
#ifdef DDB
#include <machine/db_machdep.h>
#else
#include <machine/frame.h>
#endif
#include <sparc/fpu/fpu_extern.h>
#include <sparc/sparc/memreg.h>
#include <sparc/sparc/cpuvar.h>
#define MAXARGS 8
union args {
uint64_t aligned;
register_t i[MAXARGS];
};
union rval {
uint64_t aligned;
register_t o[2];
};
static inline int handle_new(struct trapframe *, register_t *);
static inline int getargs(struct proc *p, struct trapframe *,
register_t *, const struct sysent **, union args *);
#ifdef FPU_DEBUG
static inline void save_fpu(struct trapframe *);
#endif
void syscall(register_t, struct trapframe *, register_t);
static inline int
handle_new(struct trapframe *tf, register_t *code)
{
int new = *code & (SYSCALL_G7RFLAG|SYSCALL_G2RFLAG|SYSCALL_G5RFLAG);
*code &= ~(SYSCALL_G7RFLAG|SYSCALL_G2RFLAG|SYSCALL_G5RFLAG);
if (new) {
if (__predict_true((new & SYSCALL_G5RFLAG) == SYSCALL_G5RFLAG))
tf->tf_pc = tf->tf_global[5];
else if (new & SYSCALL_G2RFLAG)
tf->tf_pc = tf->tf_global[2];
else
tf->tf_pc = tf->tf_global[7];
} else {
tf->tf_pc = tf->tf_npc;
}
return new;
}
static inline int
getargs(struct proc *p, struct trapframe *tf, register_t *code,
const struct sysent **callp, union args *args)
{
int *ap = &tf->tf_out[0];
int error, i, nap = 6;
*callp = p->p_emul->e_sysent;
switch (*code) {
case SYS_syscall:
*code = *ap++;
nap--;
break;
case SYS___syscall:
if (!(p->p_emul->e_flags & EMUL_HAS_SYS___syscall))
break;
*code = ap[_QUAD_LOWWORD];
ap += 2;
nap -= 2;
break;
}
if (*code >= p->p_emul->e_nsysent)
return ENOSYS;
*callp += *code;
i = (*callp)->sy_argsize / sizeof(register_t);
if (__predict_false(i > nap)) {
void *off = (char *)tf->tf_out[6] +
offsetof(struct frame, fr_argx);
#ifdef DIAGNOSTIC
KASSERT(i <= MAXARGS);
#endif
error = copyin(off, &args->i[nap], (i - nap) * sizeof(*ap));
if (error)
return error;
i = nap;
}
copywords(ap, args->i, i * sizeof(*ap));
return 0;
}
#ifdef FPU_DEBUG
static inline void
save_fpu(struct trapframe *tf)
{
struct lwp *l = curlwp;
if ((tf->tf_psr & PSR_EF) != 0) {
if (cpuinfo.fplwp != l)
panic("FPU enabled but wrong proc (3) [l=%p, fwlp=%p]",
l, cpuinfo.fplwp);
savefpstate(l->l_md.md_fpstate);
l->l_md.md_fpu = NULL;
cpuinfo.fplwp = NULL;
tf->tf_psr &= ~PSR_EF;
setpsr(getpsr() & ~PSR_EF);
}
}
#endif
void
syscall_intern(struct proc *p)
{
p->p_trace_enabled = trace_is_enabled(p);
p->p_md.md_syscall = syscall;
}
void
syscall(register_t code, struct trapframe *tf, register_t pc)
{
const struct sysent *callp;
struct proc *p;
struct lwp *l;
int error, new;
union args args;
union rval rval;
int opc, onpc;
u_quad_t sticks;
curcpu()->ci_data.cpu_nsyscall++;
l = curlwp;
p = l->l_proc;
sticks = p->p_sticks;
l->l_md.md_tf = tf;
#ifdef FPU_DEBUG
save_fpu(tf);
#endif
opc = tf->tf_pc;
onpc = tf->tf_npc;
new = handle_new(tf, &code);
tf->tf_npc = tf->tf_pc + 4;
if ((error = getargs(p, tf, &code, &callp, &args)) != 0)
goto bad;
rval.o[0] = 0;
rval.o[1] = tf->tf_out[1];
error = sy_invoke(callp, l, args.i, rval.o, code);
switch (error) {
case 0:
tf->tf_out[0] = rval.o[0];
tf->tf_out[1] = rval.o[1];
if (!new) {
tf->tf_psr &= ~PSR_C;
}
break;
case ERESTART:
tf->tf_pc = opc;
tf->tf_npc = onpc;
break;
case EJUSTRETURN:
break;
default:
bad:
if (p->p_emul->e_errno)
error = p->p_emul->e_errno[error];
tf->tf_out[0] = error;
tf->tf_psr |= PSR_C;
tf->tf_pc = onpc;
tf->tf_npc = tf->tf_pc + 4;
break;
}
userret(l, pc, sticks);
share_fpu(l, tf);
}
void
md_child_return(struct lwp *l)
{
userret(l, l->l_md.md_tf->tf_pc, 0);
}
void
cpu_spawn_return(struct lwp *l)
{
userret(l, l->l_md.md_tf->tf_pc, 0);
}