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/fpe_linkage.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) 1991 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 /*
   28  * HISTORY
   29  * $Log:        fpe_linkage.c,v $
   30  * Revision 2.4  93/01/14  17:28:53  danner
   31  *      Added include of mach/std_types.h.
   32  *      [92/12/04            af]
   33  * 
   34  * Revision 2.3  92/03/05  15:39:05  rpd
   35  *      Fixed disable_fpe to clear fs & gs.
   36  *      [92/03/05            rpd]
   37  * 
   38  * Revision 2.2  92/01/03  20:05:28  dbg
   39  *      Changed trap gate for interrupt 7 to PL_Kernel.  The privilege level
   40  *      affects INT $7 instructions, not coprocessor-not-present traps.
   41  *      [91/12/19            dbg]
   42  * 
   43  *      Created.
   44  *      [91/10/19            dbg]
   45  * 
   46  */
   47 
   48 /*
   49  * Support routines for FP emulator.
   50  */
   51 
   52 #include <fpe.h>
   53 
   54 #include <cpus.h>
   55 
   56 #include <mach/std_types.h>
   57 #include <mach/exception.h>
   58 #include <mach/thread_status.h>
   59 
   60 #include <kern/cpu_number.h>
   61 #include <kern/thread.h>
   62 
   63 #include <vm/vm_kern.h>
   64 
   65 #include <i386/eflags.h>
   66 #include <i386/proc_reg.h>
   67 #include <i386/pmap.h>
   68 #include <i386/seg.h>
   69 #include <i386/thread.h>
   70 #include <i386/fpu.h>
   71 
   72 #if     NCPUS > 1
   73 #include <i386/mp_desc.h>
   74 #endif
   75 
   76 extern vm_offset_t      kvtophys();
   77 
   78 /*
   79  * Symbols exported from FPE emulator.
   80  */
   81 extern char     fpe_start[];    /* start of emulator text;
   82                                    also emulation entry point */
   83 extern char     fpe_end[];      /* end of emulator text */
   84 extern int      fpe_reg_segment;
   85                                 /* word holding segment number for
   86                                    FPE register/status area */
   87 extern char     fpe_recover[];  /* emulation fault recovery entry point */
   88 
   89 /*
   90  * Descriptor tables to be modified for FPE.
   91  */
   92 extern struct fake_descriptor idt[];
   93 extern struct fake_descriptor gdt[];
   94 
   95 extern void     fix_desc();
   96 
   97 #if     NCPUS > 1
   98 #define curr_gdt(mycpu)         (mp_gdt[mycpu])
   99 #define curr_idt(mycpu)         (mp_desc_table[mycpu]->idt)
  100 #else
  101 #define curr_gdt(mycpu)         (gdt)
  102 #define curr_idt(mycpu)         (idt)
  103 #endif
  104 
  105 #define gdt_desc_p(mycpu,sel) \
  106         ((struct real_descriptor *)&curr_gdt(mycpu)[sel_idx(sel)])
  107 #define idt_desc_p(mycpu,idx) \
  108         ((struct real_gate *)&curr_idt(mycpu)[idx])
  109 
  110 void    set_user_access();      /* forward */
  111 
  112 /*
  113  * long pointer for calling FPE register recovery routine.
  114  */
  115 struct long_ptr {
  116         unsigned long   offset;
  117         unsigned short  segment;
  118 };
  119 
  120 struct long_ptr fpe_recover_ptr;
  121 
  122 /*
  123  * Initialize descriptors for FP emulator.
  124  */
  125 void
  126 fpe_init()
  127 {
  128         register struct real_descriptor *gdt_p;
  129         register struct real_gate *idt_p;
  130 
  131         /*
  132          * Map in the pages for the FP emulator:
  133          * read-only, user-accessible.
  134          */
  135         set_user_access(pmap_kernel(),
  136                         (vm_offset_t)fpe_start,
  137                         (vm_offset_t)fpe_end,
  138                         FALSE);
  139 
  140         /*
  141          * Put the USER_FPREGS segment value in the FP emulator.
  142          */
  143         fpe_reg_segment = USER_FPREGS;
  144 
  145         /*
  146          * Change exception 7 gate (coprocessor not present)
  147          * to a trap gate to the FPE code segment.
  148          */
  149         idt_p = idt_desc_p(cpu_number(), 7);
  150         idt_p->offset_low  = 0;                 /* offset of FPE entry */
  151         idt_p->offset_high = 0;
  152         idt_p->selector   = FPE_CS;             /* FPE code segment */
  153         idt_p->word_count = 0;
  154         idt_p->access     = ACC_P|ACC_PL_K|ACC_TRAP_GATE;
  155                                                 /* trap gate */
  156                                                 /* kernel privileges only,
  157                                                    so INT $7 does not call
  158                                                    the emulator */
  159 
  160         /*
  161          * Build GDT entry for FP code segment.
  162          */
  163         gdt_p = gdt_desc_p(cpu_number(), FPE_CS);
  164         gdt_p->base_low   = ((vm_offset_t) fpe_start) & 0xffff;
  165         gdt_p->base_med   = (((vm_offset_t) fpe_start) >> 16) & 0xff;
  166         gdt_p->base_high  = ((vm_offset_t) fpe_start) >> 24;
  167         gdt_p->limit_low  = (vm_offset_t) fpe_end
  168                           - (vm_offset_t) fpe_start
  169                           - 1;
  170         gdt_p->limit_high = 0;
  171         gdt_p->granularity = SZ_32;
  172         gdt_p->access     = ACC_P|ACC_PL_K|ACC_CODE_CR;
  173                                                 /* conforming segment,
  174                                                    usable by kernel */
  175 
  176         /*
  177          * Build GDT entry for user FP state area - template,
  178          * since each thread has its own.
  179          */
  180         gdt_p = gdt_desc_p(cpu_number(), USER_FPREGS);
  181         /* descriptor starts as 0 */
  182         gdt_p->limit_low  = sizeof(struct i386_fp_save)
  183                           + sizeof(struct i386_fp_regs)
  184                           - 1;
  185         gdt_p->limit_high = 0;
  186         gdt_p->granularity = 0;
  187         gdt_p->access = ACC_PL_U|ACC_DATA_W;
  188                                         /* start as "not present" */
  189 
  190         /*
  191          * Set up the recovery routine pointer
  192          */
  193         fpe_recover_ptr.offset = fpe_recover - fpe_start;
  194         fpe_recover_ptr.segment = FPE_CS;
  195 
  196         /*
  197          * Set i386 to emulate coprocessor.
  198          */
  199         set_cr0((get_cr0() & ~CR0_MP) | CR0_EM);
  200 }
  201 
  202 /*
  203  * Enable FPE use for a new thread.
  204  * Allocates the FP save area.
  205  */
  206 boolean_t
  207 fp_emul_error(regs)
  208         struct i386_saved_state *regs;
  209 {
  210         register struct i386_fpsave_state *ifps;
  211         register vm_offset_t    start_va;
  212 
  213         if ((regs->err & 0xfffc) != (USER_FPREGS & ~SEL_PL))
  214             return FALSE;
  215 
  216         /*
  217          * Make the FPU save area user-accessible (by FPE)
  218          */
  219         ifps = current_thread()->pcb->ims.ifps;
  220         if (ifps == 0) {
  221             /*
  222              * No FP register state yet - allocate it.
  223              */
  224             fp_state_alloc();
  225             ifps = current_thread()->pcb->ims.ifps;
  226         }
  227             
  228         start_va = (vm_offset_t) &ifps->fp_save_state;
  229         set_user_access(current_map()->pmap,
  230                 start_va,
  231                 start_va + sizeof(struct i386_fp_save),
  232                 TRUE);
  233 
  234         /*
  235          * Enable FPE use for this thread
  236          */
  237         enable_fpe(ifps);
  238 
  239         return TRUE;
  240 }
  241 
  242 /*
  243  * Enable FPE use.  ASSUME that kernel does NOT use FPU
  244  * except to handle user exceptions.
  245  */
  246 void
  247 enable_fpe(ifps)
  248         register struct i386_fpsave_state *ifps;
  249 {
  250         struct real_descriptor *dp;
  251         vm_offset_t     start_va;
  252 
  253         dp = gdt_desc_p(cpu_number(), USER_FPREGS);
  254         start_va = (vm_offset_t)&ifps->fp_save_state;
  255 
  256         dp->base_low = start_va & 0xffff;
  257         dp->base_med = (start_va >> 16) & 0xff;
  258         dp->base_high = start_va >> 24;
  259         dp->access |= ACC_P;
  260 }
  261 
  262 void
  263 disable_fpe()
  264 {
  265         /*
  266          *      The kernel might be running with fs & gs segments
  267          *      which refer to USER_FPREGS, if we entered the kernel
  268          *      from a FP-using thread.  We have to clear these segments
  269          *      lest we get a Segment Not Present trap.  This would happen
  270          *      if the kernel took an interrupt or fault after clearing
  271          *      the present bit but before exiting to user space (which
  272          *      would reset fs & gs from the current user thread).
  273          */
  274 
  275         asm volatile("xorl %eax, %eax");
  276         asm volatile("movw %ax, %fs");
  277         asm volatile("movw %ax, %gs");
  278 
  279         gdt_desc_p(cpu_number(), USER_FPREGS)->access &= ~ACC_P;
  280 }
  281 
  282 void
  283 set_user_access(pmap, start, end, writable)
  284         pmap_t          pmap;
  285         vm_offset_t     start;
  286         vm_offset_t     end;
  287         boolean_t       writable;
  288 {
  289         register vm_offset_t    va;
  290         register pt_entry_t *   dirbase = pmap->dirbase;
  291         register pt_entry_t *   ptep;
  292         register pt_entry_t *   pdep;
  293 
  294         start = i386_trunc_page(start);
  295         end   = i386_round_page(end);
  296 
  297         for (va = start; va < end; va += I386_PGBYTES) {
  298 
  299             pdep = &dirbase[pdenum(va)];
  300             *pdep |= INTEL_PTE_USER;
  301             ptep = (pt_entry_t *)ptetokv(*pdep);
  302             ptep = &ptep[ptenum(va)];
  303             *ptep |= INTEL_PTE_USER;
  304             if (!writable)
  305                 *ptep &= ~INTEL_PTE_WRITE;
  306         }
  307 }
  308 
  309 /*
  310  * Route exception through emulator fixup routine if
  311  * it occured within the emulator.
  312  */
  313 extern void exception();
  314 
  315 void
  316 fpe_exception_fixup(exc, code, subcode)
  317         int     exc, code, subcode;
  318 {
  319         thread_t        thread = current_thread();
  320         pcb_t           pcb = thread->pcb;
  321 
  322         if (pcb->iss.efl & EFL_VM) {
  323             /*
  324              * The emulator doesn`t handle V86 mode.
  325              * If this is a GP fault on the emulator`s
  326              * code segment, change it to an FP not present
  327              * fault.
  328              */
  329             if (exc == EXC_BAD_INSTRUCTION
  330              && code == EXC_I386_GPFLT
  331              && subcode == FPE_CS + 1)
  332             {
  333                 exc = EXC_ARITHMETIC;   /* arithmetic error: */
  334                 code = EXC_I386_NOEXT;  /* no FPU */
  335                 subcode = 0;
  336             }
  337         }
  338         else
  339         if ((pcb->iss.cs & 0xfffc) == FPE_CS) {
  340             /*
  341              * Pass registers to emulator,
  342              * to let it fix them up.
  343              * The emulator fixup routine knows about
  344              * an i386_thread_state.
  345              */
  346             struct i386_thread_state    tstate;
  347             unsigned int                count;
  348 
  349             count = i386_THREAD_STATE_COUNT;
  350             (void) thread_getstatus(thread,
  351                                 i386_REGS_SEGS_STATE,
  352                                 (thread_state_t) &tstate,
  353                                 &count);
  354 
  355             /*
  356              * long call to emulator register recovery routine
  357              */
  358             asm volatile("pushl %0; lcall %1; addl $4,%%esp"
  359                         :
  360                         : "r" (&tstate),
  361                           "m" (*(char *)&fpe_recover_ptr) );
  362 
  363             (void) thread_setstatus(thread,
  364                                 i386_REGS_SEGS_STATE,
  365                                 (thread_state_t) &tstate,
  366                                 count);
  367             /*
  368              * In addition, check for a GP fault on 'int 16' in
  369              * the emulator, since the interrupt gate is protected.
  370              * If so, change it to an arithmetic error.
  371              */
  372             if (exc == EXC_BAD_INSTRUCTION
  373              && code == EXC_I386_GPFLT
  374              && subcode == 8*16+2)      /* idt[16] */
  375             {
  376                 exc = EXC_ARITHMETIC;
  377                 code = EXC_I386_EXTERR;
  378                 subcode = pcb->ims.ifps->fp_save_state.fp_status;
  379             }
  380         }
  381         exception(exc, code, subcode);
  382 }

Cache object: e7414191a8d6923c474d52830fb260a7


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