]> git.saurik.com Git - apple/xnu.git/blob - osfmk/kern/syscall_emulation.c
xnu-344.21.74.tar.gz
[apple/xnu.git] / osfmk / kern / syscall_emulation.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
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
13 * file.
14 *
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.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /*
26 * @OSF_COPYRIGHT@
27 */
28 /*
29 * Mach Operating System
30 * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
31 * All Rights Reserved.
32 *
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.
38 *
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.
42 *
43 * Carnegie Mellon requests users of this software to return to
44 *
45 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
46 * School of Computer Science
47 * Carnegie Mellon University
48 * Pittsburgh PA 15213-3890
49 *
50 * any improvements or extensions that they make and grant Carnegie Mellon
51 * the rights to redistribute these changes.
52 */
53 /*
54 */
55
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 */
65
66 /*
67 * Exported interface
68 */
69
70 /*
71 * WARNING:
72 * This code knows that kalloc() allocates memory most efficiently
73 * in sizes that are powers of 2, and asks for those sizes.
74 */
75
76 /*
77 * Go from number of entries to size of struct eml_dispatch and back.
78 */
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))
82
83 #define size_to_count(size) \
84 ( ((size) - base_size) / sizeof(vm_offset_t) )
85
86 /* Forwards */
87 kern_return_t
88 task_set_emulation_vector_internal(
89 task_t task,
90 int vector_start,
91 emulation_vector_t emulation_vector,
92 mach_msg_type_number_t emulation_vector_count);
93
94 /*
95 * eml_init: initialize user space emulation code
96 */
97 void
98 eml_init(void)
99 {
100 }
101
102 /*
103 * eml_task_reference() [Exported]
104 *
105 * Bumps the reference count on the common emulation
106 * vector.
107 */
108
109 void
110 eml_task_reference(
111 task_t task,
112 task_t parent)
113 {
114 register eml_dispatch_t eml;
115
116 if (parent == TASK_NULL)
117 eml = EML_DISPATCH_NULL;
118 else
119 eml = parent->eml_dispatch;
120
121 if (eml != EML_DISPATCH_NULL) {
122 mutex_lock(&eml->lock);
123 eml->ref_count++;
124 mutex_unlock(&eml->lock);
125 }
126 task->eml_dispatch = eml;
127 }
128
129
130 /*
131 * eml_task_deallocate() [Exported]
132 *
133 * Cleans up after the emulation code when a process exits.
134 */
135
136 void
137 eml_task_deallocate(
138 task_t task)
139 {
140 register eml_dispatch_t eml;
141
142 eml = task->eml_dispatch;
143 if (eml != EML_DISPATCH_NULL) {
144 int count;
145
146 mutex_lock(&eml->lock);
147 count = --eml->ref_count;
148 mutex_unlock(&eml->lock);
149
150 if (count == 0)
151 kfree((vm_offset_t)eml, count_to_size(eml->disp_count));
152
153 task->eml_dispatch = EML_DISPATCH_NULL;
154 }
155 }
156
157 /*
158 * task_set_emulation_vector: [Server Entry]
159 * set a list of emulated system calls for this task.
160 */
161 kern_return_t
162 task_set_emulation_vector_internal(
163 task_t task,
164 int vector_start,
165 emulation_vector_t emulation_vector,
166 mach_msg_type_number_t emulation_vector_count)
167 {
168 eml_dispatch_t cur_eml, new_eml, old_eml;
169 vm_size_t new_size;
170 int cur_start, cur_end;
171 int new_start, new_end;
172 int vector_end;
173
174 if (task == TASK_NULL)
175 return EML_BAD_TASK;
176
177 vector_end = vector_start + (int) emulation_vector_count;
178
179 /*
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
184 * supplying.
185 *
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
190 * vector.
191 *
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.
199 *
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.
205 */
206
207 old_eml = EML_DISPATCH_NULL; /* vector to discard */
208 new_eml = EML_DISPATCH_NULL; /* new vector */
209
210 for (;;) {
211 /*
212 * Find the current emulation vector.
213 * See whether we can overwrite it.
214 */
215 task_lock(task);
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;
220
221 mutex_lock(&cur_eml->lock);
222 if (cur_eml->ref_count == 1 &&
223 cur_start <= vector_start &&
224 cur_end >= vector_end)
225 {
226 /*
227 * Can use the existing emulation vector.
228 * Discard any new one we allocated.
229 */
230 mutex_unlock(&cur_eml->lock);
231 old_eml = new_eml;
232 break;
233 }
234
235 if (new_eml != EML_DISPATCH_NULL &&
236 new_start <= cur_start &&
237 new_end >= cur_end)
238 {
239 /*
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.
244 */
245
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));
249
250
251 if (--cur_eml->ref_count == 0)
252 old_eml = cur_eml; /* discard old vector */
253 mutex_unlock(&cur_eml->lock);
254
255 task->eml_dispatch = new_eml;
256 syscall_emulation_sync(task);
257 cur_eml = new_eml;
258 break;
259 }
260 mutex_unlock(&cur_eml->lock);
261
262 /*
263 * Need a new emulation vector.
264 * Ensure it will hold all the entries from
265 * both the old and new emulation vectors.
266 */
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)
272 new_end = cur_end;
273 }
274 else {
275 /*
276 * There is no curren emulation vector.
277 * If a new one was allocated, use it.
278 */
279 if (new_eml != EML_DISPATCH_NULL) {
280 task->eml_dispatch = new_eml;
281 cur_eml = new_eml;
282 break;
283 }
284
285 /*
286 * Compute the size needed for the new vector.
287 */
288 new_start = vector_start;
289 new_end = vector_end;
290 }
291
292 /*
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.
297 */
298
299 task_unlock(task);
300
301 if (new_eml != EML_DISPATCH_NULL)
302 kfree((vm_offset_t)new_eml, count_to_size(new_eml->disp_count));
303
304 new_size = count_to_size(new_end - new_start);
305 new_eml = (eml_dispatch_t) kalloc(new_size);
306
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;
312
313 continue;
314 }
315
316 /*
317 * We have the emulation vector.
318 * Install the new emulation entries.
319 */
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));
323
324 task_unlock(task);
325
326 /*
327 * Discard any old emulation vector we don`t need.
328 */
329 if (old_eml)
330 kfree((vm_offset_t) old_eml, count_to_size(old_eml->disp_count));
331
332 return KERN_SUCCESS;
333 }
334
335 /*
336 * task_set_emulation_vector: [Server Entry]
337 *
338 * Set the list of emulated system calls for this task.
339 * The list is out-of-line.
340 */
341 kern_return_t
342 task_set_emulation_vector(
343 task_t task,
344 int vector_start,
345 emulation_vector_t emulation_vector,
346 mach_msg_type_number_t emulation_vector_count)
347 {
348 kern_return_t kr;
349 vm_offset_t emul_vector_addr;
350
351 if (task == TASK_NULL)
352 return EML_BAD_TASK; /* XXX sb KERN_INVALID_ARGUMENT */
353
354 /*
355 * XXX - barbou@gr.osf.org.
356 * If emulation_vector_count is NULL, discard the emulation
357 * vectors.
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 !?
361 */
362 if (emulation_vector_count == 0) {
363 eml_task_deallocate(task);
364 return KERN_SUCCESS;
365 }
366
367
368 /*
369 * The emulation vector is really a vm_map_copy_t.
370 */
371 kr = vm_map_copyout(ipc_kernel_map, &emul_vector_addr,
372 (vm_map_copy_t) emulation_vector);
373 if (kr != KERN_SUCCESS)
374 return kr;
375
376 /*
377 * Can't fault while we hold locks.
378 */
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);
386
387 /*
388 * Do the work.
389 */
390 kr = task_set_emulation_vector_internal(
391 task,
392 vector_start,
393 (emulation_vector_t) emul_vector_addr,
394 emulation_vector_count);
395 assert(kr == KERN_SUCCESS);
396
397 /*
398 * Discard the memory
399 */
400 (void) kmem_free(ipc_kernel_map,
401 emul_vector_addr,
402 emulation_vector_count * sizeof(eml_dispatch_t));
403
404 return KERN_SUCCESS;
405 }
406
407 /*
408 * task_get_emulation_vector: [Server Entry]
409 *
410 * Get the list of emulated system calls for this task.
411 * List is returned out-of-line.
412 */
413 kern_return_t
414 task_get_emulation_vector(
415 task_t task,
416 int *vector_start, /* out */
417 emulation_vector_t *emulation_vector, /* out */
418 mach_msg_type_number_t *emulation_vector_count) /* out */
419 {
420 eml_dispatch_t eml;
421 vm_size_t vector_size, size;
422 vm_offset_t addr;
423
424 if (task == TASK_NULL)
425 return EML_BAD_TASK;
426
427 addr = 0;
428 size = 0;
429
430 for(;;) {
431 vm_size_t size_needed;
432
433 task_lock(task);
434 eml = task->eml_dispatch;
435 if (eml == EML_DISPATCH_NULL) {
436 task_unlock(task);
437 if (addr)
438 (void) kmem_free(ipc_kernel_map, addr, size);
439 *vector_start = 0;
440 *emulation_vector = 0;
441 *emulation_vector_count = 0;
442 return KERN_SUCCESS;
443 }
444
445 /*
446 * Do we have the memory we need?
447 */
448 vector_size = eml->disp_count * sizeof(vm_offset_t);
449
450 size_needed = round_page_32(vector_size);
451 if (size_needed <= size)
452 break;
453
454 /*
455 * If not, unlock the task and allocate more memory.
456 */
457 task_unlock(task);
458
459 if (size != 0)
460 kmem_free(ipc_kernel_map, addr, size);
461
462 size = size_needed;
463 if (kmem_alloc(ipc_kernel_map, &addr, size) != KERN_SUCCESS)
464 return KERN_RESOURCE_SHORTAGE;
465 }
466
467 /*
468 * Copy out the dispatch addresses
469 */
470 *vector_start = eml->disp_min;
471 *emulation_vector_count = eml->disp_count;
472 bcopy((char *)eml->disp_vector,
473 (char *)addr,
474 vector_size);
475
476 /*
477 * Unlock the task and free any memory we did not need
478 */
479 task_unlock(task);
480 {
481 vm_size_t size_used, size_left;
482 vm_map_copy_t memory;
483
484 /*
485 * Free any unused memory beyond the end of the last page used
486 */
487 size_used = round_page_32(vector_size);
488 if (size_used != size)
489 (void) kmem_free(ipc_kernel_map,
490 addr + size_used,
491 size - size_used);
492
493 /*
494 * Zero the remainder of the page being returned.
495 */
496 size_left = size_used - vector_size;
497 if (size_left > 0)
498 bzero((char *)addr + vector_size, size_left);
499
500 /*
501 * Unwire and make memory into copyin form.
502 */
503 (void) vm_map_unwire(ipc_kernel_map, addr, addr + size_used, FALSE);
504 (void) vm_map_copyin(ipc_kernel_map, addr, vector_size,
505 TRUE, &memory);
506
507 *emulation_vector = (emulation_vector_t) memory;
508 }
509
510 return KERN_SUCCESS;
511 }
512
513 /*
514 * task_set_emulation: [Server Entry]
515 * set up for user space emulation of syscalls within this task.
516 */
517 kern_return_t
518 task_set_emulation(
519 task_t task,
520 vm_offset_t routine_entry_pt,
521 int routine_number)
522 {
523 return task_set_emulation_vector_internal(task, routine_number,
524 &routine_entry_pt, 1);
525 }
526
527
528
529