]>
Commit | Line | Data |
---|---|---|
1c79356b A |
1 | /* |
2 | * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
de355530 A |
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. | |
1c79356b | 11 | * |
de355530 A |
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 | |
1c79356b A |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
de355530 A |
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. | |
1c79356b A |
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 */ | |
84 | kern_return_t | |
85 | task_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 | */ | |
94 | void | |
95 | eml_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 | ||
106 | void | |
107 | eml_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 | ||
133 | void | |
134 | eml_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 | */ | |
158 | kern_return_t | |
159 | task_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 | */ | |
338 | kern_return_t | |
339 | task_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, | |
de355530 A |
377 | trunc_page(emul_vector_addr), |
378 | round_page(emul_vector_addr + | |
379 | emulation_vector_count * | |
380 | sizeof(eml_dispatch_t)), | |
1c79356b A |
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 | */ | |
410 | kern_return_t | |
411 | task_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 | ||
de355530 | 447 | size_needed = round_page(vector_size); |
1c79356b A |
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 | */ | |
de355530 | 484 | size_used = round_page(vector_size); |
1c79356b A |
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 | */ | |
514 | kern_return_t | |
515 | task_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 |