#include <sys/param.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/archsystm.h>
#include <sys/vmsystm.h>
#include <sys/fpu/fpusystm.h>
#include <sys/fpu/fpu_simulator.h>
#include <sys/inline.h>
#include <sys/debug.h>
#include <sys/privregs.h>
#include <sys/machpcb.h>
#include <sys/simulate.h>
#include <sys/proc.h>
#include <sys/cmn_err.h>
#include <sys/stack.h>
#include <sys/watchpoint.h>
#include <sys/trap.h>
#include <sys/machtrap.h>
#include <sys/mman.h>
#include <sys/asi.h>
#include <sys/copyops.h>
#include <vm/as.h>
#include <vm/page.h>
#include <sys/model.h>
#include <vm/seg_vn.h>
#include <sys/byteorder.h>
#include <sys/time.h>
#define IS_IBIT_SET(x) (x & 0x2000)
#define IS_VIS1(op, op3)(op == 2 && op3 == 0x36)
#define IS_FLOAT_QUAD_OP(op, op3)(op == 2 && (op3 == 0x34 || \
op3 == 0x35))
#define IS_PARTIAL_OR_SHORT_FLOAT_LD_ST(op, op3, asi) \
(op == 3 && (op3 == IOP_V8_LDDFA || \
op3 == IOP_V8_STDFA) && asi > ASI_SNFL)
static int aligndebug = 0;
int
do_unaligned(struct regs *rp, caddr_t *badaddr)
{
uint_t inst, op3, asi = 0;
uint_t rd, rs1, rs2;
int sz, nf = 0, ltlend = 0;
int floatflg;
int fsrflg;
int immflg;
int lddstdflg;
caddr_t addr;
uint64_t val;
union {
uint64_t l[2];
uint32_t i[4];
uint16_t s[8];
uint8_t c[16];
} data;
ASSERT(USERMODE(rp->r_tstate));
inst = fetch_user_instr((caddr_t)rp->r_pc);
op3 = (inst >> 19) & 0x3f;
rd = (inst >> 25) & 0x1f;
rs1 = (inst >> 14) & 0x1f;
rs2 = inst & 0x1f;
floatflg = (inst >> 24) & 1;
immflg = (inst >> 13) & 1;
lddstdflg = fsrflg = 0;
if ((inst >> 30) != 3)
return (0);
if ((inst & 0xc1680000) == 0xc0680000)
return (0);
if ((inst & 0xc1e00000) == 0xc1e00000)
return (0);
if (floatflg) {
switch ((inst >> 19) & 3) {
case 0: sz = 4;
break;
case 1: fsrflg = 1;
if (rd == 0)
sz = 4;
else if (rd == 1)
sz = 8;
else
return (SIMU_ILLEGAL);
break;
case 2: sz = 16;
break;
case 3: sz = 8;
break;
}
if ((sz > 4) && (fsrflg == 0)) {
if ((rd & 1) == 1)
rd = (rd & 0x1e) | 0x20;
rd = rd >> 1;
if ((sz == 16) && ((rd & 0x1) != 0))
return (SIMU_ILLEGAL);
}
} else {
int sz_bits = (inst >> 19) & 0xf;
switch (sz_bits) {
case 0:
case 4:
case 8:
case 0xf:
sz = 4; break;
case 1:
case 5:
case 9:
case 0xd:
sz = 1; break;
case 2:
case 6:
case 0xa:
sz = 2; break;
case 3:
case 7:
lddstdflg = 1;
sz = 8; break;
case 0xb:
case 0xe:
sz = 8; break;
}
}
if ((op3 >> 4) & 1) {
if (immflg) {
asi = (uint_t)(rp->r_tstate >> TSTATE_ASI_SHIFT) &
TSTATE_ASI_MASK;
} else {
asi = (inst >> 5) & 0xff;
}
switch (asi) {
case ASI_P:
case ASI_S:
break;
case ASI_PNF:
case ASI_SNF:
nf = 1;
break;
case ASI_PL:
case ASI_SL:
ltlend = 1;
break;
case ASI_PNFL:
case ASI_SNFL:
ltlend = 1;
nf = 1;
break;
default:
return (0);
}
if ((nf) && ((op3 == IOP_V8_STQFA) || (op3 == IOP_V8_STDFA)))
return (SIMU_ILLEGAL);
}
if (aligndebug) {
printf("unaligned access at %p, instruction: 0x%x\n",
(void *)rp->r_pc, inst);
printf("type %s", (((inst >> 21) & 1) ? "st" : "ld"));
if (((inst >> 21) & 1) == 0)
printf(" %s", (((inst >> 22) & 1) ?
"signed" : "unsigned"));
printf(" asi 0x%x size %d immflg %d\n", asi, sz, immflg);
printf("rd = %d, op3 = 0x%x, rs1 = %d, rs2 = %d, imm13=0x%x\n",
rd, op3, rs1, rs2, (inst & 0x1fff));
}
(void) flush_user_windows_to_stack(NULL);
if (getreg(rp, rs1, &val, badaddr))
return (SIMU_FAULT);
addr = (caddr_t)val;
if (aligndebug)
printf("addr 1 = %p\n", (void *)addr);
if (immflg) {
int imm;
imm = inst & 0x1fff;
imm <<= 19;
imm >>= 19;
addr += imm;
} else {
if (getreg(rp, rs2, &val, badaddr))
return (SIMU_FAULT);
addr += val;
}
if (curproc->p_model == DATAMODEL_ILP32) {
caddr32_t addr32 = (caddr32_t)(uintptr_t)addr;
addr = (caddr_t)(uintptr_t)addr32;
}
if (aligndebug)
printf("addr 2 = %p\n", (void *)addr);
if (addr >= curproc->p_as->a_userlimit) {
*badaddr = addr;
goto badret;
}
if ((inst >> 21) & 1) {
if (floatflg) {
klwp_id_t lwp = ttolwp(curthread);
kfpu_t *fp = lwptofpu(lwp);
if (fpu_exists) {
if (!(_fp_read_fprs() & FPRS_FEF))
fp_enable();
} else {
if (!fp->fpu_en)
fp_enable();
}
if (fpu_exists) {
if (fsrflg) {
_fp_read_pfsr(&data.l[0]);
} else {
if (sz == 4) {
data.i[0] = 0;
_fp_read_pfreg(
(unsigned *)&data.i[1], rd);
}
if (sz >= 8)
_fp_read_pdreg(
&data.l[0], rd);
if (sz == 16)
_fp_read_pdreg(
&data.l[1], rd+1);
}
} else {
if (fsrflg) {
fp->fpu_fsr &= ~0x30301000;
fp->fpu_fsr |= 0xE0000;
data.l[0] = fp->fpu_fsr;
} else {
if (sz == 4) {
data.i[0] = 0;
data.i[1] =
(unsigned)fp->
fpu_fr.fpu_regs[rd];
}
if (sz >= 8)
data.l[0] =
fp->fpu_fr.fpu_dregs[rd];
if (sz == 16)
data.l[1] =
fp->fpu_fr.fpu_dregs[rd+1];
}
}
} else {
if (lddstdflg) {
if (getreg(rp, rd, &data.l[0], badaddr))
return (SIMU_FAULT);
if (getreg(rp, rd+1, &data.l[1], badaddr))
return (SIMU_FAULT);
if (ltlend) {
data.i[0] = data.i[3];
} else {
data.i[0] = data.i[1];
data.i[1] = data.i[3];
}
} else {
if (getreg(rp, rd, &data.l[0], badaddr))
return (SIMU_FAULT);
}
}
if (aligndebug) {
if (sz == 16) {
printf("data %x %x %x %x\n",
data.i[0], data.i[1], data.i[2], data.c[3]);
} else {
printf("data %x %x %x %x %x %x %x %x\n",
data.c[0], data.c[1], data.c[2], data.c[3],
data.c[4], data.c[5], data.c[6], data.c[7]);
}
}
if (ltlend) {
if (sz == 1) {
if (xcopyout_little(&data.c[7], addr,
(size_t)sz) != 0)
goto badret;
} else if (sz == 2) {
if (xcopyout_little(&data.s[3], addr,
(size_t)sz) != 0)
goto badret;
} else if (sz == 4) {
if (xcopyout_little(&data.i[1], addr,
(size_t)sz) != 0)
goto badret;
} else {
if (xcopyout_little(&data.l[0], addr,
(size_t)sz) != 0)
goto badret;
}
} else {
if (sz == 1) {
if (copyout(&data.c[7], addr, (size_t)sz) == -1)
goto badret;
} else if (sz == 2) {
if (copyout(&data.s[3], addr, (size_t)sz) == -1)
goto badret;
} else if (sz == 4) {
if (copyout(&data.i[1], addr, (size_t)sz) == -1)
goto badret;
} else {
if (copyout(&data.l[0], addr, (size_t)sz) == -1)
goto badret;
}
}
} else {
if (sz == 1) {
if (ltlend) {
if (xcopyin_little(addr, &data.c[7],
(size_t)sz) != 0) {
if (nf)
data.c[7] = 0;
else
goto badret;
}
} else {
if (copyin(addr, &data.c[7],
(size_t)sz) == -1) {
if (nf)
data.c[7] = 0;
else
goto badret;
}
}
if (((inst >> 22) & 1) && ((data.c[7] >> 7) & 1)) {
data.i[0] = (uint_t)-1;
data.s[2] = (ushort_t)-1;
data.c[6] = (uchar_t)-1;
} else {
data.i[0] = 0;
data.s[2] = 0;
data.c[6] = 0;
}
} else if (sz == 2) {
if (ltlend) {
if (xcopyin_little(addr, &data.s[3],
(size_t)sz) != 0) {
if (nf)
data.s[3] = 0;
else
goto badret;
}
} else {
if (copyin(addr, &data.s[3],
(size_t)sz) == -1) {
if (nf)
data.s[3] = 0;
else
goto badret;
}
}
if (((inst >> 22) & 1) && ((data.s[3] >> 15) & 1)) {
data.i[0] = (uint_t)-1;
data.s[2] = (ushort_t)-1;
} else {
data.i[0] = 0;
data.s[2] = 0;
}
} else if (sz == 4) {
if (ltlend) {
if (xcopyin_little(addr, &data.i[1],
(size_t)sz) != 0) {
if (!nf)
goto badret;
data.i[1] = 0;
}
} else {
if (copyin(addr, &data.i[1],
(size_t)sz) == -1) {
if (!nf)
goto badret;
data.i[1] = 0;
}
}
if (((inst >> 22) & 1) && ((data.i[1] >> 31) & 1)) {
data.i[0] = (uint_t)-1;
} else {
data.i[0] = 0;
}
} else {
if (ltlend) {
if (xcopyin_little(addr, &data.l[0],
(size_t)sz) != 0) {
if (!nf)
goto badret;
data.l[0] = 0;
}
} else {
if (copyin(addr, &data.l[0],
(size_t)sz) == -1) {
if (!nf)
goto badret;
data.l[0] = 0;
}
}
}
if (aligndebug) {
if (sz == 16) {
printf("data %x %x %x %x\n",
data.i[0], data.i[1], data.i[2], data.c[3]);
} else {
printf("data %x %x %x %x %x %x %x %x\n",
data.c[0], data.c[1], data.c[2], data.c[3],
data.c[4], data.c[5], data.c[6], data.c[7]);
}
}
if (floatflg) {
klwp_id_t lwp = ttolwp(curthread);
kfpu_t *fp = lwptofpu(lwp);
if (fpu_exists) {
if (!(_fp_read_fprs() & FPRS_FEF))
fp_enable();
} else {
if (!fp->fpu_en)
fp_enable();
}
if (fpu_exists) {
if (fsrflg) {
_fp_write_pfsr(&data.l[0]);
} else {
if (sz == 4)
_fp_write_pfreg(
(unsigned *)&data.i[1], rd);
if (sz >= 8)
_fp_write_pdreg(
&data.l[0], rd);
if (sz == 16)
_fp_write_pdreg(
&data.l[1], rd+1);
}
} else {
if (fsrflg) {
fp->fpu_fsr = data.l[0];
} else {
if (sz == 4)
fp->fpu_fr.fpu_regs[rd] =
(unsigned)data.i[1];
if (sz >= 8)
fp->fpu_fr.fpu_dregs[rd] =
data.l[0];
if (sz == 16)
fp->fpu_fr.fpu_dregs[rd+1] =
data.l[1];
}
}
} else {
if (lddstdflg) {
if (ltlend) {
data.i[3] = data.i[0];
} else {
data.i[3] = data.i[1];
data.i[1] = data.i[0];
}
data.i[0] = 0;
data.i[2] = 0;
if (putreg(&data.l[0], rp, rd, badaddr) == -1)
goto badret;
if (putreg(&data.l[1], rp, rd+1, badaddr) == -1)
goto badret;
} else {
if (putreg(&data.l[0], rp, rd, badaddr) == -1)
goto badret;
}
}
}
return (SIMU_SUCCESS);
badret:
return (SIMU_FAULT);
}
int
simulate_lddstd(struct regs *rp, caddr_t *badaddr)
{
uint_t inst, op3, asi = 0;
uint_t rd, rs1, rs2;
int nf = 0, ltlend = 0, usermode;
int immflg;
uint64_t reven;
uint64_t rodd;
caddr_t addr;
uint64_t val;
uint64_t data;
usermode = USERMODE(rp->r_tstate);
if (usermode)
inst = fetch_user_instr((caddr_t)rp->r_pc);
else
inst = *(uint_t *)rp->r_pc;
op3 = (inst >> 19) & 0x3f;
rd = (inst >> 25) & 0x1f;
rs1 = (inst >> 14) & 0x1f;
rs2 = inst & 0x1f;
immflg = (inst >> 13) & 1;
if (USERMODE(rp->r_tstate))
(void) flush_user_windows_to_stack(NULL);
else
flush_windows();
if ((op3 >> 4) & 1) {
if (immflg) {
asi = (uint_t)(rp->r_tstate >> TSTATE_ASI_SHIFT) &
TSTATE_ASI_MASK;
} else {
asi = (inst >> 5) & 0xff;
}
switch (asi) {
case ASI_P:
case ASI_S:
break;
case ASI_PNF:
case ASI_SNF:
nf = 1;
break;
case ASI_PL:
case ASI_SL:
ltlend = 1;
break;
case ASI_PNFL:
case ASI_SNFL:
ltlend = 1;
nf = 1;
break;
case ASI_AIUP:
case ASI_AIUS:
usermode = 1;
break;
case ASI_AIUPL:
case ASI_AIUSL:
usermode = 1;
ltlend = 1;
break;
default:
return (SIMU_ILLEGAL);
}
}
if (getreg(rp, rs1, &val, badaddr))
return (SIMU_FAULT);
addr = (caddr_t)val;
if (immflg) {
int imm;
imm = inst & 0x1fff;
imm <<= 19;
imm >>= 19;
addr += imm;
} else {
if (getreg(rp, rs2, &val, badaddr))
return (SIMU_FAULT);
addr += val;
}
if (((uintptr_t)addr & 0x7) != 0) {
if (curproc->p_fixalignment)
return (do_unaligned(rp, badaddr));
else
return (SIMU_UNALIGN);
}
if (curproc->p_model == DATAMODEL_ILP32 && usermode) {
caddr32_t addr32 = (caddr32_t)(uintptr_t)addr;
addr = (caddr_t)(uintptr_t)addr32;
}
if ((inst >> 21) & 1) {
if (getreg(rp, rd, &reven, badaddr))
return (SIMU_FAULT);
if (getreg(rp, rd+1, &rodd, badaddr))
return (SIMU_FAULT);
if (ltlend) {
reven = BSWAP_32(reven);
rodd = BSWAP_32(rodd);
}
data = (reven << 32) | rodd;
if (usermode) {
if (suword64_nowatch(addr, data) == -1)
return (SIMU_FAULT);
} else {
*(uint64_t *)addr = data;
}
} else {
if (usermode) {
if (fuword64_nowatch(addr, &data)) {
if (nf)
data = 0;
else
return (SIMU_FAULT);
}
} else
data = *(uint64_t *)addr;
reven = (data >> 32);
rodd = (uint64_t)(uint32_t)data;
if (ltlend) {
reven = BSWAP_32(reven);
rodd = BSWAP_32(rodd);
}
if (putreg(&reven, rp, rd, badaddr) == -1)
return (SIMU_FAULT);
if (putreg(&rodd, rp, rd+1, badaddr) == -1)
return (SIMU_FAULT);
}
return (SIMU_SUCCESS);
}
static int
simulate_popc(struct regs *rp, caddr_t *badaddr, uint_t inst)
{
uint_t rd, rs2, rs1;
uint_t immflg;
uint64_t val, cnt = 0;
rd = (inst >> 25) & 0x1f;
rs1 = (inst >> 14) & 0x1f;
rs2 = inst & 0x1f;
immflg = (inst >> 13) & 1;
if (rs1 > 0)
return (SIMU_ILLEGAL);
(void) flush_user_windows_to_stack(NULL);
if (immflg) {
int64_t imm;
imm = inst & 0x1fff;
imm <<= 51;
imm >>= 51;
if (imm != 0) {
for (cnt = 0; imm != 0; imm &= imm-1)
cnt++;
}
} else {
if (getreg(rp, rs2, &val, badaddr))
return (SIMU_FAULT);
if (val != 0) {
for (cnt = 0; val != 0; val &= val-1)
cnt++;
}
}
if (putreg(&cnt, rp, rd, badaddr) == -1)
return (SIMU_FAULT);
return (SIMU_SUCCESS);
}
static int
simulate_mulscc(struct regs *rp, caddr_t *badaddr, uint_t inst)
{
uint32_t s1, s2;
uint32_t c, d, v;
uint_t rd, rs1;
int64_t d64;
uint64_t ud64;
uint64_t drs1;
(void) flush_user_windows_to_stack(NULL);
if ((inst >> 13) & 1) {
d64 = inst & 0x1fff;
d64 <<= 51;
d64 >>= 51;
} else {
uint_t rs2;
uint64_t drs2;
if (inst & 0x1fe0) {
return (SIMU_ILLEGAL);
}
rs2 = inst & 0x1f;
if (getreg(rp, rs2, &drs2, badaddr)) {
return (SIMU_FAULT);
}
d64 = (int64_t)drs2;
}
rs1 = (inst >> 14) & 0x1f;
if (getreg(rp, rs1, &drs1, badaddr)) {
return (SIMU_FAULT);
}
s1 = ((rp->r_tstate & TSTATE_IN) >> (TSTATE_CCR_SHIFT + 3)) ^
((rp->r_tstate & TSTATE_IV) >> (TSTATE_CCR_SHIFT + 1));
s1 = (s1 << 31) | (((uint32_t)drs1) >> 1);
if (rp->r_y & 1) {
s2 = (uint32_t)d64;
} else {
s2 = 0;
}
d = s1 + s2;
ud64 = (uint64_t)d;
v = (s1 & s2 & ~d) | (~s1 & ~s2 & d);
c = (s1 & s2) | (~d & (s1 | s2));
rp->r_tstate &= ~TSTATE_ICC;
rp->r_tstate |= (uint64_t)((c >> 31) & 1) << (TSTATE_CCR_SHIFT + 0);
rp->r_tstate |= (uint64_t)((v >> 31) & 1) << (TSTATE_CCR_SHIFT + 1);
rp->r_tstate |= (uint64_t)(d ? 0 : 1) << (TSTATE_CCR_SHIFT + 2);
rp->r_tstate |= (uint64_t)((d >> 31) & 1) << (TSTATE_CCR_SHIFT + 3);
if (rp->r_tstate & TSTATE_IC) {
ud64 |= (1ULL << 32);
}
rp->r_tstate &= ~TSTATE_XCC;
if (ud64 == 0) {
rp->r_tstate |= TSTATE_XZ;
}
rd = (inst >> 25) & 0x1f;
if (putreg(&ud64, rp, rd, badaddr)) {
return (SIMU_FAULT);
}
d64 = (drs1 << 32) | (uint32_t)rp->r_y;
d64 >>= 1;
rp->r_y = (uint32_t)d64;
return (SIMU_SUCCESS);
}
int
simulate_unimp(struct regs *rp, caddr_t *badaddr)
{
uint_t inst, optype, op3, asi;
uint_t rs1, rd;
uint_t ignor, i;
machpcb_t *mpcb = lwptompcb(ttolwp(curthread));
int nomatch = 0;
caddr_t addr = (caddr_t)rp->r_pc;
struct as *as;
caddr_t ka;
pfn_t pfnum;
page_t *pp;
proc_t *p = ttoproc(curthread);
struct seg *mapseg;
struct segvn_data *svd;
ASSERT(USERMODE(rp->r_tstate));
inst = fetch_user_instr(addr);
if (inst == (uint_t)-1) {
mpcb->mpcb_illexcaddr = addr;
mpcb->mpcb_illexcinsn = (uint32_t)-1;
return (SIMU_ILLEGAL);
}
if (addr != mpcb->mpcb_illexcaddr ||
inst != mpcb->mpcb_illexcinsn)
nomatch = 1;
mpcb->mpcb_illexcaddr = addr;
mpcb->mpcb_illexcinsn = inst;
i = (inst >> 13) & 0x1;
rd = (inst >> 25) & 0x1f;
optype = (inst >> 30) & 0x3;
op3 = (inst >> 19) & 0x3f;
ignor = (inst >> 5) & 0xff;
if (IS_IBIT_SET(inst)) {
asi = (uint32_t)((rp->r_tstate >> TSTATE_ASI_SHIFT) &
TSTATE_ASI_MASK);
} else {
asi = ignor;
}
if (IS_VIS1(optype, op3) ||
IS_PARTIAL_OR_SHORT_FLOAT_LD_ST(optype, op3, asi) ||
IS_FLOAT_QUAD_OP(optype, op3)) {
klwp_t *lwp = ttolwp(curthread);
kfpu_t *fp = lwptofpu(lwp);
if (fpu_exists) {
if (!(_fp_read_fprs() & FPRS_FEF))
fp_enable();
_fp_read_pfsr(&fp->fpu_fsr);
} else {
if (!fp->fpu_en)
fp_enable();
}
fp_precise(rp);
return (SIMU_RETRY);
}
if (optype == 2 && op3 == IOP_V8_POPC) {
return (simulate_popc(rp, badaddr, inst));
} else if (optype == 3 && op3 == IOP_V8_POPC) {
return (SIMU_ILLEGAL);
} else if (optype == OP_V8_ARITH && op3 == IOP_V8_MULScc) {
return (simulate_mulscc(rp, badaddr, inst));
}
if (optype == OP_V8_LDSTR) {
if (op3 == IOP_V8_LDQF || op3 == IOP_V8_LDQFA ||
op3 == IOP_V8_STQF || op3 == IOP_V8_STQFA)
return (do_unaligned(rp, badaddr));
}
if (nomatch) {
mpcb->mpcb_illexccnt = 0;
return (SIMU_RETRY);
}
if (mpcb->mpcb_illexccnt >= 3)
return (SIMU_ILLEGAL);
mpcb->mpcb_illexccnt += 1;
if (lwp_getdatamodel(curthread->t_lwp) != DATAMODEL_ILP32)
return (SIMU_ILLEGAL);
switch (optype) {
case OP_V8_BRANCH:
case OP_V8_CALL:
return (SIMU_ILLEGAL);
case OP_V8_ARITH:
switch (op3) {
case IOP_V8_RETT:
if (rd == 0 && !(i == 0 && ignor))
return (SIMU_ILLEGAL);
if (rd)
inst &= ~(0x1f << 25);
if (i == 0 && ignor)
inst &= ~(0xff << 5);
break;
case IOP_V8_TCC:
if (i == 0 && ignor != 0) {
inst &= ~(0xff << 5);
} else if (i == 1 && (((inst >> 7) & 0x3f) != 0)) {
inst &= ~(0x3f << 7);
} else {
return (SIMU_ILLEGAL);
}
break;
case IOP_V8_JMPL:
case IOP_V8_RESTORE:
case IOP_V8_SAVE:
if ((op3 == IOP_V8_RETT && rd) ||
(i == 0 && ignor)) {
inst &= ~(0xff << 5);
} else {
return (SIMU_ILLEGAL);
}
break;
case IOP_V8_FCMP:
if (rd == 0)
return (SIMU_ILLEGAL);
inst &= ~(0x1f << 25);
break;
case IOP_V8_RDASR:
rs1 = ((inst >> 14) & 0x1f);
if (rs1 == 1 || (rs1 >= 7 && rs1 <= 14)) {
return (SIMU_ILLEGAL);
}
if ((ignor = (inst & 0x3fff)) != 0)
inst &= ~(0x3fff);
break;
case IOP_V8_SRA:
case IOP_V8_SRL:
case IOP_V8_SLL:
if (ignor == 0)
return (SIMU_ILLEGAL);
inst &= ~(0xff << 5);
break;
case IOP_V8_ADD:
case IOP_V8_AND:
case IOP_V8_OR:
case IOP_V8_XOR:
case IOP_V8_SUB:
case IOP_V8_ANDN:
case IOP_V8_ORN:
case IOP_V8_XNOR:
case IOP_V8_ADDC:
case IOP_V8_UMUL:
case IOP_V8_SMUL:
case IOP_V8_SUBC:
case IOP_V8_UDIV:
case IOP_V8_SDIV:
case IOP_V8_ADDcc:
case IOP_V8_ANDcc:
case IOP_V8_ORcc:
case IOP_V8_XORcc:
case IOP_V8_SUBcc:
case IOP_V8_ANDNcc:
case IOP_V8_ORNcc:
case IOP_V8_XNORcc:
case IOP_V8_ADDCcc:
case IOP_V8_UMULcc:
case IOP_V8_SMULcc:
case IOP_V8_SUBCcc:
case IOP_V8_UDIVcc:
case IOP_V8_SDIVcc:
case IOP_V8_TADDcc:
case IOP_V8_TSUBcc:
case IOP_V8_TADDccTV:
case IOP_V8_TSUBccTV:
case IOP_V8_MULScc:
case IOP_V8_WRASR:
case IOP_V8_FLUSH:
if (i != 0 || ignor == 0)
return (SIMU_ILLEGAL);
inst &= ~(0xff << 5);
break;
default:
return (SIMU_ILLEGAL);
}
break;
case OP_V8_LDSTR:
switch (op3) {
case IOP_V8_STFSR:
case IOP_V8_LDFSR:
if (rd == 0 && !(i == 0 && ignor))
return (SIMU_ILLEGAL);
if (rd)
inst &= ~(0x1f << 25);
if (i == 0 && ignor)
inst &= ~(0xff << 5);
break;
default:
if (optype == OP_V8_LDSTR && !IS_LDST_ALT(op3) &&
i == 0 && ignor)
inst &= ~(0xff << 5);
else
return (SIMU_ILLEGAL);
break;
}
break;
default:
return (SIMU_ILLEGAL);
}
as = p->p_as;
AS_LOCK_ENTER(as, RW_READER);
mapseg = as_findseg(as, (caddr_t)rp->r_pc, 0);
ASSERT(mapseg != NULL);
svd = (struct segvn_data *)mapseg->s_data;
SEGVN_LOCK_ENTER(as, &svd->lock, RW_READER);
if ((svd->type & MAP_TYPE) & MAP_SHARED) {
SEGVN_LOCK_EXIT(as, &svd->lock);
AS_LOCK_EXIT(as);
return (SIMU_ILLEGAL);
}
SEGVN_LOCK_EXIT(as, &svd->lock);
AS_LOCK_EXIT(as);
if (as_fault(as->a_hat, as, (caddr_t)(rp->r_pc & PAGEMASK), PAGESIZE,
F_SOFTLOCK, S_READ))
return (SIMU_FAULT);
AS_LOCK_ENTER(as, RW_READER);
pfnum = hat_getpfnum(as->a_hat, (caddr_t)rp->r_pc);
AS_LOCK_EXIT(as);
if (pf_is_memory(pfnum)) {
pp = page_numtopp_nolock(pfnum);
ASSERT(pp == NULL || PAGE_LOCKED(pp));
} else {
(void) as_fault(as->a_hat, as, (caddr_t)(rp->r_pc & PAGEMASK),
PAGESIZE, F_SOFTUNLOCK, S_READ);
return (SIMU_FAULT);
}
AS_LOCK_ENTER(as, RW_READER);
ka = ppmapin(pp, PROT_READ|PROT_WRITE, (caddr_t)rp->r_pc);
*(uint_t *)(ka + (uintptr_t)(rp->r_pc % PAGESIZE)) = inst;
doflush(ka + (uintptr_t)(rp->r_pc % PAGESIZE));
ppmapout(ka);
AS_LOCK_EXIT(as);
(void) as_fault(as->a_hat, as, (caddr_t)(rp->r_pc & PAGEMASK),
PAGESIZE, F_SOFTUNLOCK, S_READ);
return (SIMU_RETRY);
}
int
simulate_rdtick(struct regs *rp)
{
uint_t inst, op, op3, rd, rs1, i;
caddr_t badaddr;
inst = fetch_user_instr((caddr_t)rp->r_pc);
op = (inst >> 30) & 0x3;
rd = (inst >> 25) & 0x1F;
op3 = (inst >> 19) & 0x3F;
i = (inst >> 13) & 0x1;
if (op == 2 && op3 == 0x28 && i == 0) {
rs1 = (inst >> 14) & 0x1F;
if (rs1 == 0x4) {
uint64_t tick;
(void) flush_user_windows_to_stack(NULL);
tick = gettick_counter();
if (putreg(&tick, rp, rd, &badaddr) == 0)
return (SIMU_SUCCESS);
} else if (rs1 == 0x18) {
uint64_t stick;
(void) flush_user_windows_to_stack(NULL);
stick = gethrtime_unscaled();
if (putreg(&stick, rp, rd, &badaddr) == 0)
return (SIMU_SUCCESS);
}
}
return (SIMU_FAULT);
}
int
getreg(struct regs *rp, uint_t reg, uint64_t *val, caddr_t *badaddr)
{
uint64_t *rgs, *sp;
int rv = 0;
rgs = (uint64_t *)&rp->r_ps;
sp = (uint64_t *)rp->r_sp;
if (reg == 0) {
*val = 0;
} else if (reg < 16) {
*val = rgs[reg];
} else if (IS_V9STACK(sp)) {
uint64_t *rw = (uint64_t *)((uintptr_t)sp + V9BIAS64);
uint64_t *addr = (uint64_t *)&rw[reg - 16];
uint64_t res;
if (USERMODE(rp->r_tstate)) {
if (fuword64_nowatch(addr, &res) == -1) {
*badaddr = (caddr_t)addr;
rv = -1;
}
} else {
res = *addr;
}
*val = res;
} else {
caddr32_t sp32 = (caddr32_t)(uintptr_t)sp;
uint32_t *rw = (uint32_t *)(uintptr_t)sp32;
uint32_t *addr = (uint32_t *)&rw[reg - 16];
uint32_t res;
if (USERMODE(rp->r_tstate)) {
if (fuword32_nowatch(addr, &res) == -1) {
*badaddr = (caddr_t)addr;
rv = -1;
}
} else {
res = *addr;
}
*val = (uint64_t)res;
}
return (rv);
}
int
putreg(uint64_t *data, struct regs *rp, uint_t reg, caddr_t *badaddr)
{
uint64_t *rgs, *sp;
int rv = 0;
rgs = (uint64_t *)&rp->r_ps;
sp = (uint64_t *)rp->r_sp;
if (reg == 0) {
return (0);
} else if (reg < 16) {
rgs[reg] = *data;
} else if (IS_V9STACK(sp)) {
uint64_t *rw = (uint64_t *)((uintptr_t)sp + V9BIAS64);
uint64_t *addr = (uint64_t *)&rw[reg - 16];
uint64_t res;
if (USERMODE(rp->r_tstate)) {
struct machpcb *mpcb = lwptompcb(curthread->t_lwp);
res = *data;
if (suword64_nowatch(addr, res) != 0) {
*badaddr = (caddr_t)addr;
rv = -1;
}
mpcb->mpcb_rsp[0] = NULL;
mpcb->mpcb_rsp[1] = NULL;
} else {
res = *data;
*addr = res;
}
} else {
caddr32_t sp32 = (caddr32_t)(uintptr_t)sp;
uint32_t *rw = (uint32_t *)(uintptr_t)sp32;
uint32_t *addr = (uint32_t *)&rw[reg - 16];
uint32_t res;
if (USERMODE(rp->r_tstate)) {
struct machpcb *mpcb = lwptompcb(curthread->t_lwp);
res = (uint_t)*data;
if (suword32_nowatch(addr, res) != 0) {
*badaddr = (caddr_t)addr;
rv = -1;
}
mpcb->mpcb_rsp[0] = NULL;
mpcb->mpcb_rsp[1] = NULL;
} else {
res = (uint_t)*data;
*addr = res;
}
}
return (rv);
}
int
calc_memaddr(struct regs *rp, caddr_t *badaddr)
{
uint_t inst;
uint_t rd, rs1, rs2;
int sz;
int immflg;
int floatflg;
caddr_t addr;
uint64_t val;
if (USERMODE(rp->r_tstate))
inst = fetch_user_instr((caddr_t)rp->r_pc);
else
inst = *(uint_t *)rp->r_pc;
rd = (inst >> 25) & 0x1f;
rs1 = (inst >> 14) & 0x1f;
rs2 = inst & 0x1f;
floatflg = (inst >> 24) & 1;
immflg = (inst >> 13) & 1;
if (floatflg) {
switch ((inst >> 19) & 3) {
case 0: sz = 4; break;
case 1: return (0);
case 2: sz = 16; break;
case 3: sz = 8; break;
}
if (sz > 4) {
if ((rd & 1) == 1)
rd = (rd & 0x1e) | 0x20;
rd = rd >> 1;
}
} else {
switch ((inst >> 19) & 0xf) {
case 0:
case 4:
case 8:
case 0xf:
sz = 4; break;
case 1:
case 5:
case 9:
case 0xd:
sz = 1; break;
case 2:
case 6:
case 0xa:
sz = 2; break;
case 3:
case 7:
case 0xb:
case 0xe:
sz = 8; break;
}
}
if (USERMODE(rp->r_tstate))
(void) flush_user_windows_to_stack(NULL);
else
flush_windows();
if (getreg(rp, rs1, &val, badaddr))
return (SIMU_FAULT);
addr = (caddr_t)val;
if (immflg) {
int imm;
imm = inst & 0x1fff;
imm <<= 19;
imm >>= 19;
addr += imm;
} else {
if (getreg(rp, rs2, &val, badaddr))
return (SIMU_FAULT);
addr += val;
}
if (curproc->p_model == DATAMODEL_ILP32 && USERMODE(rp->r_tstate)) {
caddr32_t addr32 = (caddr32_t)(uintptr_t)addr;
addr = (caddr_t)(uintptr_t)addr32;
}
*badaddr = addr;
return ((uintptr_t)addr & (sz - 1) ? SIMU_UNALIGN : SIMU_SUCCESS);
}
int
instr_size(struct regs *rp, caddr_t *addrp, enum seg_rw rdwr)
{
uint_t inst, op3, asi;
uint_t rd, rs1, rs2;
int sz = 0;
int immflg;
int floatflg;
caddr_t addr;
caddr_t badaddr;
uint64_t val;
if (rdwr == S_EXEC) {
*addrp = (caddr_t)rp->r_pc;
return (4);
}
inst = fetch_user_instr((caddr_t)rp->r_pc);
op3 = (inst >> 19) & 0x3f;
rd = (inst >> 25) & 0x1f;
rs1 = (inst >> 14) & 0x1f;
rs2 = inst & 0x1f;
floatflg = (inst >> 24) & 1;
immflg = (inst >> 13) & 1;
if ((inst >> 30) != 3)
return (0);
if (immflg)
asi = (uint_t)((rp->r_tstate >> TSTATE_ASI_SHIFT) &
TSTATE_ASI_MASK);
else
asi = (inst >> 5) & 0xff;
if (floatflg) {
if ((op3 & 0x30) == 0x30 && asi > ASI_SNFL) {
sz = extended_asi_size(asi);
} else {
switch (op3 & 3) {
case 0:
sz = 4;
break;
case 1:
if (rd == 0)
sz = 4;
else
sz = 8;
break;
case 2:
if (op3 == 0x3e)
sz = 8;
else
sz = 16;
break;
case 3:
sz = 8;
break;
}
}
} else {
switch (op3 & 0xf) {
case 0:
case 4:
case 8:
case 0xf:
sz = 4; break;
case 1:
case 5:
case 9:
case 0xd:
sz = 1; break;
case 2:
case 6:
case 0xa:
sz = 2; break;
case 3:
case 7:
case 0xb:
case 0xe:
sz = 8; break;
}
}
if (sz == 0)
return (0);
(void) flush_user_windows_to_stack(NULL);
if (getreg(rp, rs1, &val, &badaddr))
return (0);
addr = (caddr_t)val;
if ((op3 & 0x3d) != 0x3c) {
if (immflg) {
int imm;
imm = inst & 0x1fff;
imm <<= 19;
imm >>= 19;
addr += imm;
} else {
if (!(floatflg && (asi & 0xf0) == 0xc0)) {
if (getreg(rp, rs2, &val, &badaddr))
return (0);
addr += val;
}
}
}
if (curproc->p_model == DATAMODEL_ILP32) {
caddr32_t addr32 = (caddr32_t)(uintptr_t)addr;
addr = (caddr_t)(uintptr_t)addr32;
}
*addrp = addr;
ASSERT(sz != 0);
return (sz);
}
int32_t
fetch_user_instr(caddr_t vaddr)
{
proc_t *p = curproc;
int32_t instr;
if (p->p_model == DATAMODEL_ILP32) {
caddr32_t vaddr32 = (caddr32_t)(uintptr_t)vaddr;
vaddr = (caddr_t)(uintptr_t)vaddr32;
}
if (fuword32_nowatch(vaddr, (uint32_t *)&instr) == -1)
instr = -1;
return (instr);
}