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