2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
26 * Mach Operating System
27 * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
28 * All Rights Reserved.
30 * Permission to use, copy, modify and ditribute this software and its
31 * documentation is hereby granted, provided that both the copyright
32 * notice and this permission notice appear in all copies of the
33 * software, derivative works or modified versions, and any portions
34 * thereof, and that both notices appear in supporting documentation.
36 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
37 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
38 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
40 * Carnegie Mellon requests users of this software to return to
42 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
43 * School of Computer Science
44 * Carnegie Mellon University
45 * Pittsburgh PA 15213-3890
47 * any improvements or extensions that they make and grant Carnegie Mellon
48 * the rights to redistribute these changes.
53 #include <mach/error.h>
54 #include <mach/vm_param.h>
55 #include <mach/boolean.h>
56 #include <kern/misc_protos.h>
57 #include <kern/syscall_emulation.h>
58 #include <kern/task.h>
59 #include <kern/kalloc.h>
60 #include <vm/vm_kern.h>
61 #include <machine/thread.h> /* for syscall_emulation_sync */
69 * This code knows that kalloc() allocates memory most efficiently
70 * in sizes that are powers of 2, and asks for those sizes.
74 * Go from number of entries to size of struct eml_dispatch and back.
76 #define base_size (sizeof(struct eml_dispatch) - sizeof(eml_routine_t))
77 #define count_to_size(count) \
78 (base_size + sizeof(vm_offset_t) * (count))
80 #define size_to_count(size) \
81 ( ((size) - base_size) / sizeof(vm_offset_t) )
85 task_set_emulation_vector_internal(
88 emulation_vector_t emulation_vector
,
89 mach_msg_type_number_t emulation_vector_count
);
92 * eml_init: initialize user space emulation code
100 * eml_task_reference() [Exported]
102 * Bumps the reference count on the common emulation
111 register eml_dispatch_t eml
;
113 if (parent
== TASK_NULL
)
114 eml
= EML_DISPATCH_NULL
;
116 eml
= parent
->eml_dispatch
;
118 if (eml
!= EML_DISPATCH_NULL
) {
119 mutex_lock(&eml
->lock
);
121 mutex_unlock(&eml
->lock
);
123 task
->eml_dispatch
= eml
;
128 * eml_task_deallocate() [Exported]
130 * Cleans up after the emulation code when a process exits.
137 register eml_dispatch_t eml
;
139 eml
= task
->eml_dispatch
;
140 if (eml
!= EML_DISPATCH_NULL
) {
143 mutex_lock(&eml
->lock
);
144 count
= --eml
->ref_count
;
145 mutex_unlock(&eml
->lock
);
148 kfree((vm_offset_t
)eml
, count_to_size(eml
->disp_count
));
150 task
->eml_dispatch
= EML_DISPATCH_NULL
;
155 * task_set_emulation_vector: [Server Entry]
156 * set a list of emulated system calls for this task.
159 task_set_emulation_vector_internal(
162 emulation_vector_t emulation_vector
,
163 mach_msg_type_number_t emulation_vector_count
)
165 eml_dispatch_t cur_eml
, new_eml
, old_eml
;
167 int cur_start
, cur_end
;
168 int new_start
, new_end
;
171 if (task
== TASK_NULL
)
174 vector_end
= vector_start
+ (int) emulation_vector_count
;
177 * We try to re-use the existing emulation vetor
178 * if possible. We can reuse the vector if it
179 * is not shared with another task and if it is
180 * large enough to contain the entries we are
183 * We must grab the lock on the task to check whether
184 * there is an emulation vector.
185 * If the vector is shared or not large enough, we
186 * need to drop the lock and allocate a new emulation
189 * While the lock is dropped, the emulation vector
190 * may be released by all other tasks (giving us
191 * exclusive use), or may be enlarged by another
192 * task_set_emulation_vector call. Therefore,
193 * after allocating the new emulation vector, we
194 * must grab the lock again to check whether we
195 * really need the new vector we just allocated.
197 * Since an emulation vector cannot be altered
198 * if it is in use by more than one task, the
199 * task lock is sufficient to protect the vector`s
200 * start, count, and contents. The lock in the
201 * vector protects only the reference count.
204 old_eml
= EML_DISPATCH_NULL
; /* vector to discard */
205 new_eml
= EML_DISPATCH_NULL
; /* new vector */
209 * Find the current emulation vector.
210 * See whether we can overwrite it.
213 cur_eml
= task
->eml_dispatch
;
214 if (cur_eml
!= EML_DISPATCH_NULL
) {
215 cur_start
= cur_eml
->disp_min
;
216 cur_end
= cur_eml
->disp_count
+ cur_start
;
218 mutex_lock(&cur_eml
->lock
);
219 if (cur_eml
->ref_count
== 1 &&
220 cur_start
<= vector_start
&&
221 cur_end
>= vector_end
)
224 * Can use the existing emulation vector.
225 * Discard any new one we allocated.
227 mutex_unlock(&cur_eml
->lock
);
232 if (new_eml
!= EML_DISPATCH_NULL
&&
233 new_start
<= cur_start
&&
237 * A new vector was allocated, and it is large enough
238 * to hold all the entries from the current vector.
239 * Copy the entries to the new emulation vector,
240 * deallocate the current one, and use the new one.
243 bcopy((char *)&cur_eml
->disp_vector
[0],
244 (char *)&new_eml
->disp_vector
[cur_start
-new_start
],
245 cur_eml
->disp_count
* sizeof(vm_offset_t
));
248 if (--cur_eml
->ref_count
== 0)
249 old_eml
= cur_eml
; /* discard old vector */
250 mutex_unlock(&cur_eml
->lock
);
252 task
->eml_dispatch
= new_eml
;
253 syscall_emulation_sync(task
);
257 mutex_unlock(&cur_eml
->lock
);
260 * Need a new emulation vector.
261 * Ensure it will hold all the entries from
262 * both the old and new emulation vectors.
264 new_start
= vector_start
;
265 if (new_start
> cur_start
)
266 new_start
= cur_start
;
267 new_end
= vector_end
;
268 if (new_end
< cur_end
)
273 * There is no curren emulation vector.
274 * If a new one was allocated, use it.
276 if (new_eml
!= EML_DISPATCH_NULL
) {
277 task
->eml_dispatch
= new_eml
;
283 * Compute the size needed for the new vector.
285 new_start
= vector_start
;
286 new_end
= vector_end
;
290 * Have no vector (or one that is no longer large enough).
291 * Drop all the locks and allocate a new vector.
292 * Repeat the loop to check whether the old vector was
293 * changed while we didn`t hold the locks.
298 if (new_eml
!= EML_DISPATCH_NULL
)
299 kfree((vm_offset_t
)new_eml
, count_to_size(new_eml
->disp_count
));
301 new_size
= count_to_size(new_end
- new_start
);
302 new_eml
= (eml_dispatch_t
) kalloc(new_size
);
304 bzero((char *)new_eml
, new_size
);
305 mutex_init(&new_eml
->lock
, ETAP_MISC_EMULATE
);
306 new_eml
->ref_count
= 1;
307 new_eml
->disp_min
= new_start
;
308 new_eml
->disp_count
= new_end
- new_start
;
314 * We have the emulation vector.
315 * Install the new emulation entries.
317 bcopy((char *)&emulation_vector
[0],
318 (char *)&cur_eml
->disp_vector
[vector_start
- cur_eml
->disp_min
],
319 emulation_vector_count
* sizeof(vm_offset_t
));
324 * Discard any old emulation vector we don`t need.
327 kfree((vm_offset_t
) old_eml
, count_to_size(old_eml
->disp_count
));
333 * task_set_emulation_vector: [Server Entry]
335 * Set the list of emulated system calls for this task.
336 * The list is out-of-line.
339 task_set_emulation_vector(
342 emulation_vector_t emulation_vector
,
343 mach_msg_type_number_t emulation_vector_count
)
346 vm_offset_t emul_vector_addr
;
348 if (task
== TASK_NULL
)
349 return EML_BAD_TASK
; /* XXX sb KERN_INVALID_ARGUMENT */
352 * XXX - barbou@gr.osf.org.
353 * If emulation_vector_count is NULL, discard the emulation
355 * We need a way to do that for emulator-less servers started
356 * from a classic server. There seems to be no way to get rid
357 * of or to avoid inheriting the emulation vector !?
359 if (emulation_vector_count
== 0) {
360 eml_task_deallocate(task
);
366 * The emulation vector is really a vm_map_copy_t.
368 kr
= vm_map_copyout(ipc_kernel_map
, &emul_vector_addr
,
369 (vm_map_copy_t
) emulation_vector
);
370 if (kr
!= KERN_SUCCESS
)
374 * Can't fault while we hold locks.
376 kr
= vm_map_wire(ipc_kernel_map
,
377 trunc_page_32(emul_vector_addr
),
378 round_page_32(emul_vector_addr
+
379 emulation_vector_count
*
380 sizeof(eml_dispatch_t
)),
381 VM_PROT_READ
|VM_PROT_WRITE
, FALSE
);
382 assert(kr
== KERN_SUCCESS
);
387 kr
= task_set_emulation_vector_internal(
390 (emulation_vector_t
) emul_vector_addr
,
391 emulation_vector_count
);
392 assert(kr
== KERN_SUCCESS
);
397 (void) kmem_free(ipc_kernel_map
,
399 emulation_vector_count
* sizeof(eml_dispatch_t
));
405 * task_get_emulation_vector: [Server Entry]
407 * Get the list of emulated system calls for this task.
408 * List is returned out-of-line.
411 task_get_emulation_vector(
413 int *vector_start
, /* out */
414 emulation_vector_t
*emulation_vector
, /* out */
415 mach_msg_type_number_t
*emulation_vector_count
) /* out */
418 vm_size_t vector_size
, size
;
421 if (task
== TASK_NULL
)
428 vm_size_t size_needed
;
431 eml
= task
->eml_dispatch
;
432 if (eml
== EML_DISPATCH_NULL
) {
435 (void) kmem_free(ipc_kernel_map
, addr
, size
);
437 *emulation_vector
= 0;
438 *emulation_vector_count
= 0;
443 * Do we have the memory we need?
445 vector_size
= eml
->disp_count
* sizeof(vm_offset_t
);
447 size_needed
= round_page_32(vector_size
);
448 if (size_needed
<= size
)
452 * If not, unlock the task and allocate more memory.
457 kmem_free(ipc_kernel_map
, addr
, size
);
460 if (kmem_alloc(ipc_kernel_map
, &addr
, size
) != KERN_SUCCESS
)
461 return KERN_RESOURCE_SHORTAGE
;
465 * Copy out the dispatch addresses
467 *vector_start
= eml
->disp_min
;
468 *emulation_vector_count
= eml
->disp_count
;
469 bcopy((char *)eml
->disp_vector
,
474 * Unlock the task and free any memory we did not need
478 vm_size_t size_used
, size_left
;
479 vm_map_copy_t memory
;
482 * Free any unused memory beyond the end of the last page used
484 size_used
= round_page_32(vector_size
);
485 if (size_used
!= size
)
486 (void) kmem_free(ipc_kernel_map
,
491 * Zero the remainder of the page being returned.
493 size_left
= size_used
- vector_size
;
495 bzero((char *)addr
+ vector_size
, size_left
);
498 * Unwire and make memory into copyin form.
500 (void) vm_map_unwire(ipc_kernel_map
, addr
, addr
+ size_used
, FALSE
);
501 (void) vm_map_copyin(ipc_kernel_map
, addr
, vector_size
,
504 *emulation_vector
= (emulation_vector_t
) memory
;
511 * task_set_emulation: [Server Entry]
512 * set up for user space emulation of syscalls within this task.
517 vm_offset_t routine_entry_pt
,
520 return task_set_emulation_vector_internal(task
, routine_number
,
521 &routine_entry_pt
, 1);