root/arch/i386/i386/gdt.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. setgdt
  2. gdt_init
  3. gdt_alloc_cpu
  4. gdt_init_cpu
  5. gdt_grow
  6. gdt_get_slot
  7. gdt_put_slot
  8. tss_alloc
  9. tss_free
  10. ldt_alloc
  11. ldt_free

    1 /*      $OpenBSD: gdt.c,v 1.27 2007/07/02 17:11:29 thib Exp $   */
    2 /*      $NetBSD: gdt.c,v 1.28 2002/12/14 09:38:50 junyoung Exp $        */
    3 
    4 /*-
    5  * Copyright (c) 1996, 1997 The NetBSD Foundation, Inc.
    6  * All rights reserved.
    7  *
    8  * This code is derived from software contributed to The NetBSD Foundation
    9  * by John T. Kohl and Charles M. Hannum.
   10  *
   11  * Redistribution and use in source and binary forms, with or without
   12  * modification, are permitted provided that the following conditions
   13  * are met:
   14  * 1. Redistributions of source code must retain the above copyright
   15  *    notice, this list of conditions and the following disclaimer.
   16  * 2. Redistributions in binary form must reproduce the above copyright
   17  *    notice, this list of conditions and the following disclaimer in the
   18  *    documentation and/or other materials provided with the distribution.
   19  * 3. All advertising materials mentioning features or use of this software
   20  *    must display the following acknowledgement:
   21  *        This product includes software developed by the NetBSD
   22  *        Foundation, Inc. and its contributors.
   23  * 4. Neither the name of The NetBSD Foundation nor the names of its
   24  *    contributors may be used to endorse or promote products derived
   25  *    from this software without specific prior written permission.
   26  *
   27  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   29  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   30  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   37  * POSSIBILITY OF SUCH DAMAGE.
   38  */
   39 
   40 /*
   41  * The GDT handling has two phases.  During the early lifetime of the
   42  * kernel there is a static gdt which will be stored in bootstrap_gdt.
   43  * Later, when the virtual memory is initialized, this will be
   44  * replaced with a dynamically resizable GDT (although, we will only
   45  * ever be growing it, there is almost no gain at all to compact it,
   46  * and it has proven to be a complicated thing to do, considering
   47  * parallel access, so it's just not worth the effort.
   48  *
   49  * The static GDT area will hold the initial requirement of NGDT descriptors.
   50  * The dynamic GDT will have a statically sized virtual memory area of size
   51  * GDTMAXPAGES, the physical area backing this will be allocated as needed
   52  * starting with the size needed for holding a copy of the bootstrap gdt.
   53  *
   54  * Every CPU in a system has its own copy of the GDT.  The only real difference
   55  * between the two are currently that there is a cpu-specific segment holding
   56  * the struct cpu_info of the processor, for simplicity at getting cpu_info
   57  * fields from assembly.  The boot processor will actually refer to the global
   58  * copy of the GDT as pointed to by the gdt variable.
   59  */
   60 
   61 #include <sys/cdefs.h>
   62 
   63 #include <sys/param.h>
   64 #include <sys/systm.h>
   65 #include <sys/proc.h>
   66 #include <sys/lock.h>
   67 #include <sys/user.h>
   68 #include <sys/rwlock.h>
   69 
   70 #include <uvm/uvm.h>
   71 
   72 #include <machine/gdt.h>
   73 
   74 union descriptor bootstrap_gdt[NGDT];
   75 union descriptor *gdt = bootstrap_gdt;
   76 
   77 int gdt_size;           /* total number of GDT entries */
   78 int gdt_next;           /* next available slot for sweeping */
   79 int gdt_free;           /* next free slot; terminated with GNULL_SEL */
   80 
   81 struct rwlock gdt_lock_store = RWLOCK_INITIALIZER("gdtlk");
   82 
   83 void gdt_grow(void);
   84 int gdt_get_slot(void);
   85 void gdt_put_slot(int);
   86 
   87 /*
   88  * Lock and unlock the GDT, to avoid races in case gdt_{ge,pu}t_slot() sleep
   89  * waiting for memory.
   90  */
   91 #define gdt_lock()                                      \
   92         do {                                            \
   93                 if (curproc != NULL)                    \
   94                         rw_enter_write(&gdt_lock_store);\
   95         } while (0)
   96 
   97 #define gdt_unlock()                                    \
   98         do {                                            \
   99                 if (curproc != NULL)                    \
  100                         rw_exit_write(&gdt_lock_store); \
  101         } while (0)
  102 
  103 /* XXX needs spinlocking if we ever mean to go finegrained. */
  104 void
  105 setgdt(int sel, void *base, size_t limit, int type, int dpl, int def32,
  106     int gran)
  107 {
  108         struct segment_descriptor *sd = &gdt[sel].sd;
  109         CPU_INFO_ITERATOR cii;
  110         struct cpu_info *ci;
  111 
  112         KASSERT(sel < gdt_size);
  113 
  114         setsegment(sd, base, limit, type, dpl, def32, gran);
  115         CPU_INFO_FOREACH(cii, ci)
  116                 if (ci->ci_gdt != NULL && ci->ci_gdt != gdt)
  117                         ci->ci_gdt[sel].sd = *sd;
  118 }
  119 
  120 /*
  121  * Initialize the GDT subsystem.  Called from autoconf().
  122  */
  123 void
  124 gdt_init()
  125 {
  126         size_t max_len, min_len;
  127         struct vm_page *pg;
  128         vaddr_t va;
  129         struct cpu_info *ci = &cpu_info_primary;
  130 
  131         max_len = MAXGDTSIZ * sizeof(union descriptor);
  132         min_len = MINGDTSIZ * sizeof(union descriptor);
  133 
  134         gdt_size = MINGDTSIZ;
  135         gdt_next = NGDT;
  136         gdt_free = GNULL_SEL;
  137 
  138         gdt = (union descriptor *)uvm_km_valloc(kernel_map, max_len);
  139         for (va = (vaddr_t)gdt; va < (vaddr_t)gdt + min_len; va += PAGE_SIZE) {
  140                 pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_ZERO);
  141                 if (pg == NULL)
  142                         panic("gdt_init: no pages");
  143                 pmap_kenter_pa(va, VM_PAGE_TO_PHYS(pg),
  144                     VM_PROT_READ | VM_PROT_WRITE);
  145         }
  146         bcopy(bootstrap_gdt, gdt, NGDT * sizeof(union descriptor));
  147         ci->ci_gdt = gdt;
  148         setsegment(&ci->ci_gdt[GCPU_SEL].sd, ci, sizeof(struct cpu_info)-1,
  149             SDT_MEMRWA, SEL_KPL, 0, 0);
  150 
  151         gdt_init_cpu(ci);
  152 }
  153 
  154 #ifdef MULTIPROCESSOR
  155 /*
  156  * Allocate shadow GDT for a slave cpu.
  157  */
  158 void
  159 gdt_alloc_cpu(struct cpu_info *ci)
  160 {
  161         int max_len = MAXGDTSIZ * sizeof(union descriptor);
  162         int min_len = MINGDTSIZ * sizeof(union descriptor);
  163 
  164         ci->ci_gdt = (union descriptor *)uvm_km_valloc(kernel_map, max_len);
  165         uvm_map_pageable(kernel_map, (vaddr_t)ci->ci_gdt,
  166             (vaddr_t)ci->ci_gdt + min_len, FALSE, FALSE);
  167         bzero(ci->ci_gdt, min_len);
  168         bcopy(gdt, ci->ci_gdt, gdt_size * sizeof(union descriptor));
  169         setsegment(&ci->ci_gdt[GCPU_SEL].sd, ci, sizeof(struct cpu_info)-1,
  170             SDT_MEMRWA, SEL_KPL, 0, 0);
  171 }
  172 #endif  /* MULTIPROCESSOR */
  173 
  174 
  175 /*
  176  * Load appropriate gdt descriptor; we better be running on *ci
  177  * (for the most part, this is how a cpu knows who it is).
  178  */
  179 void
  180 gdt_init_cpu(struct cpu_info *ci)
  181 {
  182         struct region_descriptor region;
  183 
  184         setregion(&region, ci->ci_gdt,
  185             MAXGDTSIZ * sizeof(union descriptor) - 1);
  186         lgdt(&region);
  187 }
  188 
  189 /*
  190  * Grow the GDT.
  191  */
  192 void
  193 gdt_grow()
  194 {
  195         size_t old_len, new_len;
  196         CPU_INFO_ITERATOR cii;
  197         struct cpu_info *ci;
  198         struct vm_page *pg;
  199         vaddr_t va;
  200 
  201         old_len = gdt_size * sizeof(union descriptor);
  202         gdt_size <<= 1;
  203         new_len = old_len << 1;
  204 
  205         CPU_INFO_FOREACH(cii, ci) {
  206                 for (va = (vaddr_t)(ci->ci_gdt) + old_len;
  207                      va < (vaddr_t)(ci->ci_gdt) + new_len;
  208                      va += PAGE_SIZE) {
  209                         while (
  210                             (pg =
  211                             uvm_pagealloc(NULL, 0, NULL, UVM_PGA_ZERO)) ==
  212                             NULL) {
  213                                 uvm_wait("gdt_grow");
  214                         }
  215                         pmap_kenter_pa(va, VM_PAGE_TO_PHYS(pg),
  216                             VM_PROT_READ | VM_PROT_WRITE);
  217                 }
  218         }
  219 }
  220 
  221 /*
  222  * Allocate a GDT slot as follows:
  223  * 1) If there are entries on the free list, use those.
  224  * 2) If there are fewer than gdt_size entries in use, there are free slots
  225  *    near the end that we can sweep through.
  226  * 3) As a last resort, we increase the size of the GDT, and sweep through
  227  *    the new slots.
  228  */
  229 int
  230 gdt_get_slot()
  231 {
  232         int slot;
  233 
  234         gdt_lock();
  235 
  236         if (gdt_free != GNULL_SEL) {
  237                 slot = gdt_free;
  238                 gdt_free = gdt[slot].gd.gd_selector;
  239         } else {
  240                 if (gdt_next >= gdt_size) {
  241                         if (gdt_size >= MAXGDTSIZ)
  242                                 panic("gdt_get_slot: out of GDT descriptors");
  243                         gdt_grow();
  244                 }
  245                 slot = gdt_next++;
  246         }
  247 
  248         gdt_unlock();
  249         return (slot);
  250 }
  251 
  252 /*
  253  * Deallocate a GDT slot, putting it on the free list.
  254  */
  255 void
  256 gdt_put_slot(int slot)
  257 {
  258 
  259         gdt_lock();
  260 
  261         gdt[slot].gd.gd_type = SDT_SYSNULL;
  262         gdt[slot].gd.gd_selector = gdt_free;
  263         gdt_free = slot;
  264 
  265         gdt_unlock();
  266 }
  267 
  268 int
  269 tss_alloc(struct pcb *pcb)
  270 {
  271         int slot;
  272 
  273         slot = gdt_get_slot();
  274         setgdt(slot, &pcb->pcb_tss, sizeof(struct pcb) - 1,
  275             SDT_SYS386TSS, SEL_KPL, 0, 0);
  276         return GSEL(slot, SEL_KPL);
  277 }
  278 
  279 void
  280 tss_free(int sel)
  281 {
  282 
  283         gdt_put_slot(IDXSEL(sel));
  284 }
  285 
  286 #ifdef USER_LDT
  287 /*
  288  * Caller must have pmap locked for both of these functions.
  289  */
  290 void
  291 ldt_alloc(struct pmap *pmap, union descriptor *ldt, size_t len)
  292 {
  293         int slot;
  294 
  295         slot = gdt_get_slot();
  296         setgdt(slot, ldt, len - 1, SDT_SYSLDT, SEL_KPL, 0, 0);
  297         pmap->pm_ldt_sel = GSEL(slot, SEL_KPL);
  298 }
  299 
  300 void
  301 ldt_free(struct pmap *pmap)
  302 {
  303         int slot;
  304 
  305         slot = IDXSEL(pmap->pm_ldt_sel);
  306 
  307         gdt_put_slot(slot);
  308 }
  309 #endif /* USER_LDT */

/* [<][>][^][v][top][bottom][index][help] */