#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: fpu.c,v 1.42 2020/07/15 09:19:49 rin Exp $");
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/atomic.h>
#include <sys/siginfo.h>
#include <sys/pcu.h>
#include <machine/pcb.h>
#include <machine/fpu.h>
#include <machine/psl.h>
static void fpu_state_load(lwp_t *, u_int);
static void fpu_state_save(lwp_t *);
static void fpu_state_release(lwp_t *);
const pcu_ops_t fpu_ops = {
.pcu_id = PCU_FPU,
.pcu_state_load = fpu_state_load,
.pcu_state_save = fpu_state_save,
.pcu_state_release = fpu_state_release,
};
bool
fpu_used_p(lwp_t *l)
{
return pcu_valid_p(&fpu_ops, l);
}
void
fpu_mark_used(lwp_t *l)
{
pcu_discard(&fpu_ops, l, true);
}
void
fpu_state_load(lwp_t *l, u_int flags)
{
#ifdef PPC_HAVE_FPU
struct pcb * const pcb = lwp_getpcb(l);
if ((flags & PCU_VALID) == 0) {
memset(&pcb->pcb_fpu, 0, sizeof(pcb->pcb_fpu));
}
if ((flags & PCU_REENABLE) == 0) {
const register_t msr = mfmsr();
mtmsr((msr & ~PSL_EE) | PSL_FP);
__asm volatile ("isync");
fpu_load_from_fpreg(&pcb->pcb_fpu);
__asm volatile ("sync");
mtmsr(msr);
__asm volatile ("isync");
}
curcpu()->ci_ev_fpusw.ev_count++;
l->l_md.md_utf->tf_srr1 |= PSL_FP|(pcb->pcb_flags & (PCB_FE0|PCB_FE1));
#endif
}
void
fpu_state_save(lwp_t *l)
{
#ifdef PPC_HAVE_FPU
struct pcb * const pcb = lwp_getpcb(l);
const register_t msr = mfmsr();
mtmsr((msr & ~PSL_EE) | PSL_FP);
__asm volatile ("isync");
fpu_unload_to_fpreg(&pcb->pcb_fpu);
__asm volatile ("sync");
mtmsr(msr);
__asm volatile ("isync");
#endif
}
void
fpu_state_release(lwp_t *l)
{
#ifdef PPC_HAVE_FPU
l->l_md.md_utf->tf_srr1 &= ~PSL_FP;
#endif
}
#define STICKYBITS (FPSCR_VX|FPSCR_OX|FPSCR_UX|FPSCR_ZX|FPSCR_XX)
#define STICKYSHIFT 25
#define MASKBITS (FPSCR_VE|FPSCR_OE|FPSCR_UE|FPSCR_ZE|FPSCR_XE)
#define MASKSHIFT 3
int
fpu_get_fault_code(void)
{
lwp_t * const l = curlwp;
struct pcb * const pcb = lwp_getpcb(l);
uint64_t fpscr64;
uint32_t fpscr, ofpscr;
int code;
#ifdef PPC_HAVE_FPU
kpreempt_disable();
struct cpu_info * const ci = curcpu();
KASSERT(fpu_used_p(l));
if (__predict_true(l->l_pcu_cpu[PCU_FPU] == ci)) {
uint64_t tmp;
const register_t msr = mfmsr();
mtmsr((msr & ~PSL_EE) | PSL_FP);
__asm volatile ("isync");
__asm volatile (
"stfd 0,0(%[tmp])\n"
"mffs 0\n"
"stfd 0,0(%[fpscr64])\n"
"mtfsb0 0\n"
"mtfsb0 24\n"
"mtfsb0 25\n"
"mtfsb0 26\n"
"mtfsb0 27\n"
"mtfsb0 28\n"
"mffs 0\n"
"stfd 0,0(%[fpscr])\n"
"lfd 0,0(%[tmp])\n"
:: [tmp] "b"(&tmp),
[fpscr] "b"(&pcb->pcb_fpu.fpscr),
[fpscr64] "b"(&fpscr64));
mtmsr(msr);
__asm volatile ("isync");
} else {
fpu_save(l);
fpscr64 = *(uint64_t *)&pcb->pcb_fpu.fpscr;
((uint32_t *)&pcb->pcb_fpu.fpscr)[_QUAD_LOWWORD] &= ~MASKBITS;
}
kpreempt_enable();
#else
fpscr64 = *(uint64_t *)&pcb->pcb_fpu.fpscr;
((uint32_t *)&pcb->pcb_fpu.fpscr)[_QUAD_LOWWORD] &= ~MASKBITS;
#endif
ofpscr = (uint32_t)fpscr64;
ofpscr &= ofpscr << (STICKYSHIFT - MASKSHIFT);
fpscr = ((uint32_t *)&pcb->pcb_fpu.fpscr)[_QUAD_LOWWORD];
if (fpscr & ofpscr & STICKYBITS)
fpscr &= ofpscr;
if (fpscr & FPSCR_VX) code = FPE_FLTINV;
else if (fpscr & FPSCR_OX) code = FPE_FLTOVF;
else if (fpscr & FPSCR_UX) code = FPE_FLTUND;
else if (fpscr & FPSCR_ZX) code = FPE_FLTDIV;
else if (fpscr & FPSCR_XX) code = FPE_FLTRES;
else code = 0;
return code;
}
bool
fpu_save_to_mcontext(lwp_t *l, mcontext_t *mcp, unsigned int *flagp)
{
KASSERT(l == curlwp);
if (!pcu_valid_p(&fpu_ops, l))
return false;
struct pcb * const pcb = lwp_getpcb(l);
#ifdef PPC_HAVE_FPU
pcu_save(&fpu_ops, l);
#endif
(void)memcpy(mcp->__fpregs.__fpu_regs, pcb->pcb_fpu.fpreg,
sizeof (mcp->__fpregs.__fpu_regs));
mcp->__fpregs.__fpu_fpscr =
((int *)&pcb->pcb_fpu.fpscr)[_QUAD_LOWWORD];
mcp->__fpregs.__fpu_valid = 1;
*flagp |= _UC_FPU;
return true;
}
void
fpu_restore_from_mcontext(lwp_t *l, const mcontext_t *mcp)
{
if (!mcp->__fpregs.__fpu_valid)
return;
struct pcb * const pcb = lwp_getpcb(l);
#ifdef PPC_HAVE_FPU
pcu_discard(&fpu_ops, l, true);
#endif
(void)memcpy(&pcb->pcb_fpu.fpreg, &mcp->__fpregs.__fpu_regs,
sizeof (pcb->pcb_fpu.fpreg));
((int *)&pcb->pcb_fpu.fpscr)[_QUAD_LOWWORD] = mcp->__fpregs.__fpu_fpscr;
}