]> git.saurik.com Git - apple/xnu.git/blob - osfmk/kern/subsystem.c
xnu-201.tar.gz
[apple/xnu.git] / osfmk / kern / subsystem.c
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 * HISTORY
27 *
28 * Revision 1.1.1.1 1998/09/22 21:05:32 wsanchez
29 * Import of Mac OS X kernel (~semeria)
30 *
31 * Revision 1.2 1998/04/29 17:36:19 mburg
32 * MK7.3 merger
33 *
34 * Revision 1.1.18.1 1998/02/03 09:30:24 gdt
35 * Merge up to MK7.3
36 * [1998/02/03 09:15:10 gdt]
37 *
38 * Revision 1.1.16.1 1997/06/17 02:59:23 devrcs
39 * Added call to `ipc_subsystem_terminate()' to `subsystem_deallocate()'
40 * to close port leak.
41 * [1997/03/18 18:25:55 rkc]
42 *
43 * Revision 1.1.7.4 1995/01/10 05:14:19 devrcs
44 * mk6 CR801 - merge up from nmk18b4 to nmk18b7
45 * * Rev 1.1.7.3 1994/10/19 16:24:57 watkins
46 * Define subsystem_print if mach_debug.
47 * [1994/12/09 21:01:02 dwm]
48 *
49 * mk6 CR668 - 1.3b26 merge
50 * splx is void.
51 * [1994/11/04 09:32:40 dwm]
52 *
53 * Revision 1.1.7.2 1994/09/23 02:27:05 ezf
54 * change marker to not FREE
55 * [1994/09/22 21:36:22 ezf]
56 *
57 * Revision 1.1.7.1 1994/09/16 15:30:10 emcmanus
58 * Implement "show subsystem" command.
59 * [1994/09/16 15:29:11 emcmanus]
60 *
61 * Revision 1.1.3.4 1994/06/02 01:53:14 bolinger
62 * mk6 CR125: Initialize subsystem_lock().
63 * [1994/06/01 22:30:18 bolinger]
64 *
65 * Revision 1.1.3.3 1994/01/21 01:22:58 condict
66 * Fix too stringent error checking. Change subsys from ool to in-line.
67 * [1994/01/21 01:19:32 condict]
68 *
69 * Revision 1.1.3.2 1994/01/20 16:25:29 condict
70 * Testing bsubmit.
71 * [1994/01/20 16:24:32 condict]
72 *
73 * Revision 1.1.3.1 1994/01/20 11:09:26 emcmanus
74 * Copied for submission.
75 * [1994/01/20 11:08:20 emcmanus]
76 *
77 * Revision 1.1.1.4 1994/01/20 02:45:10 condict
78 * Make user subsystem point at containing system subsytem struct.
79 *
80 * Revision 1.1.1.3 1994/01/15 22:01:19 condict
81 * Validate user subsystem data, convert user ptrs to kernel ptrs.
82 *
83 * Revision 1.1.1.2 1994/01/13 02:39:58 condict
84 * Implementation of RPC subsystem object, for server co-location.
85 *
86 * $EndLog$
87 */
88 /*
89 * Functions to manipulate RPC subsystem descriptions.
90 */
91
92 #include <mach/port.h>
93 #include <mach/kern_return.h>
94 #include <kern/task.h>
95 #include <kern/lock.h>
96 #include <kern/spl.h>
97 #include <kern/zalloc.h>
98 #include <kern/ipc_subsystem.h>
99 #include <kern/subsystem.h>
100 #include <kern/misc_protos.h>
101
102 #define SUBSYSTEM_MIN_SIZE 12
103 #define SUBSYSTEM_MAX_SIZE (2*1024*1024) /* What value is correct? */
104
105 void
106 subsystem_init(
107 void)
108 {
109 /* Nothing to do on bootstrap, at the moment. */
110 }
111
112 /*
113 * Routine: mach_subsystem_create
114 * Purpose:
115 * Create a new RPC subsystem.
116 * Conditions:
117 * Nothing locked. If successful, the subsystem is returned
118 * unlocked. (The caller has a reference.)
119 * Returns:
120 * KERN_SUCCESS The subsystem is allocated.
121 * KERN_INVALID_TASK The task is dead.
122 * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
123 */
124
125 kern_return_t
126 mach_subsystem_create(
127 register task_t parent_task,
128 user_subsystem_t user_subsys,
129 mach_msg_type_number_t user_subsysCount,
130 subsystem_t *subsystem_p)
131 {
132 int i;
133 subsystem_t new_subsystem;
134 kern_return_t kr;
135 boolean_t deallocate = FALSE;
136 vm_size_t size;
137 vm_offset_t offset;
138 int num_routines;
139 boolean_t bad_arg = FALSE;
140
141 if (parent_task == TASK_NULL)
142 return(KERN_INVALID_ARGUMENT);
143
144 if (user_subsysCount < SUBSYSTEM_MIN_SIZE ||
145 user_subsysCount > SUBSYSTEM_MAX_SIZE)
146 return(KERN_INVALID_ARGUMENT);
147
148 /*
149 * Allocate a subsystem and initialize:
150 */
151
152 size = (vm_size_t)user_subsysCount + sizeof(struct subsystem) -
153 sizeof(struct rpc_subsystem);
154 new_subsystem = (subsystem_t) kalloc(size);
155
156 if (new_subsystem == 0)
157 return(KERN_RESOURCE_SHORTAGE);
158
159 new_subsystem->task = parent_task;
160 new_subsystem->ref_count = 1; /* A reference for our caller */
161 new_subsystem->size = size;
162 subsystem_lock_init(new_subsystem);
163
164 /* Copy the user subsystem data to a permanent place: */
165 bcopy((char *)user_subsys, (char *)&(new_subsystem->user),
166 (int)user_subsysCount);
167
168 /* Validate the user-specified fields of the subsystem: */
169
170 num_routines = new_subsystem->user.end - new_subsystem->user.start;
171 if (num_routines < 0 ||
172 (char *)&new_subsystem->user.routine[num_routines] >
173 (char *)&new_subsystem->user + (int)user_subsysCount
174 ) {
175 kfree((vm_offset_t)new_subsystem, size);
176 return(KERN_INVALID_ADDRESS);
177 }
178
179 /* The following is for converting the user pointers in the
180 * subsystem struct to kernel pointers:
181 */
182 offset = (char *)&new_subsystem->user -
183 (char *)new_subsystem->user.base_addr; /* The user addr */
184
185 for (i = 0; i < num_routines; i++) {
186 routine_descriptor_t routine = &new_subsystem->user.routine[i];
187
188 /* If this is a "skip" routine, ignore it: */
189 if (!routine->impl_routine)
190 continue;
191
192 /* Convert the user arg_descr pointer to a kernel pointer: */
193 routine->arg_descr = (routine_arg_descriptor_t)
194 ((char *)routine->arg_descr + offset);
195
196 if (routine->argc > 1000000 ||
197 routine->argc < routine->descr_count) {
198 bad_arg = TRUE;
199 break;
200 }
201 /* Validate that the arg_descr field is within the part of
202 * the struct that follows the routine array: */
203 if ((char *)&routine->arg_descr[0] <
204 (char *)&new_subsystem->user.routine[num_routines]
205 ||
206 (char *)&routine->arg_descr[routine->descr_count] >
207 (char *)&new_subsystem->user + (int)user_subsysCount
208 ) {
209 printf("Arg descr out of bounds: arg_descr=%x, &routine.num_routines=%x\n",
210 &routine->arg_descr[0], &new_subsystem->user.routine[num_routines]);
211 printf(" new_subsys->user + subsysCount = %x\n",
212 (char *)&new_subsystem->user + (int)user_subsysCount);
213 #if MACH_DEBUG && MACH_KDB
214 subsystem_print(new_subsystem);
215 /* Not all of the arg_descr pointers have necessarily
216 been corrected, but this just means that we print
217 the arg_descr from the user's input subsystem
218 instead of the copy we are building. */
219 #endif /* MACH_DEBUG && MACH_KDB */
220 bad_arg = TRUE;
221 break;
222 }
223 }
224 if (bad_arg) {
225 kfree((vm_offset_t)new_subsystem, size);
226 return(KERN_INVALID_ADDRESS);
227 }
228
229 /* Convert the user base address to a kernel address: */
230 new_subsystem->user.base_addr = (vm_address_t)&new_subsystem->user;
231
232 /* Make the user subsystem point at the containing system data
233 * structure, so we can get from a port (which points to the user
234 * subsystem data) to the system subsystem struct:
235 */
236 new_subsystem->user.subsystem = new_subsystem;
237
238 ipc_subsystem_init(new_subsystem);
239
240 task_lock(parent_task);
241 if (parent_task->active) {
242 parent_task->subsystem_count++;
243 queue_enter(&parent_task->subsystem_list, new_subsystem,
244 subsystem_t, subsystem_list);
245 } else
246 deallocate = TRUE;
247 task_unlock(parent_task);
248
249 if (deallocate) {
250 /* release ref we would have given our caller */
251 subsystem_deallocate(new_subsystem);
252 return(KERN_INVALID_TASK);
253 }
254
255 ipc_subsystem_enable(new_subsystem);
256
257 *subsystem_p = new_subsystem;
258 return(KERN_SUCCESS);
259 }
260
261 /*
262 * Routine: subsystem_reference
263 * Purpose:
264 * Increments the reference count on a subsystem.
265 * Conditions:
266 * Nothing is locked.
267 */
268 void
269 subsystem_reference(
270 register subsystem_t subsystem)
271 {
272 spl_t s;
273
274 if (subsystem == SUBSYSTEM_NULL)
275 return;
276
277 s = splsched();
278 subsystem_lock(subsystem);
279 subsystem->ref_count++;
280 subsystem_unlock(subsystem);
281 splx(s);
282 }
283
284
285
286 /*
287 * Routine: subsystem_deallocate
288 * Purpose:
289 * Decrements the reference count on a subsystem. If 0,
290 * destroys the subsystem. Must have no ports registered on it
291 * when it is destroyed.
292 * Conditions:
293 * The subsystem is locked, and
294 * the caller has a reference, which is consumed.
295 */
296
297 void
298 subsystem_deallocate(
299 subsystem_t subsystem)
300 {
301 task_t task;
302 spl_t s;
303
304 if (subsystem == SUBSYSTEM_NULL)
305 return;
306
307 s = splsched();
308 subsystem_lock(subsystem);
309 if (--subsystem->ref_count > 0) {
310 subsystem_unlock(subsystem);
311 splx(s);
312 return;
313 }
314
315 /*
316 * Count is 0, so destroy the subsystem. Need to restore the
317 * reference temporarily, and lock the task first:
318 */
319 ipc_subsystem_disable(subsystem);
320
321 subsystem->ref_count = 1;
322 subsystem_unlock(subsystem);
323 splx(s);
324
325 task = subsystem->task;
326 task_lock(task);
327 s = splsched();
328 subsystem_lock(subsystem);
329
330 /* Check again, since we temporarily unlocked the subsystem: */
331 if (--subsystem->ref_count == 0) {
332
333 task->subsystem_count--;
334 queue_remove(&task->subsystem_list, subsystem, subsystem_t,
335 subsystem_list);
336 ipc_subsystem_terminate(subsystem);
337 subsystem_unlock(subsystem);
338 splx(s);
339 kfree((vm_offset_t) subsystem, subsystem->size);
340 task_unlock(task);
341 return;
342 }
343
344 ipc_subsystem_enable(subsystem);
345
346 subsystem_unlock(subsystem);
347 splx(s);
348 task_unlock(task);
349 }
350
351
352 #include <mach_kdb.h>
353 #if MACH_KDB
354
355 #include <ddb/db_output.h>
356 #include <ddb/db_sym.h>
357 #include <ddb/db_print.h>
358 #include <ddb/db_command.h>
359
360 #define printf kdbprintf
361
362 /*
363 * Routine: subsystem_print
364 * Purpose:
365 * Pretty-print a subsystem for kdb.
366 */
367
368 void rpc_subsystem_print(rpc_subsystem_t subsys);
369
370 void
371 subsystem_print(
372 subsystem_t subsystem)
373 {
374 extern int db_indent;
375
376 iprintf("subsystem 0x%x\n", subsystem);
377
378 db_indent += 2;
379
380 iprintf("ref %d size %x task %x port %x\n", subsystem->ref_count,
381 subsystem->size, subsystem->task, subsystem->ipc_self);
382 rpc_subsystem_print(&subsystem->user);
383
384 /* ipc_object_print(&port->ip_object);
385 * iprintf("receiver=0x%x", port->ip_receiver);
386 * printf(", receiver_name=0x%x\n", port->ip_receiver_name);
387 */
388 db_indent -=2;
389 }
390
391 struct flagnames {
392 char *name;
393 int bit;
394 } arg_type_names[] = {
395 "port", MACH_RPC_PORT, "array", MACH_RPC_ARRAY,
396 "variable", MACH_RPC_VARIABLE, "in", MACH_RPC_IN, "out", MACH_RPC_OUT,
397 "pointer", MACH_RPC_POINTER, "phys_copy", MACH_RPC_PHYSICAL_COPY,
398 "virt_copy", MACH_RPC_VIRTUAL_COPY, "deallocate", MACH_RPC_DEALLOCATE,
399 "onstack", MACH_RPC_ONSTACK, "bounded", MACH_RPC_BOUND,
400 };
401
402 void
403 rpc_subsystem_print(
404 rpc_subsystem_t subsys)
405 {
406 int i, num_routines;
407
408 iprintf("rpc_subsystem 0x%x\n", subsys);
409
410 db_indent += 2;
411
412 num_routines = subsys->end - subsys->start;
413 iprintf("start %d end %d (%d routines) maxsize %x base %x\n",
414 subsys->start, subsys->end, num_routines, subsys->maxsize,
415 subsys->base_addr);
416 for (i = 0; i < num_routines; i++) {
417 routine_descriptor_t routine = subsys->routine + i;
418 routine_arg_descriptor_t args = routine->arg_descr;
419 int j, type, disposition;
420 struct flagnames *n;
421 char *sep;
422
423 iprintf("%x #%d:", routine, subsys->start + i);
424 if (routine->impl_routine == 0) {
425 printf(" skip\n");
426 continue;
427 }
428 printf("\n");
429 db_indent += 2;
430 iprintf("impl ");
431 db_printsym((db_expr_t) routine->impl_routine, DB_STGY_PROC);
432 printf("\n");
433 iprintf("stub ");
434 db_printsym((db_expr_t) routine->stub_routine, DB_STGY_PROC);
435 printf("\n");
436 iprintf("argc %d descr_count %d max_reply %x\n",
437 routine->argc, routine->descr_count, routine->max_reply_msg);
438 for (j = 0; j < routine->descr_count; j++) {
439 iprintf("%x desc %d: size %d count %d offset %x type", &args[j], j,
440 args[j].size, args[j].count, args[j].offset);
441 sep = " ";
442 type = args[j].type;
443 for (n = arg_type_names; n->name != 0; n++) {
444 if (type & n->bit) {
445 printf("%s%s", sep, n->name);
446 sep = "|";
447 type &= ~n->bit; /* Might have an alias */
448 }
449 }
450 #define NAME_MASK (3 << NAME_SHIFT) /* XXX magic numbers */
451 #define ACTION_MASK (3 << ACTION_SHIFT)
452 #define DISPOSITION_MASK (NAME_MASK | ACTION_MASK)
453 disposition = type & DISPOSITION_MASK;
454 type &= ~DISPOSITION_MASK;
455 if (sep[0] != '|' || type != 0)
456 printf("%s%x", sep, type);
457 switch (disposition & ACTION_MASK) {
458 case MACH_RPC_MOVE: printf(" move"); break;
459 case MACH_RPC_COPY: printf(" copy"); break;
460 case MACH_RPC_MAKE: printf(" make"); break;
461 }
462 switch (disposition & NAME_MASK) {
463 case MACH_RPC_RECEIVE: printf(" receive"); break;
464 case MACH_RPC_SEND: printf(" send"); break;
465 case MACH_RPC_SEND_ONCE: printf(" send-once"); break;
466 }
467 printf("\n");
468 }
469 db_indent -= 2;
470 }
471
472 db_indent -= 2;
473 }
474
475 void
476 db_show_subsystem(
477 db_expr_t addr,
478 boolean_t have_addr,
479 db_expr_t count,
480 char *modif)
481 {
482 if (!have_addr || addr == 0) {
483 db_printf("No subsystem\n");
484 return;
485 }
486 if (db_option(modif, 'r'))
487 rpc_subsystem_print((rpc_subsystem_t) addr);
488 else
489 subsystem_print((subsystem_t) addr);
490 }
491
492 #endif /* MACH_KDB || MACH_DEBUG */