1 /*
2 * Mach Operating System
3 * Copyright (c) 1993,1992,1991,1990,1989,1988,1987 Carnegie Mellon University
4 * All Rights Reserved.
5 *
6 * Permission to use, copy, modify and distribute this software and its
7 * documentation is hereby granted, provided that both the copyright
8 * notice and this permission notice appear in all copies of the
9 * software, derivative works or modified versions, and any portions
10 * thereof, and that both notices appear in supporting documentation.
11 *
12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
13 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15 *
16 * Carnegie Mellon requests users of this software to return to
17 *
18 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
19 * School of Computer Science
20 * Carnegie Mellon University
21 * Pittsburgh PA 15213-3890
22 *
23 * any improvements or extensions that they make and grant Carnegie Mellon
24 * the rights to redistribute these changes.
25 */
26 /*
27 * HISTORY
28 * $Log: syscall_emulation.c,v $
29 * Revision 2.20 93/11/17 17:25:24 dbg
30 * ANSI-fied.
31 * [93/06/16 dbg]
32 *
33 * Revision 2.19 92/08/03 17:39:21 jfriedl
34 * removed silly prototypes
35 * [92/08/02 jfriedl]
36 *
37 * Revision 2.18 92/05/21 17:15:54 jfriedl
38 * Added init to 'new_start' and 'new_end' to quiet gcc warnings.
39 * [92/05/16 jfriedl]
40 *
41 * Revision 2.17 92/03/04 20:20:33 rpd
42 * Use vector_size instead of size_used in vm_map_copyin.
43 * [92/03/04 14:54:49 jsb]
44 *
45 * Revision 2.16 92/03/03 12:28:57 rpd
46 * Added syscall_emulation_sync.
47 * [92/03/03 rpd]
48 *
49 * Revision 2.15 92/02/23 19:50:06 elf
50 * Eliminate keep_wired argument from vm_map_copyin()
51 * [92/02/23 danner]
52 *
53 * Revision 2.14 92/01/03 20:15:18 dbg
54 * Change new versions of calls to pass dispatch table out-of-line.
55 * Old calls (xxx_*) use inline data.
56 * [92/01/03 dbg]
57 *
58 * Don't round up allocated vector size.
59 * [92/01/02 dbg]
60 *
61 * Remove fixed lower and upper bounds on emulated system call
62 * table. Now everyone can get as much as the MiG interface will
63 * allow.
64 * [91/12/18 dbg]
65 *
66 * Revision 2.13 91/12/13 13:43:31 jsb
67 * Increased eml_max_emulate_count for OSF/1+ server support.
68 *
69 * Revision 2.12 91/11/15 14:08:35 rpd
70 * Rewrote task_set_emulation_vector for greater clarity.
71 * Fixed bcopy bug in task_get_emulation_vector.
72 * Simplified and removed debugging printf from task_set_emulation.
73 * [91/09/24 14:15:28 jsb]
74 *
75 * Revision 2.11 91/10/09 16:11:47 af
76 * Everyone gets as many syscalls as mips, needed
77 * on e.g. vax and sun for AFS support.
78 * [91/10/07 af]
79 *
80 * Revision 2.10 91/08/24 12:00:20 af
81 * Cast tags for bcopy
82 * [91/08/14 rvb]
83 *
84 * Revision 2.9 91/06/25 10:29:13 rpd
85 * Fixed the includes.
86 * [91/06/24 rpd]
87 *
88 * Revision 2.8 91/06/06 17:07:33 jsb
89 * Added task_get_emulation_vector, task_set_emulation_vector.
90 * Task_set_emulation is now a call to task_set_emulation_vector.
91 * [91/05/24 18:30:16 jsb]
92 *
93 * Revision 2.7 91/05/18 14:33:39 rpd
94 * Fixed eml_task_deallocate to always unlock.
95 * [91/05/02 rpd]
96 *
97 * Revision 2.6 91/05/14 16:47:09 mrt
98 * Correcting copyright
99 *
100 * Revision 2.5 91/02/05 17:29:26 mrt
101 * Changed to new Mach copyright
102 * [91/02/01 16:17:54 mrt]
103 *
104 * Revision 2.3 90/12/05 20:42:15 af
105 * I beg your pardon, Ultrix uses up to syscall #257 inclusive.
106 * [90/12/03 22:57:35 af]
107 *
108 * Revision 2.2 89/11/29 14:09:18 af
109 * For mips, grow the max syscall limit, as Ultrix uses up to 256.
110 * Rcs-ed.
111 * [89/11/16 af]
112 *
113 */
114
115 #include <mach/error.h>
116 #include <mach/vm_param.h>
117 #include <kern/syscall_emulation.h>
118 #include <kern/task.h>
119 #include <kern/kalloc.h>
120 #include <kern/memory.h>
121 #include <vm/vm_kern.h>
122 #include <machine/thread.h> /* for syscall_emulation_sync */
123
124
125
126 /*
127 * WARNING:
128 * This code knows that kalloc() allocates memory most efficiently
129 * in sizes that are powers of 2, and asks for those sizes.
130 */
131
132 /*
133 * Go from number of entries to size of struct eml_dispatch and back.
134 */
135 #define base_size (sizeof(struct eml_dispatch) - sizeof(eml_routine_t))
136 #define count_to_size(count) \
137 (base_size + sizeof(vm_offset_t) * (count))
138
139 #define size_to_count(size) \
140 ( ((size) - base_size) / sizeof(vm_offset_t) )
141
142 /*
143 * eml_init: initialize user space emulation code
144 */
145 void eml_init(void)
146 {
147 }
148
149 /*
150 * eml_task_reference() [Exported]
151 *
152 * Bumps the reference count on the common emulation
153 * vector.
154 */
155
156 void eml_task_reference(
157 task_t task,
158 task_t parent)
159 {
160 register eml_dispatch_t eml;
161
162 if (parent == TASK_NULL)
163 eml = EML_DISPATCH_NULL;
164 else
165 eml = parent->eml_dispatch;
166
167 if (eml != EML_DISPATCH_NULL) {
168 simple_lock(&eml->lock);
169 eml->ref_count++;
170 simple_unlock(&eml->lock);
171 }
172 task->eml_dispatch = eml;
173 }
174
175
176 /*
177 * eml_task_deallocate() [Exported]
178 *
179 * Cleans up after the emulation code when a process exits.
180 */
181
182 void eml_task_deallocate(
183 task_t task)
184 {
185 register eml_dispatch_t eml;
186
187 eml = task->eml_dispatch;
188 if (eml != EML_DISPATCH_NULL) {
189 int count;
190
191 simple_lock(&eml->lock);
192 count = --eml->ref_count;
193 simple_unlock(&eml->lock);
194
195 if (count == 0)
196 kfree((vm_offset_t)eml, count_to_size(eml->disp_count));
197 }
198 }
199
200 /*
201 * task_set_emulation_vector: [Server Entry]
202 * set a list of emulated system calls for this task.
203 */
204 kern_return_t
205 task_set_emulation_vector_internal(
206 task_t task,
207 int vector_start,
208 emulation_vector_t emulation_vector,
209 natural_t emulation_vector_count)
210 {
211 eml_dispatch_t cur_eml, new_eml, old_eml;
212 vm_size_t new_size;
213 int cur_start, cur_end;
214 int new_start = 0, new_end = 0;
215 int vector_end;
216
217 if (task == TASK_NULL)
218 return KERN_INVALID_ARGUMENT;
219
220 vector_end = vector_start + emulation_vector_count;
221
222 /*
223 * We try to re-use the existing emulation vector
224 * if possible. We can reuse the vector if it
225 * is not shared with another task and if it is
226 * large enough to contain the entries we are
227 * supplying.
228 *
229 * We must grab the lock on the task to check whether
230 * there is an emulation vector.
231 * If the vector is shared or not large enough, we
232 * need to drop the lock and allocate a new emulation
233 * vector.
234 *
235 * While the lock is dropped, the emulation vector
236 * may be released by all other tasks (giving us
237 * exclusive use), or may be enlarged by another
238 * task_set_emulation_vector call. Therefore,
239 * after allocating the new emulation vector, we
240 * must grab the lock again to check whether we
241 * really need the new vector we just allocated.
242 *
243 * Since an emulation vector cannot be altered
244 * if it is in use by more than one task, the
245 * task lock is sufficient to protect the vector`s
246 * start, count, and contents. The lock in the
247 * vector protects only the reference count.
248 */
249
250 old_eml = EML_DISPATCH_NULL; /* vector to discard */
251 new_eml = EML_DISPATCH_NULL; /* new vector */
252
253 for (;;) {
254 /*
255 * Find the current emulation vector.
256 * See whether we can overwrite it.
257 */
258 task_lock(task);
259 cur_eml = task->eml_dispatch;
260 if (cur_eml != EML_DISPATCH_NULL) {
261 cur_start = cur_eml->disp_min;
262 cur_end = cur_eml->disp_count + cur_start;
263
264 simple_lock(&cur_eml->lock);
265 if (cur_eml->ref_count == 1 &&
266 cur_start <= vector_start &&
267 cur_end >= vector_end)
268 {
269 /*
270 * Can use the existing emulation vector.
271 * Discard any new one we allocated.
272 */
273 simple_unlock(&cur_eml->lock);
274 old_eml = new_eml;
275 break;
276 }
277
278 if (new_eml != EML_DISPATCH_NULL &&
279 new_start <= cur_start &&
280 new_end >= cur_end)
281 {
282 /*
283 * A new vector was allocated, and it is large enough
284 * to hold all the entries from the current vector.
285 * Copy the entries to the new emulation vector,
286 * deallocate the current one, and use the new one.
287 */
288 bcopy(&cur_eml->disp_vector[0],
289 &new_eml->disp_vector[cur_start-new_start],
290 cur_eml->disp_count * sizeof(vm_offset_t));
291
292 if (--cur_eml->ref_count == 0)
293 old_eml = cur_eml; /* discard old vector */
294 simple_unlock(&cur_eml->lock);
295
296 task->eml_dispatch = new_eml;
297 syscall_emulation_sync(task);
298 cur_eml = new_eml;
299 break;
300 }
301 simple_unlock(&cur_eml->lock);
302
303 /*
304 * Need a new emulation vector.
305 * Ensure it will hold all the entries from
306 * both the old and new emulation vectors.
307 */
308 new_start = vector_start;
309 if (new_start > cur_start)
310 new_start = cur_start;
311 new_end = vector_end;
312 if (new_end < cur_end)
313 new_end = cur_end;
314 }
315 else {
316 /*
317 * There is no current emulation vector.
318 * If a new one was allocated, use it.
319 */
320 if (new_eml != EML_DISPATCH_NULL) {
321 task->eml_dispatch = new_eml;
322 cur_eml = new_eml;
323 break;
324 }
325
326 /*
327 * Compute the size needed for the new vector.
328 */
329 new_start = vector_start;
330 new_end = vector_end;
331 }
332
333 /*
334 * Have no vector (or one that is no longer large enough).
335 * Drop all the locks and allocate a new vector.
336 * Repeat the loop to check whether the old vector was
337 * changed while we didn`t hold the locks.
338 */
339
340 task_unlock(task);
341
342 if (new_eml != EML_DISPATCH_NULL)
343 kfree((vm_offset_t)new_eml, count_to_size(new_eml->disp_count));
344
345 new_size = count_to_size(new_end - new_start);
346 new_eml = (eml_dispatch_t) kalloc(new_size);
347
348 bzero(new_eml, new_size);
349 simple_lock_init(&new_eml->lock);
350 new_eml->ref_count = 1;
351 new_eml->disp_min = new_start;
352 new_eml->disp_count = new_end - new_start;
353
354 continue;
355 }
356
357 /*
358 * We have the emulation vector.
359 * Install the new emulation entries.
360 */
361 bcopy(&emulation_vector[0],
362 &cur_eml->disp_vector[vector_start - cur_eml->disp_min],
363 emulation_vector_count * sizeof(vm_offset_t));
364
365 task_unlock(task);
366
367 /*
368 * Discard any old emulation vector we don`t need.
369 */
370 if (old_eml)
371 kfree((vm_offset_t) old_eml, count_to_size(old_eml->disp_count));
372
373 return KERN_SUCCESS;
374 }
375
376 /*
377 * task_set_emulation_vector: [Server Entry]
378 *
379 * Set the list of emulated system calls for this task.
380 * The list is out-of-line.
381 */
382 kern_return_t
383 task_set_emulation_vector(
384 task_t task,
385 int vector_start,
386 emulation_vector_t emulation_vector,
387 natural_t emulation_vector_count)
388 {
389 kern_return_t kr;
390 vm_offset_t emul_vector_addr;
391
392 if (task == TASK_NULL)
393 return KERN_INVALID_ARGUMENT;
394
395 /*
396 * The emulation vector is really a vm_map_copy_t.
397 */
398 kr = vm_map_copyout(ipc_kernel_map, &emul_vector_addr,
399 (vm_map_copy_t) emulation_vector);
400 if (kr != KERN_SUCCESS)
401 return kr;
402
403 /*
404 * Do the work.
405 */
406 kr = task_set_emulation_vector_internal(
407 task,
408 vector_start,
409 (emulation_vector_t) emul_vector_addr,
410 emulation_vector_count);
411
412 /*
413 * Discard the memory
414 */
415 (void) kmem_free(ipc_kernel_map,
416 emul_vector_addr,
417 emulation_vector_count * sizeof(eml_dispatch_t));
418
419 return kr;
420 }
421
422 /*
423 * Compatibility entry. Vector is passed inline.
424 */
425 kern_return_t
426 xxx_task_set_emulation_vector(task, vector_start, emulation_vector,
427 emulation_vector_count)
428 task_t task;
429 int vector_start;
430 emulation_vector_t emulation_vector;
431 unsigned int emulation_vector_count;
432 {
433 return task_set_emulation_vector_internal(
434 task,
435 vector_start,
436 emulation_vector,
437 emulation_vector_count);
438 }
439
440 /*
441 * task_get_emulation_vector: [Server Entry]
442 *
443 * Get the list of emulated system calls for this task.
444 * List is returned out-of-line.
445 */
446 kern_return_t
447 task_get_emulation_vector(
448 task_t task,
449 int *vector_start, /* out */
450 emulation_vector_t *emulation_vector, /* out */
451 natural_t *emulation_vector_count) /* out */
452 {
453 eml_dispatch_t eml;
454 vm_size_t vector_size, size;
455 vm_offset_t addr;
456
457 if (task == TASK_NULL)
458 return KERN_INVALID_ARGUMENT;
459
460 addr = 0;
461 size = 0;
462
463 for(;;) {
464 vm_size_t size_needed;
465
466 task_lock(task);
467 eml = task->eml_dispatch;
468 if (eml == EML_DISPATCH_NULL) {
469 task_unlock(task);
470 if (addr)
471 (void) kmem_free(ipc_kernel_map, addr, size);
472 *vector_start = 0;
473 *emulation_vector = 0;
474 *emulation_vector_count = 0;
475 return KERN_SUCCESS;
476 }
477
478 /*
479 * Do we have the memory we need?
480 */
481 vector_size = eml->disp_count * sizeof(vm_offset_t);
482
483 size_needed = round_page(vector_size);
484 if (size_needed <= size)
485 break;
486
487 /*
488 * If not, unlock the task and allocate more memory.
489 */
490 task_unlock(task);
491
492 if (size != 0)
493 kmem_free(ipc_kernel_map, addr, size);
494
495 size = size_needed;
496 if (kmem_alloc(ipc_kernel_map, &addr, size) != KERN_SUCCESS)
497 return KERN_RESOURCE_SHORTAGE;
498 }
499
500 /*
501 * Copy out the dispatch addresses
502 */
503 *vector_start = eml->disp_min;
504 *emulation_vector_count = eml->disp_count;
505 bcopy(eml->disp_vector,
506 (void *) addr,
507 vector_size);
508
509 /*
510 * Unlock the task and free any memory we did not need
511 */
512 task_unlock(task);
513
514 {
515 vm_size_t size_used, size_left;
516 vm_map_copy_t memory;
517
518 /*
519 * Free any unused memory beyond the end of the last page used
520 */
521 size_used = round_page(vector_size);
522 if (size_used != size)
523 (void) kmem_free(ipc_kernel_map,
524 addr + size_used,
525 size - size_used);
526
527 /*
528 * Zero the remainder of the page being returned.
529 */
530 size_left = size_used - vector_size;
531 if (size_left > 0)
532 bzero((void *) (addr + vector_size), size_left);
533
534 /*
535 * Make memory into copyin form - this unwires it.
536 */
537 (void) vm_map_copyin(ipc_kernel_map, addr, vector_size, TRUE, &memory);
538
539 *emulation_vector = (emulation_vector_t) memory;
540 }
541
542 return KERN_SUCCESS;
543 }
544
545 /*
546 * xxx_task_get_emulation: [Server Entry]
547 * get the list of emulated system calls for this task.
548 * Compatibility code: return list in-line.
549 */
550 kern_return_t
551 xxx_task_get_emulation_vector(
552 task_t task,
553 int *vector_start,
554 emulation_vector_t emulation_vector, /* pointer to OUT array */
555 natural_t *emulation_vector_count) /*IN/OUT*/
556 {
557 register eml_dispatch_t eml;
558
559 if (task == TASK_NULL)
560 return KERN_INVALID_ARGUMENT;
561
562 task_lock(task);
563
564 eml = task->eml_dispatch;
565 if (eml == EML_DISPATCH_NULL) {
566 task_unlock(task);
567 *vector_start = 0;
568 *emulation_vector_count = 0;
569 return KERN_SUCCESS;
570 }
571
572 simple_lock(&eml->lock);
573
574 if (*emulation_vector_count < eml->disp_count) {
575 simple_unlock(&eml->lock);
576 task_unlock(task);
577 return KERN_INVALID_ARGUMENT;
578 }
579
580 *vector_start = eml->disp_min;
581 *emulation_vector_count = eml->disp_count;
582 bcopy(eml->disp_vector,
583 emulation_vector,
584 *emulation_vector_count * sizeof(vm_offset_t));
585 simple_unlock(&eml->lock);
586
587 task_unlock(task);
588
589 return KERN_SUCCESS;
590 }
591
592 /*
593 * task_set_emulation: [Server Entry]
594 * set up for user space emulation of syscalls within this task.
595 */
596 kern_return_t task_set_emulation(
597 task_t task,
598 vm_offset_t routine_entry_pt,
599 int routine_number)
600 {
601 return task_set_emulation_vector_internal(task, routine_number,
602 &routine_entry_pt, 1);
603 }
Cache object: b14de6ec8427ca790f1870a4cceeaed4
|