Index: head/sys/powerpc/fpu/fpu_emu.c =================================================================== --- head/sys/powerpc/fpu/fpu_emu.c (revision 340652) +++ head/sys/powerpc/fpu/fpu_emu.c (revision 340653) @@ -1,803 +1,796 @@ /* $NetBSD: fpu_emu.c,v 1.14 2005/12/11 12:18:42 christos Exp $ */ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright 2001 Wasabi Systems, Inc. * All rights reserved. * * Written by Eduardo Horvath and Simon Burge for Wasabi Systems, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed for the NetBSD Project by * Wasabi Systems, Inc. * 4. The name of Wasabi Systems, Inc. may not be used to endorse * or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Lawrence Berkeley Laboratory. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)fpu.c 8.1 (Berkeley) 6/11/93 */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include static SYSCTL_NODE(_hw, OID_AUTO, fpu_emu, CTLFLAG_RW, 0, "FPU emulator"); #define FPU_EMU_EVCNT_DECL(name) \ static u_int fpu_emu_evcnt_##name; \ SYSCTL_INT(_hw_fpu_emu, OID_AUTO, evcnt_##name, CTLFLAG_RD, \ &fpu_emu_evcnt_##name, 0, "") #define FPU_EMU_EVCNT_INCR(name) fpu_emu_evcnt_##name++ FPU_EMU_EVCNT_DECL(stfiwx); FPU_EMU_EVCNT_DECL(fpstore); FPU_EMU_EVCNT_DECL(fpload); FPU_EMU_EVCNT_DECL(fcmpu); FPU_EMU_EVCNT_DECL(frsp); FPU_EMU_EVCNT_DECL(fctiw); FPU_EMU_EVCNT_DECL(fcmpo); FPU_EMU_EVCNT_DECL(mtfsb1); FPU_EMU_EVCNT_DECL(fnegabs); FPU_EMU_EVCNT_DECL(mcrfs); FPU_EMU_EVCNT_DECL(mtfsb0); FPU_EMU_EVCNT_DECL(fmr); FPU_EMU_EVCNT_DECL(mtfsfi); FPU_EMU_EVCNT_DECL(fnabs); FPU_EMU_EVCNT_DECL(fabs); FPU_EMU_EVCNT_DECL(mffs); FPU_EMU_EVCNT_DECL(mtfsf); FPU_EMU_EVCNT_DECL(fctid); FPU_EMU_EVCNT_DECL(fcfid); FPU_EMU_EVCNT_DECL(fdiv); FPU_EMU_EVCNT_DECL(fsub); FPU_EMU_EVCNT_DECL(fadd); FPU_EMU_EVCNT_DECL(fsqrt); FPU_EMU_EVCNT_DECL(fsel); FPU_EMU_EVCNT_DECL(fpres); FPU_EMU_EVCNT_DECL(fmul); FPU_EMU_EVCNT_DECL(frsqrte); FPU_EMU_EVCNT_DECL(fmulsub); FPU_EMU_EVCNT_DECL(fmuladd); FPU_EMU_EVCNT_DECL(fnmsub); FPU_EMU_EVCNT_DECL(fnmadd); /* FPSR exception masks */ #define FPSR_EX_MSK (FPSCR_VX|FPSCR_OX|FPSCR_UX|FPSCR_ZX| \ FPSCR_XX|FPSCR_VXSNAN|FPSCR_VXISI|FPSCR_VXIDI| \ FPSCR_VXZDZ|FPSCR_VXIMZ|FPSCR_VXVC|FPSCR_VXSOFT|\ FPSCR_VXSQRT|FPSCR_VXCVI) #define FPSR_EX (FPSCR_VE|FPSCR_OE|FPSCR_UE|FPSCR_ZE|FPSCR_XE) #define FPSR_EXOP (FPSR_EX_MSK&(~FPSR_EX)) int fpe_debug = 0; #ifdef DEBUG vm_offset_t opc_disasm(vm_offset_t, int); /* * Dump a `fpn' structure. */ void fpu_dumpfpn(struct fpn *fp) { static const char *class[] = { "SNAN", "QNAN", "ZERO", "NUM", "INF" }; printf("%s %c.%x %x %x %xE%d", class[fp->fp_class + 2], fp->fp_sign ? '-' : ' ', fp->fp_mant[0], fp->fp_mant[1], fp->fp_mant[2], fp->fp_mant[3], fp->fp_exp); } #endif /* * fpu_execute returns the following error numbers (0 = no error): */ #define FPE 1 /* take a floating point exception */ #define NOTFPU 2 /* not an FPU instruction */ #define FAULT 3 /* * Emulate a floating-point instruction. * Return zero for success, else signal number. * (Typically: zero, SIGFPE, SIGILL, SIGSEGV) */ int fpu_emulate(struct trapframe *frame, struct fpu *fpf) { union instr insn; struct fpemu fe; - static int lastill = 0; int sig; /* initialize insn.is_datasize to tell it is *not* initialized */ fe.fe_fpstate = fpf; fe.fe_cx = 0; /* always set this (to avoid a warning) */ if (copyin((void *) (frame->srr0), &insn.i_int, sizeof (insn.i_int))) { #ifdef DEBUG printf("fpu_emulate: fault reading opcode\n"); #endif return SIGSEGV; } DPRINTF(FPE_EX, ("fpu_emulate: emulating insn %x at %p\n", insn.i_int, (void *)frame->srr0)); if ((insn.i_any.i_opcd == OPC_TWI) || ((insn.i_any.i_opcd == OPC_integer_31) && (insn.i_x.i_xo == OPC31_TW))) { /* Check for the two trap insns. */ DPRINTF(FPE_EX, ("fpu_emulate: SIGTRAP\n")); return (SIGTRAP); } sig = 0; switch (fpu_execute(frame, &fe, &insn)) { case 0: DPRINTF(FPE_EX, ("fpu_emulate: success\n")); frame->srr0 += 4; break; case FPE: DPRINTF(FPE_EX, ("fpu_emulate: SIGFPE\n")); sig = SIGFPE; break; case FAULT: DPRINTF(FPE_EX, ("fpu_emulate: SIGSEGV\n")); sig = SIGSEGV; break; case NOTFPU: default: DPRINTF(FPE_EX, ("fpu_emulate: SIGILL\n")); #ifdef DEBUG if (fpe_debug & FPE_EX) { printf("fpu_emulate: illegal insn %x at %p:", insn.i_int, (void *) (frame->srr0)); opc_disasm(frame->srr0, insn.i_int); } #endif - /* - * XXXX retry an illegal insn once due to cache issues. - */ - if (lastill == frame->srr0) { - sig = SIGILL; + sig = SIGILL; #ifdef DEBUG - if (fpe_debug & FPE_EX) - kdb_enter(KDB_WHY_UNSET, "illegal instruction"); + if (fpe_debug & FPE_EX) + kdb_enter(KDB_WHY_UNSET, "illegal instruction"); #endif - } - lastill = frame->srr0; break; } return (sig); } /* * Execute an FPU instruction (one that runs entirely in the FPU; not * FBfcc or STF, for instance). On return, fe->fe_fs->fs_fsr will be * modified to reflect the setting the hardware would have left. * * Note that we do not catch all illegal opcodes, so you can, for instance, * multiply two integers this way. */ int fpu_execute(struct trapframe *tf, struct fpemu *fe, union instr *insn) { struct fpn *fp; union instr instr = *insn; int *a; vm_offset_t addr; int ra, rb, rc, rt, type, mask, fsr, cx, bf, setcr; unsigned int cond; struct fpu *fs; /* Setup work. */ fp = NULL; fs = fe->fe_fpstate; fe->fe_fpscr = ((int *)&fs->fpscr)[1]; /* * On PowerPC all floating point values are stored in registers * as doubles, even when used for single precision operations. */ type = FTYPE_DBL; cond = instr.i_any.i_rc; setcr = 0; bf = 0; /* XXX gcc */ #if defined(DDB) && defined(DEBUG) if (fpe_debug & FPE_EX) { vm_offset_t loc = tf->srr0; printf("Trying to emulate: %p ", (void *)loc); opc_disasm(loc, instr.i_int); } #endif /* * `Decode' and execute instruction. */ if ((instr.i_any.i_opcd >= OPC_LFS && instr.i_any.i_opcd <= OPC_STFDU) || instr.i_any.i_opcd == OPC_integer_31) { /* * Handle load/store insns: * * Convert to/from single if needed, calculate addr, * and update index reg if needed. */ double buf; size_t size = sizeof(float); int store, update; cond = 0; /* ld/st never set condition codes */ if (instr.i_any.i_opcd == OPC_integer_31) { if (instr.i_x.i_xo == OPC31_STFIWX) { FPU_EMU_EVCNT_INCR(stfiwx); /* Store as integer */ ra = instr.i_x.i_ra; rb = instr.i_x.i_rb; DPRINTF(FPE_INSN, ("reg %d has %jx reg %d has %jx\n", ra, (uintmax_t)tf->fixreg[ra], rb, (uintmax_t)tf->fixreg[rb])); addr = tf->fixreg[rb]; if (ra != 0) addr += tf->fixreg[ra]; rt = instr.i_x.i_rt; a = (int *)&fs->fpr[rt].fpr; DPRINTF(FPE_INSN, ("fpu_execute: Store INT %x at %p\n", a[1], (void *)addr)); if (copyout(&a[1], (void *)addr, sizeof(int))) return (FAULT); return (0); } if ((instr.i_x.i_xo & OPC31_FPMASK) != OPC31_FPOP) /* Not an indexed FP load/store op */ return (NOTFPU); store = (instr.i_x.i_xo & 0x80); if (instr.i_x.i_xo & 0x40) size = sizeof(double); else type = FTYPE_SNG; update = (instr.i_x.i_xo & 0x20); /* calculate EA of load/store */ ra = instr.i_x.i_ra; rb = instr.i_x.i_rb; DPRINTF(FPE_INSN, ("reg %d has %jx reg %d has %jx\n", ra, (uintmax_t)tf->fixreg[ra], rb, (uintmax_t)tf->fixreg[rb])); addr = tf->fixreg[rb]; if (ra != 0) addr += tf->fixreg[ra]; rt = instr.i_x.i_rt; } else { store = instr.i_d.i_opcd & 0x4; if (instr.i_d.i_opcd & 0x2) size = sizeof(double); else type = FTYPE_SNG; update = instr.i_d.i_opcd & 0x1; /* calculate EA of load/store */ ra = instr.i_d.i_ra; addr = instr.i_d.i_d; DPRINTF(FPE_INSN, ("reg %d has %jx displ %jx\n", ra, (uintmax_t)tf->fixreg[ra], (uintmax_t)addr)); if (ra != 0) addr += tf->fixreg[ra]; rt = instr.i_d.i_rt; } if (update && ra == 0) return (NOTFPU); if (store) { /* Store */ FPU_EMU_EVCNT_INCR(fpstore); if (type != FTYPE_DBL) { DPRINTF(FPE_INSN, ("fpu_execute: Store SNG at %p\n", (void *)addr)); fpu_explode(fe, fp = &fe->fe_f1, FTYPE_DBL, rt); fpu_implode(fe, fp, type, (void *)&buf); if (copyout(&buf, (void *)addr, size)) return (FAULT); } else { DPRINTF(FPE_INSN, ("fpu_execute: Store DBL at %p\n", (void *)addr)); if (copyout(&fs->fpr[rt].fpr, (void *)addr, size)) return (FAULT); } } else { /* Load */ FPU_EMU_EVCNT_INCR(fpload); DPRINTF(FPE_INSN, ("fpu_execute: Load from %p\n", (void *)addr)); if (copyin((const void *)addr, &fs->fpr[rt].fpr, size)) return (FAULT); if (type != FTYPE_DBL) { fpu_explode(fe, fp = &fe->fe_f1, type, rt); fpu_implode(fe, fp, FTYPE_DBL, (u_int *)&fs->fpr[rt].fpr); } } if (update) tf->fixreg[ra] = addr; /* Complete. */ return (0); #ifdef notyet } else if (instr.i_any.i_opcd == OPC_load_st_62) { /* These are 64-bit extensions */ return (NOTFPU); #endif } else if (instr.i_any.i_opcd == OPC_sp_fp_59 || instr.i_any.i_opcd == OPC_dp_fp_63) { if (instr.i_any.i_opcd == OPC_dp_fp_63 && !(instr.i_a.i_xo & OPC63M_MASK)) { /* Format X */ rt = instr.i_x.i_rt; ra = instr.i_x.i_ra; rb = instr.i_x.i_rb; /* One of the special opcodes.... */ switch (instr.i_x.i_xo) { case OPC63_FCMPU: FPU_EMU_EVCNT_INCR(fcmpu); DPRINTF(FPE_INSN, ("fpu_execute: FCMPU\n")); rt >>= 2; fpu_explode(fe, &fe->fe_f1, type, ra); fpu_explode(fe, &fe->fe_f2, type, rb); fpu_compare(fe, 0); /* Make sure we do the condition regs. */ cond = 0; /* N.B.: i_rs is already left shifted by two. */ bf = instr.i_x.i_rs & 0xfc; setcr = 1; break; case OPC63_FRSP: /* * Convert to single: * * PowerPC uses this to round a double * precision value to single precision, * but values in registers are always * stored in double precision format. */ FPU_EMU_EVCNT_INCR(frsp); DPRINTF(FPE_INSN, ("fpu_execute: FRSP\n")); fpu_explode(fe, fp = &fe->fe_f1, FTYPE_DBL, rb); fpu_implode(fe, fp, FTYPE_SNG, (u_int *)&fs->fpr[rt].fpr); fpu_explode(fe, fp = &fe->fe_f1, FTYPE_SNG, rt); type = FTYPE_DBL; break; case OPC63_FCTIW: case OPC63_FCTIWZ: FPU_EMU_EVCNT_INCR(fctiw); DPRINTF(FPE_INSN, ("fpu_execute: FCTIW\n")); fpu_explode(fe, fp = &fe->fe_f1, type, rb); type = FTYPE_INT; break; case OPC63_FCMPO: FPU_EMU_EVCNT_INCR(fcmpo); DPRINTF(FPE_INSN, ("fpu_execute: FCMPO\n")); rt >>= 2; fpu_explode(fe, &fe->fe_f1, type, ra); fpu_explode(fe, &fe->fe_f2, type, rb); fpu_compare(fe, 1); /* Make sure we do the condition regs. */ cond = 0; /* N.B.: i_rs is already left shifted by two. */ bf = instr.i_x.i_rs & 0xfc; setcr = 1; break; case OPC63_MTFSB1: FPU_EMU_EVCNT_INCR(mtfsb1); DPRINTF(FPE_INSN, ("fpu_execute: MTFSB1\n")); fe->fe_fpscr |= (~(FPSCR_VX|FPSR_EX) & (1<<(31-rt))); break; case OPC63_FNEG: FPU_EMU_EVCNT_INCR(fnegabs); DPRINTF(FPE_INSN, ("fpu_execute: FNEGABS\n")); memcpy(&fs->fpr[rt].fpr, &fs->fpr[rb].fpr, sizeof(double)); a = (int *)&fs->fpr[rt].fpr; *a ^= (1U << 31); break; case OPC63_MCRFS: FPU_EMU_EVCNT_INCR(mcrfs); DPRINTF(FPE_INSN, ("fpu_execute: MCRFS\n")); cond = 0; rt &= 0x1c; ra &= 0x1c; /* Extract the bits we want */ mask = (fe->fe_fpscr >> (28 - ra)) & 0xf; /* Clear the bits we copied. */ fe->fe_cx = (FPSR_EX_MSK | (0xf << (28 - ra))); fe->fe_fpscr &= fe->fe_cx; /* Now shove them in the right part of cr */ tf->cr &= ~(0xf << (28 - rt)); tf->cr |= (mask << (28 - rt)); break; case OPC63_MTFSB0: FPU_EMU_EVCNT_INCR(mtfsb0); DPRINTF(FPE_INSN, ("fpu_execute: MTFSB0\n")); fe->fe_fpscr &= ((FPSCR_VX|FPSR_EX) & ~(1<<(31-rt))); break; case OPC63_FMR: FPU_EMU_EVCNT_INCR(fmr); DPRINTF(FPE_INSN, ("fpu_execute: FMR\n")); memcpy(&fs->fpr[rt].fpr, &fs->fpr[rb].fpr, sizeof(double)); break; case OPC63_MTFSFI: FPU_EMU_EVCNT_INCR(mtfsfi); DPRINTF(FPE_INSN, ("fpu_execute: MTFSFI\n")); rb >>= 1; rt &= 0x1c; /* Already left-shifted 4 */ fe->fe_cx = rb << (28 - rt); mask = 0xf<<(28 - rt); fe->fe_fpscr = (fe->fe_fpscr & ~mask) | fe->fe_cx; /* XXX weird stuff about OX, FX, FEX, and VX should be handled */ break; case OPC63_FNABS: FPU_EMU_EVCNT_INCR(fnabs); DPRINTF(FPE_INSN, ("fpu_execute: FABS\n")); memcpy(&fs->fpr[rt].fpr, &fs->fpr[rb].fpr, sizeof(double)); a = (int *)&fs->fpr[rt].fpr; *a |= (1U << 31); break; case OPC63_FABS: FPU_EMU_EVCNT_INCR(fabs); DPRINTF(FPE_INSN, ("fpu_execute: FABS\n")); memcpy(&fs->fpr[rt].fpr, &fs->fpr[rb].fpr, sizeof(double)); a = (int *)&fs->fpr[rt].fpr; *a &= ~(1U << 31); break; case OPC63_MFFS: FPU_EMU_EVCNT_INCR(mffs); DPRINTF(FPE_INSN, ("fpu_execute: MFFS\n")); memcpy(&fs->fpr[rt].fpr, &fs->fpscr, sizeof(fs->fpscr)); break; case OPC63_MTFSF: FPU_EMU_EVCNT_INCR(mtfsf); DPRINTF(FPE_INSN, ("fpu_execute: MTFSF\n")); if ((rt = instr.i_xfl.i_flm) == -1) mask = -1; else { mask = 0; /* Convert 1 bit -> 4 bits */ for (ra = 0; ra < 8; ra ++) if (rt & (1<fpr[rt].fpr; fe->fe_cx = mask & a[1]; fe->fe_fpscr = (fe->fe_fpscr&~mask) | (fe->fe_cx); /* XXX weird stuff about OX, FX, FEX, and VX should be handled */ break; case OPC63_FCTID: case OPC63_FCTIDZ: FPU_EMU_EVCNT_INCR(fctid); DPRINTF(FPE_INSN, ("fpu_execute: FCTID\n")); fpu_explode(fe, fp = &fe->fe_f1, type, rb); type = FTYPE_LNG; break; case OPC63_FCFID: FPU_EMU_EVCNT_INCR(fcfid); DPRINTF(FPE_INSN, ("fpu_execute: FCFID\n")); type = FTYPE_LNG; fpu_explode(fe, fp = &fe->fe_f1, type, rb); type = FTYPE_DBL; break; default: return (NOTFPU); break; } } else { /* Format A */ rt = instr.i_a.i_frt; ra = instr.i_a.i_fra; rb = instr.i_a.i_frb; rc = instr.i_a.i_frc; /* * All arithmetic operations work on registers, which * are stored as doubles. */ type = FTYPE_DBL; switch ((unsigned int)instr.i_a.i_xo) { case OPC59_FDIVS: FPU_EMU_EVCNT_INCR(fdiv); DPRINTF(FPE_INSN, ("fpu_execute: FDIV\n")); fpu_explode(fe, &fe->fe_f1, type, ra); fpu_explode(fe, &fe->fe_f2, type, rb); fp = fpu_div(fe); break; case OPC59_FSUBS: FPU_EMU_EVCNT_INCR(fsub); DPRINTF(FPE_INSN, ("fpu_execute: FSUB\n")); fpu_explode(fe, &fe->fe_f1, type, ra); fpu_explode(fe, &fe->fe_f2, type, rb); fp = fpu_sub(fe); break; case OPC59_FADDS: FPU_EMU_EVCNT_INCR(fadd); DPRINTF(FPE_INSN, ("fpu_execute: FADD\n")); fpu_explode(fe, &fe->fe_f1, type, ra); fpu_explode(fe, &fe->fe_f2, type, rb); fp = fpu_add(fe); break; case OPC59_FSQRTS: FPU_EMU_EVCNT_INCR(fsqrt); DPRINTF(FPE_INSN, ("fpu_execute: FSQRT\n")); fpu_explode(fe, &fe->fe_f1, type, rb); fp = fpu_sqrt(fe); break; case OPC63M_FSEL: FPU_EMU_EVCNT_INCR(fsel); DPRINTF(FPE_INSN, ("fpu_execute: FSEL\n")); a = (int *)&fe->fe_fpstate->fpr[ra].fpr; if ((*a & 0x80000000) && (*a & 0x7fffffff)) /* fra < 0 */ rc = rb; DPRINTF(FPE_INSN, ("f%d => f%d\n", rc, rt)); memcpy(&fs->fpr[rt].fpr, &fs->fpr[rc].fpr, sizeof(double)); break; case OPC59_FRES: FPU_EMU_EVCNT_INCR(fpres); DPRINTF(FPE_INSN, ("fpu_execute: FPRES\n")); fpu_explode(fe, &fe->fe_f1, type, rb); fp = fpu_sqrt(fe); /* now we've gotta overwrite the dest reg */ *((int *)&fe->fe_fpstate->fpr[rt].fpr) = 1; fpu_explode(fe, &fe->fe_f1, FTYPE_INT, rt); fpu_div(fe); break; case OPC59_FMULS: FPU_EMU_EVCNT_INCR(fmul); DPRINTF(FPE_INSN, ("fpu_execute: FMUL\n")); fpu_explode(fe, &fe->fe_f1, type, ra); fpu_explode(fe, &fe->fe_f2, type, rc); fp = fpu_mul(fe); break; case OPC63M_FRSQRTE: /* Reciprocal sqrt() estimate */ FPU_EMU_EVCNT_INCR(frsqrte); DPRINTF(FPE_INSN, ("fpu_execute: FRSQRTE\n")); fpu_explode(fe, &fe->fe_f1, type, rb); fp = fpu_sqrt(fe); fe->fe_f2 = *fp; /* now we've gotta overwrite the dest reg */ *((int *)&fe->fe_fpstate->fpr[rt].fpr) = 1; fpu_explode(fe, &fe->fe_f1, FTYPE_INT, rt); fpu_div(fe); break; case OPC59_FMSUBS: FPU_EMU_EVCNT_INCR(fmulsub); DPRINTF(FPE_INSN, ("fpu_execute: FMULSUB\n")); fpu_explode(fe, &fe->fe_f1, type, ra); fpu_explode(fe, &fe->fe_f2, type, rc); fp = fpu_mul(fe); fe->fe_f1 = *fp; fpu_explode(fe, &fe->fe_f2, type, rb); fp = fpu_sub(fe); break; case OPC59_FMADDS: FPU_EMU_EVCNT_INCR(fmuladd); DPRINTF(FPE_INSN, ("fpu_execute: FMULADD\n")); fpu_explode(fe, &fe->fe_f1, type, ra); fpu_explode(fe, &fe->fe_f2, type, rc); fp = fpu_mul(fe); fe->fe_f1 = *fp; fpu_explode(fe, &fe->fe_f2, type, rb); fp = fpu_add(fe); break; case OPC59_FNMSUBS: FPU_EMU_EVCNT_INCR(fnmsub); DPRINTF(FPE_INSN, ("fpu_execute: FNMSUB\n")); fpu_explode(fe, &fe->fe_f1, type, ra); fpu_explode(fe, &fe->fe_f2, type, rc); fp = fpu_mul(fe); fe->fe_f1 = *fp; fpu_explode(fe, &fe->fe_f2, type, rb); fp = fpu_sub(fe); /* Negate */ fp->fp_sign ^= 1; break; case OPC59_FNMADDS: FPU_EMU_EVCNT_INCR(fnmadd); DPRINTF(FPE_INSN, ("fpu_execute: FNMADD\n")); fpu_explode(fe, &fe->fe_f1, type, ra); fpu_explode(fe, &fe->fe_f2, type, rc); fp = fpu_mul(fe); fe->fe_f1 = *fp; fpu_explode(fe, &fe->fe_f2, type, rb); fp = fpu_add(fe); /* Negate */ fp->fp_sign ^= 1; break; default: return (NOTFPU); break; } /* If the instruction was single precision, round */ if (!(instr.i_any.i_opcd & 0x4)) { fpu_implode(fe, fp, FTYPE_SNG, (u_int *)&fs->fpr[rt].fpr); fpu_explode(fe, fp = &fe->fe_f1, FTYPE_SNG, rt); } } } else { return (NOTFPU); } /* * ALU operation is complete. Collapse the result and then check * for exceptions. If we got any, and they are enabled, do not * alter the destination register, just stop with an exception. * Otherwise set new current exceptions and accrue. */ if (fp) fpu_implode(fe, fp, type, (u_int *)&fs->fpr[rt].fpr); cx = fe->fe_cx; fsr = fe->fe_fpscr; if (cx != 0) { fsr &= ~FPSCR_FX; if ((cx^fsr)&FPSR_EX_MSK) fsr |= FPSCR_FX; mask = fsr & FPSR_EX; mask <<= (25-3); if (cx & mask) fsr |= FPSCR_FEX; if (cx & FPSCR_FPRF) { /* Need to replace CC */ fsr &= ~FPSCR_FPRF; } if (cx & (FPSR_EXOP)) fsr |= FPSCR_VX; fsr |= cx; DPRINTF(FPE_INSN, ("fpu_execute: cx %x, fsr %x\n", cx, fsr)); } if (cond) { cond = fsr & 0xf0000000; /* Isolate condition codes */ cond >>= 28; /* Move fpu condition codes to cr[1] */ tf->cr &= (0x0f000000); tf->cr |= (cond<<24); DPRINTF(FPE_INSN, ("fpu_execute: cr[1] <= %x\n", cond)); } if (setcr) { cond = fsr & FPSCR_FPCC; /* Isolate condition codes */ cond <<= 16; /* Move fpu condition codes to cr[1] */ tf->cr &= ~(0xf0000000>>bf); tf->cr |= (cond>>bf); DPRINTF(FPE_INSN, ("fpu_execute: cr[%d] (cr=%jx) <= %x\n", bf/4, (uintmax_t)tf->cr, cond)); } ((int *)&fs->fpscr)[1] = fsr; if (fsr & FPSCR_FEX) return(FPE); return (0); /* success */ } Index: head/sys/powerpc/include/pcb.h =================================================================== --- head/sys/powerpc/include/pcb.h (revision 340652) +++ head/sys/powerpc/include/pcb.h (revision 340653) @@ -1,110 +1,111 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $NetBSD: pcb.h,v 1.4 2000/06/04 11:57:17 tsubai Exp $ * $FreeBSD$ */ #ifndef _MACHINE_PCB_H_ #define _MACHINE_PCB_H_ #include #ifndef _STANDALONE struct pcb { register_t pcb_context[20]; /* non-volatile r14-r31 */ register_t pcb_cr; /* Condition register */ register_t pcb_sp; /* stack pointer */ register_t pcb_toc; /* toc pointer */ register_t pcb_lr; /* link register */ register_t pcb_dscr; /* dscr value */ struct pmap *pcb_pm; /* pmap of our vmspace */ jmp_buf *pcb_onfault; /* For use during copyin/copyout */ int pcb_flags; #define PCB_FPU 0x1 /* Process uses FPU */ #define PCB_FPREGS 0x2 /* Process had FPU registers initialized */ #define PCB_VEC 0x4 /* Process had Altivec initialized */ #define PCB_VSX 0x8 /* Process had VSX initialized */ #define PCB_CDSCR 0x10 /* Process had Custom DSCR initialized */ #define PCB_HTM 0x20 /* Process had HTM initialized */ struct fpu { union { double fpr; uint32_t vsr[4]; } fpr[32]; double fpscr; /* FPSCR stored as double for easier access */ } pcb_fpu; /* Floating point processor */ unsigned int pcb_fpcpu; /* which CPU had our FPU stuff. */ struct vec { uint32_t vr[32][4]; uint32_t spare[2]; uint32_t vrsave; uint32_t vscr; /* aligned at vector element 3 */ } pcb_vec __aligned(16); /* Vector processor */ unsigned int pcb_veccpu; /* which CPU had our vector stuff. */ struct htm { uint64_t tfhar; uint64_t texasr; uint64_t tfiar; } pcb_htm; union { struct { vm_offset_t usr_segm; /* Base address */ register_t usr_vsid; /* USER_SR segment */ } aim; struct { register_t dbcr0; } booke; } pcb_cpu; + vm_offset_t pcb_lastill; /* Last illegal instruction */ }; #endif #ifdef _KERNEL struct trapframe; #ifndef curpcb extern struct pcb *curpcb; #endif extern struct pmap *curpm; extern struct proc *fpuproc; void makectx(struct trapframe *, struct pcb *); void savectx(struct pcb *) __returns_twice; #endif #endif /* _MACHINE_PCB_H_ */ Index: head/sys/powerpc/powerpc/exec_machdep.c =================================================================== --- head/sys/powerpc/powerpc/exec_machdep.c (revision 340652) +++ head/sys/powerpc/powerpc/exec_machdep.c (revision 340653) @@ -1,1105 +1,1115 @@ /*- * SPDX-License-Identifier: BSD-4-Clause AND BSD-2-Clause-FreeBSD * * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /*- * Copyright (C) 2001 Benno Rice * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * $NetBSD: machdep.c,v 1.74.2.1 2000/11/01 16:13:48 tv Exp $ */ #include __FBSDID("$FreeBSD$"); #include "opt_fpu_emu.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include + #ifdef FPU_EMU #include #endif #ifdef COMPAT_FREEBSD32 #include #include #include typedef struct __ucontext32 { sigset_t uc_sigmask; mcontext32_t uc_mcontext; uint32_t uc_link; struct sigaltstack32 uc_stack; uint32_t uc_flags; uint32_t __spare__[4]; } ucontext32_t; struct sigframe32 { ucontext32_t sf_uc; struct siginfo32 sf_si; }; static int grab_mcontext32(struct thread *td, mcontext32_t *, int flags); #endif static int grab_mcontext(struct thread *, mcontext_t *, int); void sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) { struct trapframe *tf; struct sigacts *psp; struct sigframe sf; struct thread *td; struct proc *p; #ifdef COMPAT_FREEBSD32 struct siginfo32 siginfo32; struct sigframe32 sf32; #endif size_t sfpsize; caddr_t sfp, usfp; int oonstack, rndfsize; int sig; int code; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); psp = p->p_sigacts; mtx_assert(&psp->ps_mtx, MA_OWNED); tf = td->td_frame; oonstack = sigonstack(tf->fixreg[1]); /* * Fill siginfo structure. */ ksi->ksi_info.si_signo = ksi->ksi_signo; ksi->ksi_info.si_addr = (void *)((tf->exc == EXC_DSI || tf->exc == EXC_DSE) ? tf->dar : tf->srr0); #ifdef COMPAT_FREEBSD32 if (SV_PROC_FLAG(p, SV_ILP32)) { siginfo_to_siginfo32(&ksi->ksi_info, &siginfo32); sig = siginfo32.si_signo; code = siginfo32.si_code; sfp = (caddr_t)&sf32; sfpsize = sizeof(sf32); rndfsize = roundup(sizeof(sf32), 16); /* * Save user context */ memset(&sf32, 0, sizeof(sf32)); grab_mcontext32(td, &sf32.sf_uc.uc_mcontext, 0); sf32.sf_uc.uc_sigmask = *mask; sf32.sf_uc.uc_stack.ss_sp = (uintptr_t)td->td_sigstk.ss_sp; sf32.sf_uc.uc_stack.ss_size = (uint32_t)td->td_sigstk.ss_size; sf32.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE; sf32.sf_uc.uc_mcontext.mc_onstack = (oonstack) ? 1 : 0; } else { #endif sig = ksi->ksi_signo; code = ksi->ksi_code; sfp = (caddr_t)&sf; sfpsize = sizeof(sf); #ifdef __powerpc64__ /* * 64-bit PPC defines a 288 byte scratch region * below the stack. */ rndfsize = 288 + roundup(sizeof(sf), 48); #else rndfsize = roundup(sizeof(sf), 16); #endif /* * Save user context */ memset(&sf, 0, sizeof(sf)); grab_mcontext(td, &sf.sf_uc.uc_mcontext, 0); sf.sf_uc.uc_sigmask = *mask; sf.sf_uc.uc_stack = td->td_sigstk; sf.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE; sf.sf_uc.uc_mcontext.mc_onstack = (oonstack) ? 1 : 0; #ifdef COMPAT_FREEBSD32 } #endif CTR4(KTR_SIG, "sendsig: td=%p (%s) catcher=%p sig=%d", td, p->p_comm, catcher, sig); /* * Allocate and validate space for the signal handler context. */ if ((td->td_pflags & TDP_ALTSTACK) != 0 && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { usfp = (void *)(((uintptr_t)td->td_sigstk.ss_sp + td->td_sigstk.ss_size - rndfsize) & ~0xFul); } else { usfp = (void *)((tf->fixreg[1] - rndfsize) & ~0xFul); } /* * Save the floating-point state, if necessary, then copy it. */ /* XXX */ /* * Set up the registers to return to sigcode. * * r1/sp - sigframe ptr * lr - sig function, dispatched to by blrl in trampoline * r3 - sig number * r4 - SIGINFO ? &siginfo : exception code * r5 - user context * srr0 - trampoline function addr */ tf->lr = (register_t)catcher; tf->fixreg[1] = (register_t)usfp; tf->fixreg[FIRSTARG] = sig; #ifdef COMPAT_FREEBSD32 tf->fixreg[FIRSTARG+2] = (register_t)usfp + ((SV_PROC_FLAG(p, SV_ILP32)) ? offsetof(struct sigframe32, sf_uc) : offsetof(struct sigframe, sf_uc)); #else tf->fixreg[FIRSTARG+2] = (register_t)usfp + offsetof(struct sigframe, sf_uc); #endif if (SIGISMEMBER(psp->ps_siginfo, sig)) { /* * Signal handler installed with SA_SIGINFO. */ #ifdef COMPAT_FREEBSD32 if (SV_PROC_FLAG(p, SV_ILP32)) { sf32.sf_si = siginfo32; tf->fixreg[FIRSTARG+1] = (register_t)usfp + offsetof(struct sigframe32, sf_si); sf32.sf_si = siginfo32; } else { #endif tf->fixreg[FIRSTARG+1] = (register_t)usfp + offsetof(struct sigframe, sf_si); sf.sf_si = ksi->ksi_info; #ifdef COMPAT_FREEBSD32 } #endif } else { /* Old FreeBSD-style arguments. */ tf->fixreg[FIRSTARG+1] = code; tf->fixreg[FIRSTARG+3] = (tf->exc == EXC_DSI) ? tf->dar : tf->srr0; } mtx_unlock(&psp->ps_mtx); PROC_UNLOCK(p); tf->srr0 = (register_t)p->p_sysent->sv_sigcode_base; /* * copy the frame out to userland. */ if (copyout(sfp, usfp, sfpsize) != 0) { /* * Process has trashed its stack. Kill it. */ CTR2(KTR_SIG, "sendsig: sigexit td=%p sfp=%p", td, sfp); PROC_LOCK(p); sigexit(td, SIGILL); } CTR3(KTR_SIG, "sendsig: return td=%p pc=%#x sp=%#x", td, tf->srr0, tf->fixreg[1]); PROC_LOCK(p); mtx_lock(&psp->ps_mtx); } int sys_sigreturn(struct thread *td, struct sigreturn_args *uap) { ucontext_t uc; int error; CTR2(KTR_SIG, "sigreturn: td=%p ucp=%p", td, uap->sigcntxp); if (copyin(uap->sigcntxp, &uc, sizeof(uc)) != 0) { CTR1(KTR_SIG, "sigreturn: efault td=%p", td); return (EFAULT); } error = set_mcontext(td, &uc.uc_mcontext); if (error != 0) return (error); kern_sigprocmask(td, SIG_SETMASK, &uc.uc_sigmask, NULL, 0); CTR3(KTR_SIG, "sigreturn: return td=%p pc=%#x sp=%#x", td, uc.uc_mcontext.mc_srr0, uc.uc_mcontext.mc_gpr[1]); return (EJUSTRETURN); } #ifdef COMPAT_FREEBSD4 int freebsd4_sigreturn(struct thread *td, struct freebsd4_sigreturn_args *uap) { return sys_sigreturn(td, (struct sigreturn_args *)uap); } #endif /* * Construct a PCB from a trapframe. This is called from kdb_trap() where * we want to start a backtrace from the function that caused us to enter * the debugger. We have the context in the trapframe, but base the trace * on the PCB. The PCB doesn't have to be perfect, as long as it contains * enough for a backtrace. */ void makectx(struct trapframe *tf, struct pcb *pcb) { pcb->pcb_lr = tf->srr0; pcb->pcb_sp = tf->fixreg[1]; } /* * get_mcontext/sendsig helper routine that doesn't touch the * proc lock */ static int grab_mcontext(struct thread *td, mcontext_t *mcp, int flags) { struct pcb *pcb; int i; pcb = td->td_pcb; memset(mcp, 0, sizeof(mcontext_t)); mcp->mc_vers = _MC_VERSION; mcp->mc_flags = 0; memcpy(&mcp->mc_frame, td->td_frame, sizeof(struct trapframe)); if (flags & GET_MC_CLEAR_RET) { mcp->mc_gpr[3] = 0; mcp->mc_gpr[4] = 0; } /* * This assumes that floating-point context is *not* lazy, * so if the thread has used FP there would have been a * FP-unavailable exception that would have set things up * correctly. */ if (pcb->pcb_flags & PCB_FPREGS) { if (pcb->pcb_flags & PCB_FPU) { KASSERT(td == curthread, ("get_mcontext: fp save not curthread")); critical_enter(); save_fpu(td); critical_exit(); } mcp->mc_flags |= _MC_FP_VALID; memcpy(&mcp->mc_fpscr, &pcb->pcb_fpu.fpscr, sizeof(double)); for (i = 0; i < 32; i++) memcpy(&mcp->mc_fpreg[i], &pcb->pcb_fpu.fpr[i].fpr, sizeof(double)); } if (pcb->pcb_flags & PCB_VSX) { for (i = 0; i < 32; i++) memcpy(&mcp->mc_vsxfpreg[i], &pcb->pcb_fpu.fpr[i].vsr[2], sizeof(double)); } /* * Repeat for Altivec context */ if (pcb->pcb_flags & PCB_VEC) { KASSERT(td == curthread, ("get_mcontext: fp save not curthread")); critical_enter(); save_vec(td); critical_exit(); mcp->mc_flags |= _MC_AV_VALID; mcp->mc_vscr = pcb->pcb_vec.vscr; mcp->mc_vrsave = pcb->pcb_vec.vrsave; memcpy(mcp->mc_avec, pcb->pcb_vec.vr, sizeof(mcp->mc_avec)); } mcp->mc_len = sizeof(*mcp); return (0); } int get_mcontext(struct thread *td, mcontext_t *mcp, int flags) { int error; error = grab_mcontext(td, mcp, flags); if (error == 0) { PROC_LOCK(curthread->td_proc); mcp->mc_onstack = sigonstack(td->td_frame->fixreg[1]); PROC_UNLOCK(curthread->td_proc); } return (error); } int set_mcontext(struct thread *td, mcontext_t *mcp) { struct pcb *pcb; struct trapframe *tf; register_t tls; int i; pcb = td->td_pcb; tf = td->td_frame; if (mcp->mc_vers != _MC_VERSION || mcp->mc_len != sizeof(*mcp)) return (EINVAL); /* * Don't let the user set privileged MSR bits */ if ((mcp->mc_srr1 & psl_userstatic) != (tf->srr1 & psl_userstatic)) { return (EINVAL); } /* Copy trapframe, preserving TLS pointer across context change */ if (SV_PROC_FLAG(td->td_proc, SV_LP64)) tls = tf->fixreg[13]; else tls = tf->fixreg[2]; memcpy(tf, mcp->mc_frame, sizeof(mcp->mc_frame)); if (SV_PROC_FLAG(td->td_proc, SV_LP64)) tf->fixreg[13] = tls; else tf->fixreg[2] = tls; if (mcp->mc_flags & _MC_FP_VALID) { /* enable_fpu() will happen lazily on a fault */ pcb->pcb_flags |= PCB_FPREGS; memcpy(&pcb->pcb_fpu.fpscr, &mcp->mc_fpscr, sizeof(double)); bzero(pcb->pcb_fpu.fpr, sizeof(pcb->pcb_fpu.fpr)); for (i = 0; i < 32; i++) { memcpy(&pcb->pcb_fpu.fpr[i].fpr, &mcp->mc_fpreg[i], sizeof(double)); memcpy(&pcb->pcb_fpu.fpr[i].vsr[2], &mcp->mc_vsxfpreg[i], sizeof(double)); } } if (mcp->mc_flags & _MC_AV_VALID) { if ((pcb->pcb_flags & PCB_VEC) != PCB_VEC) { critical_enter(); enable_vec(td); critical_exit(); } pcb->pcb_vec.vscr = mcp->mc_vscr; pcb->pcb_vec.vrsave = mcp->mc_vrsave; memcpy(pcb->pcb_vec.vr, mcp->mc_avec, sizeof(mcp->mc_avec)); } return (0); } /* * Set set up registers on exec. */ void exec_setregs(struct thread *td, struct image_params *imgp, u_long stack) { struct trapframe *tf; register_t argc; tf = trapframe(td); bzero(tf, sizeof *tf); #ifdef __powerpc64__ tf->fixreg[1] = -roundup(-stack + 48, 16); #else tf->fixreg[1] = -roundup(-stack + 8, 16); #endif /* * Set up arguments for _start(): * _start(argc, argv, envp, obj, cleanup, ps_strings); * * Notes: * - obj and cleanup are the auxilliary and termination * vectors. They are fixed up by ld.elf_so. * - ps_strings is a NetBSD extention, and will be * ignored by executables which are strictly * compliant with the SVR4 ABI. */ /* Collect argc from the user stack */ argc = fuword((void *)stack); tf->fixreg[3] = argc; tf->fixreg[4] = stack + sizeof(register_t); tf->fixreg[5] = stack + (2 + argc)*sizeof(register_t); tf->fixreg[6] = 0; /* auxillary vector */ tf->fixreg[7] = 0; /* termination vector */ tf->fixreg[8] = (register_t)imgp->ps_strings; /* NetBSD extension */ tf->srr0 = imgp->entry_addr; #ifdef __powerpc64__ tf->fixreg[12] = imgp->entry_addr; #endif tf->srr1 = psl_userset | PSL_FE_DFLT; td->td_pcb->pcb_flags = 0; } #ifdef COMPAT_FREEBSD32 void ppc32_setregs(struct thread *td, struct image_params *imgp, u_long stack) { struct trapframe *tf; uint32_t argc; tf = trapframe(td); bzero(tf, sizeof *tf); tf->fixreg[1] = -roundup(-stack + 8, 16); argc = fuword32((void *)stack); tf->fixreg[3] = argc; tf->fixreg[4] = stack + sizeof(uint32_t); tf->fixreg[5] = stack + (2 + argc)*sizeof(uint32_t); tf->fixreg[6] = 0; /* auxillary vector */ tf->fixreg[7] = 0; /* termination vector */ tf->fixreg[8] = (register_t)imgp->ps_strings; /* NetBSD extension */ tf->srr0 = imgp->entry_addr; tf->srr1 = psl_userset32 | PSL_FE_DFLT; td->td_pcb->pcb_flags = 0; } #endif int fill_regs(struct thread *td, struct reg *regs) { struct trapframe *tf; tf = td->td_frame; memcpy(regs, tf, sizeof(struct reg)); return (0); } int fill_dbregs(struct thread *td, struct dbreg *dbregs) { /* No debug registers on PowerPC */ return (ENOSYS); } int fill_fpregs(struct thread *td, struct fpreg *fpregs) { struct pcb *pcb; int i; pcb = td->td_pcb; if ((pcb->pcb_flags & PCB_FPREGS) == 0) memset(fpregs, 0, sizeof(struct fpreg)); else { memcpy(&fpregs->fpscr, &pcb->pcb_fpu.fpscr, sizeof(double)); for (i = 0; i < 32; i++) memcpy(&fpregs->fpreg[i], &pcb->pcb_fpu.fpr[i].fpr, sizeof(double)); } return (0); } int set_regs(struct thread *td, struct reg *regs) { struct trapframe *tf; tf = td->td_frame; memcpy(tf, regs, sizeof(struct reg)); return (0); } int set_dbregs(struct thread *td, struct dbreg *dbregs) { /* No debug registers on PowerPC */ return (ENOSYS); } int set_fpregs(struct thread *td, struct fpreg *fpregs) { struct pcb *pcb; int i; pcb = td->td_pcb; pcb->pcb_flags |= PCB_FPREGS; memcpy(&pcb->pcb_fpu.fpscr, &fpregs->fpscr, sizeof(double)); for (i = 0; i < 32; i++) { memcpy(&pcb->pcb_fpu.fpr[i].fpr, &fpregs->fpreg[i], sizeof(double)); } return (0); } #ifdef COMPAT_FREEBSD32 int set_regs32(struct thread *td, struct reg32 *regs) { struct trapframe *tf; int i; tf = td->td_frame; for (i = 0; i < 32; i++) tf->fixreg[i] = regs->fixreg[i]; tf->lr = regs->lr; tf->cr = regs->cr; tf->xer = regs->xer; tf->ctr = regs->ctr; tf->srr0 = regs->pc; return (0); } int fill_regs32(struct thread *td, struct reg32 *regs) { struct trapframe *tf; int i; tf = td->td_frame; for (i = 0; i < 32; i++) regs->fixreg[i] = tf->fixreg[i]; regs->lr = tf->lr; regs->cr = tf->cr; regs->xer = tf->xer; regs->ctr = tf->ctr; regs->pc = tf->srr0; return (0); } static int grab_mcontext32(struct thread *td, mcontext32_t *mcp, int flags) { mcontext_t mcp64; int i, error; error = grab_mcontext(td, &mcp64, flags); if (error != 0) return (error); mcp->mc_vers = mcp64.mc_vers; mcp->mc_flags = mcp64.mc_flags; mcp->mc_onstack = mcp64.mc_onstack; mcp->mc_len = mcp64.mc_len; memcpy(mcp->mc_avec,mcp64.mc_avec,sizeof(mcp64.mc_avec)); memcpy(mcp->mc_av,mcp64.mc_av,sizeof(mcp64.mc_av)); for (i = 0; i < 42; i++) mcp->mc_frame[i] = mcp64.mc_frame[i]; memcpy(mcp->mc_fpreg,mcp64.mc_fpreg,sizeof(mcp64.mc_fpreg)); memcpy(mcp->mc_vsxfpreg,mcp64.mc_vsxfpreg,sizeof(mcp64.mc_vsxfpreg)); return (0); } static int get_mcontext32(struct thread *td, mcontext32_t *mcp, int flags) { int error; error = grab_mcontext32(td, mcp, flags); if (error == 0) { PROC_LOCK(curthread->td_proc); mcp->mc_onstack = sigonstack(td->td_frame->fixreg[1]); PROC_UNLOCK(curthread->td_proc); } return (error); } static int set_mcontext32(struct thread *td, mcontext32_t *mcp) { mcontext_t mcp64; int i, error; mcp64.mc_vers = mcp->mc_vers; mcp64.mc_flags = mcp->mc_flags; mcp64.mc_onstack = mcp->mc_onstack; mcp64.mc_len = mcp->mc_len; memcpy(mcp64.mc_avec,mcp->mc_avec,sizeof(mcp64.mc_avec)); memcpy(mcp64.mc_av,mcp->mc_av,sizeof(mcp64.mc_av)); for (i = 0; i < 42; i++) mcp64.mc_frame[i] = mcp->mc_frame[i]; mcp64.mc_srr1 |= (td->td_frame->srr1 & 0xFFFFFFFF00000000ULL); memcpy(mcp64.mc_fpreg,mcp->mc_fpreg,sizeof(mcp64.mc_fpreg)); memcpy(mcp64.mc_vsxfpreg,mcp->mc_vsxfpreg,sizeof(mcp64.mc_vsxfpreg)); error = set_mcontext(td, &mcp64); return (error); } #endif #ifdef COMPAT_FREEBSD32 int freebsd32_sigreturn(struct thread *td, struct freebsd32_sigreturn_args *uap) { ucontext32_t uc; int error; CTR2(KTR_SIG, "sigreturn: td=%p ucp=%p", td, uap->sigcntxp); if (copyin(uap->sigcntxp, &uc, sizeof(uc)) != 0) { CTR1(KTR_SIG, "sigreturn: efault td=%p", td); return (EFAULT); } error = set_mcontext32(td, &uc.uc_mcontext); if (error != 0) return (error); kern_sigprocmask(td, SIG_SETMASK, &uc.uc_sigmask, NULL, 0); CTR3(KTR_SIG, "sigreturn: return td=%p pc=%#x sp=%#x", td, uc.uc_mcontext.mc_srr0, uc.uc_mcontext.mc_gpr[1]); return (EJUSTRETURN); } /* * The first two fields of a ucontext_t are the signal mask and the machine * context. The next field is uc_link; we want to avoid destroying the link * when copying out contexts. */ #define UC32_COPY_SIZE offsetof(ucontext32_t, uc_link) int freebsd32_getcontext(struct thread *td, struct freebsd32_getcontext_args *uap) { ucontext32_t uc; int ret; if (uap->ucp == NULL) ret = EINVAL; else { get_mcontext32(td, &uc.uc_mcontext, GET_MC_CLEAR_RET); PROC_LOCK(td->td_proc); uc.uc_sigmask = td->td_sigmask; PROC_UNLOCK(td->td_proc); ret = copyout(&uc, uap->ucp, UC32_COPY_SIZE); } return (ret); } int freebsd32_setcontext(struct thread *td, struct freebsd32_setcontext_args *uap) { ucontext32_t uc; int ret; if (uap->ucp == NULL) ret = EINVAL; else { ret = copyin(uap->ucp, &uc, UC32_COPY_SIZE); if (ret == 0) { ret = set_mcontext32(td, &uc.uc_mcontext); if (ret == 0) { kern_sigprocmask(td, SIG_SETMASK, &uc.uc_sigmask, NULL, 0); } } } return (ret == 0 ? EJUSTRETURN : ret); } int freebsd32_swapcontext(struct thread *td, struct freebsd32_swapcontext_args *uap) { ucontext32_t uc; int ret; if (uap->oucp == NULL || uap->ucp == NULL) ret = EINVAL; else { get_mcontext32(td, &uc.uc_mcontext, GET_MC_CLEAR_RET); PROC_LOCK(td->td_proc); uc.uc_sigmask = td->td_sigmask; PROC_UNLOCK(td->td_proc); ret = copyout(&uc, uap->oucp, UC32_COPY_SIZE); if (ret == 0) { ret = copyin(uap->ucp, &uc, UC32_COPY_SIZE); if (ret == 0) { ret = set_mcontext32(td, &uc.uc_mcontext); if (ret == 0) { kern_sigprocmask(td, SIG_SETMASK, &uc.uc_sigmask, NULL, 0); } } } } return (ret == 0 ? EJUSTRETURN : ret); } #endif void cpu_set_syscall_retval(struct thread *td, int error) { struct proc *p; struct trapframe *tf; int fixup; if (error == EJUSTRETURN) return; p = td->td_proc; tf = td->td_frame; if (tf->fixreg[0] == SYS___syscall && (SV_PROC_FLAG(p, SV_ILP32))) { int code = tf->fixreg[FIRSTARG + 1]; if (p->p_sysent->sv_mask) code &= p->p_sysent->sv_mask; fixup = ( #if defined(COMPAT_FREEBSD6) && defined(SYS_freebsd6_lseek) code != SYS_freebsd6_lseek && #endif code != SYS_lseek) ? 1 : 0; } else fixup = 0; switch (error) { case 0: if (fixup) { /* * 64-bit return, 32-bit syscall. Fixup byte order */ tf->fixreg[FIRSTARG] = 0; tf->fixreg[FIRSTARG + 1] = td->td_retval[0]; } else { tf->fixreg[FIRSTARG] = td->td_retval[0]; tf->fixreg[FIRSTARG + 1] = td->td_retval[1]; } tf->cr &= ~0x10000000; /* Unset summary overflow */ break; case ERESTART: /* * Set user's pc back to redo the system call. */ tf->srr0 -= 4; break; default: tf->fixreg[FIRSTARG] = SV_ABI_ERRNO(p, error); tf->cr |= 0x10000000; /* Set summary overflow */ break; } } /* * Threading functions */ void cpu_thread_exit(struct thread *td) { } void cpu_thread_clean(struct thread *td) { } void cpu_thread_alloc(struct thread *td) { struct pcb *pcb; pcb = (struct pcb *)((td->td_kstack + td->td_kstack_pages * PAGE_SIZE - sizeof(struct pcb)) & ~0x2fUL); td->td_pcb = pcb; td->td_frame = (struct trapframe *)pcb - 1; } void cpu_thread_free(struct thread *td) { } int cpu_set_user_tls(struct thread *td, void *tls_base) { if (SV_PROC_FLAG(td->td_proc, SV_LP64)) td->td_frame->fixreg[13] = (register_t)tls_base + 0x7010; else td->td_frame->fixreg[2] = (register_t)tls_base + 0x7008; return (0); } void cpu_copy_thread(struct thread *td, struct thread *td0) { struct pcb *pcb2; struct trapframe *tf; struct callframe *cf; pcb2 = td->td_pcb; /* Copy the upcall pcb */ bcopy(td0->td_pcb, pcb2, sizeof(*pcb2)); /* Create a stack for the new thread */ tf = td->td_frame; bcopy(td0->td_frame, tf, sizeof(struct trapframe)); tf->fixreg[FIRSTARG] = 0; tf->fixreg[FIRSTARG + 1] = 0; tf->cr &= ~0x10000000; /* Set registers for trampoline to user mode. */ cf = (struct callframe *)tf - 1; memset(cf, 0, sizeof(struct callframe)); cf->cf_func = (register_t)fork_return; cf->cf_arg0 = (register_t)td; cf->cf_arg1 = (register_t)tf; pcb2->pcb_sp = (register_t)cf; #if defined(__powerpc64__) && (!defined(_CALL_ELF) || _CALL_ELF == 1) pcb2->pcb_lr = ((register_t *)fork_trampoline)[0]; pcb2->pcb_toc = ((register_t *)fork_trampoline)[1]; #else pcb2->pcb_lr = (register_t)fork_trampoline; pcb2->pcb_context[0] = pcb2->pcb_lr; #endif pcb2->pcb_cpu.aim.usr_vsid = 0; #ifdef __SPE__ pcb2->pcb_vec.vscr = SPEFSCR_FINVE | SPEFSCR_FDBZE | SPEFSCR_FUNFE | SPEFSCR_FOVFE; #endif /* Setup to release spin count in fork_exit(). */ td->td_md.md_spinlock_count = 1; td->td_md.md_saved_msr = psl_kernset; } void cpu_set_upcall(struct thread *td, void (*entry)(void *), void *arg, stack_t *stack) { struct trapframe *tf; uintptr_t sp; tf = td->td_frame; /* align stack and alloc space for frame ptr and saved LR */ #ifdef __powerpc64__ sp = ((uintptr_t)stack->ss_sp + stack->ss_size - 48) & ~0x1f; #else sp = ((uintptr_t)stack->ss_sp + stack->ss_size - 8) & ~0x1f; #endif bzero(tf, sizeof(struct trapframe)); tf->fixreg[1] = (register_t)sp; tf->fixreg[3] = (register_t)arg; if (SV_PROC_FLAG(td->td_proc, SV_ILP32)) { tf->srr0 = (register_t)entry; #ifdef __powerpc64__ tf->srr1 = psl_userset32 | PSL_FE_DFLT; #else tf->srr1 = psl_userset | PSL_FE_DFLT; #endif } else { #ifdef __powerpc64__ register_t entry_desc[3]; (void)copyin((void *)entry, entry_desc, sizeof(entry_desc)); tf->srr0 = entry_desc[0]; tf->fixreg[2] = entry_desc[1]; tf->fixreg[11] = entry_desc[2]; tf->srr1 = psl_userset | PSL_FE_DFLT; #endif } td->td_pcb->pcb_flags = 0; #ifdef __SPE__ td->td_pcb->pcb_vec.vscr = SPEFSCR_FINVE | SPEFSCR_FDBZE | SPEFSCR_FUNFE | SPEFSCR_FOVFE; #endif td->td_retval[0] = (register_t)entry; td->td_retval[1] = 0; } static int emulate_mfspr(int spr, int reg, struct trapframe *frame){ struct thread *td; td = curthread; if (spr == SPR_DSCR) { // If DSCR was never set, get the default DSCR if ((td->td_pcb->pcb_flags & PCB_CDSCR) == 0) td->td_pcb->pcb_dscr = mfspr(SPR_DSCR); frame->fixreg[reg] = td->td_pcb->pcb_dscr; frame->srr0 += 4; return 0; } else return SIGILL; } static int emulate_mtspr(int spr, int reg, struct trapframe *frame){ struct thread *td; td = curthread; if (spr == SPR_DSCR) { td->td_pcb->pcb_flags |= PCB_CDSCR; td->td_pcb->pcb_dscr = frame->fixreg[reg]; frame->srr0 += 4; return 0; } else return SIGILL; } #define XFX 0xFC0007FF int ppc_instr_emulate(struct trapframe *frame, struct pcb *pcb) { uint32_t instr; int reg, sig; int rs, spr; instr = fuword32((void *)frame->srr0); sig = SIGILL; if ((instr & 0xfc1fffff) == 0x7c1f42a6) { /* mfpvr */ reg = (instr & ~0xfc1fffff) >> 21; frame->fixreg[reg] = mfpvr(); frame->srr0 += 4; return (0); } else if ((instr & XFX) == 0x7c0002a6) { /* mfspr */ rs = (instr & 0x3e00000) >> 21; spr = (instr & 0x1ff800) >> 16; return emulate_mfspr(spr, rs, frame); } else if ((instr & XFX) == 0x7c0003a6) { /* mtspr */ rs = (instr & 0x3e00000) >> 21; spr = (instr & 0x1ff800) >> 16; return emulate_mtspr(spr, rs, frame); } else if ((instr & 0xfc000ffe) == 0x7c0004ac) { /* various sync */ powerpc_sync(); /* Do a heavy-weight sync */ frame->srr0 += 4; return (0); } #ifdef FPU_EMU if (!(pcb->pcb_flags & PCB_FPREGS)) { bzero(&pcb->pcb_fpu, sizeof(pcb->pcb_fpu)); pcb->pcb_flags |= PCB_FPREGS; } sig = fpu_emulate(frame, &pcb->pcb_fpu); #endif + if (sig == SIGILL) { + if (pcb->pcb_lastill != frame->srr0) { + /* Allow a second chance, in case of cache sync issues. */ + sig = 0; + pmap_sync_icache(PCPU_GET(curpmap), frame->srr0, 4); + pcb->pcb_lastill = frame->srr0; + } + } return (sig); }