]>
Commit | Line | Data |
---|---|---|
1c79356b A |
1 | /* |
2 | * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
43866e37 A |
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 | |
1c79356b A |
17 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
18 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
43866e37 A |
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. | |
1c79356b A |
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, | |
de355530 A |
380 | trunc_page(emul_vector_addr), |
381 | round_page(emul_vector_addr + | |
382 | emulation_vector_count * | |
383 | sizeof(eml_dispatch_t)), | |
1c79356b A |
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 | ||
de355530 | 450 | size_needed = round_page(vector_size); |
1c79356b A |
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 | */ | |
de355530 | 487 | size_used = round_page(vector_size); |
1c79356b A |
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 |