2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
29 * Mach Operating System
30 * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
31 * All Rights Reserved.
33 * Permission to use, copy, modify and ditribute this software and its
34 * documentation is hereby granted, provided that both the copyright
35 * notice and this permission notice appear in all copies of the
36 * software, derivative works or modified versions, and any portions
37 * thereof, and that both notices appear in supporting documentation.
39 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
40 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
41 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
43 * Carnegie Mellon requests users of this software to return to
45 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
46 * School of Computer Science
47 * Carnegie Mellon University
48 * Pittsburgh PA 15213-3890
50 * any improvements or extensions that they make and grant Carnegie Mellon
51 * the rights to redistribute these changes.
56 #include <mach/error.h>
57 #include <mach/vm_param.h>
58 #include <mach/boolean.h>
59 #include <kern/misc_protos.h>
60 #include <kern/syscall_emulation.h>
61 #include <kern/task.h>
62 #include <kern/kalloc.h>
63 #include <vm/vm_kern.h>
64 #include <machine/thread.h> /* for syscall_emulation_sync */
72 * This code knows that kalloc() allocates memory most efficiently
73 * in sizes that are powers of 2, and asks for those sizes.
77 * Go from number of entries to size of struct eml_dispatch and back.
79 #define base_size (sizeof(struct eml_dispatch) - sizeof(eml_routine_t))
80 #define count_to_size(count) \
81 (base_size + sizeof(vm_offset_t) * (count))
83 #define size_to_count(size) \
84 ( ((size) - base_size) / sizeof(vm_offset_t) )
88 task_set_emulation_vector_internal(
91 emulation_vector_t emulation_vector
,
92 mach_msg_type_number_t emulation_vector_count
);
95 * eml_init: initialize user space emulation code
103 * eml_task_reference() [Exported]
105 * Bumps the reference count on the common emulation
114 register eml_dispatch_t eml
;
116 if (parent
== TASK_NULL
)
117 eml
= EML_DISPATCH_NULL
;
119 eml
= parent
->eml_dispatch
;
121 if (eml
!= EML_DISPATCH_NULL
) {
122 mutex_lock(&eml
->lock
);
124 mutex_unlock(&eml
->lock
);
126 task
->eml_dispatch
= eml
;
131 * eml_task_deallocate() [Exported]
133 * Cleans up after the emulation code when a process exits.
140 register eml_dispatch_t eml
;
142 eml
= task
->eml_dispatch
;
143 if (eml
!= EML_DISPATCH_NULL
) {
146 mutex_lock(&eml
->lock
);
147 count
= --eml
->ref_count
;
148 mutex_unlock(&eml
->lock
);
151 kfree((vm_offset_t
)eml
, count_to_size(eml
->disp_count
));
153 task
->eml_dispatch
= EML_DISPATCH_NULL
;
158 * task_set_emulation_vector: [Server Entry]
159 * set a list of emulated system calls for this task.
162 task_set_emulation_vector_internal(
165 emulation_vector_t emulation_vector
,
166 mach_msg_type_number_t emulation_vector_count
)
168 eml_dispatch_t cur_eml
, new_eml
, old_eml
;
170 int cur_start
, cur_end
;
171 int new_start
, new_end
;
174 if (task
== TASK_NULL
)
177 vector_end
= vector_start
+ (int) emulation_vector_count
;
180 * We try to re-use the existing emulation vetor
181 * if possible. We can reuse the vector if it
182 * is not shared with another task and if it is
183 * large enough to contain the entries we are
186 * We must grab the lock on the task to check whether
187 * there is an emulation vector.
188 * If the vector is shared or not large enough, we
189 * need to drop the lock and allocate a new emulation
192 * While the lock is dropped, the emulation vector
193 * may be released by all other tasks (giving us
194 * exclusive use), or may be enlarged by another
195 * task_set_emulation_vector call. Therefore,
196 * after allocating the new emulation vector, we
197 * must grab the lock again to check whether we
198 * really need the new vector we just allocated.
200 * Since an emulation vector cannot be altered
201 * if it is in use by more than one task, the
202 * task lock is sufficient to protect the vector`s
203 * start, count, and contents. The lock in the
204 * vector protects only the reference count.
207 old_eml
= EML_DISPATCH_NULL
; /* vector to discard */
208 new_eml
= EML_DISPATCH_NULL
; /* new vector */
212 * Find the current emulation vector.
213 * See whether we can overwrite it.
216 cur_eml
= task
->eml_dispatch
;
217 if (cur_eml
!= EML_DISPATCH_NULL
) {
218 cur_start
= cur_eml
->disp_min
;
219 cur_end
= cur_eml
->disp_count
+ cur_start
;
221 mutex_lock(&cur_eml
->lock
);
222 if (cur_eml
->ref_count
== 1 &&
223 cur_start
<= vector_start
&&
224 cur_end
>= vector_end
)
227 * Can use the existing emulation vector.
228 * Discard any new one we allocated.
230 mutex_unlock(&cur_eml
->lock
);
235 if (new_eml
!= EML_DISPATCH_NULL
&&
236 new_start
<= cur_start
&&
240 * A new vector was allocated, and it is large enough
241 * to hold all the entries from the current vector.
242 * Copy the entries to the new emulation vector,
243 * deallocate the current one, and use the new one.
246 bcopy((char *)&cur_eml
->disp_vector
[0],
247 (char *)&new_eml
->disp_vector
[cur_start
-new_start
],
248 cur_eml
->disp_count
* sizeof(vm_offset_t
));
251 if (--cur_eml
->ref_count
== 0)
252 old_eml
= cur_eml
; /* discard old vector */
253 mutex_unlock(&cur_eml
->lock
);
255 task
->eml_dispatch
= new_eml
;
256 syscall_emulation_sync(task
);
260 mutex_unlock(&cur_eml
->lock
);
263 * Need a new emulation vector.
264 * Ensure it will hold all the entries from
265 * both the old and new emulation vectors.
267 new_start
= vector_start
;
268 if (new_start
> cur_start
)
269 new_start
= cur_start
;
270 new_end
= vector_end
;
271 if (new_end
< cur_end
)
276 * There is no curren emulation vector.
277 * If a new one was allocated, use it.
279 if (new_eml
!= EML_DISPATCH_NULL
) {
280 task
->eml_dispatch
= new_eml
;
286 * Compute the size needed for the new vector.
288 new_start
= vector_start
;
289 new_end
= vector_end
;
293 * Have no vector (or one that is no longer large enough).
294 * Drop all the locks and allocate a new vector.
295 * Repeat the loop to check whether the old vector was
296 * changed while we didn`t hold the locks.
301 if (new_eml
!= EML_DISPATCH_NULL
)
302 kfree((vm_offset_t
)new_eml
, count_to_size(new_eml
->disp_count
));
304 new_size
= count_to_size(new_end
- new_start
);
305 new_eml
= (eml_dispatch_t
) kalloc(new_size
);
307 bzero((char *)new_eml
, new_size
);
308 mutex_init(&new_eml
->lock
, ETAP_MISC_EMULATE
);
309 new_eml
->ref_count
= 1;
310 new_eml
->disp_min
= new_start
;
311 new_eml
->disp_count
= new_end
- new_start
;
317 * We have the emulation vector.
318 * Install the new emulation entries.
320 bcopy((char *)&emulation_vector
[0],
321 (char *)&cur_eml
->disp_vector
[vector_start
- cur_eml
->disp_min
],
322 emulation_vector_count
* sizeof(vm_offset_t
));
327 * Discard any old emulation vector we don`t need.
330 kfree((vm_offset_t
) old_eml
, count_to_size(old_eml
->disp_count
));
336 * task_set_emulation_vector: [Server Entry]
338 * Set the list of emulated system calls for this task.
339 * The list is out-of-line.
342 task_set_emulation_vector(
345 emulation_vector_t emulation_vector
,
346 mach_msg_type_number_t emulation_vector_count
)
349 vm_offset_t emul_vector_addr
;
351 if (task
== TASK_NULL
)
352 return EML_BAD_TASK
; /* XXX sb KERN_INVALID_ARGUMENT */
355 * XXX - barbou@gr.osf.org.
356 * If emulation_vector_count is NULL, discard the emulation
358 * We need a way to do that for emulator-less servers started
359 * from a classic server. There seems to be no way to get rid
360 * of or to avoid inheriting the emulation vector !?
362 if (emulation_vector_count
== 0) {
363 eml_task_deallocate(task
);
369 * The emulation vector is really a vm_map_copy_t.
371 kr
= vm_map_copyout(ipc_kernel_map
, &emul_vector_addr
,
372 (vm_map_copy_t
) emulation_vector
);
373 if (kr
!= KERN_SUCCESS
)
377 * Can't fault while we hold locks.
379 kr
= vm_map_wire(ipc_kernel_map
,
380 trunc_page_32(emul_vector_addr
),
381 round_page_32(emul_vector_addr
+
382 emulation_vector_count
*
383 sizeof(eml_dispatch_t
)),
384 VM_PROT_READ
|VM_PROT_WRITE
, FALSE
);
385 assert(kr
== KERN_SUCCESS
);
390 kr
= task_set_emulation_vector_internal(
393 (emulation_vector_t
) emul_vector_addr
,
394 emulation_vector_count
);
395 assert(kr
== KERN_SUCCESS
);
400 (void) kmem_free(ipc_kernel_map
,
402 emulation_vector_count
* sizeof(eml_dispatch_t
));
408 * task_get_emulation_vector: [Server Entry]
410 * Get the list of emulated system calls for this task.
411 * List is returned out-of-line.
414 task_get_emulation_vector(
416 int *vector_start
, /* out */
417 emulation_vector_t
*emulation_vector
, /* out */
418 mach_msg_type_number_t
*emulation_vector_count
) /* out */
421 vm_size_t vector_size
, size
;
424 if (task
== TASK_NULL
)
431 vm_size_t size_needed
;
434 eml
= task
->eml_dispatch
;
435 if (eml
== EML_DISPATCH_NULL
) {
438 (void) kmem_free(ipc_kernel_map
, addr
, size
);
440 *emulation_vector
= 0;
441 *emulation_vector_count
= 0;
446 * Do we have the memory we need?
448 vector_size
= eml
->disp_count
* sizeof(vm_offset_t
);
450 size_needed
= round_page_32(vector_size
);
451 if (size_needed
<= size
)
455 * If not, unlock the task and allocate more memory.
460 kmem_free(ipc_kernel_map
, addr
, size
);
463 if (kmem_alloc(ipc_kernel_map
, &addr
, size
) != KERN_SUCCESS
)
464 return KERN_RESOURCE_SHORTAGE
;
468 * Copy out the dispatch addresses
470 *vector_start
= eml
->disp_min
;
471 *emulation_vector_count
= eml
->disp_count
;
472 bcopy((char *)eml
->disp_vector
,
477 * Unlock the task and free any memory we did not need
481 vm_size_t size_used
, size_left
;
482 vm_map_copy_t memory
;
485 * Free any unused memory beyond the end of the last page used
487 size_used
= round_page_32(vector_size
);
488 if (size_used
!= size
)
489 (void) kmem_free(ipc_kernel_map
,
494 * Zero the remainder of the page being returned.
496 size_left
= size_used
- vector_size
;
498 bzero((char *)addr
+ vector_size
, size_left
);
501 * Unwire and make memory into copyin form.
503 (void) vm_map_unwire(ipc_kernel_map
, addr
, addr
+ size_used
, FALSE
);
504 (void) vm_map_copyin(ipc_kernel_map
, addr
, vector_size
,
507 *emulation_vector
= (emulation_vector_t
) memory
;
514 * task_set_emulation: [Server Entry]
515 * set up for user space emulation of syscalls within this task.
520 vm_offset_t routine_entry_pt
,
523 return task_set_emulation_vector_internal(task
, routine_number
,
524 &routine_entry_pt
, 1);