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