/* * 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" /* Integer Unit simulator for Sparc FPU simulator. */ #include #include #include #include #include #include #include #define FPU_REG_FIELD uint32_reg /* Coordinate with FPU_REGS_TYPE. */ #define FPU_DREG_FIELD uint64_reg /* Coordinate with FPU_DREGS_TYPE. */ #define FPU_FSR_FIELD uint64_reg /* Coordinate with V9_FPU_FSR_TYPE. */ /* * Simulator for loads and stores between floating-point unit and memory. */ enum ftt_type fldst( fp_simd_type *pfpsd, /* FPU simulator data. */ fp_inst_type pinst, /* FPU instruction to simulate. */ struct regs *pregs, /* Pointer to PCB image of registers. */ void *prw) /* Pointer to locals and ins. */ { uint32_t sz_bits, asi = 0; uint64_t fea, tea; uint64_t *ea; enum ftt_type ftt; char *badaddr = (caddr_t)(-1); union { fp_inst_type inst; int32_t i; } fp; fp.inst = pinst; if ((pinst.op3 >> 4) & 1) { if (pinst.ibit) { asi = (uint32_t)((pregs->r_tstate >> TSTATE_ASI_SHIFT) & TSTATE_ASI_MASK); } else { asi = (fp.i >> 5) & 0xff; } /* check for ld/st alternate and highest defined V9 asi */ if (((pinst.op3 & 0x30) == 0x30) && (asi > ASI_SNFL)) return (vis_fldst(pfpsd, pinst, pregs, prw, asi)); } if (pinst.ibit == 0) { /* effective address = rs1 + rs2 */ ftt = read_iureg(pfpsd, pinst.rs1, pregs, prw, &fea); if (ftt != ftt_none) return (ftt); ftt = read_iureg(pfpsd, pinst.rs2, pregs, prw, &tea); if (ftt != ftt_none) return (ftt); ea = (uint64_t *)(fea + tea); } else { /* effective address = rs1 + imm13 */ /* Extract simm13 field. */ fea = (uint64_t)((fp.i << 19) >> 19); ftt = read_iureg(pfpsd, pinst.rs1, pregs, prw, &tea); if (ftt != ftt_none) return (ftt); ea = (uint64_t *)(fea + tea); } sz_bits = pinst.op3 & 0x3; switch (sz_bits) { /* map size bits to a number */ case 0: /* ldf{a}/stf{a} */ /* Must be word-aligned. */ if (((uintptr_t)ea & 0x3) != 0) return (ftt_alignment); break; case 1: if (pinst.rd == 0) { /* ldfsr/stfsr */ /* Must be word-aligned. */ if (((uintptr_t)ea & 0x3) != 0) return (ftt_alignment); } else { /* ldxfsr/stxfsr */ /* Must be extword-aligned. */ if (((uintptr_t)ea & 0x7) != 0) return (ftt_alignment); } break; case 2: /* ldqf{a}/stqf{a} */ /* Require only word alignment. */ if (((uintptr_t)ea & 0x3) != 0) return (ftt_alignment); break; case 3: /* lddf{a}/stdf{a} */ if (get_udatamodel() == DATAMODEL_ILP32) { /* Require 64 bit-alignment. */ if (((uintptr_t)ea & 0x7) != 0) return (ftt_alignment); } else { if (((uintptr_t)ea & 0x3) != 0) return (ftt_alignment); } } pfpsd->fp_trapaddr = (caddr_t)ea; /* setup bad addr in case we trap */ if ((pinst.op3 >> 2) & 1) /* store */ pfpsd->fp_traprw = S_READ; else pfpsd->fp_traprw = S_WRITE; switch (do_unaligned(pregs, &badaddr)) { case SIMU_FAULT: return (ftt_fault); case SIMU_ILLEGAL: return (ftt_unimplemented); case SIMU_SUCCESS: break; } pregs->r_pc = pregs->r_npc; /* Do not retry emulated instruction. */ pregs->r_npc += 4; return (ftt_none); } /* * Floating-point conditional moves between floating point unit registers. */ static enum ftt_type fmovcc_fcc( 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. */ enum cc_type cc) /* FSR condition code field from fcc[0-3] */ { uint32_t moveit; fsr_type fsr; enum fcc_type fcc; enum icc_type { fmovn, fmovne, fmovlg, fmovul, fmovl, fmovug, fmovg, fmovu, fmova, fmove, fmovue, fmovge, fmovuge, fmovle, fmovule, fmovo } cond; fsr = *pfsr; switch (cc) { case fcc_0: fcc = fsr.fcc0; break; case fcc_1: fcc = fsr.fcc1; break; case fcc_2: fcc = fsr.fcc2; break; case fcc_3: fcc = fsr.fcc3; break; default: return (ftt_unimplemented); } cond = (enum icc_type) (inst.rs1 & 0xf); switch (cond) { case fmovn: moveit = 0; break; case fmovl: moveit = fcc == fcc_less; break; case fmovg: moveit = fcc == fcc_greater; break; case fmovu: moveit = fcc == fcc_unordered; break; case fmove: moveit = fcc == fcc_equal; break; case fmovlg: moveit = (fcc == fcc_less) || (fcc == fcc_greater); break; case fmovul: moveit = (fcc == fcc_unordered) || (fcc == fcc_less); break; case fmovug: moveit = (fcc == fcc_unordered) || (fcc == fcc_greater); break; case fmovue: moveit = (fcc == fcc_unordered) || (fcc == fcc_equal); break; case fmovge: moveit = (fcc == fcc_greater) || (fcc == fcc_equal); break; case fmovle: moveit = (fcc == fcc_less) || (fcc == fcc_equal); break; case fmovne: moveit = fcc != fcc_equal; break; case fmovuge: moveit = fcc != fcc_less; break; case fmovule: moveit = fcc != fcc_greater; break; case fmovo: moveit = fcc != fcc_unordered; break; case fmova: moveit = 1; break; default: return (ftt_unimplemented); } if (moveit) { /* Move fpu register. */ uint32_t nrs2, nrd; uint32_t usr; uint64_t lusr; nrs2 = inst.rs2; nrd = inst.rd; if (inst.prec < 2) { /* fmovs */ _fp_unpack_word(pfpsd, &usr, nrs2); _fp_pack_word(pfpsd, &usr, nrd); } else { /* fmovd */ /* fix register encoding */ if ((nrs2 & 1) == 1) nrs2 = (nrs2 & 0x1e) | 0x20; _fp_unpack_extword(pfpsd, &lusr, nrs2); if ((nrd & 1) == 1) nrd = (nrd & 0x1e) | 0x20; _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); } } } return (ftt_none); } /* * Integer conditional moves between floating point unit registers. */ static enum ftt_type fmovcc_icc( fp_simd_type *pfpsd, /* Pointer to fpu simulator data */ fp_inst_type inst, /* FPU instruction to simulate. */ enum cc_type cc) /* CCR condition code field from tstate */ { int moveit; enum icc_type { fmovn, fmove, fmovle, fmovl, fmovleu, fmovcs, fmovneg, fmovvs, fmova, fmovne, fmovg, fmovge, fmovgu, fmovcc, fmovpos, fmovvc } cond; struct regs *pregs; uint64_t tstate; union { uint32_t i; ccr_type cc; } ccr; pregs = lwptoregs(curthread->t_lwp); tstate = pregs->r_tstate; switch (cc) { case icc: ccr.i = (uint32_t)((tstate >> TSTATE_CCR_SHIFT) & 0xf); break; case xcc: ccr.i = (uint32_t)(((tstate >> TSTATE_CCR_SHIFT) & 0xf0) >> 4); break; } cond = (enum icc_type) (inst.rs1 & 0xf); switch (cond) { case fmovn: moveit = 0; break; case fmove: moveit = (int)(ccr.cc.z); break; case fmovle: moveit = (int)(ccr.cc.z | (ccr.cc.n ^ ccr.cc.v)); break; case fmovl: moveit = (int)(ccr.cc.n ^ ccr.cc.v); break; case fmovleu: moveit = (int)(ccr.cc.c | ccr.cc.z); break; case fmovcs: moveit = (int)(ccr.cc.c); break; case fmovneg: moveit = (int)(ccr.cc.n); break; case fmovvs: moveit = (int)(ccr.cc.v); break; case fmova: moveit = 1; break; case fmovne: moveit = (int)(ccr.cc.z == 0); break; case fmovg: moveit = (int)((ccr.cc.z | (ccr.cc.n ^ ccr.cc.v)) == 0); break; case fmovge: moveit = (int)((ccr.cc.n ^ ccr.cc.v) == 0); break; case fmovgu: moveit = (int)((ccr.cc.c | ccr.cc.z) == 0); break; case fmovcc: moveit = (int)(ccr.cc.c == 0); break; case fmovpos: moveit = (int)(ccr.cc.n == 0); break; case fmovvc: moveit = (int)(ccr.cc.v == 0); break; default: return (ftt_unimplemented); } if (moveit) { /* Move fpu register. */ uint32_t nrs2, nrd; uint32_t usr; uint64_t lusr; nrs2 = inst.rs2; nrd = inst.rd; if (inst.prec < 2) { /* fmovs */ _fp_unpack_word(pfpsd, &usr, nrs2); _fp_pack_word(pfpsd, &usr, nrd); } else { /* fmovd */ /* fix register encoding */ if ((nrs2 & 1) == 1) nrs2 = (nrs2 & 0x1e) | 0x20; _fp_unpack_extword(pfpsd, &lusr, nrs2); if ((nrd & 1) == 1) nrd = (nrd & 0x1e) | 0x20; _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); } } } return (ftt_none); } /* * Simulator for moving fp register on condition (FMOVcc). * FMOVccq (Quad version of instruction) not supported by Ultra-1, so this * code must always be present. */ enum ftt_type fmovcc( 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. */ { enum cc_type opf_cc; opf_cc = (enum cc_type) ((inst.ibit << 2) | (inst.opcode >> 4)); if ((opf_cc == icc) || (opf_cc == xcc)) { return (fmovcc_icc(pfpsd, inst, opf_cc)); } else { return (fmovcc_fcc(pfpsd, inst, pfsr, opf_cc)); } } /* * Simulator for moving fp register on integer register condition (FMOVr). * FMOVrq (Quad version of instruction) not supported by Ultra-1, so this * code must always be present. */ enum ftt_type fmovr( fp_simd_type *pfpsd, /* Pointer to fpu simulator data */ fp_inst_type inst) /* FPU instruction to simulate. */ { struct regs *pregs; ulong_t *prw; uint32_t nrs1; enum ftt_type ftt; enum rcond_type { none, fmovre, fmovrlez, fmovrlz, nnone, fmovrne, fmovrgz, fmovrgez } rcond; int64_t moveit, r; nrs1 = inst.rs1; if (nrs1 > 15) /* rs1 must be a global register */ return (ftt_unimplemented); if (inst.ibit) /* ibit must be unused */ return (ftt_unimplemented); pregs = lwptoregs(curthread->t_lwp); prw = (ulong_t *)pregs->r_sp; ftt = read_iureg(pfpsd, nrs1, pregs, prw, (uint64_t *)&r); if (ftt != ftt_none) return (ftt); rcond = (enum rcond_type) (inst.opcode >> 3) & 7; switch (rcond) { case fmovre: moveit = r == 0; break; case fmovrlez: moveit = r <= 0; break; case fmovrlz: moveit = r < 0; break; case fmovrne: moveit = r != 0; break; case fmovrgz: moveit = r > 0; break; case fmovrgez: moveit = r >= 0; break; default: return (ftt_unimplemented); } if (moveit) { /* Move fpu register. */ uint32_t nrs2, nrd; uint32_t usr; uint64_t lusr; nrs2 = inst.rs2; nrd = inst.rd; if (inst.prec < 2) { /* fmovs */ _fp_unpack_word(pfpsd, &usr, nrs2); _fp_pack_word(pfpsd, &usr, nrd); } 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); } } } return (ftt_none); } /* * Move integer register on condition (MOVcc). */ enum ftt_type movcc( fp_simd_type *pfpsd, /* Pointer to fpu simulator data */ fp_inst_type pinst, /* 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. */ { fsr_type fsr; enum cc_type cc; enum fcc_type fcc; enum icc_type { fmovn, fmovne, fmovlg, fmovul, fmovl, fmovug, fmovg, fmovu, fmova, fmove, fmovue, fmovge, fmovuge, fmovle, fmovule, fmovo } cond; uint32_t moveit; enum ftt_type ftt = ftt_none; cc = (enum cc_type) (pinst.opcode >> 0x4) & 3; fsr.ll = pfpu->fpu_fsr; cond = (enum icc_type) (pinst.rs1 & 0xf); switch (cc) { case fcc_0: fcc = fsr.fcc0; break; case fcc_1: fcc = fsr.fcc1; break; case fcc_2: fcc = fsr.fcc2; break; case fcc_3: fcc = fsr.fcc3; break; default: return (ftt_unimplemented); } switch (cond) { case fmovn: moveit = 0; break; case fmovl: moveit = fcc == fcc_less; break; case fmovg: moveit = fcc == fcc_greater; break; case fmovu: moveit = fcc == fcc_unordered; break; case fmove: moveit = fcc == fcc_equal; break; case fmovlg: moveit = (fcc == fcc_less) || (fcc == fcc_greater); break; case fmovul: moveit = (fcc == fcc_unordered) || (fcc == fcc_less); break; case fmovug: moveit = (fcc == fcc_unordered) || (fcc == fcc_greater); break; case fmovue: moveit = (fcc == fcc_unordered) || (fcc == fcc_equal); break; case fmovge: moveit = (fcc == fcc_greater) || (fcc == fcc_equal); break; case fmovle: moveit = (fcc == fcc_less) || (fcc == fcc_equal); break; case fmovne: moveit = fcc != fcc_equal; break; case fmovuge: moveit = fcc != fcc_less; break; case fmovule: moveit = fcc != fcc_greater; break; case fmovo: moveit = fcc != fcc_unordered; break; case fmova: moveit = 1; break; default: return (ftt_unimplemented); } if (moveit) { /* Move fpu register. */ uint32_t nrd; uint64_t r; nrd = pinst.rd; if (pinst.ibit == 0) { /* copy the value in r[rs2] */ uint32_t nrs2; nrs2 = pinst.rs2; ftt = read_iureg(pfpsd, nrs2, pregs, prw, &r); if (ftt != ftt_none) return (ftt); ftt = write_iureg(pfpsd, nrd, pregs, prw, &r); } else { /* use sign_ext(simm11) */ union { fp_inst_type inst; int32_t i; } fp; fp.inst = pinst; /* Extract simm11 field */ r = (fp.i << 21) >> 21; ftt = write_iureg(pfpsd, nrd, pregs, prw, &r); } } pregs->r_pc = pregs->r_npc; /* Do not retry emulated instruction. */ pregs->r_npc += 4; return (ftt); }