-/*
- * 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) )
-
-/* Forwards */
-kern_return_t
-task_set_emulation_vector_internal(
- task_t task,
- int vector_start,
- emulation_vector_t emulation_vector,
- mach_msg_type_number_t emulation_vector_count);
-
-/*
- * eml_init: initialize user space emulation code
- */
-void
-eml_init(void)
-{
-}
-
-/*
- * eml_task_reference() [Exported]
- *
- * Bumps the reference count on the common emulation
- * vector.
- */
-
-void
-eml_task_reference(
- task_t task,
- task_t parent)
-{
- register eml_dispatch_t eml;
-
- if (parent == TASK_NULL)
- eml = EML_DISPATCH_NULL;
- else
- eml = parent->eml_dispatch;
-
- if (eml != EML_DISPATCH_NULL) {
- mutex_lock(&eml->lock);
- eml->ref_count++;
- mutex_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_t task)
-{
- register eml_dispatch_t eml;
-
- eml = task->eml_dispatch;
- if (eml != EML_DISPATCH_NULL) {
- int count;
-
- mutex_lock(&eml->lock);
- count = --eml->ref_count;
- mutex_unlock(&eml->lock);
-
- if (count == 0)
- kfree((vm_offset_t)eml, count_to_size(eml->disp_count));
-
- task->eml_dispatch = EML_DISPATCH_NULL;
- }
-}
-
-/*
- * 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_t task,
- int vector_start,
- emulation_vector_t emulation_vector,
- mach_msg_type_number_t 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, new_end;
- int vector_end;
-
- if (task == TASK_NULL)
- return EML_BAD_TASK;
-
- vector_end = vector_start + (int) emulation_vector_count;
-
- /*
- * We try to re-use the existing emulation vetor
- * 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;
-
- mutex_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.
- */
- mutex_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 */
- mutex_unlock(&cur_eml->lock);
-
- task->eml_dispatch = new_eml;
- syscall_emulation_sync(task);
- cur_eml = new_eml;
- break;
- }
- mutex_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 curren 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);
- mutex_init(&new_eml->lock, ETAP_MISC_EMULATE);
- 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;
-}
-