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/user_ldt.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) 1993,1992,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:        user_ldt.c,v $
   30  * Revision 2.4  93/11/17  16:40:43  dbg
   31  *      Fixed bug in i386_set_ldt: descriptors were being copied into
   32  *      the old LDT (which was sometimes NULL), thus being thrown away
   33  *      (!).
   34  *      [93/09/10            dbg]
   35  *      Removed lint.
   36  *      [93/06/17            dbg]
   37  * 
   38  * Revision 2.3  92/02/23  19:45:12  elf
   39  *      Eliminate keep_wired argument from vm_map_copyin().
   40  *      [92/02/23            danner]
   41  * 
   42  * Revision 2.2  92/01/03  20:10:02  dbg
   43  *      Created.
   44  *      [91/08/20            dbg]
   45  * 
   46  */
   47 
   48 /*
   49  * User LDT management.
   50  * Each thread in a task may have its own LDT.
   51  */
   52 
   53 #include <kern/kalloc.h>
   54 #include <kern/memory.h>
   55 #include <kern/thread.h>
   56 
   57 #include <vm/vm_kern.h>
   58 
   59 #include <i386/seg.h>
   60 #include <i386/thread.h>
   61 #include <i386/user_ldt.h>
   62 
   63 char    acc_type[8][3] = {
   64     /*  code    stack   data */
   65     {   0,      0,      1       },      /* data */
   66     {   0,      1,      1       },      /* data, writable */
   67     {   0,      0,      1       },      /* data, expand-down */
   68     {   0,      1,      1       },      /* data, writable, expand-down */
   69     {   1,      0,      0       },      /* code */
   70     {   1,      0,      1       },      /* code, readable */
   71     {   1,      0,      0       },      /* code, conforming */
   72     {   1,      0,      1       },      /* code, readable, conforming */
   73 };
   74 
   75 extern struct fake_descriptor ldt[];    /* for system call gate */
   76 
   77 boolean_t selector_check(
   78         thread_t        thread,
   79         int             sel,
   80         int             type)   /* code, stack, data */
   81 {
   82         struct user_ldt *ldt;
   83         int     access;
   84 
   85         ldt = thread->pcb->ims.ldt;
   86         if (ldt == 0) {
   87             switch (type) {
   88                 case S_CODE:
   89                     return sel == USER_CS;
   90                 case S_STACK:
   91                     return sel == USER_DS;
   92                 case S_DATA:
   93                     return sel == 0 ||
   94                            sel == USER_CS ||
   95                            sel == USER_DS;
   96             }
   97         }
   98 
   99         if (type != S_DATA && sel == 0)
  100             return FALSE;
  101         if ((sel & (SEL_LDT|SEL_PL)) != (SEL_LDT|SEL_PL_U)
  102           || sel > ldt->desc.limit_low)
  103                 return FALSE;
  104 
  105         access = ldt->ldt[sel_idx(sel)].access;
  106         
  107         if ((access & (ACC_P|ACC_PL|ACC_TYPE_USER))
  108                 != (ACC_P|ACC_PL_U|ACC_TYPE_USER))
  109             return FALSE;
  110                 /* present, pl == pl.user, not system */
  111 
  112         return acc_type[(access & 0xe)>>1][type];
  113 }
  114 
  115 /*
  116  * Add the descriptors to the LDT, starting with
  117  * the descriptor for 'first_selector'.
  118  */
  119 kern_return_t
  120 i386_set_ldt(
  121         thread_t        thread,
  122         int             first_selector,
  123         struct real_descriptor *desc_list,
  124         natural_t       count,
  125         boolean_t       desc_list_inline)
  126 {
  127         user_ldt_t      new_ldt, old_ldt, temp;
  128         struct real_descriptor *dp;
  129         int             i;
  130         pcb_t           pcb;
  131         vm_size_t       ldt_size_needed;
  132         int             first_desc = sel_idx(first_selector);
  133         vm_map_copy_t   old_copy_object;
  134 
  135         if (thread == THREAD_NULL)
  136             return KERN_INVALID_ARGUMENT;
  137         if (first_desc < 0 || first_desc > 8191)
  138             return KERN_INVALID_ARGUMENT;
  139         if (first_desc + count >= 8192)
  140             return KERN_INVALID_ARGUMENT;
  141 
  142         /*
  143          * If desc_list is not inline, it is in copyin form.
  144          * We must copy it out to the kernel map, and wire
  145          * it down (we touch it while the PCB is locked).
  146          *
  147          * We make a copy of the copyin object, and clear
  148          * out the old one, so that returning KERN_INVALID_ARGUMENT
  149          * will not try to deallocate the data twice.
  150          */
  151         if (!desc_list_inline) {
  152             kern_return_t       kr;
  153             vm_offset_t         dst_addr;
  154 
  155             old_copy_object = (vm_map_copy_t) desc_list;
  156 
  157             kr = vm_map_copyout(ipc_kernel_map, &dst_addr,
  158                                 vm_map_copy_copy(old_copy_object));
  159             if (kr != KERN_SUCCESS)
  160                 return kr;
  161 
  162             (void) vm_map_pageable(ipc_kernel_map,
  163                         dst_addr,
  164                         dst_addr + count * sizeof(struct real_descriptor),
  165                         VM_PROT_READ|VM_PROT_WRITE);
  166             desc_list = (struct real_descriptor *)dst_addr;
  167         }
  168 
  169         for (i = 0, dp = desc_list;
  170              i < count;
  171              i++, dp++)
  172         {
  173             switch (dp->access & ~ACC_A) {
  174                 case 0:
  175                 case ACC_P:
  176                     /* valid empty descriptor */
  177                     break;
  178                 case ACC_P | ACC_CALL_GATE:
  179                     /* Mach kernel call */
  180                     *dp = *(struct real_descriptor *)
  181                                 &ldt[sel_idx(USER_SCALL)];
  182                     break;
  183                 case ACC_P | ACC_PL_U | ACC_DATA:
  184                 case ACC_P | ACC_PL_U | ACC_DATA_W:
  185                 case ACC_P | ACC_PL_U | ACC_DATA_E:
  186                 case ACC_P | ACC_PL_U | ACC_DATA_EW:
  187                 case ACC_P | ACC_PL_U | ACC_CODE:
  188                 case ACC_P | ACC_PL_U | ACC_CODE_R:
  189                 case ACC_P | ACC_PL_U | ACC_CODE_C:
  190                 case ACC_P | ACC_PL_U | ACC_CODE_CR:
  191                 case ACC_P | ACC_PL_U | ACC_CALL_GATE_16:
  192                 case ACC_P | ACC_PL_U | ACC_CALL_GATE:
  193                     break;
  194                 default:
  195                     return KERN_INVALID_ARGUMENT;
  196             }
  197         }
  198         ldt_size_needed = sizeof(struct real_descriptor)
  199                         * (first_desc + count - 1);
  200 
  201         pcb = thread->pcb;
  202         new_ldt = 0;
  203     Retry:
  204         simple_lock(&pcb->lock);
  205         old_ldt = pcb->ims.ldt;
  206         if (old_ldt == 0 ||
  207             old_ldt->desc.limit_low + 1 < ldt_size_needed)
  208         {
  209             /*
  210              * No old LDT, or not big enough
  211              */
  212             if (new_ldt == 0) {
  213                 simple_unlock(&pcb->lock);
  214 
  215                 new_ldt = (user_ldt_t)
  216                                 kalloc(ldt_size_needed
  217                                        + sizeof(struct real_descriptor));
  218                 new_ldt->desc.limit_low   = ldt_size_needed - 1;
  219                 new_ldt->desc.limit_high  = 0;
  220                 new_ldt->desc.base_low    = ((vm_offset_t)new_ldt) & 0xffff;
  221                 new_ldt->desc.base_med    = (((vm_offset_t)new_ldt) >> 16)
  222                                                  & 0xff;
  223                 new_ldt->desc.base_high   = ((vm_offset_t)new_ldt) >> 24;
  224                 new_ldt->desc.access      = ACC_P | ACC_LDT;
  225                 new_ldt->desc.granularity = 0;
  226 
  227                 goto Retry;
  228             }
  229 
  230             /*
  231              * Have new LDT.  Copy descriptors from old to new.
  232              */
  233             if (old_ldt)
  234                 bcopy(&old_ldt->ldt[0],
  235                       &new_ldt->ldt[0],
  236                       old_ldt->desc.limit_low + 1);
  237 
  238             temp = old_ldt;
  239             old_ldt = new_ldt;  /* use new LDT from now on */
  240             new_ldt = temp;     /* discard old LDT */
  241 
  242             pcb->ims.ldt = old_ldt;     /* new LDT for thread */
  243         }
  244 
  245         /*
  246          * Install new descriptors.
  247          */
  248         bcopy(desc_list,
  249               &new_ldt->ldt[first_desc],
  250               count * sizeof(struct real_descriptor));
  251 
  252         simple_unlock(&pcb->lock);
  253 
  254         if (old_ldt)
  255             kfree((vm_offset_t)old_ldt,
  256                   old_ldt->desc.limit_low + 1
  257                 + sizeof(struct real_descriptor));
  258 
  259         /*
  260          * Free the descriptor list, if it was
  261          * out-of-line.  Also discard the original
  262          * copy object for it.
  263          */
  264         if (!desc_list_inline) {
  265             (void) kmem_free(ipc_kernel_map,
  266                         (vm_offset_t) desc_list,
  267                         count * sizeof(struct real_descriptor));
  268             vm_map_copy_discard(old_copy_object);
  269         }
  270 
  271         return KERN_SUCCESS;
  272 }
  273 
  274 kern_return_t
  275 i386_get_ldt(
  276         thread_t        thread,
  277         int             first_selector,
  278         int             selector_count,         /* number wanted */
  279         struct real_descriptor **desc_list,     /* in/out */
  280         natural_t       *count)                 /* in/out */
  281 {
  282         struct user_ldt *user_ldt;
  283         pcb_t           pcb = thread->pcb;
  284         int             first_desc = sel_idx(first_selector);
  285         unsigned int    ldt_count;
  286         vm_size_t       ldt_size;
  287         vm_size_t       size, size_needed;
  288         vm_offset_t     addr;
  289 
  290         if (thread == THREAD_NULL)
  291             return KERN_INVALID_ARGUMENT;
  292         if (first_desc < 0 || first_desc > 8191)
  293             return KERN_INVALID_ARGUMENT;
  294         if (first_desc + selector_count >= 8192)
  295             return KERN_INVALID_ARGUMENT;
  296 
  297         addr = 0;
  298         size = 0;
  299 
  300         for (;;) {
  301             simple_lock(&pcb->lock);
  302             user_ldt = pcb->ims.ldt;
  303             if (user_ldt == 0) {
  304                 simple_unlock(&pcb->lock);
  305                 if (addr)
  306                     kmem_free(ipc_kernel_map, addr, size);
  307                 *count = 0;
  308                 return KERN_SUCCESS;
  309             }
  310 
  311             /*
  312              * Find how many descriptors we should return.
  313              */
  314             ldt_count = (user_ldt->desc.limit_low + 1) /
  315                         sizeof (struct real_descriptor);
  316             ldt_count -= first_desc;
  317             if (ldt_count > selector_count)
  318                 ldt_count = selector_count;
  319 
  320             ldt_size = ldt_count * sizeof(struct real_descriptor);
  321 
  322             /*
  323              * Do we have the memory we need?
  324              */
  325             if (ldt_count <= *count)
  326                 break;          /* fits in-line */
  327 
  328             size_needed = round_page(ldt_size);
  329             if (size_needed <= size)
  330                 break;
  331 
  332             /*
  333              * Unlock the pcb and allocate more memory
  334              */
  335             simple_unlock(&pcb->lock);
  336 
  337             if (size != 0)
  338                 kmem_free(ipc_kernel_map, addr, size);
  339 
  340             size = size_needed;
  341 
  342             if (kmem_alloc(ipc_kernel_map, &addr, size)
  343                         != KERN_SUCCESS)
  344                 return KERN_RESOURCE_SHORTAGE;
  345         }
  346 
  347         /*
  348          * copy out the descriptors
  349          */
  350         bcopy(&user_ldt[first_desc],
  351               *desc_list,
  352               ldt_size);
  353         *count = ldt_count;
  354         simple_unlock(&pcb->lock);
  355 
  356         if (addr) {
  357             vm_size_t           size_used, size_left;
  358             vm_map_copy_t       memory;
  359 
  360             /*
  361              * Free any unused memory beyond the end of the last page used
  362              */
  363             size_used = round_page(ldt_size);
  364             if (size_used != size)
  365                 kmem_free(ipc_kernel_map,
  366                         addr + size_used, size - size_used);
  367 
  368             /*
  369              * Zero the remainder of the page being returned.
  370              */
  371             size_left = size_used - ldt_size;
  372             if (size_left > 0)
  373                 bzero((void *)addr + ldt_size, size_left);
  374 
  375             /*
  376              * Make memory into copyin form - this unwires it.
  377              */
  378             (void) vm_map_copyin(ipc_kernel_map, addr, size_used, TRUE, &memory);
  379             *desc_list = (struct real_descriptor *)memory;
  380         }
  381 
  382         return KERN_SUCCESS;
  383 }
  384 
  385 void
  386 user_ldt_free(
  387         user_ldt_t      user_ldt)
  388 {
  389         kfree((vm_offset_t)user_ldt,
  390                 user_ldt->desc.limit_low + 1
  391                 + sizeof(struct real_descriptor));
  392 }

Cache object: 648a7782d56767079706fd7a75ec88ba


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