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

Cache object: d12a7ecf64f979f3715f2e76e3d71b69


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