#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/signalvar.h>
#include <sys/malloc.h>
#include <machine/reg.h>
#include <machine/instr.h>
#include <machine/cpu.h>
#include <machine/psl.h>
#include <uvm/uvm_extern.h>
#ifdef DEBUG_EMUL
# define DPRINTF(a) printf a
#else
# define DPRINTF(a)
#endif
void swap_quad(int64_t *);
#define SIGN_EXT13(v) (((int64_t)(v) << 51) >> 51)
void
swap_quad(int64_t *p)
{
int64_t t;
t = htole64(p[0]);
p[0] = htole64(p[1]);
p[1] = t;
}
int
emul_qf(int32_t insv, struct proc *p, union sigval sv, struct trapframe *tf)
{
extern const struct fpstate initfpstate;
struct fpstate *fs = p->p_md.md_fpstate;
int64_t addr, buf[2];
union instr ins;
int freg, isload, err;
u_int8_t asi;
ins.i_int = insv;
freg = ins.i_op3.i_rd & ~1;
freg |= (ins.i_op3.i_rd & 1) << 5;
if (ins.i_op3.i_op3 == IOP3_LDQF || ins.i_op3.i_op3 == IOP3_LDQFA)
isload = 1;
else
isload = 0;
if (ins.i_op3.i_op3 == IOP3_STQF || ins.i_op3.i_op3 == IOP3_LDQF)
asi = ASI_PRIMARY;
else if (ins.i_loadstore.i_i)
asi = (tf->tf_tstate & TSTATE_ASI) >> TSTATE_ASI_SHIFT;
else
asi = ins.i_asi.i_asi;
addr = tf->tf_global[ins.i_asi.i_rs1];
if (ins.i_loadstore.i_i)
addr += SIGN_EXT13(ins.i_simm13.i_simm13);
else
addr += tf->tf_global[ins.i_asi.i_rs2];
if (asi < ASI_PRIMARY) {
trapsignal(p, SIGILL, 0, ILL_PRVOPC, sv);
return (0);
}
if (asi > ASI_SECONDARY_NOFAULT_LITTLE ||
(asi > ASI_SECONDARY_NOFAULT && asi < ASI_PRIMARY_LITTLE)) {
goto segv;
}
if ((freg & 3) != 0) {
trapsignal(p, SIGILL, 0, ILL_ILLOPN, sv);
return (0);
}
if ((addr & 3) != 0) {
trapsignal(p, SIGBUS, 0, BUS_ADRALN, sv);
return (0);
}
fs = p->p_md.md_fpstate;
if (fs == NULL) {
KERNEL_LOCK();
fs = malloc(sizeof(*fs), M_SUBPROC, M_WAITOK);
*fs = initfpstate;
p->p_md.md_fpstate = fs;
KERNEL_UNLOCK();
} else
fpusave_proc(p, 1);
if (isload) {
err = copyin((caddr_t)addr, buf, sizeof(buf));
if (err != 0 && (asi & 2) == 0)
goto segv;
if (err == 0) {
if (asi & 8)
swap_quad(buf);
bcopy(buf, &fs->fs_regs[freg], sizeof(buf));
}
} else {
bcopy(&fs->fs_regs[freg], buf, sizeof(buf));
if (asi & 8)
swap_quad(buf);
if (copyout(buf, (caddr_t)addr, sizeof(buf)) && (asi & 2) == 0)
goto segv;
}
return (1);
segv:
trapsignal(p, SIGSEGV, isload ? PROT_READ : PROT_WRITE,
SEGV_MAPERR, sv);
return (0);
}
int
emul_popc(int32_t insv, struct proc *p, union sigval sv, struct trapframe *tf)
{
u_int64_t val, ret = 0;
union instr ins;
ins.i_int = insv;
if (ins.i_simm13.i_i == 0)
val = tf->tf_global[ins.i_asi.i_rs2];
else
val = SIGN_EXT13(ins.i_simm13.i_simm13);
for (; val != 0; val >>= 1)
ret += val & 1;
tf->tf_global[ins.i_asi.i_rd] = ret;
return (1);
}