diff options
author | Thomas Bushnell <thomas@gnu.org> | 1997-02-25 21:28:37 +0000 |
---|---|---|
committer | Thomas Bushnell <thomas@gnu.org> | 1997-02-25 21:28:37 +0000 |
commit | f07a4c844da9f0ecae5bbee1ab94be56505f26f7 (patch) | |
tree | 12b07c7e578fc1a5f53dbfde2632408491ff2a70 /kern/syscall_emulation.c | |
download | gnumach-f07a4c844da9f0ecae5bbee1ab94be56505f26f7.tar.gz gnumach-f07a4c844da9f0ecae5bbee1ab94be56505f26f7.tar.bz2 gnumach-f07a4c844da9f0ecae5bbee1ab94be56505f26f7.zip |
Initial source
Diffstat (limited to 'kern/syscall_emulation.c')
-rw-r--r-- | kern/syscall_emulation.c | 518 |
1 files changed, 518 insertions, 0 deletions
diff --git a/kern/syscall_emulation.c b/kern/syscall_emulation.c new file mode 100644 index 00000000..5443a33a --- /dev/null +++ b/kern/syscall_emulation.c @@ -0,0 +1,518 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +#include <mach/error.h> +#include <mach/vm_param.h> +#include <kern/syscall_emulation.h> +#include <kern/task.h> +#include <kern/kalloc.h> +#include <vm/vm_kern.h> + +/* XXX */ +#define syscall_emulation_sync(task) + + + +/* + * WARNING: + * This code knows that kalloc() allocates memory most efficiently + * in sizes that are powers of 2, and asks for those sizes. + */ + +/* + * Go from number of entries to size of struct eml_dispatch and back. + */ +#define base_size (sizeof(struct eml_dispatch) - sizeof(eml_routine_t)) +#define count_to_size(count) \ + (base_size + sizeof(vm_offset_t) * (count)) + +#define size_to_count(size) \ + ( ((size) - base_size) / sizeof(vm_offset_t) ) + +/* + * eml_init: initialize user space emulation code + */ +void eml_init() +{ +} + +/* + * eml_task_reference() [Exported] + * + * Bumps the reference count on the common emulation + * vector. + */ + +void eml_task_reference(task, parent) + task_t task, parent; +{ + register eml_dispatch_t eml; + + if (parent == TASK_NULL) + eml = EML_DISPATCH_NULL; + else + eml = parent->eml_dispatch; + + if (eml != EML_DISPATCH_NULL) { + simple_lock(&eml->lock); + eml->ref_count++; + simple_unlock(&eml->lock); + } + task->eml_dispatch = eml; +} + + +/* + * eml_task_deallocate() [Exported] + * + * Cleans up after the emulation code when a process exits. + */ + +void eml_task_deallocate(task) + task_t task; +{ + register eml_dispatch_t eml; + + eml = task->eml_dispatch; + if (eml != EML_DISPATCH_NULL) { + int count; + + simple_lock(&eml->lock); + count = --eml->ref_count; + simple_unlock(&eml->lock); + + if (count == 0) + kfree((vm_offset_t)eml, count_to_size(eml->disp_count)); + } +} + +/* + * task_set_emulation_vector: [Server Entry] + * set a list of emulated system calls for this task. + */ +kern_return_t +task_set_emulation_vector_internal(task, vector_start, emulation_vector, + emulation_vector_count) + task_t task; + int vector_start; + emulation_vector_t emulation_vector; + unsigned int emulation_vector_count; +{ + eml_dispatch_t cur_eml, new_eml, old_eml; + vm_size_t new_size; + int cur_start, cur_end; + int new_start = 0, new_end = 0; + int vector_end; + + if (task == TASK_NULL) + return EML_BAD_TASK; + + vector_end = vector_start + emulation_vector_count; + + /* + * We try to re-use the existing emulation vector + * if possible. We can reuse the vector if it + * is not shared with another task and if it is + * large enough to contain the entries we are + * supplying. + * + * We must grab the lock on the task to check whether + * there is an emulation vector. + * If the vector is shared or not large enough, we + * need to drop the lock and allocate a new emulation + * vector. + * + * While the lock is dropped, the emulation vector + * may be released by all other tasks (giving us + * exclusive use), or may be enlarged by another + * task_set_emulation_vector call. Therefore, + * after allocating the new emulation vector, we + * must grab the lock again to check whether we + * really need the new vector we just allocated. + * + * Since an emulation vector cannot be altered + * if it is in use by more than one task, the + * task lock is sufficient to protect the vector`s + * start, count, and contents. The lock in the + * vector protects only the reference count. + */ + + old_eml = EML_DISPATCH_NULL; /* vector to discard */ + new_eml = EML_DISPATCH_NULL; /* new vector */ + + for (;;) { + /* + * Find the current emulation vector. + * See whether we can overwrite it. + */ + task_lock(task); + cur_eml = task->eml_dispatch; + if (cur_eml != EML_DISPATCH_NULL) { + cur_start = cur_eml->disp_min; + cur_end = cur_eml->disp_count + cur_start; + + simple_lock(&cur_eml->lock); + if (cur_eml->ref_count == 1 && + cur_start <= vector_start && + cur_end >= vector_end) + { + /* + * Can use the existing emulation vector. + * Discard any new one we allocated. + */ + simple_unlock(&cur_eml->lock); + old_eml = new_eml; + break; + } + + if (new_eml != EML_DISPATCH_NULL && + new_start <= cur_start && + new_end >= cur_end) + { + /* + * A new vector was allocated, and it is large enough + * to hold all the entries from the current vector. + * Copy the entries to the new emulation vector, + * deallocate the current one, and use the new one. + */ + bcopy((char *)&cur_eml->disp_vector[0], + (char *)&new_eml->disp_vector[cur_start-new_start], + cur_eml->disp_count * sizeof(vm_offset_t)); + + if (--cur_eml->ref_count == 0) + old_eml = cur_eml; /* discard old vector */ + simple_unlock(&cur_eml->lock); + + task->eml_dispatch = new_eml; + syscall_emulation_sync(task); + cur_eml = new_eml; + break; + } + simple_unlock(&cur_eml->lock); + + /* + * Need a new emulation vector. + * Ensure it will hold all the entries from + * both the old and new emulation vectors. + */ + new_start = vector_start; + if (new_start > cur_start) + new_start = cur_start; + new_end = vector_end; + if (new_end < cur_end) + new_end = cur_end; + } + else { + /* + * There is no current emulation vector. + * If a new one was allocated, use it. + */ + if (new_eml != EML_DISPATCH_NULL) { + task->eml_dispatch = new_eml; + cur_eml = new_eml; + break; + } + + /* + * Compute the size needed for the new vector. + */ + new_start = vector_start; + new_end = vector_end; + } + + /* + * Have no vector (or one that is no longer large enough). + * Drop all the locks and allocate a new vector. + * Repeat the loop to check whether the old vector was + * changed while we didn`t hold the locks. + */ + + task_unlock(task); + + if (new_eml != EML_DISPATCH_NULL) + kfree((vm_offset_t)new_eml, count_to_size(new_eml->disp_count)); + + new_size = count_to_size(new_end - new_start); + new_eml = (eml_dispatch_t) kalloc(new_size); + + bzero((char *)new_eml, new_size); + simple_lock_init(&new_eml->lock); + new_eml->ref_count = 1; + new_eml->disp_min = new_start; + new_eml->disp_count = new_end - new_start; + + continue; + } + + /* + * We have the emulation vector. + * Install the new emulation entries. + */ + bcopy((char *)&emulation_vector[0], + (char *)&cur_eml->disp_vector[vector_start - cur_eml->disp_min], + emulation_vector_count * sizeof(vm_offset_t)); + + task_unlock(task); + + /* + * Discard any old emulation vector we don`t need. + */ + if (old_eml) + kfree((vm_offset_t) old_eml, count_to_size(old_eml->disp_count)); + + return KERN_SUCCESS; +} + +/* + * task_set_emulation_vector: [Server Entry] + * + * Set the list of emulated system calls for this task. + * The list is out-of-line. + */ +kern_return_t +task_set_emulation_vector(task, vector_start, emulation_vector, + emulation_vector_count) + task_t task; + int vector_start; + emulation_vector_t emulation_vector; + unsigned int emulation_vector_count; +{ + kern_return_t kr; + vm_offset_t emul_vector_addr; + + if (task == TASK_NULL) + return EML_BAD_TASK; /* XXX sb KERN_INVALID_ARGUMENT */ + + /* + * The emulation vector is really a vm_map_copy_t. + */ + kr = vm_map_copyout(ipc_kernel_map, &emul_vector_addr, + (vm_map_copy_t) emulation_vector); + if (kr != KERN_SUCCESS) + return kr; + + /* + * Do the work. + */ + kr = task_set_emulation_vector_internal( + task, + vector_start, + (emulation_vector_t) emul_vector_addr, + emulation_vector_count); + + /* + * Discard the memory + */ + (void) kmem_free(ipc_kernel_map, + emul_vector_addr, + emulation_vector_count * sizeof(eml_dispatch_t)); + + return kr; +} + +/* + * Compatibility entry. Vector is passed inline. + */ +kern_return_t +xxx_task_set_emulation_vector(task, vector_start, emulation_vector, + emulation_vector_count) + task_t task; + int vector_start; + emulation_vector_t emulation_vector; + unsigned int emulation_vector_count; +{ + return task_set_emulation_vector_internal( + task, + vector_start, + emulation_vector, + emulation_vector_count); +} + +/* + * task_get_emulation_vector: [Server Entry] + * + * Get the list of emulated system calls for this task. + * List is returned out-of-line. + */ +kern_return_t +task_get_emulation_vector(task, vector_start, emulation_vector, + emulation_vector_count) + task_t task; + int *vector_start; /* out */ + emulation_vector_t *emulation_vector; /* out */ + unsigned int *emulation_vector_count; /* out */ +{ + eml_dispatch_t eml; + vm_size_t vector_size, size; + vm_offset_t addr; + + if (task == TASK_NULL) + return EML_BAD_TASK; + + addr = 0; + size = 0; + + for(;;) { + vm_size_t size_needed; + + task_lock(task); + eml = task->eml_dispatch; + if (eml == EML_DISPATCH_NULL) { + task_unlock(task); + if (addr) + (void) kmem_free(ipc_kernel_map, addr, size); + *vector_start = 0; + *emulation_vector = 0; + *emulation_vector_count = 0; + return KERN_SUCCESS; + } + + /* + * Do we have the memory we need? + */ + vector_size = eml->disp_count * sizeof(vm_offset_t); + + size_needed = round_page(vector_size); + if (size_needed <= size) + break; + + /* + * If not, unlock the task and allocate more memory. + */ + task_unlock(task); + + if (size != 0) + kmem_free(ipc_kernel_map, addr, size); + + size = size_needed; + if (kmem_alloc(ipc_kernel_map, &addr, size) != KERN_SUCCESS) + return KERN_RESOURCE_SHORTAGE; + } + + /* + * Copy out the dispatch addresses + */ + *vector_start = eml->disp_min; + *emulation_vector_count = eml->disp_count; + bcopy((char *)eml->disp_vector, + (char *)addr, + vector_size); + + /* + * Unlock the task and free any memory we did not need + */ + task_unlock(task); + + { + vm_size_t size_used, size_left; + vm_map_copy_t memory; + + /* + * Free any unused memory beyond the end of the last page used + */ + size_used = round_page(vector_size); + if (size_used != size) + (void) kmem_free(ipc_kernel_map, + addr + size_used, + size - size_used); + + /* + * Zero the remainder of the page being returned. + */ + size_left = size_used - vector_size; + if (size_left > 0) + bzero((char *)addr + vector_size, size_left); + + /* + * Make memory into copyin form - this unwires it. + */ + (void) vm_map_copyin(ipc_kernel_map, addr, vector_size, TRUE, &memory); + + *emulation_vector = (emulation_vector_t) memory; + } + + return KERN_SUCCESS; +} + +/* + * xxx_task_get_emulation: [Server Entry] + * get the list of emulated system calls for this task. + * Compatibility code: return list in-line. + */ +kern_return_t +xxx_task_get_emulation_vector(task, vector_start, emulation_vector, + emulation_vector_count) + task_t task; + int *vector_start; + emulation_vector_t emulation_vector; /* pointer to OUT array */ + unsigned int *emulation_vector_count; /*IN/OUT*/ +{ + register eml_dispatch_t eml; + + if (task == TASK_NULL) + return( EML_BAD_TASK ); + + task_lock(task); + + eml = task->eml_dispatch; + if (eml == EML_DISPATCH_NULL) { + task_unlock(task); + *vector_start = 0; + *emulation_vector_count = 0; + return( KERN_SUCCESS ); + } + + simple_lock(&eml->lock); + + if (*emulation_vector_count < eml->disp_count) { + simple_unlock(&eml->lock); + task_unlock(task); + return( EML_BAD_CNT ); + } + + *vector_start = eml->disp_min; + *emulation_vector_count = eml->disp_count; + bcopy((char *)eml->disp_vector, (char *)emulation_vector, + *emulation_vector_count * sizeof(vm_offset_t)); + simple_unlock(&eml->lock); + + task_unlock(task); + + return( KERN_SUCCESS ); +} + +/* + * task_set_emulation: [Server Entry] + * set up for user space emulation of syscalls within this task. + */ +kern_return_t task_set_emulation(task, routine_entry_pt, routine_number) + task_t task; + vm_offset_t routine_entry_pt; + int routine_number; +{ + return task_set_emulation_vector_internal(task, routine_number, + &routine_entry_pt, 1); +} |