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/osfmk/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  * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
    3  *
    4  * @APPLE_LICENSE_HEADER_START@
    5  * 
    6  * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
    7  * 
    8  * This file contains Original Code and/or Modifications of Original Code
    9  * as defined in and that are subject to the Apple Public Source License
   10  * Version 2.0 (the 'License'). You may not use this file except in
   11  * compliance with the License. Please obtain a copy of the License at
   12  * http://www.opensource.apple.com/apsl/ and read it before using this
   13  * file.
   14  * 
   15  * The Original Code and all software distributed under the License are
   16  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
   17  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
   18  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
   19  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
   20  * Please see the License for the specific language governing rights and
   21  * limitations under the License.
   22  * 
   23  * @APPLE_LICENSE_HEADER_END@
   24  */
   25 /*
   26  * @OSF_COPYRIGHT@
   27  */
   28 /* 
   29  * Mach Operating System
   30  * Copyright (c) 1992-1990 Carnegie Mellon University
   31  * All Rights Reserved.
   32  * 
   33  * Permission to use, copy, modify and distribute this software and its
   34  * documentation is hereby granted, provided that both the copyright
   35  * notice and this permission notice appear in all copies of the
   36  * software, derivative works or modified versions, and any portions
   37  * thereof, and that both notices appear in supporting documentation.
   38  * 
   39  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
   40  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
   41  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
   42  * 
   43  * Carnegie Mellon requests users of this software to return to
   44  * 
   45  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
   46  *  School of Computer Science
   47  *  Carnegie Mellon University
   48  *  Pittsburgh PA 15213-3890
   49  * 
   50  * any improvements or extensions that they make and grant Carnegie Mellon
   51  * the rights to redistribute these changes.
   52  */
   53 /*
   54  */
   55 
   56 #include <cpus.h>
   57 #include <platforms.h>
   58 
   59 #include <mach/exception_types.h>
   60 #include <mach/i386/thread_status.h>
   61 #include <mach/i386/fp_reg.h>
   62 
   63 #include <kern/mach_param.h>
   64 #include <kern/thread.h>
   65 #include <kern/zalloc.h>
   66 #include <kern/misc_protos.h>
   67 #include <kern/spl.h>
   68 #include <kern/assert.h>
   69 
   70 #include <i386/thread.h>
   71 #include <i386/fpu.h>
   72 #include <i386/trap.h>
   73 #include <i386/pio.h>
   74 #include <i386/cpuid.h>
   75 #include <i386/misc_protos.h>
   76 
   77 #if 0
   78 #include <i386/ipl.h>
   79 extern int curr_ipl;
   80 #define ASSERT_IPL(L) \
   81 { \
   82       if (curr_ipl != L) { \
   83               printf("IPL is %d, expected %d\n", curr_ipl, L); \
   84               panic("fpu: wrong ipl"); \
   85       } \
   86 }
   87 #else
   88 #define ASSERT_IPL(L)
   89 #endif
   90 
   91 int             fp_kind = FP_387;       /* 80387 present */
   92 zone_t          ifps_zone;              /* zone for FPU save area */
   93 
   94 #if     NCPUS == 1
   95 volatile thread_act_t   fp_act = THR_ACT_NULL;
   96                                     /* thread whose state is in FPU */
   97                                     /* always THR_ACT_NULL if emulating FPU */
   98 volatile thread_act_t   fp_intr_act = THR_ACT_NULL;
   99 
  100 
  101 #define clear_fpu() \
  102     { \
  103         set_ts(); \
  104         fp_act = THR_ACT_NULL; \
  105     }
  106 
  107 #else   /* NCPUS > 1 */
  108 #define clear_fpu() \
  109     { \
  110         set_ts(); \
  111     }
  112 
  113 #endif
  114  
  115 #define ALIGNED(addr,size)      (((unsigned)(addr)&((size)-1))==0)
  116 
  117 /* Forward */
  118 
  119 extern void             fpinit(void);
  120 extern void             fp_save(
  121                                 thread_act_t    thr_act);
  122 extern void             fp_load(
  123                                 thread_act_t    thr_act);
  124 
  125 /*
  126  * Look for FPU and initialize it.
  127  * Called on each CPU.
  128  */
  129 void
  130 init_fpu(void)
  131 {
  132         unsigned short  status, control;
  133 
  134         /*
  135          * Check for FPU by initializing it,
  136          * then trying to read the correct bit patterns from
  137          * the control and status registers.
  138          */
  139         set_cr0(get_cr0() & ~(CR0_EM|CR0_TS));  /* allow use of FPU */
  140 
  141         fninit();
  142         status = fnstsw();
  143         fnstcw(&control);
  144 
  145         if ((status & 0xff) == 0 &&
  146             (control & 0x103f) == 0x3f) 
  147         {
  148             fp_kind = FP_387;   /* assume we have a 387 compatible instruction set */
  149             /* Use FPU save/restore instructions if available */
  150             if (cpuid_features() & CPUID_FEATURE_FXSR) {
  151                 fp_kind = FP_FXSR;
  152                 set_cr4(get_cr4() | CR4_FXS);
  153                 printf("Enabling XMM register save/restore");
  154                 /* And allow SIMD instructions if present */
  155                 if (cpuid_features() & CPUID_FEATURE_SSE) {
  156                     printf(" and SSE/SSE2");
  157                     set_cr4(get_cr4() | CR4_XMM);
  158                 }
  159                 printf(" opcodes\n");
  160             }
  161 
  162             /*
  163              * Trap wait instructions.  Turn off FPU for now.
  164              */
  165             set_cr0(get_cr0() | CR0_TS | CR0_MP);
  166         }
  167         else
  168         {
  169             /*
  170              * NO FPU.
  171              */
  172             fp_kind = FP_NO;
  173             set_cr0(get_cr0() | CR0_EM);
  174         }
  175 }
  176 
  177 /*
  178  * Initialize FP handling.
  179  */
  180 void
  181 fpu_module_init(void)
  182 {
  183         ifps_zone = zinit(sizeof(struct i386_fpsave_state),
  184                           THREAD_MAX * sizeof(struct i386_fpsave_state),
  185                           THREAD_CHUNK * sizeof(struct i386_fpsave_state),
  186                           "i386 fpsave state");
  187 }
  188 
  189 /*
  190  * Free a FPU save area.
  191  * Called only when thread terminating - no locking necessary.
  192  */
  193 void
  194 fp_free(fps)
  195         struct i386_fpsave_state *fps;
  196 {
  197 ASSERT_IPL(SPL0);
  198 #if     NCPUS == 1
  199         if ((fp_act != THR_ACT_NULL) && (fp_act->mact.pcb->ims.ifps == fps)) {
  200                 /* 
  201                  * Make sure we don't get FPU interrupts later for
  202                  * this thread
  203                  */
  204                 fwait();
  205 
  206                 /* Mark it free and disable access */
  207             clear_fpu();
  208         }
  209 #endif  /* NCPUS == 1 */
  210         zfree(ifps_zone, (vm_offset_t) fps);
  211 }
  212 
  213 /*
  214  * Set the floating-point state for a thread based 
  215  * on the FXSave formatted data. This is basically  
  216  * the same as fpu_set_state except it uses the 
  217  * expanded data structure. 
  218  * If the thread is not the current thread, it is
  219  * not running (held).  Locking needed against
  220  * concurrent fpu_set_state or fpu_get_state.
  221  */
  222 kern_return_t
  223 fpu_set_fxstate(
  224         thread_act_t            thr_act,
  225         struct i386_float_state *state)
  226 {
  227         register pcb_t  pcb;
  228         register struct i386_fpsave_state *ifps;
  229         register struct i386_fpsave_state *new_ifps;
  230 
  231 ASSERT_IPL(SPL0);
  232         if (fp_kind == FP_NO)
  233             return KERN_FAILURE;
  234 
  235         if (state->fpkind != FP_FXSR) {
  236             /* strange if this happens, but in case someone builds one of these manually... */
  237             return fpu_set_state(thr_act, state);
  238         }
  239         
  240         assert(thr_act != THR_ACT_NULL);
  241         pcb = thr_act->mact.pcb;
  242 
  243 #if     NCPUS == 1
  244 
  245         /*
  246          * If this thread`s state is in the FPU,
  247          * discard it; we are replacing the entire
  248          * FPU state.
  249          */
  250         if (fp_act == thr_act) {
  251             fwait();                    /* wait for possible interrupt */
  252             clear_fpu();                /* no state in FPU */
  253         }
  254 #endif
  255         
  256         if (state->initialized == 0) {
  257             /*
  258              * new FPU state is 'invalid'.
  259              * Deallocate the fp state if it exists.
  260              */
  261             simple_lock(&pcb->lock);
  262             ifps = pcb->ims.ifps;
  263             pcb->ims.ifps = 0;
  264             simple_unlock(&pcb->lock);
  265 
  266             if (ifps != 0) {
  267                 zfree(ifps_zone, (vm_offset_t) ifps);
  268             }
  269         }
  270         else {
  271             /*
  272              * Valid state.  Allocate the fp state if there is none.
  273              */
  274 
  275             new_ifps = 0;
  276         Retry:
  277             simple_lock(&pcb->lock);
  278             ifps = pcb->ims.ifps;
  279             if (ifps == 0) {
  280                 if (new_ifps == 0) {
  281                     simple_unlock(&pcb->lock);
  282                     new_ifps = (struct i386_fpsave_state *) zalloc(ifps_zone);
  283                     assert(ALIGNED(new_ifps,16));
  284                     goto Retry;
  285                 }
  286                 ifps = new_ifps;
  287                 new_ifps = 0;
  288                 bzero((char *)ifps, sizeof *ifps);
  289                 pcb->ims.ifps = ifps;
  290             }
  291 
  292             /*
  293              * now copy over the new data.
  294              */
  295             bcopy((char *)&state->hw_state[0], (char *)&ifps->fx_save_state, sizeof(struct i386_fx_save));
  296             ifps->fp_save_flavor = FP_FXSR;
  297             simple_unlock(&pcb->lock);
  298             if (new_ifps != 0)
  299                 zfree(ifps_zone, (vm_offset_t) ifps);
  300         }
  301 
  302         return KERN_SUCCESS;
  303 }
  304 
  305 /*
  306  * Get the floating-point state for a thread.
  307  * If the thread is not the current thread, it is
  308  * not running (held).  Locking needed against
  309  * concurrent fpu_set_state or fpu_get_state.
  310  */
  311 kern_return_t
  312 fpu_get_fxstate(
  313         thread_act_t                            thr_act,
  314         register struct i386_float_state        *state)
  315 {
  316         register pcb_t  pcb;
  317         register struct i386_fpsave_state *ifps;
  318 
  319 ASSERT_IPL(SPL0);
  320         if (fp_kind == FP_NO)
  321             return KERN_FAILURE;
  322 
  323         assert(thr_act != THR_ACT_NULL);
  324         pcb = thr_act->mact.pcb;
  325 
  326         simple_lock(&pcb->lock);
  327         ifps = pcb->ims.ifps;
  328         if (ifps == 0) {
  329             /*
  330              * No valid floating-point state.
  331              */
  332             simple_unlock(&pcb->lock);
  333             bzero((char *)state, sizeof(struct i386_float_state));
  334             return KERN_SUCCESS;
  335         }
  336 
  337         /* Make sure we`ve got the latest fp state info */
  338         /* If the live fpu state belongs to our target */
  339 #if     NCPUS == 1
  340         if (thr_act == fp_act)
  341 #else
  342         if (thr_act == current_act())
  343 #endif
  344         {
  345             clear_ts();
  346             fp_save(thr_act);
  347             clear_fpu();
  348         }
  349 
  350         state->fpkind = fp_kind;
  351         state->exc_status = 0;
  352         state->initialized = ifps->fp_valid;
  353         bcopy( (char *)&ifps->fx_save_state, (char *)&state->hw_state[0], sizeof(struct i386_fx_save));
  354 
  355         simple_unlock(&pcb->lock);
  356 
  357         return KERN_SUCCESS;
  358 }
  359 
  360 /*
  361  * Set the floating-point state for a thread.
  362  * If the thread is not the current thread, it is
  363  * not running (held).  Locking needed against
  364  * concurrent fpu_set_state or fpu_get_state.
  365  */
  366 kern_return_t
  367 fpu_set_state(
  368         thread_act_t            thr_act,
  369         struct i386_float_state *state)
  370 {
  371         register pcb_t  pcb;
  372         register struct i386_fpsave_state *ifps;
  373         register struct i386_fpsave_state *new_ifps;
  374 
  375 ASSERT_IPL(SPL0);
  376         if (fp_kind == FP_NO)
  377             return KERN_FAILURE;
  378 
  379         assert(thr_act != THR_ACT_NULL);
  380         pcb = thr_act->mact.pcb;
  381 
  382 #if     NCPUS == 1
  383 
  384         /*
  385          * If this thread`s state is in the FPU,
  386          * discard it; we are replacing the entire
  387          * FPU state.
  388          */
  389         if (fp_act == thr_act) {
  390             fwait();                    /* wait for possible interrupt */
  391             clear_fpu();                /* no state in FPU */
  392         }
  393 #endif
  394 
  395         if (state->initialized == 0) {
  396             /*
  397              * new FPU state is 'invalid'.
  398              * Deallocate the fp state if it exists.
  399              */
  400             simple_lock(&pcb->lock);
  401             ifps = pcb->ims.ifps;
  402             pcb->ims.ifps = 0;
  403             simple_unlock(&pcb->lock);
  404 
  405             if (ifps != 0) {
  406                 zfree(ifps_zone, (vm_offset_t) ifps);
  407             }
  408         }
  409         else {
  410             /*
  411              * Valid state.  Allocate the fp state if there is none.
  412              */
  413             register struct i386_fp_save *user_fp_state;
  414             register struct i386_fp_regs *user_fp_regs;
  415 
  416             user_fp_state = (struct i386_fp_save *) &state->hw_state[0];
  417             user_fp_regs  = (struct i386_fp_regs *)
  418                         &state->hw_state[sizeof(struct i386_fp_save)];
  419 
  420             new_ifps = 0;
  421         Retry:
  422             simple_lock(&pcb->lock);
  423             ifps = pcb->ims.ifps;
  424             if (ifps == 0) {
  425                 if (new_ifps == 0) {
  426                     simple_unlock(&pcb->lock);
  427                     new_ifps = (struct i386_fpsave_state *) zalloc(ifps_zone);
  428                     assert(ALIGNED(new_ifps,16));
  429                     goto Retry;
  430                 }
  431                 ifps = new_ifps;
  432                 new_ifps = 0;
  433                 bzero((char *)ifps, sizeof *ifps); // zero ALL fields first
  434                 pcb->ims.ifps = ifps;
  435             }
  436 
  437             /*
  438              * Ensure that reserved parts of the environment are 0.
  439              */
  440             bzero((char *)&ifps->fp_save_state, sizeof(struct i386_fp_save));
  441 
  442             ifps->fp_save_state.fp_control = user_fp_state->fp_control;
  443             ifps->fp_save_state.fp_status  = user_fp_state->fp_status;
  444             ifps->fp_save_state.fp_tag     = user_fp_state->fp_tag;
  445             ifps->fp_save_state.fp_eip     = user_fp_state->fp_eip;
  446             ifps->fp_save_state.fp_cs      = user_fp_state->fp_cs;
  447             ifps->fp_save_state.fp_opcode  = user_fp_state->fp_opcode;
  448             ifps->fp_save_state.fp_dp      = user_fp_state->fp_dp;
  449             ifps->fp_save_state.fp_ds      = user_fp_state->fp_ds;
  450             ifps->fp_regs = *user_fp_regs;
  451             ifps->fp_save_flavor = FP_387;
  452             simple_unlock(&pcb->lock);
  453             if (new_ifps != 0)
  454                 zfree(ifps_zone, (vm_offset_t) ifps);
  455         }
  456 
  457         return KERN_SUCCESS;
  458 }
  459 
  460 /*
  461  * Get the floating-point state for a thread.
  462  * If the thread is not the current thread, it is
  463  * not running (held).  Locking needed against
  464  * concurrent fpu_set_state or fpu_get_state.
  465  */
  466 kern_return_t
  467 fpu_get_state(
  468         thread_act_t                            thr_act,
  469         register struct i386_float_state        *state)
  470 {
  471         register pcb_t  pcb;
  472         register struct i386_fpsave_state *ifps;
  473 
  474 ASSERT_IPL(SPL0);
  475         if (fp_kind == FP_NO)
  476             return KERN_FAILURE;
  477 
  478         assert(thr_act != THR_ACT_NULL);
  479         pcb = thr_act->mact.pcb;
  480 
  481         simple_lock(&pcb->lock);
  482         ifps = pcb->ims.ifps;
  483         if (ifps == 0) {
  484             /*
  485              * No valid floating-point state.
  486              */
  487             simple_unlock(&pcb->lock);
  488             bzero((char *)state, sizeof(struct i386_float_state));
  489             return KERN_SUCCESS;
  490         }
  491 
  492         /* Make sure we`ve got the latest fp state info */
  493         /* If the live fpu state belongs to our target */
  494 #if     NCPUS == 1
  495         if (thr_act == fp_act)
  496 #else
  497         if (thr_act == current_act())
  498 #endif
  499         {
  500             clear_ts();
  501             fp_save(thr_act);
  502             clear_fpu();
  503         }
  504 
  505         state->fpkind = fp_kind;
  506         state->exc_status = 0;
  507 
  508         {
  509             register struct i386_fp_save *user_fp_state;
  510             register struct i386_fp_regs *user_fp_regs;
  511 
  512             state->initialized = ifps->fp_valid;
  513 
  514             user_fp_state = (struct i386_fp_save *) &state->hw_state[0];
  515             user_fp_regs  = (struct i386_fp_regs *)
  516                         &state->hw_state[sizeof(struct i386_fp_save)];
  517 
  518             /*
  519              * Ensure that reserved parts of the environment are 0.
  520              */
  521             bzero((char *)user_fp_state,  sizeof(struct i386_fp_save));
  522 
  523             user_fp_state->fp_control = ifps->fp_save_state.fp_control;
  524             user_fp_state->fp_status  = ifps->fp_save_state.fp_status;
  525             user_fp_state->fp_tag     = ifps->fp_save_state.fp_tag;
  526             user_fp_state->fp_eip     = ifps->fp_save_state.fp_eip;
  527             user_fp_state->fp_cs      = ifps->fp_save_state.fp_cs;
  528             user_fp_state->fp_opcode  = ifps->fp_save_state.fp_opcode;
  529             user_fp_state->fp_dp      = ifps->fp_save_state.fp_dp;
  530             user_fp_state->fp_ds      = ifps->fp_save_state.fp_ds;
  531             *user_fp_regs = ifps->fp_regs;
  532         }
  533         simple_unlock(&pcb->lock);
  534 
  535         return KERN_SUCCESS;
  536 }
  537 
  538 /*
  539  * Initialize FPU.
  540  *
  541  * Raise exceptions for:
  542  *      invalid operation
  543  *      divide by zero
  544  *      overflow
  545  *
  546  * Use 53-bit precision.
  547  */
  548 void
  549 fpinit(void)
  550 {
  551         unsigned short  control;
  552 
  553 ASSERT_IPL(SPL0);
  554         clear_ts();
  555         fninit();
  556         fnstcw(&control);
  557         control &= ~(FPC_PC|FPC_RC); /* Clear precision & rounding control */
  558         control |= (FPC_PC_53 |         /* Set precision */ 
  559                         FPC_RC_RN |     /* round-to-nearest */
  560                         FPC_ZE |        /* Suppress zero-divide */
  561                         FPC_OE |        /*  and overflow */
  562                         FPC_UE |        /*  underflow */
  563                         FPC_IE |        /* Allow NaNQs and +-INF */
  564                         FPC_DE |        /* Allow denorms as operands  */
  565                         FPC_PE);        /* No trap for precision loss */
  566         fldcw(control);
  567 }
  568 
  569 /*
  570  * Coprocessor not present.
  571  */
  572 
  573 void
  574 fpnoextflt(void)
  575 {
  576         /*
  577          * Enable FPU use.
  578          */
  579 ASSERT_IPL(SPL0);
  580         clear_ts();
  581 #if     NCPUS == 1
  582 
  583         /*
  584          * If this thread`s state is in the FPU, we are done.
  585          */
  586         if (fp_act == current_act())
  587             return;
  588 
  589         /* Make sure we don't do fpsave() in fp_intr while doing fpsave()
  590          * here if the current fpu instruction generates an error.
  591          */
  592         fwait();
  593         /*
  594          * If another thread`s state is in the FPU, save it.
  595          */
  596         if (fp_act != THR_ACT_NULL) {
  597             fp_save(fp_act);
  598         }
  599 
  600         /*
  601          * Give this thread the FPU.
  602          */
  603         fp_act = current_act();
  604 
  605 #endif  /* NCPUS == 1 */
  606 
  607         /*
  608          * Load this thread`s state into the FPU.
  609          */
  610         fp_load(current_act());
  611 }
  612 
  613 /*
  614  * FPU overran end of segment.
  615  * Re-initialize FPU.  Floating point state is not valid.
  616  */
  617 
  618 void
  619 fpextovrflt(void)
  620 {
  621         register thread_act_t   thr_act = current_act();
  622         register pcb_t          pcb;
  623         register struct i386_fpsave_state *ifps;
  624 
  625 #if     NCPUS == 1
  626 
  627         /*
  628          * Is exception for the currently running thread?
  629          */
  630         if (fp_act != thr_act) {
  631             /* Uh oh... */
  632             panic("fpextovrflt");
  633         }
  634 #endif
  635 
  636         /*
  637          * This is a non-recoverable error.
  638          * Invalidate the thread`s FPU state.
  639          */
  640         pcb = thr_act->mact.pcb;
  641         simple_lock(&pcb->lock);
  642         ifps = pcb->ims.ifps;
  643         pcb->ims.ifps = 0;
  644         simple_unlock(&pcb->lock);
  645 
  646         /*
  647          * Re-initialize the FPU.
  648          */
  649         clear_ts();
  650         fninit();
  651 
  652         /*
  653          * And disable access.
  654          */
  655         clear_fpu();
  656 
  657         if (ifps)
  658             zfree(ifps_zone, (vm_offset_t) ifps);
  659 
  660         /*
  661          * Raise exception.
  662          */
  663         i386_exception(EXC_BAD_ACCESS, VM_PROT_READ|VM_PROT_EXECUTE, 0);
  664         /*NOTREACHED*/
  665 }
  666 
  667 /*
  668  * FPU error. Called by AST.
  669  */
  670 
  671 void
  672 fpexterrflt(void)
  673 {
  674         register thread_act_t   thr_act = current_act();
  675 
  676 ASSERT_IPL(SPL0);
  677 #if     NCPUS == 1
  678         /*
  679          * Since FPU errors only occur on ESC or WAIT instructions,
  680          * the current thread should own the FPU.  If it didn`t,
  681          * we should have gotten the task-switched interrupt first.
  682          */
  683         if (fp_act != THR_ACT_NULL) {
  684             panic("fpexterrflt");
  685                 return;
  686         }
  687 
  688         /*
  689          * Check if we got a context switch between the interrupt and the AST
  690          * This can happen if the interrupt arrived after the FPU AST was
  691          * checked. In this case, raise the exception in fp_load when this
  692          * thread next time uses the FPU. Remember exception condition in
  693          * fp_valid (extended boolean 2).
  694          */
  695         if (fp_intr_act != thr_act) {
  696                 if (fp_intr_act == THR_ACT_NULL) {
  697                         panic("fpexterrflt: fp_intr_act == THR_ACT_NULL");
  698                         return;
  699                 }
  700                 fp_intr_act->mact.pcb->ims.ifps->fp_valid = 2;
  701                 fp_intr_act = THR_ACT_NULL;
  702                 return;
  703         }
  704         fp_intr_act = THR_ACT_NULL;
  705 #else   /* NCPUS == 1 */
  706         /*
  707          * Save the FPU state and turn off the FPU.
  708          */
  709         fp_save(thr_act);
  710 #endif  /* NCPUS == 1 */
  711 
  712         /*
  713          * Raise FPU exception.
  714          * Locking not needed on pcb->ims.ifps,
  715          * since thread is running.
  716          */
  717         i386_exception(EXC_ARITHMETIC,
  718                        EXC_I386_EXTERR,
  719                        thr_act->mact.pcb->ims.ifps->fp_save_state.fp_status);
  720         /*NOTREACHED*/
  721 }
  722 
  723 /*
  724  * Save FPU state.
  725  *
  726  * Locking not needed:
  727  * .    if called from fpu_get_state, pcb already locked.
  728  * .    if called from fpnoextflt or fp_intr, we are single-cpu
  729  * .    otherwise, thread is running.
  730  */
  731 void
  732 fp_save(
  733         thread_act_t    thr_act)
  734 {
  735         register pcb_t pcb = thr_act->mact.pcb;
  736         register struct i386_fpsave_state *ifps = pcb->ims.ifps;
  737         if (ifps != 0 && !ifps->fp_valid) {
  738             /* registers are in FPU */
  739             ifps->fp_valid = TRUE;
  740             ifps->fp_save_flavor = FP_387;
  741             if (FXSAFE()) {
  742                 fxsave(&ifps->fx_save_state);   // save the SSE2/Fp state in addition is enabled
  743                 ifps->fp_save_flavor = FP_FXSR;
  744             }
  745             fnsave(&ifps->fp_save_state);  // also update the old save area for now...
  746         }
  747 }
  748 
  749 /*
  750  * Restore FPU state from PCB.
  751  *
  752  * Locking not needed; always called on the current thread.
  753  */
  754 
  755 void
  756 fp_load(
  757         thread_act_t    thr_act)
  758 {
  759         register pcb_t pcb = thr_act->mact.pcb;
  760         register struct i386_fpsave_state *ifps;
  761 
  762 ASSERT_IPL(SPL0);
  763         ifps = pcb->ims.ifps;
  764         if (ifps == 0) {
  765             ifps = (struct i386_fpsave_state *) zalloc(ifps_zone);
  766             assert(ALIGNED(ifps,16));
  767             bzero((char *)ifps, sizeof *ifps);
  768             pcb->ims.ifps = ifps;
  769             fpinit();
  770 #if 1
  771 /* 
  772  * I'm not sure this is needed. Does the fpu regenerate the interrupt in
  773  * frstor or not? Without this code we may miss some exceptions, with it
  774  * we might send too many exceptions.
  775  */
  776         } else if (ifps->fp_valid == 2) {
  777                 /* delayed exception pending */
  778 
  779                 ifps->fp_valid = TRUE;
  780                 clear_fpu();
  781                 /*
  782                  * Raise FPU exception.
  783                  * Locking not needed on pcb->ims.ifps,
  784                  * since thread is running.
  785                  */
  786                 i386_exception(EXC_ARITHMETIC,
  787                        EXC_I386_EXTERR,
  788                        thr_act->mact.pcb->ims.ifps->fp_save_state.fp_status);
  789                 /*NOTREACHED*/
  790 #endif
  791         } else {
  792             if (ifps->fp_save_flavor == FP_FXSR) fxrstor(&ifps->fx_save_state);
  793             else frstor(ifps->fp_save_state);
  794         }
  795         ifps->fp_valid = FALSE;         /* in FPU */
  796 }
  797 
  798 
  799 /*
  800  * Allocate and initialize FP state for current thread.
  801  * Don't load state.
  802  *
  803  * Locking not needed; always called on the current thread.
  804  */
  805 void
  806 fp_state_alloc(void)
  807 {
  808         pcb_t   pcb = current_act()->mact.pcb;
  809         struct i386_fpsave_state *ifps;
  810 
  811         ifps = (struct i386_fpsave_state *)zalloc(ifps_zone);
  812         assert(ALIGNED(ifps,16));
  813         bzero((char *)ifps, sizeof *ifps);
  814         pcb->ims.ifps = ifps;
  815 
  816         ifps->fp_valid = TRUE;
  817         ifps->fp_save_state.fp_control = (0x037f
  818                         & ~(FPC_IM|FPC_ZM|FPC_OM|FPC_PC))
  819                         | (FPC_PC_53|FPC_IC_AFF);
  820         ifps->fp_save_state.fp_status = 0;
  821         ifps->fp_save_state.fp_tag = 0xffff;    /* all empty */
  822         ifps->fx_save_state.fx_control = ifps->fp_save_state.fp_control;
  823         ifps->fx_save_state.fx_status = ifps->fp_save_state.fp_status;
  824         ifps->fx_save_state.fx_tag = 0x00;
  825         ifps->fx_save_state.fx_MXCSR = 0x1f80;
  826         
  827 }
  828 
  829 
  830 /*
  831  * fpflush(thread_act_t)
  832  *      Flush the current act's state, if needed
  833  *      (used by thread_terminate_self to ensure fp faults
  834  *      aren't satisfied by overly general trap code in the
  835  *      context of the reaper thread)
  836  */
  837 void
  838 fpflush(thread_act_t thr_act)
  839 {
  840 #if     NCPUS == 1
  841         if (fp_act && thr_act == fp_act) {
  842             clear_ts();
  843             fwait();
  844             clear_fpu();
  845         }
  846 #else
  847         /* not needed on MP x86s; fp not lazily evaluated */
  848 #endif
  849 }
  850 
  851 
  852 /*
  853  *      Handle a coprocessor error interrupt on the AT386.
  854  *      This comes in on line 5 of the slave PIC at SPL1.
  855  */
  856 
  857 void
  858 fpintr(void)
  859 {
  860         spl_t   s;
  861         thread_act_t thr_act = current_act();
  862 
  863 ASSERT_IPL(SPL1);
  864         /*
  865          * Turn off the extended 'busy' line.
  866          */
  867         outb(0xf0, 0);
  868 
  869         /*
  870          * Save the FPU context to the thread using it.
  871          */
  872 #if     NCPUS == 1
  873         if (fp_act == THR_ACT_NULL) {
  874                 printf("fpintr: FPU not belonging to anyone!\n");
  875                 clear_ts();
  876                 fninit();
  877                 clear_fpu();
  878                 return;
  879         }
  880 
  881         if (fp_act != thr_act) {
  882             /*
  883              * FPU exception is for a different thread.
  884              * When that thread again uses the FPU an exception will be
  885              * raised in fp_load. Remember the condition in fp_valid (== 2).
  886              */
  887             clear_ts();
  888             fp_save(fp_act);
  889             fp_act->mact.pcb->ims.ifps->fp_valid = 2;
  890             fninit();
  891             clear_fpu();
  892             /* leave fp_intr_act THR_ACT_NULL */
  893             return;
  894         }
  895         if (fp_intr_act != THR_ACT_NULL)
  896             panic("fp_intr: already caught intr");
  897         fp_intr_act = thr_act;
  898 #endif  /* NCPUS == 1 */
  899 
  900         clear_ts();
  901         fp_save(thr_act);
  902         fninit();
  903         clear_fpu();
  904 
  905         /*
  906          * Since we are running on the interrupt stack, we must
  907          * signal the thread to take the exception when we return
  908          * to user mode.  Use an AST to do this.
  909          *
  910          * Don`t set the thread`s AST field.  If the thread is
  911          * descheduled before it takes the AST, it will notice
  912          * the FPU error when it reloads its FPU state.
  913          */
  914         s = splsched();
  915         mp_disable_preemption();
  916         ast_on(AST_I386_FP);
  917         mp_enable_preemption();
  918         splx(s);
  919 }

Cache object: 91f37483b25652eb1371045d9fcff3a6


[ 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.