fpu_simulator.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/* Main procedures for sparc FPU simulator. */
#include <sys/fpu/fpu_simulator.h>
#include <sys/fpu/globals.h>
#include <sys/fpu/fpusystm.h>
#include <sys/proc.h>
#include <sys/signal.h>
#include <sys/siginfo.h>
#include <sys/thread.h>
#include <sys/cpuvar.h>
#include <sys/cmn_err.h>
#include <sys/atomic.h>
#include <sys/privregs.h>
#include <sys/vis_simulator.h>
#define FPUINFO_KSTAT(opcode) { \
extern void __dtrace_probe___fpuinfo_##opcode(uint64_t *); \
uint64_t *stataddr = &fpuinfo.opcode.value.ui64; \
__dtrace_probe___fpuinfo_##opcode(stataddr); \
atomic_add_64(&fpuinfo.opcode.value.ui64, 1); \
}
#define FPUINFO_KSTAT_PREC(prec, kstat_s, kstat_d, kstat_q) \
if (prec < 2) { \
FPUINFO_KSTAT(kstat_s); \
} else if (prec == 2) { \
FPUINFO_KSTAT(kstat_d); \
} else { \
FPUINFO_KSTAT(kstat_q); \
}
/*
* FPU simulator global kstat data
*/
struct fpuinfo_kstat fpuinfo = {
{ "fpu_sim_fmovs", KSTAT_DATA_UINT64},
{ "fpu_sim_fmovd", KSTAT_DATA_UINT64},
{ "fpu_sim_fmovq", KSTAT_DATA_UINT64},
{ "fpu_sim_fnegs", KSTAT_DATA_UINT64},
{ "fpu_sim_fnegd", KSTAT_DATA_UINT64},
{ "fpu_sim_fnegq", KSTAT_DATA_UINT64},
{ "fpu_sim_fabss", KSTAT_DATA_UINT64},
{ "fpu_sim_fabsd", KSTAT_DATA_UINT64},
{ "fpu_sim_fabsq", KSTAT_DATA_UINT64},
{ "fpu_sim_fsqrts", KSTAT_DATA_UINT64},
{ "fpu_sim_fsqrtd", KSTAT_DATA_UINT64},
{ "fpu_sim_fsqrtq", KSTAT_DATA_UINT64},
{ "fpu_sim_fadds", KSTAT_DATA_UINT64},
{ "fpu_sim_faddd", KSTAT_DATA_UINT64},
{ "fpu_sim_faddq", KSTAT_DATA_UINT64},
{ "fpu_sim_fsubs", KSTAT_DATA_UINT64},
{ "fpu_sim_fsubd", KSTAT_DATA_UINT64},
{ "fpu_sim_fsubq", KSTAT_DATA_UINT64},
{ "fpu_sim_fmuls", KSTAT_DATA_UINT64},
{ "fpu_sim_fmuld", KSTAT_DATA_UINT64},
{ "fpu_sim_fmulq", KSTAT_DATA_UINT64},
{ "fpu_sim_fdivs", KSTAT_DATA_UINT64},
{ "fpu_sim_fdivd", KSTAT_DATA_UINT64},
{ "fpu_sim_fdivq", KSTAT_DATA_UINT64},
{ "fpu_sim_fcmps", KSTAT_DATA_UINT64},
{ "fpu_sim_fcmpd", KSTAT_DATA_UINT64},
{ "fpu_sim_fcmpq", KSTAT_DATA_UINT64},
{ "fpu_sim_fcmpes", KSTAT_DATA_UINT64},
{ "fpu_sim_fcmped", KSTAT_DATA_UINT64},
{ "fpu_sim_fcmpeq", KSTAT_DATA_UINT64},
{ "fpu_sim_fsmuld", KSTAT_DATA_UINT64},
{ "fpu_sim_fdmulx", KSTAT_DATA_UINT64},
{ "fpu_sim_fstox", KSTAT_DATA_UINT64},
{ "fpu_sim_fdtox", KSTAT_DATA_UINT64},
{ "fpu_sim_fqtox", KSTAT_DATA_UINT64},
{ "fpu_sim_fxtos", KSTAT_DATA_UINT64},
{ "fpu_sim_fxtod", KSTAT_DATA_UINT64},
{ "fpu_sim_fxtoq", KSTAT_DATA_UINT64},
{ "fpu_sim_fitos", KSTAT_DATA_UINT64},
{ "fpu_sim_fitod", KSTAT_DATA_UINT64},
{ "fpu_sim_fitoq", KSTAT_DATA_UINT64},
{ "fpu_sim_fstoi", KSTAT_DATA_UINT64},
{ "fpu_sim_fdtoi", KSTAT_DATA_UINT64},
{ "fpu_sim_fqtoi", KSTAT_DATA_UINT64},
{ "fpu_sim_fmovcc", KSTAT_DATA_UINT64},
{ "fpu_sim_fmovr", KSTAT_DATA_UINT64},
};
struct visinfo_kstat visinfo = {
{ "vis_edge8", KSTAT_DATA_UINT64},
{ "vis_edge8n", KSTAT_DATA_UINT64},
{ "vis_edge8l", KSTAT_DATA_UINT64},
{ "vis_edge8ln", KSTAT_DATA_UINT64},
{ "vis_edge16", KSTAT_DATA_UINT64},
{ "vis_edge16n", KSTAT_DATA_UINT64},
{ "vis_edge16l", KSTAT_DATA_UINT64},
{ "vis_edge16ln", KSTAT_DATA_UINT64},
{ "vis_edge32", KSTAT_DATA_UINT64},
{ "vis_edge32n", KSTAT_DATA_UINT64},
{ "vis_edge32l", KSTAT_DATA_UINT64},
{ "vis_edge32ln", KSTAT_DATA_UINT64},
{ "vis_array8", KSTAT_DATA_UINT64},
{ "vis_array16", KSTAT_DATA_UINT64},
{ "vis_array32", KSTAT_DATA_UINT64},
{ "vis_bmask", KSTAT_DATA_UINT64},
{ "vis_fcmple16", KSTAT_DATA_UINT64},
{ "vis_fcmpne16", KSTAT_DATA_UINT64},
{ "vis_fcmpgt16", KSTAT_DATA_UINT64},
{ "vis_fcmpeq16", KSTAT_DATA_UINT64},
{ "vis_fcmple32", KSTAT_DATA_UINT64},
{ "vis_fcmpne32", KSTAT_DATA_UINT64},
{ "vis_fcmpgt32", KSTAT_DATA_UINT64},
{ "vis_fcmpeq32", KSTAT_DATA_UINT64},
{ "vis_fmul8x16", KSTAT_DATA_UINT64},
{ "vis_fmul8x16au", KSTAT_DATA_UINT64},
{ "vis_fmul8x16al", KSTAT_DATA_UINT64},
{ "vis_fmul8sux16", KSTAT_DATA_UINT64},
{ "vis_fmul8ulx16", KSTAT_DATA_UINT64},
{ "vis_fmuld8sux16", KSTAT_DATA_UINT64},
{ "vis_fmuld8ulx16", KSTAT_DATA_UINT64},
{ "vis_fpack16", KSTAT_DATA_UINT64},
{ "vis_fpack32", KSTAT_DATA_UINT64},
{ "vis_fpackfix", KSTAT_DATA_UINT64},
{ "vis_fexpand", KSTAT_DATA_UINT64},
{ "vis_fpmerge", KSTAT_DATA_UINT64},
{ "vis_pdist", KSTAT_DATA_UINT64},
{ "vis_bshuffle", KSTAT_DATA_UINT64},
};
/* PUBLIC FUNCTIONS */
int fp_notp = 1; /* fp checking not a problem */
/* ARGSUSED */
static enum ftt_type
_fp_fpu_simulator(
fp_simd_type *pfpsd, /* Pointer to fpu simulator data */
fp_inst_type inst, /* FPU instruction to simulate. */
fsr_type *pfsr, /* Pointer to image of FSR to read and write. */
uint64_t gsr) /* Image of GSR to read */
{
unpacked us1, us2, ud; /* Unpacked operands and result. */
uint32_t nrs1, nrs2, nrd; /* Register number fields. */
uint32_t usr, andexcep;
fsr_type fsr;
enum fcc_type cc;
uint32_t nfcc; /* fcc number field. */
uint64_t lusr;
nrs1 = inst.rs1;
nrs2 = inst.rs2;
nrd = inst.rd;
fsr = *pfsr;
pfpsd->fp_current_exceptions = 0; /* Init current exceptions. */
pfpsd->fp_fsrtem = fsr.tem; /* Obtain fsr's tem */
/*
* Obtain rounding direction and precision
*/
pfpsd->fp_direction = GSR_IM(gsr) ? GSR_IRND(gsr) : fsr.rnd;
pfpsd->fp_precision = fsr.rnp;
nfcc = nrd & 0x3;
if (inst.op3 == 0x35) { /* fpop2 */
fsr.cexc = 0;
*pfsr = fsr;
if ((inst.opcode & 0xf) == 0) {
if ((fp_notp) && (inst.prec == 0))
return (ftt_unimplemented);
FPUINFO_KSTAT(fpu_sim_fmovcc);
return (fmovcc(pfpsd, inst, pfsr)); /* fmovcc */
} else if ((inst.opcode & 0x7) == 1) {
if ((fp_notp) && (inst.prec == 0))
return (ftt_unimplemented);
FPUINFO_KSTAT(fpu_sim_fmovr);
return (fmovr(pfpsd, inst)); /* fmovr */
}
}
/* ibit not valid for fpop1 instructions */
if ((fp_notp) && (inst.ibit != 0))
return (ftt_unimplemented);
if ((fp_notp) && (inst.prec == 0)) { /* fxto[sdq], fito[sdq] */
if ((inst.opcode != flltos) &&
(inst.opcode != flltod) &&
(inst.opcode != flltox) &&
(inst.opcode != fitos) &&
(inst.opcode != fitod) &&
(inst.opcode != fitox)) {
return (ftt_unimplemented);
}
}
switch (inst.opcode) {
case fmovs: /* also covers fmovd, fmovq */
if (inst.prec < 2) { /* fmovs */
_fp_unpack_word(pfpsd, &usr, nrs2);
_fp_pack_word(pfpsd, &usr, nrd);
FPUINFO_KSTAT(fpu_sim_fmovs);
} else { /* fmovd */
_fp_unpack_extword(pfpsd, &lusr, nrs2);
_fp_pack_extword(pfpsd, &lusr, nrd);
if (inst.prec > 2) { /* fmovq */
_fp_unpack_extword(pfpsd, &lusr, nrs2+2);
_fp_pack_extword(pfpsd, &lusr, nrd+2);
FPUINFO_KSTAT(fpu_sim_fmovq);
} else {
FPUINFO_KSTAT(fpu_sim_fmovd);
}
}
break;
case fabss: /* also covers fabsd, fabsq */
if (inst.prec < 2) { /* fabss */
_fp_unpack_word(pfpsd, &usr, nrs2);
usr &= 0x7fffffff;
_fp_pack_word(pfpsd, &usr, nrd);
FPUINFO_KSTAT(fpu_sim_fabss);
} else { /* fabsd */
_fp_unpack_extword(pfpsd, &lusr, nrs2);
lusr &= 0x7fffffffffffffff;
_fp_pack_extword(pfpsd, &lusr, nrd);
if (inst.prec > 2) { /* fabsq */
_fp_unpack_extword(pfpsd, &lusr, nrs2+2);
_fp_pack_extword(pfpsd, &lusr, nrd+2);
FPUINFO_KSTAT(fpu_sim_fabsq);
} else {
FPUINFO_KSTAT(fpu_sim_fabsd);
}
}
break;
case fnegs: /* also covers fnegd, fnegq */
if (inst.prec < 2) { /* fnegs */
_fp_unpack_word(pfpsd, &usr, nrs2);
usr ^= 0x80000000;
_fp_pack_word(pfpsd, &usr, nrd);
FPUINFO_KSTAT(fpu_sim_fnegs);
} else { /* fnegd */
_fp_unpack_extword(pfpsd, &lusr, nrs2);
lusr ^= 0x8000000000000000;
_fp_pack_extword(pfpsd, &lusr, nrd);
if (inst.prec > 2) { /* fnegq */
_fp_unpack_extword(pfpsd, &lusr, nrs2+2);
lusr ^= 0x0000000000000000;
_fp_pack_extword(pfpsd, &lusr, nrd+2);
FPUINFO_KSTAT(fpu_sim_fnegq);
} else {
FPUINFO_KSTAT(fpu_sim_fnegd);
}
}
break;
case fadd:
_fp_unpack(pfpsd, &us1, nrs1, inst.prec);
_fp_unpack(pfpsd, &us2, nrs2, inst.prec);
_fp_add(pfpsd, &us1, &us2, &ud);
_fp_pack(pfpsd, &ud, nrd, inst.prec);
FPUINFO_KSTAT_PREC(inst.prec, fpu_sim_fadds,
fpu_sim_faddd, fpu_sim_faddq);
break;
case fsub:
_fp_unpack(pfpsd, &us1, nrs1, inst.prec);
_fp_unpack(pfpsd, &us2, nrs2, inst.prec);
_fp_sub(pfpsd, &us1, &us2, &ud);
_fp_pack(pfpsd, &ud, nrd, inst.prec);
FPUINFO_KSTAT_PREC(inst.prec, fpu_sim_fsubs,
fpu_sim_fsubd, fpu_sim_fsubq);
break;
case fmul:
_fp_unpack(pfpsd, &us1, nrs1, inst.prec);
_fp_unpack(pfpsd, &us2, nrs2, inst.prec);
_fp_mul(pfpsd, &us1, &us2, &ud);
_fp_pack(pfpsd, &ud, nrd, inst.prec);
FPUINFO_KSTAT_PREC(inst.prec, fpu_sim_fmuls,
fpu_sim_fmuld, fpu_sim_fmulq);
break;
case fsmuld:
if ((fp_notp) && (inst.prec != 1))
return (ftt_unimplemented);
_fp_unpack(pfpsd, &us1, nrs1, inst.prec);
_fp_unpack(pfpsd, &us2, nrs2, inst.prec);
_fp_mul(pfpsd, &us1, &us2, &ud);
_fp_pack(pfpsd, &ud, nrd, (enum fp_op_type) ((int)inst.prec+1));
FPUINFO_KSTAT(fpu_sim_fsmuld);
break;
case fdmulx:
if ((fp_notp) && (inst.prec != 2))
return (ftt_unimplemented);
_fp_unpack(pfpsd, &us1, nrs1, inst.prec);
_fp_unpack(pfpsd, &us2, nrs2, inst.prec);
_fp_mul(pfpsd, &us1, &us2, &ud);
_fp_pack(pfpsd, &ud, nrd, (enum fp_op_type) ((int)inst.prec+1));
FPUINFO_KSTAT(fpu_sim_fdmulx);
break;
case fdiv:
_fp_unpack(pfpsd, &us1, nrs1, inst.prec);
_fp_unpack(pfpsd, &us2, nrs2, inst.prec);
_fp_div(pfpsd, &us1, &us2, &ud);
_fp_pack(pfpsd, &ud, nrd, inst.prec);
FPUINFO_KSTAT_PREC(inst.prec, fpu_sim_fdivs,
fpu_sim_fdivd, fpu_sim_fdivq);
break;
case fcmp:
_fp_unpack(pfpsd, &us1, nrs1, inst.prec);
_fp_unpack(pfpsd, &us2, nrs2, inst.prec);
cc = _fp_compare(pfpsd, &us1, &us2, 0);
if (!(pfpsd->fp_current_exceptions & pfpsd->fp_fsrtem))
switch (nfcc) {
case fcc_0:
fsr.fcc0 = cc;
break;
case fcc_1:
fsr.fcc1 = cc;
break;
case fcc_2:
fsr.fcc2 = cc;
break;
case fcc_3:
fsr.fcc3 = cc;
break;
}
FPUINFO_KSTAT_PREC(inst.prec, fpu_sim_fcmps,
fpu_sim_fcmpd, fpu_sim_fcmpq);
break;
case fcmpe:
_fp_unpack(pfpsd, &us1, nrs1, inst.prec);
_fp_unpack(pfpsd, &us2, nrs2, inst.prec);
cc = _fp_compare(pfpsd, &us1, &us2, 1);
if (!(pfpsd->fp_current_exceptions & pfpsd->fp_fsrtem))
switch (nfcc) {
case fcc_0:
fsr.fcc0 = cc;
break;
case fcc_1:
fsr.fcc1 = cc;
break;
case fcc_2:
fsr.fcc2 = cc;
break;
case fcc_3:
fsr.fcc3 = cc;
break;
}
FPUINFO_KSTAT_PREC(inst.prec, fpu_sim_fcmpes,
fpu_sim_fcmped, fpu_sim_fcmpeq);
break;
case fsqrt:
_fp_unpack(pfpsd, &us1, nrs2, inst.prec);
_fp_sqrt(pfpsd, &us1, &ud);
_fp_pack(pfpsd, &ud, nrd, inst.prec);
FPUINFO_KSTAT_PREC(inst.prec, fpu_sim_fsqrts,
fpu_sim_fsqrtd, fpu_sim_fsqrtq);
break;
case ftoi:
_fp_unpack(pfpsd, &us1, nrs2, inst.prec);
pfpsd->fp_direction = fp_tozero;
/* Force rounding toward zero. */
_fp_pack(pfpsd, &us1, nrd, fp_op_int32);
FPUINFO_KSTAT_PREC(inst.prec, fpu_sim_fstoi,
fpu_sim_fdtoi, fpu_sim_fqtoi);
break;
case ftoll:
_fp_unpack(pfpsd, &us1, nrs2, inst.prec);
pfpsd->fp_direction = fp_tozero;
/* Force rounding toward zero. */
_fp_pack(pfpsd, &us1, nrd, fp_op_int64);
FPUINFO_KSTAT_PREC(inst.prec, fpu_sim_fstox,
fpu_sim_fdtox, fpu_sim_fqtox);
break;
case flltos:
_fp_unpack(pfpsd, &us1, nrs2, fp_op_int64);
_fp_pack(pfpsd, &us1, nrd, fp_op_single);
FPUINFO_KSTAT(fpu_sim_fxtos);
break;
case flltod:
_fp_unpack(pfpsd, &us1, nrs2, fp_op_int64);
_fp_pack(pfpsd, &us1, nrd, fp_op_double);
FPUINFO_KSTAT(fpu_sim_fxtod);
break;
case flltox:
_fp_unpack(pfpsd, &us1, nrs2, fp_op_int64);
_fp_pack(pfpsd, &us1, nrd, fp_op_extended);
FPUINFO_KSTAT(fpu_sim_fxtoq);
break;
case fitos:
_fp_unpack(pfpsd, &us1, nrs2, inst.prec);
_fp_pack(pfpsd, &us1, nrd, fp_op_single);
FPUINFO_KSTAT(fpu_sim_fitos);
break;
case fitod:
_fp_unpack(pfpsd, &us1, nrs2, inst.prec);
_fp_pack(pfpsd, &us1, nrd, fp_op_double);
FPUINFO_KSTAT(fpu_sim_fitod);
break;
case fitox:
_fp_unpack(pfpsd, &us1, nrs2, inst.prec);
_fp_pack(pfpsd, &us1, nrd, fp_op_extended);
FPUINFO_KSTAT(fpu_sim_fitoq);
break;
default:
return (ftt_unimplemented);
}
fsr.cexc = pfpsd->fp_current_exceptions;
if (pfpsd->fp_current_exceptions) { /* Exception(s) occurred. */
andexcep = pfpsd->fp_current_exceptions & fsr.tem;
if (andexcep != 0) { /* Signal an IEEE SIGFPE here. */
if (andexcep & (1 << fp_invalid)) {
pfpsd->fp_trapcode = FPE_FLTINV;
fsr.cexc = FSR_CEXC_NV;
} else if (andexcep & (1 << fp_overflow)) {
pfpsd->fp_trapcode = FPE_FLTOVF;
fsr.cexc = FSR_CEXC_OF;
} else if (andexcep & (1 << fp_underflow)) {
pfpsd->fp_trapcode = FPE_FLTUND;
fsr.cexc = FSR_CEXC_UF;
} else if (andexcep & (1 << fp_division)) {
pfpsd->fp_trapcode = FPE_FLTDIV;
fsr.cexc = FSR_CEXC_DZ;
} else if (andexcep & (1 << fp_inexact)) {
pfpsd->fp_trapcode = FPE_FLTRES;
fsr.cexc = FSR_CEXC_NX;
} else {
pfpsd->fp_trapcode = 0;
}
*pfsr = fsr;
return (ftt_ieee);
} else { /* Just set accrued exception field. */
fsr.aexc |= pfpsd->fp_current_exceptions;
}
}
*pfsr = fsr;
return (ftt_none);
}
/*
* fpu_vis_sim simulates fpu and vis instructions;
* It can work with both real and pcb image registers.
*/
enum ftt_type
fpu_vis_sim(
fp_simd_type *pfpsd, /* Pointer to simulator data */
fp_inst_type *pinst, /* Address of FPU instruction to simulate */
struct regs *pregs, /* Pointer to PCB image of registers. */
fsr_type *pfsr, /* Pointer to image of FSR to read and write */
uint64_t gsr, /* Image of GSR to read */
uint32_t inst) /* The FPU instruction to simulate */
{
klwp_id_t lwp = ttolwp(curthread);
union {
uint32_t i;
fp_inst_type inst;
} fp;
kfpu_t *pfp = lwptofpu(lwp);
enum ftt_type ftt;
fp.i = inst;
pfpsd->fp_trapaddr = (caddr_t)pinst;
if (fpu_exists) {
pfpsd->fp_current_read_freg = _fp_read_pfreg;
pfpsd->fp_current_write_freg = _fp_write_pfreg;
pfpsd->fp_current_read_dreg = _fp_read_pdreg;
pfpsd->fp_current_write_dreg = _fp_write_pdreg;
pfpsd->get_gsr = get_phys_gsr;
pfpsd->set_gsr = set_phys_gsr;
pfpsd->get_ccr = get_ccr;
pfpsd->set_ccr = set_ccr;
pfpsd->get_pstate = get_pstate;
} else {
pfpsd->fp_current_pfregs = pfp;
pfpsd->fp_current_read_freg = _fp_read_vfreg;
pfpsd->fp_current_write_freg = _fp_write_vfreg;
pfpsd->fp_current_read_dreg = _fp_read_vdreg;
pfpsd->fp_current_write_dreg = _fp_write_vdreg;
pfpsd->get_gsr = get_gsr;
pfpsd->set_gsr = set_gsr;
/* There are no virtual routines */
/* for getting/setting ccr and pstate */
pfpsd->get_ccr = get_ccr;
pfpsd->set_ccr = set_ccr;
pfpsd->get_pstate = get_pstate;
}
if ((fp.inst.hibits == 2) && (fp.inst.op3 == 0x36)) {
ftt = vis_fpu_simulator(pfpsd, fp.inst,
pregs, (ulong_t *)pregs->r_sp, pfp);
return (ftt);
} else if ((fp.inst.hibits == 2) &&
((fp.inst.op3 == 0x34) || (fp.inst.op3 == 0x35))) {
ftt = _fp_fpu_simulator(pfpsd, fp.inst, pfsr, gsr);
if (ftt == ftt_none || ftt == ftt_ieee) {
pregs->r_pc = pregs->r_npc;
pregs->r_npc += 4;
}
return (ftt);
} else {
ftt = _fp_iu_simulator(pfpsd, fp.inst, pregs,
(ulong_t *)pregs->r_sp, pfp);
return (ftt);
}
}
/*
* fpu_simulator simulates FPU instructions only;
* reads and writes FPU data registers directly.
*/
enum ftt_type
fpu_simulator(
fp_simd_type *pfpsd, /* Pointer to simulator data */
fp_inst_type *pinst, /* Address of FPU instruction to simulate */
fsr_type *pfsr, /* Pointer to image of FSR to read and write */
uint64_t gsr, /* Image of GSR to read */
uint32_t inst) /* The FPU instruction to simulate */
{
union {
uint32_t i;
fp_inst_type inst;
} fp;
fp.i = inst;
pfpsd->fp_trapaddr = (caddr_t)pinst;
pfpsd->fp_current_read_freg = _fp_read_pfreg;
pfpsd->fp_current_write_freg = _fp_write_pfreg;
pfpsd->fp_current_read_dreg = _fp_read_pdreg;
pfpsd->fp_current_write_dreg = _fp_write_pdreg;
pfpsd->get_gsr = get_phys_gsr;
pfpsd->set_gsr = set_phys_gsr;
pfpsd->get_ccr = get_ccr;
pfpsd->set_ccr = set_ccr;
pfpsd->get_pstate = get_pstate;
return (_fp_fpu_simulator(pfpsd, fp.inst, pfsr, gsr));
}
/*
* fp_emulator simulates FPU and CPU-FPU instructions; reads and writes FPU
* data registers from image in pfpu.
*/
enum ftt_type
fp_emulator(
fp_simd_type *pfpsd, /* Pointer to simulator data */
fp_inst_type *pinst, /* Pointer to FPU instruction to simulate. */
struct regs *pregs, /* Pointer to PCB image of registers. */
void *prw, /* Pointer to locals and ins. */
kfpu_t *pfpu) /* Pointer to FPU register block. */
{
klwp_id_t lwp = ttolwp(curthread);
union {
uint32_t i;
fp_inst_type inst;
} fp;
enum ftt_type ftt;
uint64_t gsr = get_gsr(pfpu);
kfpu_t *pfp = lwptofpu(lwp);
uint64_t tfsr;
tfsr = pfpu->fpu_fsr;
pfpsd->fp_current_pfregs = pfpu;
pfpsd->fp_current_read_freg = _fp_read_vfreg;
pfpsd->fp_current_write_freg = _fp_write_vfreg;
pfpsd->fp_current_read_dreg = _fp_read_vdreg;
pfpsd->fp_current_write_dreg = _fp_write_vdreg;
pfpsd->get_gsr = get_gsr;
pfpsd->set_gsr = set_gsr;
/* There are no virtual routines */
/* for getting/setting ccr and pstate */
pfpsd->get_ccr = get_ccr;
pfpsd->set_ccr = set_ccr;
pfpsd->get_pstate = get_pstate;
pfpsd->fp_trapaddr = (caddr_t)pinst; /* bad inst addr in case we trap */
ftt = _fp_read_inst((uint32_t *)pinst, &(fp.i), pfpsd);
if (ftt != ftt_none)
return (ftt);
if ((fp.inst.hibits == 2) &&
((fp.inst.op3 == 0x34) || (fp.inst.op3 == 0x35))) {
ftt = _fp_fpu_simulator(pfpsd, fp.inst, (fsr_type *)&tfsr, gsr);
/* Do not retry emulated instruction. */
pregs->r_pc = pregs->r_npc;
pregs->r_npc += 4;
pfpu->fpu_fsr = tfsr;
if (ftt != ftt_none) {
/*
* Simulation generated an exception of some kind,
* simulate the fp queue for a signal.
*/
pfpu->fpu_q->FQu.fpq.fpq_addr = (uint32_t *)pinst;
pfpu->fpu_q->FQu.fpq.fpq_instr = fp.i;
pfpu->fpu_qcnt = 1;
}
} else if ((fp.inst.hibits == 2) && (fp.inst.op3 == 0x36)) {
ftt = vis_fpu_simulator(pfpsd, fp.inst,
pregs, prw, pfp);
} else
ftt = _fp_iu_simulator(pfpsd, fp.inst, pregs, prw, pfpu);
if (ftt != ftt_none)
return (ftt);
/*
* If we are single-stepping, don't emulate any more instructions.
*/
if (lwp->lwp_pcb.pcb_step != STEP_NONE)
return (ftt);
again:
/*
* now read next instruction and see if it can be emulated
*/
pinst = (fp_inst_type *)pregs->r_pc;
pfpsd->fp_trapaddr = (caddr_t)pinst; /* bad inst addr in case we trap */
ftt = _fp_read_inst((uint32_t *)pinst, &(fp.i), pfpsd);
if (ftt != ftt_none)
return (ftt);
if ((fp.inst.hibits == 2) && /* fpops */
((fp.inst.op3 == 0x34) || (fp.inst.op3 == 0x35))) {
ftt = _fp_fpu_simulator(pfpsd, fp.inst, (fsr_type *)&tfsr, gsr);
/* Do not retry emulated instruction. */
pfpu->fpu_fsr = tfsr;
pregs->r_pc = pregs->r_npc;
pregs->r_npc += 4;
if (ftt != ftt_none) {
/*
* Simulation generated an exception of some kind,
* simulate the fp queue for a signal.
*/
pfpu->fpu_q->FQu.fpq.fpq_addr = (uint32_t *)pinst;
pfpu->fpu_q->FQu.fpq.fpq_instr = fp.i;
pfpu->fpu_qcnt = 1;
}
} else if ((fp.inst.hibits == 2) && (fp.inst.op3 == 0x36)) {
ftt = vis_fpu_simulator(pfpsd, fp.inst,
pregs, prw, pfp);
} else if (
/* rd %gsr */
((fp.inst.hibits == 2) && ((fp.inst.op3 & 0x3f) == 0x28) &&
(fp.inst.rs1 == 0x13)) ||
/* wr %gsr */
((fp.inst.hibits == 2) && ((fp.inst.op3 & 0x3f) == 0x30) &&
(fp.inst.rd == 0x13)) ||
/* movcc */
((fp.inst.hibits == 2) && ((fp.inst.op3 & 0x3f) == 0x2c) &&
(((fp.i>>18) & 0x1) == 0)) ||
/* fbpcc */
((fp.inst.hibits == 0) && (((fp.i>>22) & 0x7) == 5)) ||
/* fldst */
((fp.inst.hibits == 3) && ((fp.inst.op3 & 0x38) == 0x20)) ||
/* fbcc */
((fp.inst.hibits == 0) && (((fp.i>>22) & 0x7) == 6))) {
ftt = _fp_iu_simulator(pfpsd, fp.inst, pregs, prw, pfpu);
} else
return (ftt);
if (ftt != ftt_none)
return (ftt);
else
goto again;
}
/*
* FPU simulator global kstat data
*/
struct fpustat_kstat fpustat = {
{ "fpu_ieee_traps", KSTAT_DATA_UINT64 },
{ "fpu_unfinished_traps", KSTAT_DATA_UINT64 },
{ "fpu_unimplemented", KSTAT_DATA_UINT64 },
};
kstat_t *fpu_kstat = NULL;
kstat_t *fpuinfo_kstat = NULL;
kstat_t *visinfo_kstat = NULL;
void
fp_kstat_init(void)
{
const uint_t fpustat_ndata = sizeof (fpustat) / sizeof (kstat_named_t);
const uint_t fpuinfo_ndata = sizeof (fpuinfo) / sizeof (kstat_named_t);
const uint_t visinfo_ndata = sizeof (visinfo) /sizeof (kstat_named_t);
ASSERT(fpu_kstat == NULL);
if ((fpu_kstat = kstat_create("unix", 0, "fpu_traps", "misc",
KSTAT_TYPE_NAMED, fpustat_ndata, KSTAT_FLAG_VIRTUAL)) == NULL) {
cmn_err(CE_WARN, "CPU%d: kstat_create for fpu_traps failed",
CPU->cpu_id);
} else {
fpu_kstat->ks_data = (void *)&fpustat;
kstat_install(fpu_kstat);
}
ASSERT(fpuinfo_kstat == NULL);
if ((fpuinfo_kstat = kstat_create("unix", 0, "fpu_info", "misc",
KSTAT_TYPE_NAMED, fpuinfo_ndata, KSTAT_FLAG_VIRTUAL)) == NULL) {
cmn_err(CE_WARN, "CPU%d: kstat_create for fpu_info failed",
CPU->cpu_id);
} else {
fpuinfo_kstat->ks_data = (void *)&fpuinfo;
kstat_install(fpuinfo_kstat);
}
ASSERT(visinfo_kstat == NULL);
if ((visinfo_kstat = kstat_create("unix", 0, "vis_info", "misc",
KSTAT_TYPE_NAMED, visinfo_ndata, KSTAT_FLAG_VIRTUAL)) == NULL) {
cmn_err(CE_WARN, "CPU%d: kstat_create for vis_info failed",
CPU->cpu_id);
} else {
visinfo_kstat->ks_data = (void *)&visinfo;
kstat_install(visinfo_kstat);
}
}
void
fp_kstat_update(enum ftt_type ftt)
{
ASSERT((ftt == ftt_ieee) || (ftt == ftt_unfinished) ||
(ftt == ftt_unimplemented));
if (ftt == ftt_ieee)
atomic_add_64(&fpustat.fpu_ieee_traps.value.ui64, 1);
else if (ftt == ftt_unfinished)
atomic_add_64(&fpustat.fpu_unfinished_traps.value.ui64, 1);
else if (ftt == ftt_unimplemented)
atomic_add_64(&fpustat.fpu_unimplemented_traps.value.ui64, 1);
}