The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/i386/fpu.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1  /* 
    2  * Mach Operating System
    3  * Copyright (c) 1992-1990 Carnegie Mellon University
    4  * All Rights Reserved.
    5  * 
    6  * Permission to use, copy, modify and distribute this software and its
    7  * documentation is hereby granted, provided that both the copyright
    8  * notice and this permission notice appear in all copies of the
    9  * software, derivative works or modified versions, and any portions
   10  * thereof, and that both notices appear in supporting documentation.
   11  * 
   12  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
   13  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
   14  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
   15  * 
   16  * Carnegie Mellon requests users of this software to return to
   17  * 
   18  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
   19  *  School of Computer Science
   20  *  Carnegie Mellon University
   21  *  Pittsburgh PA 15213-3890
   22  * 
   23  * any improvements or extensions that they make and grant Carnegie Mellon
   24  * the rights to redistribute these changes.
   25  */
   26 /*
   27  * HISTORY
   28  * $Log:        fpu.c,v $
   29  * Revision 2.16  93/05/15  19:30:15  mrt
   30  *      machparam.h -> machspl.h
   31  * 
   32  * Revision 2.15  93/05/10  21:18:27  rvb
   33  *      spl_t fixes.
   34  *      [93/05/06  11:15:58  af]
   35  * 
   36  * Revision 2.14  93/02/04  07:55:57  danner
   37  *      Add PS2 to #if AT386 conditional.
   38  *      [93/01/25            rvb]
   39  * 
   40  * Revision 2.12  92/08/06  10:02:35  jfriedl
   41  *      Added volatile to declarations of fp_inifinity, fp_one, fp_zero
   42  *      to prevent the code from being optimized away. 
   43  *      Fix from Michael Bushnell - FSF
   44  *      [92/08/06            jfriedl]
   45  * 
   46  * Revision 2.11  92/02/26  13:12:30  elf
   47  *      Fpinit fixes from dlb.
   48  *      [92/02/26            danner]
   49  * 
   50  * Revision 2.10  92/02/19  15:08:00  elf
   51  *      Make sure the current thread's floating instruction has completed
   52  *      before freeing its fpu context and before context switching the FPU.
   53  * 
   54  *      Remember which thread the FPU AST was meant for and delay sending
   55  *      exception if a context switch has occurred between the interrupt and
   56  *      the AST handling. This can happen if an fpu interrupt arrives after 
   57  *      FPU ASTs have been checked for but before thread_block.
   58  * 
   59  *      Mark pending exception for thread in fp_valid (= 2) in fpexterrflt or
   60  *      fp_intr if the thread that caused the exception is not running and
   61  *      send the exception when the thread next runs. (I'm not really sure 
   62  *      this is necessary).
   63  * 
   64  *      Check fp_thread for THREAD_NULL in fp_free and fp_intr before
   65  *      accessing its PCB.
   66  * 
   67  *      Don't save FPU context in fpexterrflt. It is already saved in 
   68  *      fp_intr. 
   69  *      [92/02/01            jvh]
   70  * 
   71  * Revision 2.9  92/01/03  20:05:42  dbg
   72  *      Move floating-point state manipulation from i386/pcb.c to this
   73  *      file.  Add support for floating-point emulator.  Restore
   74  *      FPU shuffling between threads if single CPU.  Add locking
   75  *      to PCB to avoid losing fp_save structures.
   76  *      [91/10/20            dbg]
   77  * 
   78  * Revision 2.8  91/06/19  11:55:02  rvb
   79  *      cputypes.h->platforms.h
   80  *      [91/06/12  13:44:41  rvb]
   81  * 
   82  * Revision 2.7  91/05/14  16:07:37  mrt
   83  *      Correcting copyright
   84  * 
   85  * Revision 2.6  91/05/08  12:31:32  dbg
   86  *      Simplify.  Always store FP state on context switch.
   87  *      1:      It's unknown what happens when the i386 switches
   88  *              mappings while the i387 has a store instruction
   89  *              (e.g. fbstp) in progress.  Where does the data go?
   90  *      2:      On a multiprocessor, we'd need interprocessor interrupts
   91  *              to fetch a thread's FP state from the CPU it last ran on.
   92  *      [91/04/26  14:34:04  dbg]
   93  * 
   94  * Revision 2.5  91/03/16  14:44:07  rpd
   95  *      Pulled i386_fpsave_state out of i386_machine_state.
   96  *      Picked up fixes from dbg.
   97  *      [91/02/18            rpd]
   98  * 
   99  * Revision 2.4  91/02/05  17:11:45  mrt
  100  *      Changed to new Mach copyright
  101  *      [91/02/01  17:33:57  mrt]
  102  * 
  103  * Revision 2.3  91/01/08  15:10:29  rpd
  104  *      Split i386_machine_state off of i386_kernel_state.
  105  *      [90/12/31            rpd]
  106  *      Reorganized the pcb.
  107  *      [90/12/11            rpd]
  108  * 
  109  * Revision 2.2  90/05/03  15:25:18  dbg
  110  *      Created.
  111  *      [90/02/11            dbg]
  112  * 
  113  */
  114 
  115 /*
  116  * Support for 80387 floating point or FP emulator.
  117  */
  118 #include <cpus.h>
  119 #include <fpe.h>
  120 #include <platforms.h>
  121 
  122 #include <mach/exception.h>
  123 #include <mach/i386/thread_status.h>
  124 #include <mach/i386/fp_reg.h>
  125 
  126 #include <machine/machspl.h>    /* spls */
  127 #include <kern/mach_param.h>
  128 #include <kern/thread.h>
  129 #include <kern/zalloc.h>
  130 
  131 #include <i386/thread.h>
  132 #include <i386/fpu.h>
  133 
  134 #if 0
  135 #include <i386/ipl.h>
  136 extern int curr_ipl;
  137 #define ASSERT_IPL(L) \
  138 { \
  139       if (curr_ipl != L) { \
  140               printf("IPL is %d, expected %d\n", curr_ipl, L); \
  141               panic("fpu: wrong ipl"); \
  142       } \
  143 }
  144 #else
  145 #define ASSERT_IPL(L)
  146 #endif
  147 
  148 extern void i386_exception();
  149 
  150 int             fp_kind = FP_387;       /* 80387 present */
  151 zone_t          ifps_zone;              /* zone for FPU save area */
  152 
  153 #if     NCPUS == 1
  154 volatile thread_t       fp_thread = THREAD_NULL;
  155                                         /* thread whose state is in FPU */
  156                                         /* always THREAD_NULL if emulating
  157                                            FPU */
  158 volatile thread_t       fp_intr_thread = THREAD_NULL;
  159 
  160 
  161 #define clear_fpu() \
  162     { \
  163         set_ts(); \
  164         fp_thread = THREAD_NULL; \
  165     }
  166 
  167 #else   /* NCPUS > 1 */
  168 #define clear_fpu() \
  169     { \
  170         set_ts(); \
  171     }
  172 
  173 #endif
  174 
  175 
  176 /*
  177  * Look for FPU and initialize it.
  178  * Called on each CPU.
  179  */
  180 void
  181 init_fpu()
  182 {
  183         unsigned short  status, control;
  184 
  185         /*
  186          * Check for FPU by initializing it,
  187          * then trying to read the correct bit patterns from
  188          * the control and status registers.
  189          */
  190         set_cr0(get_cr0() & ~(CR0_EM|CR0_TS));  /* allow use of FPU */
  191 
  192         fninit();
  193         status = fnstsw();
  194         fnstcw(&control);
  195 
  196         if ((status & 0xff) == 0 &&
  197             (control & 0x103f) == 0x3f)
  198         {
  199             /*
  200              * We have a FPU of some sort.
  201              * Compare -infinity against +infinity
  202              * to check whether we have a 287 or a 387.
  203              */
  204             volatile double fp_infinity, fp_one, fp_zero;
  205             fp_one = 1.0;
  206             fp_zero = 0.0;
  207             fp_infinity = fp_one / fp_zero;
  208             if (fp_infinity == -fp_infinity) {
  209                 /*
  210                  * We have an 80287.
  211                  */
  212                 fp_kind = FP_287;
  213                 asm volatile(".byte 0xdb; .byte 0xe4"); /* fnsetpm */
  214             }
  215             else {
  216                 /*
  217                  * We have a 387.
  218                  */
  219                 fp_kind = FP_387;
  220             }
  221             /*
  222              * Trap wait instructions.  Turn off FPU for now.
  223              */
  224             set_cr0(get_cr0() | CR0_TS | CR0_MP);
  225         }
  226         else {
  227 #if     FPE
  228             /*
  229              * Use the floating-point emulator.
  230              */
  231             fp_kind = FP_SOFT;
  232             fpe_init();
  233 #else   /* no fpe */
  234             /*
  235              * NO FPU.
  236              */
  237             fp_kind = FP_NO;
  238             set_cr0(get_cr0() | CR0_EM);
  239 #endif
  240         }
  241 }
  242 
  243 /*
  244  * Initialize FP handling.
  245  */
  246 void
  247 fpu_module_init()
  248 {
  249         ifps_zone = zinit(sizeof(struct i386_fpsave_state),
  250                           THREAD_MAX * sizeof(struct i386_fpsave_state),
  251                           THREAD_CHUNK * sizeof(struct i386_fpsave_state),
  252                           FALSE, "i386 fpsave state");
  253 }
  254 
  255 /*
  256  * Free a FPU save area.
  257  * Called only when thread terminating - no locking necessary.
  258  */
  259 void
  260 fp_free(fps)
  261         struct i386_fpsave_state *fps;
  262 {
  263 ASSERT_IPL(SPL0);
  264 #if     NCPUS == 1
  265         if ((fp_thread != THREAD_NULL) && (fp_thread->pcb->ims.ifps == fps)) {
  266                 /* 
  267                  * Make sure we don't get FPU interrupts later for
  268                  * this thread
  269                  */
  270                 fwait();
  271 
  272                 /* Mark it free and disable access */
  273             clear_fpu();
  274         }
  275 #endif  /* NCPUS == 1 */
  276         zfree(ifps_zone, (vm_offset_t) fps);
  277 }
  278 
  279 /*
  280  * Set the floating-point state for a thread.
  281  * If the thread is not the current thread, it is
  282  * not running (held).  Locking needed against
  283  * concurrent fpu_set_state or fpu_get_state.
  284  */
  285 kern_return_t
  286 fpu_set_state(thread, state)
  287         thread_t        thread;
  288         struct i386_float_state *state;
  289 {
  290         register pcb_t  pcb = thread->pcb;
  291         register struct i386_fpsave_state *ifps;
  292         register struct i386_fpsave_state *new_ifps;
  293 
  294 ASSERT_IPL(SPL0);
  295         if (fp_kind == FP_NO)
  296             return KERN_FAILURE;
  297 
  298 #if     NCPUS == 1
  299 
  300         /*
  301          * If this thread`s state is in the FPU,
  302          * discard it; we are replacing the entire
  303          * FPU state.
  304          */
  305         if (fp_thread == thread) {
  306             fwait();                    /* wait for possible interrupt */
  307             clear_fpu();                /* no state in FPU */
  308         }
  309 #endif
  310 
  311         if (state->initialized == 0) {
  312             /*
  313              * new FPU state is 'invalid'.
  314              * Deallocate the fp state if it exists.
  315              */
  316             simple_lock(&pcb->lock);
  317             ifps = pcb->ims.ifps;
  318             pcb->ims.ifps = 0;
  319             simple_unlock(&pcb->lock);
  320 
  321             if (ifps != 0) {
  322                 zfree(ifps_zone, (vm_offset_t) ifps);
  323             }
  324         }
  325         else {
  326             /*
  327              * Valid state.  Allocate the fp state if there is none.
  328              */
  329             register struct i386_fp_save *user_fp_state;
  330             register struct i386_fp_regs *user_fp_regs;
  331 
  332             user_fp_state = (struct i386_fp_save *) &state->hw_state[0];
  333             user_fp_regs  = (struct i386_fp_regs *)
  334                         &state->hw_state[sizeof(struct i386_fp_save)];
  335 
  336             new_ifps = 0;
  337         Retry:
  338             simple_lock(&pcb->lock);
  339             ifps = pcb->ims.ifps;
  340             if (ifps == 0) {
  341                 if (new_ifps == 0) {
  342                     simple_unlock(&pcb->lock);
  343                     new_ifps = (struct i386_fpsave_state *) zalloc(ifps_zone);
  344                     goto Retry;
  345                 }
  346                 ifps = new_ifps;
  347                 new_ifps = 0;
  348                 pcb->ims.ifps = ifps;
  349             }
  350 
  351             /*
  352              * Ensure that reserved parts of the environment are 0.
  353              */
  354             bzero((char *)&ifps->fp_save_state, sizeof(struct i386_fp_save));
  355 
  356             ifps->fp_save_state.fp_control = user_fp_state->fp_control;
  357             ifps->fp_save_state.fp_status  = user_fp_state->fp_status;
  358             ifps->fp_save_state.fp_tag     = user_fp_state->fp_tag;
  359             ifps->fp_save_state.fp_eip     = user_fp_state->fp_eip;
  360             ifps->fp_save_state.fp_cs      = user_fp_state->fp_cs;
  361             ifps->fp_save_state.fp_opcode  = user_fp_state->fp_opcode;
  362             ifps->fp_save_state.fp_dp      = user_fp_state->fp_dp;
  363             ifps->fp_save_state.fp_ds      = user_fp_state->fp_ds;
  364 
  365 #if     FPE
  366             if (fp_kind == FP_SOFT) {
  367                 /*
  368                  * The emulator stores the registers by physical
  369                  * register number, not from top-of-stack.
  370                  * Shuffle the registers into the correct order.
  371                  */
  372                 register char *src;     /* user regs */
  373                 register char *dst;     /* kernel regs */
  374                 int     i;
  375 
  376                 src = (char *)user_fp_regs;
  377                 dst = (char *)&ifps->fp_regs;
  378                 i = (ifps->fp_save_state.fp_status & FPS_TOS)
  379                         >> FPS_TOS_SHIFT;       /* physical register
  380                                                    for st(0) */
  381                 if (i == 0)
  382                     bcopy(src, dst, 8 * 10);
  383                 else {
  384                     bcopy(src,
  385                           dst + 10 * i,
  386                           10 * (8 - i));
  387                     bcopy(src + 10 * (8 - i),
  388                           dst,
  389                           10 * i);
  390                 }
  391             }
  392             else
  393                 ifps->fp_regs = *user_fp_regs;
  394 #else   /* no FPE */
  395             ifps->fp_regs = *user_fp_regs;
  396 #endif  /* FPE */
  397 
  398             simple_unlock(&pcb->lock);
  399             if (new_ifps != 0)
  400                 zfree(ifps_zone, (vm_offset_t) ifps);
  401         }
  402 
  403         return KERN_SUCCESS;
  404 }
  405 
  406 /*
  407  * Get the floating-point state for a thread.
  408  * If the thread is not the current thread, it is
  409  * not running (held).  Locking needed against
  410  * concurrent fpu_set_state or fpu_get_state.
  411  */
  412 kern_return_t
  413 fpu_get_state(thread, state)
  414         thread_t        thread;
  415         register struct i386_float_state *state;
  416 {
  417         register pcb_t  pcb = thread->pcb;
  418         register struct i386_fpsave_state *ifps;
  419 
  420 ASSERT_IPL(SPL0);
  421         if (fp_kind == FP_NO)
  422             return KERN_FAILURE;
  423 
  424         simple_lock(&pcb->lock);
  425         ifps = pcb->ims.ifps;
  426         if (ifps == 0) {
  427             /*
  428              * No valid floating-point state.
  429              */
  430             simple_unlock(&pcb->lock);
  431             bzero((char *)state, sizeof(struct i386_float_state));
  432             return KERN_SUCCESS;
  433         }
  434 
  435         /* Make sure we`ve got the latest fp state info */
  436         clear_ts();
  437         fp_save(thread);
  438         clear_fpu();
  439 
  440         state->fpkind = fp_kind;
  441         state->exc_status = 0;
  442 
  443         {
  444             register struct i386_fp_save *user_fp_state;
  445             register struct i386_fp_regs *user_fp_regs;
  446 
  447             state->initialized = ifps->fp_valid;
  448 
  449             user_fp_state = (struct i386_fp_save *) &state->hw_state[0];
  450             user_fp_regs  = (struct i386_fp_regs *)
  451                         &state->hw_state[sizeof(struct i386_fp_save)];
  452 
  453             /*
  454              * Ensure that reserved parts of the environment are 0.
  455              */
  456             bzero((char *)user_fp_state,  sizeof(struct i386_fp_save));
  457 
  458             user_fp_state->fp_control = ifps->fp_save_state.fp_control;
  459             user_fp_state->fp_status  = ifps->fp_save_state.fp_status;
  460             user_fp_state->fp_tag     = ifps->fp_save_state.fp_tag;
  461             user_fp_state->fp_eip     = ifps->fp_save_state.fp_eip;
  462             user_fp_state->fp_cs      = ifps->fp_save_state.fp_cs;
  463             user_fp_state->fp_opcode  = ifps->fp_save_state.fp_opcode;
  464             user_fp_state->fp_dp      = ifps->fp_save_state.fp_dp;
  465             user_fp_state->fp_ds      = ifps->fp_save_state.fp_ds;
  466 
  467 #if     FPE
  468             if (fp_kind == FP_SOFT) {
  469                 /*
  470                  * The emulator stores the registers by physical
  471                  * register number, not from top-of-stack.
  472                  * Shuffle the registers into the correct order.
  473                  */
  474                 register char *src;     /* kernel regs */
  475                 register char *dst;     /* user regs */
  476                 int     i;
  477 
  478                 src = (char *)&ifps->fp_regs;
  479                 dst = (char *)user_fp_regs;
  480                 i = (ifps->fp_save_state.fp_status & FPS_TOS)
  481                         >> FPS_TOS_SHIFT;       /* physical register
  482                                                    for st(0) */
  483                 if (i == 0)
  484                     bcopy(src, dst, 8 * 10);
  485                 else {
  486                     bcopy(src + 10 * i,
  487                           dst,
  488                           10 * (8 - i));
  489                     bcopy(src,
  490                           dst + 10 * (8 - i),
  491                           10 * i);
  492                 }
  493             }
  494             else
  495                 *user_fp_regs = ifps->fp_regs;
  496 #else   /* no FPE */
  497             *user_fp_regs = ifps->fp_regs;
  498 #endif  /* FPE */
  499         }
  500         simple_unlock(&pcb->lock);
  501 
  502         return KERN_SUCCESS;
  503 }
  504 
  505 /*
  506  * Initialize FPU.
  507  *
  508  * Raise exceptions for:
  509  *      invalid operation
  510  *      divide by zero
  511  *      overflow
  512  *
  513  * Use 53-bit precision.
  514  */
  515 void fpinit()
  516 {
  517         unsigned short  control;
  518 
  519 ASSERT_IPL(SPL0);
  520         clear_ts();
  521         fninit();
  522         fnstcw(&control);
  523         control &= ~(FPC_PC|FPC_RC); /* Clear precision & rounding control */
  524         control |= (FPC_PC_53 |         /* Set precision */ 
  525                         FPC_RC_RN |     /* round-to-nearest */
  526                         FPC_ZE |        /* Suppress zero-divide */
  527                         FPC_OE |        /*  and overflow */
  528                         FPC_UE |        /*  underflow */
  529                         FPC_IE |        /* Allow NaNQs and +-INF */
  530                         FPC_DE |        /* Allow denorms as operands  */
  531                         FPC_PE);        /* No trap for precision loss */
  532         fldcw(control);
  533 }
  534 
  535 /*
  536  * Coprocessor not present.
  537  */
  538 fpnoextflt()
  539 {
  540         /*
  541          * Enable FPU use.
  542          */
  543 ASSERT_IPL(SPL0);
  544         clear_ts();
  545 #if     NCPUS == 1
  546 
  547         /*
  548          * If this thread`s state is in the FPU, we are done.
  549          */
  550         if (fp_thread == current_thread())
  551             return;
  552 
  553         /* Make sure we don't do fpsave() in fp_intr while doing fpsave()
  554          * here if the current fpu instruction generates an error.
  555          */
  556         fwait();
  557         /*
  558          * If another thread`s state is in the FPU, save it.
  559          */
  560         if (fp_thread != THREAD_NULL) {
  561             fp_save(fp_thread);
  562         }
  563 
  564         /*
  565          * Give this thread the FPU.
  566          */
  567         fp_thread = current_thread();
  568 
  569 #endif  /* NCPUS == 1 */
  570 
  571         /*
  572          * Load this thread`s state into the FPU.
  573          */
  574         fp_load(current_thread());
  575 }
  576 
  577 /*
  578  * FPU overran end of segment.
  579  * Re-initialize FPU.  Floating point state is not valid.
  580  */
  581 fpextovrflt()
  582 {
  583         register thread_t       thread = current_thread();
  584         register pcb_t          pcb;
  585         register struct i386_fpsave_state *ifps;
  586 
  587 #if     NCPUS == 1
  588 
  589         /*
  590          * Is exception for the currently running thread?
  591          */
  592         if (fp_thread != thread) {
  593             /* Uh oh... */
  594             panic("fpextovrflt");
  595         }
  596 #endif
  597 
  598         /*
  599          * This is a non-recoverable error.
  600          * Invalidate the thread`s FPU state.
  601          */
  602         pcb = thread->pcb;
  603         simple_lock(&pcb->lock);
  604         ifps = pcb->ims.ifps;
  605         pcb->ims.ifps = 0;
  606         simple_unlock(&pcb->lock);
  607 
  608         /*
  609          * Re-initialize the FPU.
  610          */
  611         clear_ts();
  612         fninit();
  613 
  614         /*
  615          * And disable access.
  616          */
  617         clear_fpu();
  618 
  619         if (ifps)
  620             zfree(ifps_zone, (vm_offset_t) ifps);
  621 
  622         /*
  623          * Raise exception.
  624          */
  625         i386_exception(EXC_BAD_ACCESS, VM_PROT_READ|VM_PROT_EXECUTE, 0);
  626         /*NOTREACHED*/
  627 }
  628 
  629 /*
  630  * FPU error. Called by AST.
  631  */
  632 fpexterrflt()
  633 {
  634         register thread_t       thread = current_thread();
  635 
  636 ASSERT_IPL(SPL0);
  637 #if     NCPUS == 1
  638         /*
  639          * Since FPU errors only occur on ESC or WAIT instructions,
  640          * the current thread should own the FPU.  If it didn`t,
  641          * we should have gotten the task-switched interrupt first.
  642          */
  643         if (fp_thread != THREAD_NULL) {
  644             panic("fpexterrflt");
  645                 return;
  646         }
  647 
  648         /*
  649          * Check if we got a context switch between the interrupt and the AST
  650          * This can happen if the interrupt arrived after the FPU AST was
  651          * checked. In this case, raise the exception in fp_load when this
  652          * thread next time uses the FPU. Remember exception condition in
  653          * fp_valid (extended boolean 2).
  654          */
  655         if (fp_intr_thread != thread) {
  656                 if (fp_intr_thread == THREAD_NULL) {
  657                         panic("fpexterrflt: fp_intr_thread == THREAD_NULL");
  658                         return;
  659                 }
  660                 fp_intr_thread->pcb->ims.ifps->fp_valid = 2;
  661                 fp_intr_thread = THREAD_NULL;
  662                 return;
  663         }
  664         fp_intr_thread = THREAD_NULL;
  665 #else   /* NCPUS == 1 */
  666         /*
  667          * Save the FPU state and turn off the FPU.
  668          */
  669         fp_save(thread);
  670 #endif  /* NCPUS == 1 */
  671 
  672         /*
  673          * Raise FPU exception.
  674          * Locking not needed on pcb->ims.ifps,
  675          * since thread is running.
  676          */
  677         i386_exception(EXC_ARITHMETIC,
  678                        EXC_I386_EXTERR,
  679                        thread->pcb->ims.ifps->fp_save_state.fp_status);
  680         /*NOTREACHED*/
  681 }
  682 
  683 /*
  684  * Save FPU state.
  685  *
  686  * Locking not needed:
  687  * .    if called from fpu_get_state, pcb already locked.
  688  * .    if called from fpnoextflt or fp_intr, we are single-cpu
  689  * .    otherwise, thread is running.
  690  */
  691 fp_save(thread)
  692         register thread_t       thread;
  693 {
  694         register pcb_t pcb = thread->pcb;
  695         register struct i386_fpsave_state *ifps = pcb->ims.ifps;
  696 
  697         if (ifps != 0 && !ifps->fp_valid) {
  698             /* registers are in FPU */
  699             ifps->fp_valid = TRUE;
  700             fnsave(&ifps->fp_save_state);
  701         }
  702 }
  703 
  704 /*
  705  * Restore FPU state from PCB.
  706  *
  707  * Locking not needed; always called on the current thread.
  708  */
  709 fp_load(thread)
  710         register thread_t       thread;
  711 {
  712         register pcb_t pcb = thread->pcb;
  713         register struct i386_fpsave_state *ifps;
  714 
  715 ASSERT_IPL(SPL0);
  716         ifps = pcb->ims.ifps;
  717         if (ifps == 0) {
  718             ifps = (struct i386_fpsave_state *) zalloc(ifps_zone);
  719             bzero(ifps, sizeof *ifps);
  720             pcb->ims.ifps = ifps;
  721             fpinit();
  722 #if 1
  723 /* 
  724  * I'm not sure this is needed. Does the fpu regenerate the interrupt in
  725  * frstor or not? Without this code we may miss some exceptions, with it
  726  * we might send too many exceptions.
  727  */
  728         } else if (ifps->fp_valid == 2) {
  729                 /* delayed exception pending */
  730 
  731                 ifps->fp_valid = TRUE;
  732                 clear_fpu();
  733                 /*
  734                  * Raise FPU exception.
  735                  * Locking not needed on pcb->ims.ifps,
  736                  * since thread is running.
  737                  */
  738                 i386_exception(EXC_ARITHMETIC,
  739                                EXC_I386_EXTERR,
  740                                thread->pcb->ims.ifps->fp_save_state.fp_status);
  741                 /*NOTREACHED*/
  742 #endif
  743         } else {
  744             frstor(ifps->fp_save_state);
  745         }
  746         ifps->fp_valid = FALSE;         /* in FPU */
  747 }
  748 
  749 /*
  750  * Allocate and initialize FP state for current thread.
  751  * Don't load state.
  752  *
  753  * Locking not needed; always called on the current thread.
  754  */
  755 void
  756 fp_state_alloc()
  757 {
  758         pcb_t   pcb = current_thread()->pcb;
  759         struct i386_fpsave_state *ifps;
  760 
  761         ifps = (struct i386_fpsave_state *)zalloc(ifps_zone);
  762         bzero(ifps, sizeof *ifps);
  763         pcb->ims.ifps = ifps;
  764 
  765         ifps->fp_valid = TRUE;
  766         ifps->fp_save_state.fp_control = (0x037f
  767                         & ~(FPC_IM|FPC_ZM|FPC_OM|FPC_PC))
  768                         | (FPC_PC_53|FPC_IC_AFF);
  769         ifps->fp_save_state.fp_status = 0;
  770         ifps->fp_save_state.fp_tag = 0xffff;    /* all empty */
  771 }
  772 
  773 #if     AT386 || PS2
  774 /*
  775  *      Handle a coprocessor error interrupt on the AT386.
  776  *      This comes in on line 5 of the slave PIC at SPL1.
  777  */
  778 fpintr()
  779 {
  780         spl_t   s;
  781         thread_t thread = current_thread();
  782 
  783 ASSERT_IPL(SPL1);
  784         /*
  785          * Turn off the extended 'busy' line.
  786          */
  787         outb(0xf0, 0);
  788 
  789         /*
  790          * Save the FPU context to the thread using it.
  791          */
  792 #if     NCPUS == 1
  793         if (fp_thread == THREAD_NULL) {
  794                 printf("fpintr: FPU not belonging to anyone!\n");
  795                 clear_ts();
  796                 fninit();
  797                 clear_fpu();
  798                 return;
  799         }
  800 
  801         if (fp_thread != thread) {
  802             /*
  803              * FPU exception is for a different thread.
  804              * When that thread again uses the FPU an exception will be
  805              * raised in fp_load. Remember the condition in fp_valid (== 2).
  806              */
  807             clear_ts();
  808             fp_save(fp_thread);
  809             fp_thread->pcb->ims.ifps->fp_valid = 2;
  810             fninit();
  811             clear_fpu();
  812             /* leave fp_intr_thread THREAD_NULL */
  813             return;
  814         }
  815         if (fp_intr_thread != THREAD_NULL)
  816             panic("fp_intr: already caught intr");
  817         fp_intr_thread = thread;
  818 #endif  /* NCPUS == 1 */
  819 
  820         clear_ts();
  821         fp_save(thread);
  822         fninit();
  823         clear_fpu();
  824 
  825         /*
  826          * Since we are running on the interrupt stack, we must
  827          * signal the thread to take the exception when we return
  828          * to user mode.  Use an AST to do this.
  829          *
  830          * Don`t set the thread`s AST field.  If the thread is
  831          * descheduled before it takes the AST, it will notice
  832          * the FPU error when it reloads its FPU state.
  833          */
  834         s = splsched();
  835         ast_on(cpu_number(), AST_I386_FP);
  836         splx(s);
  837 }
  838 #endif  /* AT386 || PS2 */

Cache object: c918270a48bcbe467b0fbe94305ad402


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.