]> git.saurik.com Git - apple/xnu.git/blame - osfmk/kern/ipc_kobject.c
xnu-517.tar.gz
[apple/xnu.git] / osfmk / kern / ipc_kobject.c
CommitLineData
1c79356b
A
1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
43866e37 6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
1c79356b 7 *
43866e37
A
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 Carnegie Mellon University
31 * All Rights Reserved.
32 *
33 * Permission to use, copy, modify and distribute 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 * File: kern/ipc_kobject.c
57 * Author: Rich Draves
58 * Date: 1989
59 *
60 * Functions for letting a port represent a kernel object.
61 */
62
63#include <mach_debug.h>
64#include <mach_ipc_test.h>
65#include <mach_machine_routines.h>
66#include <norma_task.h>
67#include <mach_rt.h>
68#include <platforms.h>
69
9bccf70c 70#include <mach/mig.h>
1c79356b
A
71#include <mach/port.h>
72#include <mach/kern_return.h>
73#include <mach/message.h>
74#include <mach/mig_errors.h>
75#include <mach/notify.h>
9bccf70c 76
1c79356b
A
77#include <kern/etap_macros.h>
78#include <kern/ipc_mig.h>
79#include <kern/ipc_kobject.h>
55e303ae 80#include <kern/host_notify.h>
1c79356b 81#include <kern/mk_timer.h>
55e303ae 82#include <kern/misc_protos.h>
1c79356b
A
83#include <ipc/ipc_kmsg.h>
84#include <ipc/ipc_port.h>
85#include <kern/counters.h>
86
87
1c79356b
A
88/*
89 * Routine: ipc_kobject_notify
90 * Purpose:
91 * Deliver notifications to kobjects that care about them.
92 */
93boolean_t
94ipc_kobject_notify(
95 mach_msg_header_t *request_header,
96 mach_msg_header_t *reply_header);
97
98#include <mach/ndr.h>
99
100typedef struct {
101 mach_msg_id_t num;
102 mig_routine_t routine;
103 int size;
104#if MACH_COUNTERS
105 mach_counter_t callcount;
106#endif
107} mig_hash_t;
108
109#define MAX_MIG_ENTRIES 1024
110#define MIG_HASH(x) (x)
111
112#ifndef max
113#define max(a,b) (((a) > (b)) ? (a) : (b))
114#endif /* max */
115
116mig_hash_t mig_buckets[MAX_MIG_ENTRIES];
117int mig_table_max_displ;
118mach_msg_size_t mig_reply_size;
119
120
121#include <mach/mach_port_server.h>
122#include <mach/mach_host_server.h>
123#include <mach/host_priv_server.h>
124#include <mach/host_security_server.h>
125#include <mach/clock_server.h>
126#include <mach/clock_priv_server.h>
127#include <mach/ledger_server.h>
128#include <mach/lock_set_server.h>
129#include <default_pager/default_pager_object_server.h>
130#include <mach/memory_object_server.h>
131#include <mach/memory_object_control_server.h>
132#include <mach/memory_object_default_server.h>
133#include <mach/memory_object_name_server.h>
134#include <mach/processor_server.h>
135#include <mach/processor_set_server.h>
136#include <mach/semaphore_server.h>
137#include <mach/task_server.h>
138#include <mach/vm_map_server.h>
139#include <mach/thread_act_server.h>
140#include <device/device_server.h>
141#include <UserNotification/UNDReplyServer.h>
142
143#if MACH_MACHINE_ROUTINES
144#include <machine/machine_routines.h>
145#endif /* MACH_MACHINE_ROUTINES */
146#if XK_PROXY
147#include <uk_xkern/xk_uproxy_server.h>
148#endif /* XK_PROXY */
149
150
9bccf70c
A
151mig_subsystem_t mig_e[] = {
152 (mig_subsystem_t)&mach_port_subsystem,
153 (mig_subsystem_t)&mach_host_subsystem,
154 (mig_subsystem_t)&host_priv_subsystem,
155 (mig_subsystem_t)&host_security_subsystem,
156 (mig_subsystem_t)&clock_subsystem,
157 (mig_subsystem_t)&clock_priv_subsystem,
158 (mig_subsystem_t)&processor_subsystem,
159 (mig_subsystem_t)&processor_set_subsystem,
160 (mig_subsystem_t)&is_iokit_subsystem,
161 (mig_subsystem_t)&memory_object_name_subsystem,
162 (mig_subsystem_t)&lock_set_subsystem,
163 (mig_subsystem_t)&ledger_subsystem,
164 (mig_subsystem_t)&semaphore_subsystem,
165 (mig_subsystem_t)&task_subsystem,
166 (mig_subsystem_t)&thread_act_subsystem,
167 (mig_subsystem_t)&vm_map_subsystem,
168 (mig_subsystem_t)&UNDReply_subsystem,
1c79356b
A
169
170#if XK_PROXY
9bccf70c 171 (mig_subsystem_t)&do_uproxy_xk_uproxy_subsystem,
1c79356b
A
172#endif /* XK_PROXY */
173#if MACH_MACHINE_ROUTINES
9bccf70c 174 (mig_subsystem_t)&MACHINE_SUBSYSTEM,
1c79356b
A
175#endif /* MACH_MACHINE_ROUTINES */
176#if MCMSG && iPSC860
9bccf70c 177 (mig_subsystem_t)&mcmsg_info_subsystem,
1c79356b
A
178#endif /* MCMSG && iPSC860 */
179};
180
181void
182mig_init(void)
183{
9bccf70c 184 register unsigned int i, n = sizeof(mig_e)/sizeof(mig_subsystem_t);
1c79356b
A
185 register unsigned int howmany;
186 register mach_msg_id_t j, pos, nentry, range;
187
188 for (i = 0; i < n; i++) {
189 range = mig_e[i]->end - mig_e[i]->start;
190 if (!mig_e[i]->start || range < 0)
191 panic("the msgh_ids in mig_e[] aren't valid!");
192 mig_reply_size = max(mig_reply_size, mig_e[i]->maxsize);
193
194 for (j = 0; j < range; j++) {
195 if (mig_e[i]->routine[j].stub_routine) {
196 /* Only put real entries in the table */
197 nentry = j + mig_e[i]->start;
198 for (pos = MIG_HASH(nentry) % MAX_MIG_ENTRIES, howmany = 1;
199 mig_buckets[pos].num;
200 pos = ++pos % MAX_MIG_ENTRIES, howmany++) {
201 if (mig_buckets[pos].num == nentry) {
202 printf("message id = %d\n", nentry);
203 panic("multiple entries with the same msgh_id");
204 }
205 if (howmany == MAX_MIG_ENTRIES)
206 panic("the mig dispatch table is too small");
207 }
208
209 mig_buckets[pos].num = nentry;
210 mig_buckets[pos].routine = mig_e[i]->routine[j].stub_routine;
211 if (mig_e[i]->routine[j].max_reply_msg)
212 mig_buckets[pos].size = mig_e[i]->routine[j].max_reply_msg;
213 else
214 mig_buckets[pos].size = mig_e[i]->maxsize;
215
216 mig_table_max_displ = max(howmany, mig_table_max_displ);
217 }
218 }
219 }
0b4e3aa0 220 printf("mig_table_max_displ = %d\n", mig_table_max_displ);
1c79356b
A
221}
222
223
224/*
225 * Routine: ipc_kobject_server
226 * Purpose:
227 * Handle a message sent to the kernel.
228 * Generates a reply message.
229 * Version for Untyped IPC.
230 * Conditions:
231 * Nothing locked.
232 */
233
234ipc_kmsg_t
235ipc_kobject_server(
236 ipc_kmsg_t request)
237{
238 mach_msg_size_t reply_size;
239 ipc_kmsg_t reply;
240 kern_return_t kr;
241 mig_routine_t routine;
242 ipc_port_t *destp;
243 mach_msg_format_0_trailer_t *trailer;
244 register mig_hash_t *ptr;
245 unsigned int th;
246
247 /* Only fetch current thread if ETAP is configured */
248 ETAP_DATA_LOAD(th, current_thread());
249 ETAP_PROBE_DATA(ETAP_P_SYSCALL_MACH,
250 EVENT_BEGIN,
251 ((thread_t) th),
252 &request->ikm_header.msgh_id,
253 sizeof(int));
254 /*
255 * Find out corresponding mig_hash entry if any
256 */
257 {
258 register int key = request->ikm_header.msgh_id;
259 register int i = MIG_HASH(key);
260 register int max_iter = mig_table_max_displ;
261
262 do
263 ptr = &mig_buckets[i++ % MAX_MIG_ENTRIES];
264 while (key != ptr->num && ptr->num && --max_iter);
265
266 if (!ptr->routine || key != ptr->num) {
267 ptr = (mig_hash_t *)0;
268 reply_size = mig_reply_size;
269 } else {
270 reply_size = ptr->size;
271#if MACH_COUNTER
272 ptr->callcount++;
273#endif
274 }
275 }
276
277 /* round up for trailer size */
278 reply_size += MAX_TRAILER_SIZE;
279 reply = ipc_kmsg_alloc(reply_size);
280
281 if (reply == IKM_NULL) {
282 printf("ipc_kobject_server: dropping request\n");
283 ipc_kmsg_destroy(request);
284 return IKM_NULL;
285 }
286
287 /*
288 * Initialize reply message.
289 */
290 {
291#define InP ((mach_msg_header_t *) &request->ikm_header)
292#define OutP ((mig_reply_error_t *) &reply->ikm_header)
293
294 OutP->NDR = NDR_record;
295 OutP->Head.msgh_size = sizeof(mig_reply_error_t);
296
297 OutP->Head.msgh_bits =
298 MACH_MSGH_BITS(MACH_MSGH_BITS_LOCAL(InP->msgh_bits), 0);
299 OutP->Head.msgh_remote_port = InP->msgh_local_port;
300 OutP->Head.msgh_local_port = MACH_PORT_NULL;
301 OutP->Head.msgh_id = InP->msgh_id + 100;
302
303#undef InP
304#undef OutP
305 }
306
307 /*
308 * Find the routine to call, and call it
309 * to perform the kernel function
310 */
311 {
312 if (ptr) {
313 (*ptr->routine)(&request->ikm_header, &reply->ikm_header);
314 kernel_task->messages_received++;
315 }
316 else {
317 if (!ipc_kobject_notify(&request->ikm_header, &reply->ikm_header)){
318#if MACH_IPC_TEST
319 printf("ipc_kobject_server: bogus kernel message, id=%d\n",
320 request->ikm_header.msgh_id);
321#endif /* MACH_IPC_TEST */
322 _MIG_MSGID_INVALID(request->ikm_header.msgh_id);
323
324 ((mig_reply_error_t *) &reply->ikm_header)->RetCode
325 = MIG_BAD_ID;
326 }
327 else
328 kernel_task->messages_received++;
329 }
330 kernel_task->messages_sent++;
331 }
332
333 /*
334 * Destroy destination. The following code differs from
335 * ipc_object_destroy in that we release the send-once
336 * right instead of generating a send-once notification
337 * (which would bring us here again, creating a loop).
338 * It also differs in that we only expect send or
339 * send-once rights, never receive rights.
340 *
341 * We set msgh_remote_port to IP_NULL so that the kmsg
342 * destroy routines don't try to destroy the port twice.
343 */
344 destp = (ipc_port_t *) &request->ikm_header.msgh_remote_port;
345 switch (MACH_MSGH_BITS_REMOTE(request->ikm_header.msgh_bits)) {
346 case MACH_MSG_TYPE_PORT_SEND:
347 ipc_port_release_send(*destp);
348 break;
349
350 case MACH_MSG_TYPE_PORT_SEND_ONCE:
351 ipc_port_release_sonce(*destp);
352 break;
353
354 default:
9bccf70c 355 panic("ipc_kobject_server: strange destination rights");
1c79356b
A
356 }
357 *destp = IP_NULL;
358
359 if (!(reply->ikm_header.msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
360 ((mig_reply_error_t *) &reply->ikm_header)->RetCode != KERN_SUCCESS)
361 kr = ((mig_reply_error_t *) &reply->ikm_header)->RetCode;
362 else
363 kr = KERN_SUCCESS;
364
365 if ((kr == KERN_SUCCESS) || (kr == MIG_NO_REPLY)) {
366 /*
367 * The server function is responsible for the contents
368 * of the message. The reply port right is moved
369 * to the reply message, and we have deallocated
370 * the destination port right, so we just need
371 * to free the kmsg.
372 */
373 ipc_kmsg_free(request);
374
375 } else {
376 /*
377 * The message contents of the request are intact.
378 * Destroy everthing except the reply port right,
379 * which is needed in the reply message.
380 */
381 request->ikm_header.msgh_local_port = MACH_PORT_NULL;
382 ipc_kmsg_destroy(request);
383 }
384
385 if (kr == MIG_NO_REPLY) {
386 /*
387 * The server function will send a reply message
388 * using the reply port right, which it has saved.
389 */
390
391 ipc_kmsg_free(reply);
392
393 ETAP_PROBE_DATA(ETAP_P_SYSCALL_MACH,
394 EVENT_END,
395 ((thread_t) th),
396 &request->ikm_header.msgh_id,
397 sizeof(int));
398
399 return IKM_NULL;
400 } else if (!IP_VALID((ipc_port_t)reply->ikm_header.msgh_remote_port)) {
401 /*
402 * Can't queue the reply message if the destination
403 * (the reply port) isn't valid.
404 */
405
406 ipc_kmsg_destroy(reply);
407
408 ETAP_PROBE_DATA(ETAP_P_SYSCALL_MACH,
409 EVENT_END,
410 ((thread_t) th),
411 &request->ikm_header.msgh_id,
412 sizeof(int));
413
414 return IKM_NULL;
415 }
416
417 trailer = (mach_msg_format_0_trailer_t *)
418 ((vm_offset_t)&reply->ikm_header + (int)reply->ikm_header.msgh_size);
419 trailer->msgh_sender = KERNEL_SECURITY_TOKEN;
420 trailer->msgh_trailer_type = MACH_MSG_TRAILER_FORMAT_0;
421 trailer->msgh_trailer_size = MACH_MSG_TRAILER_MINIMUM_SIZE;
422
423 ETAP_PROBE_DATA(ETAP_P_SYSCALL_MACH,
424 EVENT_END,
425 ((thread_t) th),
426 &request->ikm_header.msgh_id,
427 sizeof(int));
428
429 return reply;
430}
431
432/*
433 * Routine: ipc_kobject_set
434 * Purpose:
435 * Make a port represent a kernel object of the given type.
436 * The caller is responsible for handling refs for the
437 * kernel object, if necessary.
438 * Conditions:
439 * Nothing locked. The port must be active if setting
440 * a kobject linkage. Clearing a linkage is OK on an
441 * inactive port.
442 */
443void
444ipc_kobject_set(
445 ipc_port_t port,
446 ipc_kobject_t kobject,
447 ipc_kobject_type_t type)
448{
449 ip_lock(port);
450 ipc_kobject_set_atomically(port, kobject, type);
451 ip_unlock(port);
452}
453
454void
455ipc_kobject_set_atomically(
456 ipc_port_t port,
457 ipc_kobject_t kobject,
458 ipc_kobject_type_t type)
459{
460 assert(type == IKOT_NONE || ip_active(port));
461#if MACH_ASSERT
462 port->ip_spares[2] = (port->ip_bits & IO_BITS_KOTYPE);
463#endif /* MACH_ASSERT */
464 port->ip_bits = (port->ip_bits &~ IO_BITS_KOTYPE) | type;
465 port->ip_kobject = kobject;
466}
467
468/*
469 * Routine: ipc_kobject_destroy
470 * Purpose:
471 * Release any kernel object resources associated
472 * with the port, which is being destroyed.
473 *
474 * This should only be needed when resources are
475 * associated with a user's port. In the normal case,
476 * when the kernel is the receiver, the code calling
477 * ipc_port_dealloc_kernel should clean up the resources.
478 * Conditions:
479 * The port is not locked, but it is dead.
480 */
481
482void
483ipc_kobject_destroy(
484 ipc_port_t port)
485{
486 switch (ip_kotype(port)) {
487
488 case IKOT_TIMER:
489 mk_timer_port_destroy(port);
490 break;
491
492 case IKOT_NAMED_ENTRY:
493 mach_destroy_memory_entry(port);
494 break;
495
55e303ae
A
496 case IKOT_HOST_NOTIFY:
497 host_notify_port_destroy(port);
498 break;
499
500 default:
1c79356b
A
501 break;
502 }
503}
504
505
506extern int vnode_pager_workaround;
507
508boolean_t
509ipc_kobject_notify(
510 mach_msg_header_t *request_header,
511 mach_msg_header_t *reply_header)
512{
513 ipc_port_t port = (ipc_port_t) request_header->msgh_remote_port;
9bccf70c 514 mig_subsystem_t paging_subsystem_object;
1c79356b
A
515 mach_port_seqno_t seqno;
516
517 ((mig_reply_error_t *) reply_header)->RetCode = MIG_NO_REPLY;
518 switch (request_header->msgh_id) {
519 case MACH_NOTIFY_NO_SENDERS:
520 if(ip_kotype(port) == IKOT_NAMED_ENTRY) {
521 ip_lock(port);
522
523 /*
524 * Bring the sequence number and mscount in
525 * line with ipc_port_destroy assertion.
526 */
527 port->ip_mscount = 0;
528 port->ip_messages.imq_seqno = 0;
529 ipc_port_destroy(port); /* releases lock */
530 return TRUE;
531 }
0b4e3aa0
A
532 if (ip_kotype(port) == IKOT_UPL) {
533 upl_no_senders(
534 (ipc_port_t)request_header->msgh_remote_port,
535 (mach_port_mscount_t)
536 ((mach_no_senders_notification_t *)
537 request_header)->not_count);
538 (ipc_port_t)reply_header->msgh_remote_port
539 = MACH_PORT_NULL;
540 return TRUE;
1c79356b 541 }
0b4e3aa0 542
1c79356b 543 break;
0b4e3aa0 544
1c79356b
A
545 case MACH_NOTIFY_PORT_DELETED:
546 case MACH_NOTIFY_PORT_DESTROYED:
547 case MACH_NOTIFY_SEND_ONCE:
548 case MACH_NOTIFY_DEAD_NAME:
549 break;
550
551 default:
552 return FALSE;
553 }
554 switch (ip_kotype(port)) {
555
556#ifdef IOKIT
557 case IKOT_IOKIT_OBJECT:
558 case IKOT_IOKIT_CONNECT:
559 case IKOT_IOKIT_SPARE:
560 {
561 extern boolean_t iokit_notify( mach_msg_header_t *msg);
562
563 return iokit_notify(request_header);
564 }
565#endif
566 default:
567 return FALSE;
568 }
569}
570
571
572
573#include <mach_kdb.h>
574#if MACH_COUNTERS && MACH_KDB
575
576#include <ddb/db_output.h>
577#include <ddb/db_sym.h>
578
579#define printf kdbprintf
580
581extern void kobjserver_stats(void);
582extern void bucket_stats_print(mig_hash_t *bucket);
583
584extern void kobjserver_stats_clear(void);
585
586
587void
588kobjserver_stats_clear(void)
589{
590 int i;
591 for (i = 0; i < MAX_MIG_ENTRIES; i++) {
592 mig_buckets[i].callcount = 0;
593 }
594}
595
596void
597kobjserver_stats(void)
598{
9bccf70c 599 register unsigned int i, n = sizeof(mig_e)/sizeof(mig_subsystem_t);
1c79356b
A
600 register unsigned int howmany;
601 register mach_msg_id_t j, pos, nentry, range;
602
603 db_printf("Kobject server call counts:\n");
604 for (i = 0; i < n; i++) {
605 db_printf(" ");
606 db_printsym((vm_offset_t)mig_e[i], DB_STGY_ANY);
607 db_printf(":\n");
608 range = mig_e[i]->end - mig_e[i]->start;
609 if (!mig_e[i]->start || range < 0) continue;
610
611 for (j = 0; j < range; j++) {
612 nentry = j + mig_e[i]->start;
613 for (pos = MIG_HASH(nentry) % MAX_MIG_ENTRIES, howmany = 1;
614 mig_buckets[pos].num;
615 pos = ++pos % MAX_MIG_ENTRIES, howmany++) {
616 if (mig_buckets[pos].num == nentry)
617 bucket_stats_print(&mig_buckets[pos]);
618 }
619 }
620 }
621}
622
623void
624bucket_stats_print(mig_hash_t *bucket)
625{
626 if (bucket->callcount) {
627 db_printf(" ");
628 db_printsym((vm_offset_t)bucket->routine, DB_STGY_ANY);
629 db_printf(" (%d):\t%d\n", bucket->num, bucket->callcount);
630 }
631}
632
633
634#endif /* MACH_COUNTERS && MACH_KDB */