/* @(#)fpu.c 1.1 94/10/31 SMI; from UCB 4.3 83/06/14 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fpa.h" #include #ifdef FPU #if NFPA > 0 enum fpa_state fpa_state = FPA_SINGLE_USER; #endif NFPA > 0 fpu_fork_context(p, pnfp, pnew_context) struct proc *p; struct file **pnfp; int *pnew_context; { #if NFPA > 0 if (u.u_pcb.u_fpa_flags) if (fpa_fork_context(p, pnfp, pnew_context) < 0) { /* no FPA context, inode, or file, error quit */ u.u_error = EAGAIN; return (0); } #endif NFPA > 0 return (1); } fpu_newproc(nfp, new_context, childu) struct file *nfp; int new_context; struct user *childu; { #if NFPA > 0 #define u_fpas_state u_fpa_status.fpas_state if (childu->u_pcb.u_fpa_flags) { /* * If the parent had the FPA open, but the FPA is not * initialized, the child does not inherit FPA access */ if (fpa_state == FPA_SINGLE_USER) { childu->u_ofile[u.u_pcb.u_fpa_flags & U_FPA_FDBITS] = NULL; childu->u_pcb.u_fpa_flags = 0; return; } /* * All FPA registers including the pipe are in * child u and have been restored to the FPA board. * Set up childu->u_ofile[] to the opened FPA file * and childu->u_fpa_state to the new FPA context * number for the child process. */ fpa_save(childu); /* make sure pipe is still ready */ childu->u_ofile[childu->u_pcb.u_fpa_flags & U_FPA_FDBITS] = nfp; childu->u_pcb.u_fpas_state = (childu->u_pcb.u_fpas_state & FPA_PBITS) | (long)new_context; fpa_dorestore(childu); /* state reg is written here */ } #endif NFPA > 0 } fpu_core(corep) struct core *corep; { u.u_pcb.u_fp_status.fps_flags = EXT_FPS_FLAGS(&u.u_pcb.u_fp_istate); corep->c_fpu.f_fpstatus = u.u_pcb.u_fp_status; #if NFPA > 0 /* * Dump FPA regs only when u.u_fpa_flags is nonzero and * FPA_LOAD_BIT is off. * The reason is that if FPA_LOAD_BIT is on, it means that there * is no microcode in FPA board, we cannot access FPA data regs. */ if ((corep->c_fpu.f_fparegs.fpar_flags = u.u_pcb.u_fpa_flags) && !(fpa->fp_state & FPA_LOAD_BIT)) { /* loop until FPA pipe become stable */ CDELAY(fpa->fp_pipe_status & FPA_STABLE, 300); if (fpa->fp_pipe_status & FPA_STABLE) { corep->c_fpu.f_fparegs.fpar_status = *((struct fpa_status *) &fpa->fp_state); fpa->fp_clear_pipe = 0; /* clear pipe to read data */ lcopy((char *)fpa->fp_data, (char *)corep->c_fpu.f_fparegs.fpar_data, sizeof (fpa->fp_data)/4); } else { fpa_shutdown(); printf("FPA not stable in core(), FPA is shutdown!\n"); } } #endif NFPA > 0 } fpu_setregs() { extern struct proc *fpprocp; /* * Clear any external and internal fpp state. * If this process was the last one to load its * external fpp state, erase that fact also. */ bzero((caddr_t)&u.u_pcb.u_fp_status, sizeof (u.u_pcb.u_fp_status)); bzero((caddr_t)&u.u_pcb.u_fp_istate, sizeof (u.u_pcb.u_fp_istate)); if (u.u_procp == fpprocp) fpprocp = (struct proc *)0; } #ifdef sun3x fpu_berror(be, locregs, fmt, mmube) #else fpu_berror(be, locregs, fmt) #endif u_char be; struct stkfmt fmt; register struct regs *locregs; #ifdef sun3x u_short mmube; #endif { #if NFPA > 0 /* * If enable register is not turned on, panic. * In case of FPA bus error in the kernel mode, shutdown * FPA, instead of panicing the system. */ if (be & BE_FPAENA) panic("FPA not enabled"); if (be & BE_FPABERR) { #ifdef sun3x showregs("FPA KERNEL BUS ERROR", fmt.f_vector, locregs, &fmt, be, mmube); #else showregs("FPA KERNEL BUS ERROR", fmt.f_vector, locregs, &fmt, be); #endif traceback((long)locregs->r_areg[6], (long)locregs->r_sp); printf("FPA BUS ERROR: IERR == %x\n", fpa->fp_ierr); fpa_shutdown(); return (SIGSEGV); } #endif NFPA > 0 return (0); } fpu_user_berror(be) u_char be; { #if NFPA > 0 if (u.u_pcb.u_fpa_flags && (be & (BE_FPAENA | BE_FPABERR))) { u.u_code = (be & BE_FPAENA) ? FPE_FPA_ENABLE : FPE_FPA_ERROR; return (SIGFPE); } #endif NFPA > 0 return (0); } 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.f_fpstatus), ipc->ip_addr, sizeof (struct fp_status)) != 0) { ipc->ip_error = 1; } break; case PTRACE_SETFPREGS: if (copyin(ipc->ip_addr, (caddr_t)&(ipc->ip_fpu.f_fpstatus), sizeof (struct fp_status)) != 0) { ipc->ip_error = 1; } runchild(p); break; #if NFPA > 0 case PTRACE_GETFPAREGS: runchild(p); if (copyout((caddr_t)&ipc->ip_fpu.f_fparegs, ipc->ip_addr, sizeof (struct fpa_regs)) != 0) { ipc->ip_error = 1; } break; case PTRACE_SETFPAREGS: if (copyin(ipc->ip_addr, (caddr_t)&ipc->ip_fpu.f_fparegs, sizeof (struct fpa_regs)) != 0) { ipc->ip_error = 1; } runchild(p); break; #endif NFPA > 0 } } /*ARGSUSED*/ fpu_procxmt(req, ipc) enum ptracereq req; struct ipc *ipc; { extern short fppstate; switch (req) { /* * Read floating point registers - * Copy from the u area to the ipc buffer, * after setting the fps_flags * from the internal fpp info. */ case PTRACE_GETFPREGS: if (fppstate == 0) { ipc->ip_error = 1; } else { u.u_pcb.u_fp_status.fps_flags = EXT_FPS_FLAGS(&u.u_pcb.u_fp_istate); ipc->ip_fpu.f_fpstatus = u.u_pcb.u_fp_status; } break; /* * Write floating point registers - * Copy from the ipc buffer to the u area, * and set u.u_code from the code in fp_status, * then initialize the fpp registers. */ case PTRACE_SETFPREGS: if (fppstate == 0) { ipc->ip_error = 1; } else { u.u_pcb.u_fp_status = ipc->ip_fpu.f_fpstatus; fsave(&u.u_pcb.u_fp_istate); setfppregs(); frestore(&u.u_pcb.u_fp_istate); } break; #if NFPA > 0 /* Read FPA registers from u area and FPA board to ipc buffer. */ case PTRACE_GETFPAREGS: if ((!u.u_pcb.u_fpa_flags) || (fpa->fp_pipe_status & FPA_WAIT2)) return (-1); ipc->ip_fpa_flags = u.u_pcb.u_fpa_flags; fpa_save(&u); /* force a save to u area */ ipc->ip_fpa_status = u.u_pcb.u_fpa_status; fpa->fp_clear_pipe = 0; /* clear FPA pipe to read data regs */ lcopy((char *)fpa->fp_data, (char *)ipc->ip_fpa_data, sizeof (fpa->fp_data)/4); /* copy from fpa */ fpa_dorestore(&u); /* restore u area to FPA */ break; /* * Write FPA registers from ipc buffer to the U area and FPA * board. U.u_fpa_flags, FPA STATE register, and WSTATUS register * are protected from being written. * Fpa_restore() is needed, o/w u.u_fpa_status is erased by * fpa context save during the coming context switch. */ case PTRACE_SETFPAREGS: if ((!u.u_pcb.u_fpa_flags) || (fpa->fp_pipe_status & FPA_WAIT2)) return (-1); /* STATE reg is protected from being modified */ ipc->ip_fpa_state = u.u_pcb.u_fpa_status.fpas_state; u.u_pcb.u_fpa_status = ipc->ip_fpa_status; fpa->fp_clear_pipe = 0; /* clear FPA pipe to read data regs */ lcopy((char *)ipc->ip_fpa_data, (char *)fpa->fp_data, sizeof (fpa->fp_data)/4); /* copy to fpa */ fpa_dorestore(&u); break; #endif NFPA > 0 } return (0); } fpu_ofile() { #if NFPA > 0 /* * F_count associated with /dev/fpa should not be * incremented in the above for loop. */ if (u.u_pcb.u_fpa_flags) u.u_ofile[u.u_pcb.u_fpa_flags & U_FPA_FDBITS]->f_count--; #endif NFPA > 0 } #endif FPU