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