#include <sys/param.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/limits.h>
#include <machine/fpu.h>
#include <machine/pcb.h>
#include <machine/psl.h>
#include <machine/altivec.h>
static void
save_fpu_int(struct thread *td)
{
register_t msr;
struct pcb *pcb;
pcb = td->td_pcb;
msr = mfmsr();
if (pcb->pcb_flags & PCB_VSX)
mtmsr(msr | PSL_FP | PSL_VSX);
else
mtmsr(msr | PSL_FP);
if (pcb->pcb_flags & PCB_VSX) {
#if _BYTE_ORDER == _BIG_ENDIAN
#define SFP(n) __asm("stxvw4x " #n ", 0,%0" \
:: "b"(&pcb->pcb_fpu.fpr[n]));
#else
#define SFP(n) __asm("stxvd2x " #n ", 0,%0" \
:: "b"(&pcb->pcb_fpu.fpr[n]));
#endif
SFP(0); SFP(1); SFP(2); SFP(3);
SFP(4); SFP(5); SFP(6); SFP(7);
SFP(8); SFP(9); SFP(10); SFP(11);
SFP(12); SFP(13); SFP(14); SFP(15);
SFP(16); SFP(17); SFP(18); SFP(19);
SFP(20); SFP(21); SFP(22); SFP(23);
SFP(24); SFP(25); SFP(26); SFP(27);
SFP(28); SFP(29); SFP(30); SFP(31);
#undef SFP
} else {
#define SFP(n) __asm("stfd " #n ", 0(%0)" \
:: "b"(&pcb->pcb_fpu.fpr[n].fpr));
SFP(0); SFP(1); SFP(2); SFP(3);
SFP(4); SFP(5); SFP(6); SFP(7);
SFP(8); SFP(9); SFP(10); SFP(11);
SFP(12); SFP(13); SFP(14); SFP(15);
SFP(16); SFP(17); SFP(18); SFP(19);
SFP(20); SFP(21); SFP(22); SFP(23);
SFP(24); SFP(25); SFP(26); SFP(27);
SFP(28); SFP(29); SFP(30); SFP(31);
#undef SFP
}
__asm __volatile ("mffs 0; stfd 0,0(%0)" :: "b"(&pcb->pcb_fpu.fpscr));
isync();
mtmsr(msr);
}
void
enable_fpu(struct thread *td)
{
register_t msr;
struct pcb *pcb;
struct trapframe *tf;
pcb = td->td_pcb;
tf = trapframe(td);
td->td_pcb->pcb_fpcpu = PCPU_GET(cpuid);
PCPU_SET(fputhread, td);
pcb->pcb_flags |= PCB_FPU;
if (pcb->pcb_flags & PCB_VSX)
tf->srr1 |= PSL_FP | PSL_VSX;
else
tf->srr1 |= PSL_FP;
if (!(pcb->pcb_flags & PCB_FPREGS)) {
memset(&pcb->pcb_fpu, 0, sizeof pcb->pcb_fpu);
pcb->pcb_flags |= PCB_FPREGS;
}
msr = mfmsr();
if (pcb->pcb_flags & PCB_VSX)
mtmsr(msr | PSL_FP | PSL_VSX);
else
mtmsr(msr | PSL_FP);
__asm __volatile ("lfd 0,0(%0); mtfsf 0xff,0"
:: "b"(&pcb->pcb_fpu.fpscr));
if (pcb->pcb_flags & PCB_VSX) {
#if _BYTE_ORDER == _BIG_ENDIAN
#define LFP(n) __asm("lxvw4x " #n ", 0,%0" \
:: "b"(&pcb->pcb_fpu.fpr[n]));
#else
#define LFP(n) __asm("lxvd2x " #n ", 0,%0" \
:: "b"(&pcb->pcb_fpu.fpr[n]));
#endif
LFP(0); LFP(1); LFP(2); LFP(3);
LFP(4); LFP(5); LFP(6); LFP(7);
LFP(8); LFP(9); LFP(10); LFP(11);
LFP(12); LFP(13); LFP(14); LFP(15);
LFP(16); LFP(17); LFP(18); LFP(19);
LFP(20); LFP(21); LFP(22); LFP(23);
LFP(24); LFP(25); LFP(26); LFP(27);
LFP(28); LFP(29); LFP(30); LFP(31);
#undef LFP
} else {
#define LFP(n) __asm("lfd " #n ", 0(%0)" \
:: "b"(&pcb->pcb_fpu.fpr[n].fpr));
LFP(0); LFP(1); LFP(2); LFP(3);
LFP(4); LFP(5); LFP(6); LFP(7);
LFP(8); LFP(9); LFP(10); LFP(11);
LFP(12); LFP(13); LFP(14); LFP(15);
LFP(16); LFP(17); LFP(18); LFP(19);
LFP(20); LFP(21); LFP(22); LFP(23);
LFP(24); LFP(25); LFP(26); LFP(27);
LFP(28); LFP(29); LFP(30); LFP(31);
#undef LFP
}
isync();
mtmsr(msr);
}
void
save_fpu(struct thread *td)
{
struct pcb *pcb;
pcb = td->td_pcb;
save_fpu_int(td);
pcb->pcb_fpcpu = INT_MAX;
PCPU_SET(fputhread, NULL);
}
void
save_fpu_nodrop(struct thread *td)
{
if (td == PCPU_GET(fputhread))
save_fpu_int(td);
}
void
cleanup_fpscr(void)
{
register_t msr;
msr = mfmsr();
mtmsr(msr | PSL_FP);
mtfsf(0);
isync();
mtmsr(msr);
}
u_int
get_fpu_exception(struct thread *td)
{
register_t msr;
u_int ucode;
register_t reg;
critical_enter();
msr = mfmsr();
mtmsr(msr | PSL_FP);
reg = mffs();
isync();
mtmsr(msr);
critical_exit();
if (reg & FPSCR_ZX)
ucode = FPE_FLTDIV;
else if (reg & FPSCR_OX)
ucode = FPE_FLTOVF;
else if (reg & FPSCR_UX)
ucode = FPE_FLTUND;
else if (reg & FPSCR_XX)
ucode = FPE_FLTRES;
else
ucode = FPE_FLTINV;
return ucode;
}
void
enable_fpu_kern(void)
{
register_t msr;
msr = mfmsr() | PSL_FP;
if (cpu_features & PPC_FEATURE_HAS_VSX)
msr |= PSL_VSX;
mtmsr(msr);
}
void
disable_fpu(struct thread *td)
{
register_t msr;
struct pcb *pcb;
struct trapframe *tf;
pcb = td->td_pcb;
tf = trapframe(td);
msr = mfmsr() & ~(PSL_FP | PSL_VSX);
isync();
mtmsr(msr);
tf->srr1 &= ~(PSL_FP | PSL_VSX);
pcb->pcb_flags &= ~(PCB_FPU | PCB_VSX);
}
struct fpu_kern_ctx {
#define FPU_KERN_CTX_DUMMY 0x01
#define FPU_KERN_CTX_INUSE 0x02
uint32_t flags;
};
void
fpu_kern_enter(struct thread *td, struct fpu_kern_ctx *ctx, u_int flags)
{
struct pcb *pcb;
pcb = td->td_pcb;
KASSERT((flags & FPU_KERN_NOCTX) != 0 || ctx != NULL,
("ctx is required when !FPU_KERN_NOCTX"));
KASSERT(ctx == NULL || (ctx->flags & FPU_KERN_CTX_INUSE) == 0,
("using inuse ctx"));
KASSERT((pcb->pcb_flags & PCB_KERN_FPU_NOSAVE) == 0,
("recursive fpu_kern_enter while in PCB_KERN_FPU_NOSAVE state"));
if ((flags & FPU_KERN_NOCTX) != 0) {
critical_enter();
if (pcb->pcb_flags & PCB_FPU) {
save_fpu(td);
pcb->pcb_flags |= PCB_FPREGS;
}
enable_fpu_kern();
if (pcb->pcb_flags & PCB_VEC) {
save_vec(td);
pcb->pcb_flags |= PCB_VECREGS;
}
enable_vec_kern();
pcb->pcb_flags |= PCB_KERN_FPU | PCB_KERN_FPU_NOSAVE;
return;
}
KASSERT(0, ("fpu_kern_enter with !FPU_KERN_NOCTX not implemented!"));
}
int
fpu_kern_leave(struct thread *td, struct fpu_kern_ctx *ctx)
{
struct pcb *pcb;
pcb = td->td_pcb;
if ((pcb->pcb_flags & PCB_KERN_FPU_NOSAVE) != 0) {
KASSERT(ctx == NULL, ("non-null ctx after FPU_KERN_NOCTX"));
KASSERT(PCPU_GET(fpcurthread) == NULL,
("non-NULL fpcurthread for PCB_FP_NOSAVE"));
CRITICAL_ASSERT(td);
disable_fpu(td);
disable_vec(td);
pcb->pcb_flags &= ~PCB_KERN_FPU_NOSAVE;
critical_exit();
} else {
KASSERT(0, ("fpu_kern_leave with !FPU_KERN_NOCTX not implemented!"));
}
pcb->pcb_flags &= ~PCB_KERN_FPU;
return 0;
}
int
is_fpu_kern_thread(u_int flags __unused)
{
struct pcb *curpcb;
if ((curthread->td_pflags & TDP_KTHREAD) == 0)
return (0);
curpcb = curthread->td_pcb;
return ((curpcb->pcb_flags & PCB_KERN_FPU) != 0);
}