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