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