Files
seta75D 2e8a93c394 Init
2021-10-11 18:20:23 -03:00

394 lines
9.2 KiB
C

/* @(#)fpu.c 1.1 92/07/30 SMI; */
#include <sys/types.h>
#include <machine/reg.h>
#include <machine/psl.h>
#include <machine/buserr.h>
#include <machine/trap.h>
#include <sun/fault.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sys/proc.h>
#include <sys/map.h>
#include <sys/user.h>
#include <sys/core.h>
#include <sys/ptrace.h>
#include <machine/fpu/fpu_simulator.h>
#include <machine/fpu/globals.h>
/*
* the global pointer to the current floating point context
*/
#ifdef MULTIPROCESSOR
extern struct fpu *fp_ctxp;
#else MULTIPROCESSOR
struct fpu *fp_ctxp = (struct fpu *)0;
#endif MULTIPROCESSOR
struct fpu *
fpu_ctxalloc()
{
register struct fpu *fp;
register int i;
fp = (struct fpu *)new_kmem_alloc(sizeof (*fp), KMEM_SLEEP);
fp->fpu_fsr = 0; /* zero fsr */
i = -1;
fp->fpu_regs[0] = i; /* initialize registers to NAN */
fp->fpu_regs[1] = i;
fp->fpu_regs[2] = i;
fp->fpu_regs[3] = i;
fp->fpu_regs[4] = i;
fp->fpu_regs[5] = i;
fp->fpu_regs[6] = i;
fp->fpu_regs[7] = i;
fp->fpu_regs[8] = i;
fp->fpu_regs[9] = i;
fp->fpu_regs[10] = i;
fp->fpu_regs[11] = i;
fp->fpu_regs[12] = i;
fp->fpu_regs[13] = i;
fp->fpu_regs[14] = i;
fp->fpu_regs[15] = i;
fp->fpu_regs[16] = i;
fp->fpu_regs[17] = i;
fp->fpu_regs[18] = i;
fp->fpu_regs[19] = i;
fp->fpu_regs[20] = i;
fp->fpu_regs[21] = i;
fp->fpu_regs[22] = i;
fp->fpu_regs[23] = i;
fp->fpu_regs[24] = i;
fp->fpu_regs[25] = i;
fp->fpu_regs[26] = i;
fp->fpu_regs[27] = i;
fp->fpu_regs[28] = i;
fp->fpu_regs[29] = i;
fp->fpu_regs[30] = i;
fp->fpu_regs[31] = i;
return (fp);
}
fpu_ctxfree()
{
register struct pcb *pcb;
pcb = &u.u_pcb;
if (pcb->pcb_fpctxp) {
if (fp_ctxp == pcb->pcb_fpctxp)
fp_ctxp = 0;
kmem_free((caddr_t)pcb->pcb_fpctxp, sizeof (struct fpu));
pcb->pcb_fpctxp = (struct fpu *) 0;
}
}
/* ARGSUSED */
fpu_fork_context(p2, nfp, new_context)
struct proc *p2;
struct file **nfp;
int *new_context;
{
register struct fpu *fp;
register struct pcb *pcb;
/*
* if the current process is using the fpu,
* allocate a new fp context and initialize it
* with the current fp context state.
*
*/
pcb = &u.u_pcb;
if (pcb->pcb_fpctxp)
{
/* we are forking fpu context due to users fork() call.
* At this time the fpu chip registers belong to the
* parent. First dump the registers to the fpu context.
* then copy the context to the child's context.
*
* Child runs first and therefore the fp_ctxp should
* point to childs fp context. However, the child may not
* started due to other errors during the rest of the fork.
* therefore shutoff PSR_EF for the parent and this will be
* copied to the child in fork(). THen make fp_ctxp 0.
* This will ensure the child and parent have different
* fpu contexts.
*/
fp_dumpregs((caddr_t)pcb->pcb_fpctxp);
fp = fpu_ctxalloc();
*new_context = (int)fp;
bcopy((caddr_t)pcb->pcb_fpctxp, (caddr_t)fp, sizeof *fp);
u.u_ar0[PS] = u.u_ar0[PS] & ~PSR_EF;
fp_ctxp = 0; /* current loaded context does not belong
* to anyone.
*/
}
return (1);
}
fpu_ofile()
{
}
/* ARGSUSED */
fpu_newproc(nfp, new_context, childu)
struct file *nfp;
int new_context;
struct user *childu;
{
/*
* initialize the fp context pointer in the new procs
* uarea, it was allocated above in fpu_fork_context
*/
childu->u_pcb.pcb_fpctxp = (struct fpu *)new_context;
}
fpu_core(corep)
struct core *corep;
{
if (u.u_pcb.pcb_fpctxp)
bcopy((caddr_t)u.u_pcb.pcb_fpctxp, (caddr_t)&corep->c_fpu,
sizeof (struct fpu));
}
fpu_ptrace(req, p, ipc)
enum ptracereq req;
struct proc *p;
struct ipc *ipc;
{
switch (req) {
case PTRACE_GETFPREGS:
runchild(p);
if (copyout((caddr_t)&(ipc->ip_fpu), ipc->ip_addr,
sizeof (struct fpu)) != 0) {
ipc->ip_error = 1;
}
break;
case PTRACE_SETFPREGS:
if (copyin(ipc->ip_addr, (caddr_t)&ipc->ip_fpu,
sizeof (struct fpu)) != 0) {
ipc->ip_error = 1;
}
runchild(p);
break;
}
}
fpu_procxmt(req, ipc)
enum ptracereq req;
struct ipc *ipc;
{
register struct pcb *pcb = &u.u_pcb;
extern int fpu_exists;
extern struct fpu *fpu_ctxalloc();
switch (req) {
case PTRACE_GETFPREGS:
if (pcb->pcb_fpctxp)
bcopy((caddr_t)pcb->pcb_fpctxp, (caddr_t)&ipc->ip_fpu,
sizeof (struct fpu));
break;
case PTRACE_SETFPREGS:
if (fpu_exists && pcb->pcb_fpctxp) {
register u_int i;
for (i = 0; i < 32; i++) {
_fp_write_pfreg((FPU_REGS_TYPE *)&ipc->ip_fpu.fpu_regs[i], i);
}
_fp_write_pfsr(&ipc->ip_fpu.fpu_fsr);
} else {
if (pcb->pcb_fpctxp == (struct fpu *) 0) {
pcb->pcb_fpctxp = fpu_ctxalloc();
}
bcopy((caddr_t)&ipc->ip_fpu, (caddr_t)pcb->pcb_fpctxp,
sizeof (struct fpu));
pcb->pcb_fpflags &= ~FP_UNINITIALIZED;
if (!fpu_exists)
pcb->pcb_fpflags |= FP_DISABLE;
}
break;
default:
return (-1);
}
return (1);
}
/*
* Handle floaing point traps generated by simulation/emulation.
*/
void
fp_traps(pfpsd, ftt, rp)
fp_simd_type *pfpsd; /* Pointer to simulator data */
register enum ftt_type ftt; /* trap type */
register struct regs *rp; /* ptr to regs fro trap */
{
extern void trap();
extern u_int beval; /* beval is set in trap.c for
specific architectures */
struct regs *saved_fptraprp = 0;
/*
* If we take a user's exception in kernel mode, we want to trap
* with the user's registers. Clear fptraprp in case we don't
* return from trap.
*/
saved_fptraprp = fptraprp;
if (fptraprp != 0) {
rp = fptraprp;
fptraprp = 0;
}
switch (ftt) {
case ftt_ieee:
trap(T_FP_EXCEPTION, rp, pfpsd->fp_trapaddr,
pfpsd->fp_trapcode, 0);
break;
case ftt_fault:
trap(T_DATA_FAULT, rp, pfpsd->fp_trapaddr, beval, pfpsd->fp_traprw);
break;
case ftt_alignment:
trap(T_ALIGNMENT, rp, pfpsd->fp_trapaddr, 0, 0);
break;
case ftt_unimplemented:
trap(T_UNIMP_INSTR, rp, pfpsd->fp_trapaddr, 0, 0);
break;
default:
/*
* We don't expect any of the other types here.
*/
panic("fp_traps: bad ftt");
}
fptraprp = saved_fptraprp;
}
/*
* floating point unit disabled:
* One of two possibilities
* 1. There is no fpu in the confguration. If so, emulate in SW.
* 2. THere is a FPU in config., however, this is the first time the
* process doing a fp op. If so, allocate a fpu context and
* turn on the EF bit in the PSR and let the proc go.
*/
fp_is_disabled(rp)
struct regs *rp;
{
struct fpu *fp;
enum ftt_type ftt;
if (u.u_pcb.pcb_fpctxp == 0) {
fp = fpu_ctxalloc(); /* allocate a fpu context */
uunix->u_pcb.pcb_fpctxp = fp;
}
else
fp = uunix->u_pcb.pcb_fpctxp;
fp_ctxp = fp;
if (fpu_exists) { /* there is a fpu, go ahead and enable the fpu */
if (rp->r_psr&PSR_EF) {
panic("fp_disabled: no FPU but EF bit set");
/* NOT REACHED */
}
else {
/* turn on enable fpu bit in the PSR and let the proc go */
rp->r_psr |= PSR_EF;
fp_enable(fp);
}
} else { /* no fpu, emulate the instruction */
fp_simd_type fpsd;
flush_user_windows_to_stack();
if (ftt = fp_emulator(&fpsd, (fp_inst_type *)rp->r_pc,
rp, (struct rwindow *)rp->r_sp,
(struct fpu *) &fp->fpu_regs[0]))
fp_traps(&fpsd, ftt, rp);
}
}
/*
* Process the floating point queue
*
* Each entry in the floating point queue is processed in turn.
* If processing an entry results in an exception fp_traps() is called to
* handle the exception - this usually results in the generation of a signal
* to be delivered to the user. There are 2 possible outcomes to this (note
* that hardware generated signals cannot be held!):
*
* 1. If the signal is being ignored we continue to process the rest
* of the entries in the queue.
*
* 2. If arrangements have been made for return to a user signal handler,
* sendsig() will have copied the floating point queue onto the user's
* signal stack and zero'ed the queue count in the u_pcb. Note that
* this has the side effect of terminating fp_runq's processing loop.
* We will re-run the floating point queue on return from the user
* signal handler if necessary as part of normal setcontext processing.
*/
void
fp_runq(rp)
register struct regs *rp; /* ptr to regs for trap */
{
register struct fpu *fp = u.u_pcb.pcb_fpctxp;
register struct fq *fqp = fp->fpu_q;
fp_simd_type fpsd;
extern int fpu_exists;
/*
* don't preempt while manipulating the queue
*/
while (fp->fpu_qcnt) {
enum ftt_type fptrap;
fptrap = fpu_simulator((fp_simd_type *)&fpsd,
(fp_inst_type *)fqp->FQu.fpq.addr,
(fsr_type *)&fp->fpu_fsr,
fqp->FQu.fpq.instr);
if (fptrap) {
/*
* fpu_simulator uses the fp registers directly but it
* uses the software copy of the fsr. We need to write
* that back to fpu so that fpu's state is current for
* ucontext.
*/
if (fpu_exists)
_fp_write_pfsr(&fp->fpu_fsr);
/* post signal */
fp_traps(&fpsd, fptrap, rp);
/*
* Break from loop to allow signal to be sent.
*/
break;
}
fp->fpu_qcnt--;
fqp++;
}
/*
* fpu_simulator uses the fp registers directly, so we have
* to update the pcb copies to keep current, but it uses the
* software copy of the fsr, so we write that back to fpu
*/
if (fpu_exists) {
register u_int i;
for (i = 0; i < 32; i++)
_fp_read_pfreg((FPU_REGS_TYPE *)&fp->fpu_regs[i], i);
_fp_write_pfsr((FPU_FSR_TYPE *)&fp->fpu_fsr);
}
}