#include <sys/param.h>
#include <sys/proc.h>
#include <sys/signalvar.h>
#include <sys/systm.h>
#include <machine/fpu.h>
#include <machine/frame.h>
#include <machine/ieeefp.h>
#include <machine/trap.h>
#include <machine/m88110.h>
#include <lib/libkern/softfloat.h>
#include <m88k/m88k/fpu.h>
int m88110_fpu_emulate(struct trapframe *, u_int32_t);
void m88110_fpu_fetch(struct trapframe *, u_int, u_int, u_int, fparg *);
void
m88110_fpu_exception(struct trapframe *frame)
{
struct proc *p = curproc;
int fault_type;
vaddr_t fault_addr;
union sigval sv;
u_int32_t insn;
int sig;
fault_addr = frame->tf_exip & XIP_ADDR;
m88110_skip_insn(frame);
__asm__ volatile ("fldcr %0, %%fcr0" : "=r"(frame->tf_fpecr));
__asm__ volatile ("fldcr %0, %%fcr62" : "=r"(frame->tf_fpsr));
__asm__ volatile ("fldcr %0, %%fcr63" : "=r"(frame->tf_fpcr));
__asm__ volatile ("fstcr %r0, %fcr0");
if (copyinsn(p, (u_int32_t *)fault_addr, (u_int32_t *)&insn) != 0) {
sig = SIGBUS;
fault_type = BUS_OBJERR;
goto deliver;
}
switch (insn >> 26) {
case 0x20:
sig = SIGILL;
fault_type = ILL_PRVREG;
goto deliver;
case 0x21:
if (frame->tf_epsr & PSR_SFD1) {
sig = SIGFPE;
fault_type = FPE_FLTINV;
goto deliver;
}
sig = m88110_fpu_emulate(frame, insn);
fault_type = SI_NOINFO;
__asm__ volatile ("fstcr %0, %%fcr62" :: "r"(frame->tf_fpsr));
break;
default:
sig = SIGILL;
fault_type = ILL_ILLOPC;
goto deliver;
}
if (sig != 0) {
if (sig == SIGILL)
fault_type = ILL_ILLOPC;
else {
if (frame->tf_fpecr & FPECR_FIOV)
fault_type = FPE_FLTSUB;
else if (frame->tf_fpecr & FPECR_FROP)
fault_type = FPE_FLTINV;
else if (frame->tf_fpecr & FPECR_FDVZ)
fault_type = FPE_INTDIV;
else if (frame->tf_fpecr & FPECR_FUNF) {
if (frame->tf_fpsr & FPSR_EFUNF)
fault_type = FPE_FLTUND;
else if (frame->tf_fpsr & FPSR_EFINX)
fault_type = FPE_FLTRES;
} else if (frame->tf_fpecr & FPECR_FOVF) {
if (frame->tf_fpsr & FPSR_EFOVF)
fault_type = FPE_FLTOVF;
else if (frame->tf_fpsr & FPSR_EFINX)
fault_type = FPE_FLTRES;
} else if (frame->tf_fpecr & FPECR_FINX)
fault_type = FPE_FLTRES;
}
deliver:
sv.sival_ptr = (void *)fault_addr;
trapsignal(p, sig, 0, fault_type, sv);
}
}
void
m88110_fpu_fetch(struct trapframe *frame, u_int regno, u_int orig_width,
u_int width, fparg *dest)
{
u_int32_t tmp;
switch (orig_width) {
case FTYPE_INT:
tmp = regno == 0 ? 0 : frame->tf_r[regno];
switch (width) {
case FTYPE_SNG:
dest->sng = int32_to_float32(tmp);
break;
case FTYPE_DBL:
dest->dbl = int32_to_float64(tmp);
break;
}
break;
case FTYPE_SNG:
tmp = regno == 0 ? 0 : frame->tf_r[regno];
switch (width) {
case FTYPE_SNG:
dest->sng = tmp;
break;
case FTYPE_DBL:
dest->dbl = float32_to_float64(tmp);
break;
}
break;
case FTYPE_DBL:
tmp = regno == 0 ? 0 : frame->tf_r[regno];
dest->dbl = ((float64)tmp) << 32;
tmp = regno == 31 ? 0 : frame->tf_r[regno + 1];
dest->dbl |= (float64)tmp;
break;
}
}
int
m88110_fpu_emulate(struct trapframe *frame, u_int32_t insn)
{
u_int rf, rd, rs1, rs2, t1, t2, td, tmax, opcode;
u_int32_t old_fpsr, old_fpcr;
int rc;
fparg arg1, arg2, dest;
rd = (insn >> 21) & 0x1f;
rs1 = (insn >> 16) & 0x1f;
rs2 = insn & 0x1f;
rf = (insn >> 15) & 0x01;
opcode = (insn >> 11) & 0x0f;
t1 = (insn >> 9) & 0x03;
t2 = (insn >> 7) & 0x03;
td = (insn >> 5) & 0x03;
if (rf != 0)
return (SIGILL);
switch (opcode) {
case 0x00:
case 0x05:
case 0x06:
case 0x0e:
if ((t1 != FTYPE_SNG && t1 != FTYPE_DBL) ||
(t2 != FTYPE_SNG && t2 != FTYPE_DBL) ||
(td != FTYPE_SNG && td != FTYPE_DBL))
return (SIGILL);
break;
case 0x04:
if (t1 != 0x00)
return (SIGILL);
if ((td != FTYPE_SNG && td != FTYPE_DBL) ||
t2 != 0x00 || rs1 != 0)
return (SIGILL);
break;
case 0x07:
if ((t1 != FTYPE_SNG && t1 != FTYPE_DBL) ||
(t2 != FTYPE_SNG && t2 != FTYPE_DBL))
return (SIGILL);
if (td != 0x00 && td != 0x01 )
return (SIGILL);
break;
case 0x09:
case 0x0a:
case 0x0b:
if ((t2 != FTYPE_SNG && t2 != FTYPE_DBL) ||
t1 != 0x00 || td != 0x00 || rs1 != 0)
return (SIGILL);
break;
case 0x01:
if (t2 == td)
return (SIGILL);
case 0x0f:
if ((t2 != FTYPE_SNG && t2 != FTYPE_DBL) ||
(td != FTYPE_SNG && td != FTYPE_DBL) ||
t1 != 0x00 || rs1 != 0)
return (SIGILL);
break;
default:
case 0x08:
return (SIGILL);
}
old_fpsr = frame->tf_fpsr;
frame->tf_fpsr = 0;
old_fpcr = frame->tf_fpcr;
switch (opcode) {
case 0x00:
tmax = fpu_precision(t1, t2, td);
m88110_fpu_fetch(frame, rs1, t1, tmax, &arg1);
m88110_fpu_fetch(frame, rs2, t2, tmax, &arg2);
switch (tmax) {
case FTYPE_SNG:
dest.sng = float32_mul(arg1.sng, arg2.sng);
break;
case FTYPE_DBL:
dest.dbl = float64_mul(arg1.dbl, arg2.dbl);
break;
}
fpu_store(frame, rd, tmax, td, &dest);
break;
case 0x01:
tmax = fpu_precision(IGNORE_PRECISION, t2, td);
m88110_fpu_fetch(frame, rs2, t2, tmax, &dest);
fpu_store(frame, rd, tmax, td, &dest);
break;
case 0x04:
m88110_fpu_fetch(frame, rs2, FTYPE_INT, td, &dest);
fpu_store(frame, rd, td, td, &dest);
break;
case 0x05:
tmax = fpu_precision(t1, t2, td);
m88110_fpu_fetch(frame, rs1, t1, tmax, &arg1);
m88110_fpu_fetch(frame, rs2, t2, tmax, &arg2);
switch (tmax) {
case FTYPE_SNG:
dest.sng = float32_add(arg1.sng, arg2.sng);
break;
case FTYPE_DBL:
dest.dbl = float64_add(arg1.dbl, arg2.dbl);
break;
}
fpu_store(frame, rd, tmax, td, &dest);
break;
case 0x06:
tmax = fpu_precision(t1, t2, td);
m88110_fpu_fetch(frame, rs1, t1, tmax, &arg1);
m88110_fpu_fetch(frame, rs2, t2, tmax, &arg2);
switch (tmax) {
case FTYPE_SNG:
dest.sng = float32_sub(arg1.sng, arg2.sng);
break;
case FTYPE_DBL:
dest.dbl = float64_sub(arg1.dbl, arg2.dbl);
break;
}
fpu_store(frame, rd, tmax, td, &dest);
break;
case 0x07:
tmax = fpu_precision(t1, t2, IGNORE_PRECISION);
m88110_fpu_fetch(frame, rs1, t1, tmax, &arg1);
m88110_fpu_fetch(frame, rs2, t2, tmax, &arg2);
fpu_compare(frame, &arg1, &arg2, tmax, rd, td );
break;
case 0x09:
do_int:
m88110_fpu_fetch(frame, rs2, t2, t2, &dest);
fpu_store(frame, rd, t2, FTYPE_INT, &dest);
break;
case 0x0a:
frame->tf_fpcr = (old_fpcr & ~(FPCR_RD_MASK << FPCR_RD_SHIFT)) |
(FP_RN << FPCR_RD_SHIFT);
goto do_int;
case 0x0b:
frame->tf_fpcr = (old_fpcr & ~(FPCR_RD_MASK << FPCR_RD_SHIFT)) |
(FP_RZ << FPCR_RD_SHIFT);
goto do_int;
case 0x0e:
tmax = fpu_precision(t1, t2, td);
m88110_fpu_fetch(frame, rs1, t1, tmax, &arg1);
m88110_fpu_fetch(frame, rs2, t2, tmax, &arg2);
switch (tmax) {
case FTYPE_SNG:
dest.sng = float32_div(arg1.sng, arg2.sng);
break;
case FTYPE_DBL:
dest.dbl = float64_div(arg1.dbl, arg2.dbl);
break;
}
fpu_store(frame, rd, tmax, td, &dest);
break;
case 0x0f:
tmax = fpu_precision(IGNORE_PRECISION, t2, td);
m88110_fpu_fetch(frame, rs2, t2, tmax, &arg1);
switch (tmax) {
case FTYPE_SNG:
dest.sng = float32_sqrt(arg1.sng);
break;
case FTYPE_DBL:
dest.dbl = float64_sqrt(arg1.dbl);
break;
}
fpu_store(frame, rd, tmax, td, &dest);
break;
}
if (frame->tf_fpsr & old_fpcr)
rc = SIGFPE;
else
rc = 0;
frame->tf_fpsr |= old_fpsr;
frame->tf_fpcr = old_fpcr;
return (rc);
}