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/kern/syscall_emulation.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,1990,1989,1988,1987 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:        syscall_emulation.c,v $
   29  * Revision 2.20  93/11/17  17:25:24  dbg
   30  *      ANSI-fied.
   31  *      [93/06/16            dbg]
   32  * 
   33  * Revision 2.19  92/08/03  17:39:21  jfriedl
   34  *      removed silly prototypes
   35  *      [92/08/02            jfriedl]
   36  * 
   37  * Revision 2.18  92/05/21  17:15:54  jfriedl
   38  *      Added init to 'new_start' and 'new_end' to quiet gcc warnings.
   39  *      [92/05/16            jfriedl]
   40  * 
   41  * Revision 2.17  92/03/04  20:20:33  rpd
   42  *      Use vector_size instead of size_used in vm_map_copyin.
   43  *      [92/03/04  14:54:49  jsb]
   44  * 
   45  * Revision 2.16  92/03/03  12:28:57  rpd
   46  *      Added syscall_emulation_sync.
   47  *      [92/03/03            rpd]
   48  * 
   49  * Revision 2.15  92/02/23  19:50:06  elf
   50  *      Eliminate keep_wired argument from vm_map_copyin()
   51  *      [92/02/23            danner]
   52  * 
   53  * Revision 2.14  92/01/03  20:15:18  dbg
   54  *      Change new versions of calls to pass dispatch table out-of-line.
   55  *      Old calls (xxx_*) use inline data.
   56  *      [92/01/03            dbg]
   57  * 
   58  *      Don't round up allocated vector size.
   59  *      [92/01/02            dbg]
   60  * 
   61  *      Remove fixed lower and upper bounds on emulated system call
   62  *      table.  Now everyone can get as much as the MiG interface will
   63  *      allow.
   64  *      [91/12/18            dbg]
   65  * 
   66  * Revision 2.13  91/12/13  13:43:31  jsb
   67  *      Increased eml_max_emulate_count for OSF/1+ server support.
   68  * 
   69  * Revision 2.12  91/11/15  14:08:35  rpd
   70  *      Rewrote task_set_emulation_vector for greater clarity.
   71  *      Fixed bcopy bug in task_get_emulation_vector.
   72  *      Simplified and removed debugging printf from task_set_emulation.
   73  *      [91/09/24  14:15:28  jsb]
   74  * 
   75  * Revision 2.11  91/10/09  16:11:47  af
   76  *      Everyone gets as many syscalls as mips, needed
   77  *      on e.g. vax and sun for AFS support.
   78  *      [91/10/07            af]
   79  * 
   80  * Revision 2.10  91/08/24  12:00:20  af
   81  *      Cast tags for bcopy
   82  *      [91/08/14            rvb]
   83  * 
   84  * Revision 2.9  91/06/25  10:29:13  rpd
   85  *      Fixed the includes.
   86  *      [91/06/24            rpd]
   87  * 
   88  * Revision 2.8  91/06/06  17:07:33  jsb
   89  *      Added task_get_emulation_vector, task_set_emulation_vector.
   90  *      Task_set_emulation is now a call to task_set_emulation_vector.
   91  *      [91/05/24  18:30:16  jsb]
   92  * 
   93  * Revision 2.7  91/05/18  14:33:39  rpd
   94  *      Fixed eml_task_deallocate to always unlock.
   95  *      [91/05/02            rpd]
   96  * 
   97  * Revision 2.6  91/05/14  16:47:09  mrt
   98  *      Correcting copyright
   99  * 
  100  * Revision 2.5  91/02/05  17:29:26  mrt
  101  *      Changed to new Mach copyright
  102  *      [91/02/01  16:17:54  mrt]
  103  * 
  104  * Revision 2.3  90/12/05  20:42:15  af
  105  *      I beg your pardon, Ultrix uses up to syscall #257 inclusive.
  106  *      [90/12/03  22:57:35  af]
  107  * 
  108  * Revision 2.2  89/11/29  14:09:18  af
  109  *      For mips, grow the max syscall limit, as Ultrix uses up to 256.
  110  *      Rcs-ed.
  111  *      [89/11/16            af]
  112  * 
  113  */
  114 
  115 #include <mach/error.h>
  116 #include <mach/vm_param.h>
  117 #include <kern/syscall_emulation.h>
  118 #include <kern/task.h>
  119 #include <kern/kalloc.h>
  120 #include <kern/memory.h>
  121 #include <vm/vm_kern.h>
  122 #include <machine/thread.h>     /* for syscall_emulation_sync */
  123 
  124 
  125 
  126 /*
  127  * WARNING:
  128  * This code knows that kalloc() allocates memory most efficiently
  129  * in sizes that are powers of 2, and asks for those sizes.
  130  */
  131 
  132 /*
  133  * Go from number of entries to size of struct eml_dispatch and back.
  134  */
  135 #define base_size       (sizeof(struct eml_dispatch) - sizeof(eml_routine_t))
  136 #define count_to_size(count) \
  137         (base_size + sizeof(vm_offset_t) * (count))
  138 
  139 #define size_to_count(size) \
  140         ( ((size) - base_size) / sizeof(vm_offset_t) )
  141 
  142 /*
  143  *  eml_init:   initialize user space emulation code
  144  */
  145 void eml_init(void)
  146 {
  147 }
  148 
  149 /*
  150  * eml_task_reference() [Exported]
  151  *
  152  *      Bumps the reference count on the common emulation
  153  *      vector.
  154  */
  155 
  156 void eml_task_reference(
  157         task_t  task,
  158         task_t  parent)
  159 {
  160         register eml_dispatch_t eml;
  161 
  162         if (parent == TASK_NULL)
  163             eml = EML_DISPATCH_NULL;
  164         else
  165             eml = parent->eml_dispatch;
  166 
  167         if (eml != EML_DISPATCH_NULL) {
  168             simple_lock(&eml->lock);
  169             eml->ref_count++;
  170             simple_unlock(&eml->lock);
  171         }
  172         task->eml_dispatch = eml;
  173 }
  174 
  175 
  176 /*
  177  * eml_task_deallocate() [Exported]
  178  *
  179  *      Cleans up after the emulation code when a process exits.
  180  */
  181  
  182 void eml_task_deallocate(
  183         task_t task)
  184 {
  185         register eml_dispatch_t eml;
  186 
  187         eml = task->eml_dispatch;
  188         if (eml != EML_DISPATCH_NULL) {
  189             int count;
  190 
  191             simple_lock(&eml->lock);
  192             count = --eml->ref_count;
  193             simple_unlock(&eml->lock);
  194 
  195             if (count == 0)
  196                 kfree((vm_offset_t)eml, count_to_size(eml->disp_count));
  197         }
  198 }
  199 
  200 /*
  201  *   task_set_emulation_vector:  [Server Entry]
  202  *   set a list of emulated system calls for this task.
  203  */
  204 kern_return_t
  205 task_set_emulation_vector_internal(
  206         task_t                  task,
  207         int                     vector_start,
  208         emulation_vector_t      emulation_vector,
  209         natural_t               emulation_vector_count)
  210 {
  211         eml_dispatch_t  cur_eml, new_eml, old_eml;
  212         vm_size_t       new_size;
  213         int             cur_start, cur_end;
  214         int             new_start = 0, new_end = 0;
  215         int             vector_end;
  216 
  217         if (task == TASK_NULL)
  218                 return KERN_INVALID_ARGUMENT;
  219 
  220         vector_end = vector_start + emulation_vector_count;
  221 
  222         /*
  223          * We try to re-use the existing emulation vector
  224          * if possible.  We can reuse the vector if it
  225          * is not shared with another task and if it is
  226          * large enough to contain the entries we are
  227          * supplying.
  228          *
  229          * We must grab the lock on the task to check whether
  230          * there is an emulation vector.
  231          * If the vector is shared or not large enough, we
  232          * need to drop the lock and allocate a new emulation
  233          * vector.
  234          *
  235          * While the lock is dropped, the emulation vector
  236          * may be released by all other tasks (giving us
  237          * exclusive use), or may be enlarged by another
  238          * task_set_emulation_vector call.  Therefore,
  239          * after allocating the new emulation vector, we
  240          * must grab the lock again to check whether we
  241          * really need the new vector we just allocated.
  242          *
  243          * Since an emulation vector cannot be altered
  244          * if it is in use by more than one task, the
  245          * task lock is sufficient to protect the vector`s
  246          * start, count, and contents.  The lock in the
  247          * vector protects only the reference count.
  248          */
  249 
  250         old_eml = EML_DISPATCH_NULL;    /* vector to discard */
  251         new_eml = EML_DISPATCH_NULL;    /* new vector */
  252 
  253         for (;;) {
  254             /*
  255              * Find the current emulation vector.
  256              * See whether we can overwrite it.
  257              */
  258             task_lock(task);
  259             cur_eml = task->eml_dispatch;
  260             if (cur_eml != EML_DISPATCH_NULL) {
  261                 cur_start = cur_eml->disp_min;
  262                 cur_end   = cur_eml->disp_count + cur_start;
  263 
  264                 simple_lock(&cur_eml->lock);
  265                 if (cur_eml->ref_count == 1 &&
  266                     cur_start <= vector_start &&
  267                     cur_end >= vector_end)
  268                 {
  269                     /*
  270                      * Can use the existing emulation vector.
  271                      * Discard any new one we allocated.
  272                      */
  273                     simple_unlock(&cur_eml->lock);
  274                     old_eml = new_eml;
  275                     break;
  276                 }
  277 
  278                 if (new_eml != EML_DISPATCH_NULL &&
  279                     new_start <= cur_start &&
  280                     new_end >= cur_end)
  281                 {
  282                     /*
  283                      * A new vector was allocated, and it is large enough
  284                      * to hold all the entries from the current vector.
  285                      * Copy the entries to the new emulation vector,
  286                      * deallocate the current one, and use the new one.
  287                      */
  288                     bcopy(&cur_eml->disp_vector[0],
  289                           &new_eml->disp_vector[cur_start-new_start],
  290                           cur_eml->disp_count * sizeof(vm_offset_t));
  291 
  292                     if (--cur_eml->ref_count == 0)
  293                         old_eml = cur_eml;      /* discard old vector */
  294                     simple_unlock(&cur_eml->lock);
  295 
  296                     task->eml_dispatch = new_eml;
  297                     syscall_emulation_sync(task);
  298                     cur_eml = new_eml;
  299                     break;
  300                 }
  301                 simple_unlock(&cur_eml->lock);
  302 
  303                 /*
  304                  * Need a new emulation vector.
  305                  * Ensure it will hold all the entries from
  306                  * both the old and new emulation vectors.
  307                  */
  308                 new_start = vector_start;
  309                 if (new_start > cur_start)
  310                     new_start = cur_start;
  311                 new_end = vector_end;
  312                 if (new_end < cur_end)
  313                     new_end = cur_end;
  314             }
  315             else {
  316                 /*
  317                  * There is no current emulation vector.
  318                  * If a new one was allocated, use it.
  319                  */
  320                 if (new_eml != EML_DISPATCH_NULL) {
  321                     task->eml_dispatch = new_eml;
  322                     cur_eml = new_eml;
  323                     break;
  324                 }
  325 
  326                 /*
  327                  * Compute the size needed for the new vector.
  328                  */
  329                 new_start = vector_start;
  330                 new_end = vector_end;
  331             }
  332 
  333             /*
  334              * Have no vector (or one that is no longer large enough).
  335              * Drop all the locks and allocate a new vector.
  336              * Repeat the loop to check whether the old vector was
  337              * changed while we didn`t hold the locks.
  338              */
  339 
  340             task_unlock(task);
  341 
  342             if (new_eml != EML_DISPATCH_NULL)
  343                 kfree((vm_offset_t)new_eml, count_to_size(new_eml->disp_count));
  344 
  345             new_size = count_to_size(new_end - new_start);
  346             new_eml = (eml_dispatch_t) kalloc(new_size);
  347 
  348             bzero(new_eml, new_size);
  349             simple_lock_init(&new_eml->lock);
  350             new_eml->ref_count = 1;
  351             new_eml->disp_min   = new_start;
  352             new_eml->disp_count = new_end - new_start;
  353 
  354             continue;
  355         }
  356 
  357         /*
  358          * We have the emulation vector.
  359          * Install the new emulation entries.
  360          */
  361         bcopy(&emulation_vector[0],
  362               &cur_eml->disp_vector[vector_start - cur_eml->disp_min],
  363               emulation_vector_count * sizeof(vm_offset_t));
  364 
  365         task_unlock(task);
  366 
  367         /*
  368          * Discard any old emulation vector we don`t need.
  369          */
  370         if (old_eml)
  371             kfree((vm_offset_t) old_eml, count_to_size(old_eml->disp_count));
  372 
  373         return KERN_SUCCESS;
  374 }
  375 
  376 /*
  377  *      task_set_emulation_vector:  [Server Entry]
  378  *
  379  *      Set the list of emulated system calls for this task.
  380  *      The list is out-of-line.
  381  */
  382 kern_return_t
  383 task_set_emulation_vector(
  384         task_t                  task,
  385         int                     vector_start,
  386         emulation_vector_t      emulation_vector,
  387         natural_t               emulation_vector_count)
  388 {
  389         kern_return_t           kr;
  390         vm_offset_t             emul_vector_addr;
  391 
  392         if (task == TASK_NULL)
  393             return KERN_INVALID_ARGUMENT;
  394 
  395         /*
  396          *      The emulation vector is really a vm_map_copy_t.
  397          */
  398         kr = vm_map_copyout(ipc_kernel_map, &emul_vector_addr,
  399                         (vm_map_copy_t) emulation_vector);
  400         if (kr != KERN_SUCCESS)
  401             return kr;
  402 
  403         /*
  404          *      Do the work.
  405          */
  406         kr = task_set_emulation_vector_internal(
  407                         task,
  408                         vector_start,
  409                         (emulation_vector_t) emul_vector_addr,
  410                         emulation_vector_count);
  411 
  412         /*
  413          *      Discard the memory
  414          */
  415         (void) kmem_free(ipc_kernel_map,
  416                          emul_vector_addr,
  417                          emulation_vector_count * sizeof(eml_dispatch_t));
  418 
  419         return kr;
  420 }
  421 
  422 /*
  423  *      Compatibility entry.  Vector is passed inline.
  424  */
  425 kern_return_t
  426 xxx_task_set_emulation_vector(task, vector_start, emulation_vector,
  427                           emulation_vector_count)
  428         task_t                  task;
  429         int                     vector_start;
  430         emulation_vector_t      emulation_vector;
  431         unsigned int            emulation_vector_count;
  432 {
  433         return task_set_emulation_vector_internal(
  434                         task,
  435                         vector_start,
  436                         emulation_vector,
  437                         emulation_vector_count);
  438 }
  439 
  440 /*
  441  *      task_get_emulation_vector: [Server Entry]
  442  *
  443  *      Get the list of emulated system calls for this task.
  444  *      List is returned out-of-line.
  445  */
  446 kern_return_t
  447 task_get_emulation_vector(
  448         task_t                  task,
  449         int                     *vector_start,                  /* out */
  450         emulation_vector_t      *emulation_vector,              /* out */
  451         natural_t               *emulation_vector_count)        /* out */
  452 {
  453         eml_dispatch_t          eml;
  454         vm_size_t               vector_size, size;
  455         vm_offset_t             addr;
  456 
  457         if (task == TASK_NULL)
  458             return KERN_INVALID_ARGUMENT;
  459 
  460         addr = 0;
  461         size = 0;
  462 
  463         for(;;) {
  464             vm_size_t   size_needed;
  465 
  466             task_lock(task);
  467             eml = task->eml_dispatch;
  468             if (eml == EML_DISPATCH_NULL) {
  469                 task_unlock(task);
  470                 if (addr)
  471                     (void) kmem_free(ipc_kernel_map, addr, size);
  472                 *vector_start = 0;
  473                 *emulation_vector = 0;
  474                 *emulation_vector_count = 0;
  475                 return KERN_SUCCESS;
  476             }
  477 
  478             /*
  479              * Do we have the memory we need?
  480              */
  481             vector_size = eml->disp_count * sizeof(vm_offset_t);
  482 
  483             size_needed = round_page(vector_size);
  484             if (size_needed <= size)
  485                 break;
  486 
  487             /*
  488              * If not, unlock the task and allocate more memory.
  489              */
  490             task_unlock(task);
  491 
  492             if (size != 0)
  493                 kmem_free(ipc_kernel_map, addr, size);
  494 
  495             size = size_needed;
  496             if (kmem_alloc(ipc_kernel_map, &addr, size) != KERN_SUCCESS)
  497                 return KERN_RESOURCE_SHORTAGE;
  498         }
  499 
  500         /*
  501          * Copy out the dispatch addresses
  502          */
  503         *vector_start = eml->disp_min;
  504         *emulation_vector_count = eml->disp_count;
  505         bcopy(eml->disp_vector,
  506               (void *) addr,
  507               vector_size);
  508 
  509         /*
  510          * Unlock the task and free any memory we did not need
  511          */
  512         task_unlock(task);
  513 
  514     {
  515         vm_size_t       size_used, size_left;
  516         vm_map_copy_t   memory;
  517 
  518         /*
  519          * Free any unused memory beyond the end of the last page used
  520          */
  521         size_used = round_page(vector_size);
  522         if (size_used != size)
  523             (void) kmem_free(ipc_kernel_map,
  524                              addr + size_used,
  525                              size - size_used);
  526 
  527         /*
  528          * Zero the remainder of the page being returned.
  529          */
  530         size_left = size_used - vector_size;
  531         if (size_left > 0)
  532             bzero((void *) (addr + vector_size), size_left);
  533 
  534         /*
  535          * Make memory into copyin form - this unwires it.
  536          */
  537         (void) vm_map_copyin(ipc_kernel_map, addr, vector_size, TRUE, &memory);
  538 
  539         *emulation_vector = (emulation_vector_t) memory;
  540     }
  541 
  542         return KERN_SUCCESS;
  543 }
  544 
  545 /*
  546  *      xxx_task_get_emulation:  [Server Entry]
  547  *      get the list of emulated system calls for this task.
  548  *      Compatibility code: return list in-line.
  549  */
  550 kern_return_t
  551 xxx_task_get_emulation_vector(
  552         task_t                  task,
  553         int                     *vector_start,
  554         emulation_vector_t      emulation_vector, /* pointer to OUT array */
  555         natural_t               *emulation_vector_count)        /*IN/OUT*/
  556 {
  557         register eml_dispatch_t eml;
  558 
  559         if (task == TASK_NULL)
  560                 return KERN_INVALID_ARGUMENT;
  561 
  562         task_lock(task);
  563 
  564         eml = task->eml_dispatch;
  565         if (eml == EML_DISPATCH_NULL) {
  566                 task_unlock(task);
  567                 *vector_start = 0;
  568                 *emulation_vector_count = 0;
  569                 return KERN_SUCCESS;
  570         }
  571 
  572         simple_lock(&eml->lock);
  573 
  574         if (*emulation_vector_count < eml->disp_count) {
  575                 simple_unlock(&eml->lock);
  576                 task_unlock(task);
  577                 return KERN_INVALID_ARGUMENT;
  578         }
  579 
  580         *vector_start = eml->disp_min;
  581         *emulation_vector_count = eml->disp_count;
  582         bcopy(eml->disp_vector,
  583               emulation_vector,
  584               *emulation_vector_count * sizeof(vm_offset_t));
  585         simple_unlock(&eml->lock);
  586 
  587         task_unlock(task);
  588 
  589         return KERN_SUCCESS;
  590 }
  591 
  592 /*
  593  *   task_set_emulation:  [Server Entry]
  594  *   set up for user space emulation of syscalls within this task.
  595  */
  596 kern_return_t task_set_emulation(
  597         task_t          task,
  598         vm_offset_t     routine_entry_pt,
  599         int             routine_number)
  600 {
  601         return task_set_emulation_vector_internal(task, routine_number,
  602                                          &routine_entry_pt, 1);
  603 }

Cache object: b14de6ec8427ca790f1870a4cceeaed4


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