/* * Mach Operating System * Copyright (c) 1991,1990,1989 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ /* */ /* * File: kern/ipc_kobject.c * Author: Rich Draves * Date: 1989 * * Functions for letting a port represent a kernel object. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if MACH_DEBUG #include #endif #if MACH_MACHINE_ROUTINES #include #include MACHINE_SERVER_HEADER #endif /* * Routine: ipc_kobject_server * Purpose: * Handle a message sent to the kernel. * Generates a reply message. * Conditions: * Nothing locked. */ ipc_kmsg_t ipc_kobject_server(request) ipc_kmsg_t request; { mach_msg_size_t reply_size = ikm_less_overhead(8192); ipc_kmsg_t reply; kern_return_t kr; mig_routine_t routine; ipc_port_t *destp; reply = ikm_alloc(reply_size); if (reply == IKM_NULL) { printf("ipc_kobject_server: dropping request\n"); ipc_kmsg_destroy(request); return IKM_NULL; } ikm_init(reply, reply_size); /* * Initialize reply message. */ { #define InP ((mach_msg_header_t *) &request->ikm_header) #define OutP ((mig_reply_header_t *) &reply->ikm_header) static mach_msg_type_t RetCodeType = { /* msgt_name = */ MACH_MSG_TYPE_INTEGER_32, /* msgt_size = */ 32, /* msgt_number = */ 1, /* msgt_inline = */ TRUE, /* msgt_longform = */ FALSE, /* msgt_unused = */ 0 }; OutP->Head.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_LOCAL(InP->msgh_bits), 0); OutP->Head.msgh_size = sizeof(mig_reply_header_t); OutP->Head.msgh_remote_port = InP->msgh_local_port; OutP->Head.msgh_local_port = MACH_PORT_NULL; OutP->Head.msgh_seqno = 0; OutP->Head.msgh_id = InP->msgh_id + 100; #if 0 if (InP->msgh_id) { static long _calls; static struct { long id, count; } _counts[512]; int i, id; id = InP->msgh_id; for (i = 0; i < 511; i++) { if (_counts[i].id == 0) { _counts[i].id = id; _counts[i].count++; break; } if (_counts[i].id == id) { _counts[i].count++; break; } } if (i == 511) { _counts[i].id = id; _counts[i].count++; } if ((++_calls & 0x7fff) == 0) for (i = 0; i < 512; i++) { if (_counts[i].id == 0) break; printf("%d: %d\n", _counts[i].id, _counts[i].count); } } #endif OutP->RetCodeType = RetCodeType; #undef InP #undef OutP } /* * Find the server routine to call, and call it * to perform the kernel function */ { check_simple_locks(); if ((routine = mach_server_routine(&request->ikm_header)) != 0 || (routine = mach_port_server_routine(&request->ikm_header)) != 0 || (routine = mach_host_server_routine(&request->ikm_header)) != 0 || (routine = device_server_routine(&request->ikm_header)) != 0 || (routine = device_pager_server_routine(&request->ikm_header)) != 0 #if MACH_DEBUG || (routine = mach_debug_server_routine(&request->ikm_header)) != 0 #endif /* MACH_DEBUG */ || (routine = mach4_server_routine(&request->ikm_header)) != 0 || (routine = gnumach_server_routine(&request->ikm_header)) != 0 || (routine = experimental_server_routine(&request->ikm_header)) != 0 #if MACH_MACHINE_ROUTINES || (routine = MACHINE_SERVER_ROUTINE(&request->ikm_header)) != 0 #endif /* MACH_MACHINE_ROUTINES */ ) { (*routine)(&request->ikm_header, &reply->ikm_header); kernel_task->messages_received++; } else { if (!ipc_kobject_notify(&request->ikm_header, &reply->ikm_header)) { ((mig_reply_header_t *) &reply->ikm_header)->RetCode = MIG_BAD_ID; #if MACH_IPC_TEST printf("ipc_kobject_server: bogus kernel message, id=%d\n", request->ikm_header.msgh_id); #endif /* MACH_IPC_TEST */ } else { kernel_task->messages_received++; } } kernel_task->messages_sent++; } check_simple_locks(); /* * Destroy destination. The following code differs from * ipc_object_destroy in that we release the send-once * right instead of generating a send-once notification * (which would bring us here again, creating a loop). * It also differs in that we only expect send or * send-once rights, never receive rights. * * We set msgh_remote_port to IP_NULL so that the kmsg * destroy routines don't try to destroy the port twice. */ destp = (ipc_port_t *) &request->ikm_header.msgh_remote_port; switch (MACH_MSGH_BITS_REMOTE(request->ikm_header.msgh_bits)) { case MACH_MSG_TYPE_PORT_SEND: ipc_port_release_send(*destp); break; case MACH_MSG_TYPE_PORT_SEND_ONCE: ipc_port_release_sonce(*destp); break; default: #if MACH_ASSERT assert(!"ipc_object_destroy: strange destination rights"); #else panic("ipc_object_destroy: strange destination rights"); #endif } *destp = IP_NULL; kr = ((mig_reply_header_t *) &reply->ikm_header)->RetCode; if ((kr == KERN_SUCCESS) || (kr == MIG_NO_REPLY)) { /* * The server function is responsible for the contents * of the message. The reply port right is moved * to the reply message, and we have deallocated * the destination port right, so we just need * to free the kmsg. */ /* like ipc_kmsg_put, but without the copyout */ ikm_check_initialized(request, request->ikm_size); if ((request->ikm_size == IKM_SAVED_KMSG_SIZE) && (ikm_cache() == IKM_NULL)) ikm_cache() = request; else ikm_free(request); } else { /* * The message contents of the request are intact. * Destroy everything except the reply port right, * which is needed in the reply message. */ request->ikm_header.msgh_local_port = MACH_PORT_NULL; ipc_kmsg_destroy(request); } if (kr == MIG_NO_REPLY) { /* * The server function will send a reply message * using the reply port right, which it has saved. */ ikm_free(reply); return IKM_NULL; } else if (!IP_VALID((ipc_port_t)reply->ikm_header.msgh_remote_port)) { /* * Can't queue the reply message if the destination * (the reply port) isn't valid. */ ipc_kmsg_destroy(reply); return IKM_NULL; } return reply; } /* * Routine: ipc_kobject_set * Purpose: * Make a port represent a kernel object of the given type. * The caller is responsible for handling refs for the * kernel object, if necessary. * Conditions: * Nothing locked. The port must be active. */ void ipc_kobject_set(port, kobject, type) ipc_port_t port; ipc_kobject_t kobject; ipc_kobject_type_t type; { ip_lock(port); assert(ip_active(port)); port->ip_bits = (port->ip_bits &~ IO_BITS_KOTYPE) | type; port->ip_kobject = kobject; ip_unlock(port); } /* * Routine: ipc_kobject_destroy * Purpose: * Release any kernel object resources associated * with the port, which is being destroyed. * * This should only be needed when resources are * associated with a user's port. In the normal case, * when the kernel is the receiver, the code calling * ipc_port_dealloc_kernel should clean up the resources. * Conditions: * The port is not locked, but it is dead. */ void ipc_kobject_destroy( ipc_port_t port) { switch (ip_kotype(port)) { case IKOT_PAGER: vm_object_destroy(port); break; case IKOT_PAGER_TERMINATING: vm_object_pager_wakeup(port); break; default: #if MACH_ASSERT printf("ipc_kobject_destroy: port 0x%p, kobj 0x%lx, type %d\n", port, port->ip_kobject, ip_kotype(port)); #endif /* MACH_ASSERT */ break; } } /* * Routine: ipc_kobject_notify * Purpose: * Deliver notifications to kobjects that care about them. */ boolean_t ipc_kobject_notify(request_header, reply_header) mach_msg_header_t *request_header; mach_msg_header_t *reply_header; { ipc_port_t port = (ipc_port_t) request_header->msgh_remote_port; ((mig_reply_header_t *) reply_header)->RetCode = MIG_NO_REPLY; switch (request_header->msgh_id) { case MACH_NOTIFY_PORT_DELETED: case MACH_NOTIFY_MSG_ACCEPTED: case MACH_NOTIFY_PORT_DESTROYED: case MACH_NOTIFY_NO_SENDERS: case MACH_NOTIFY_SEND_ONCE: case MACH_NOTIFY_DEAD_NAME: break; default: return FALSE; } switch (ip_kotype(port)) { case IKOT_DEVICE: return ds_notify(request_header); case IKOT_PAGER_PROXY: return memory_object_proxy_notify(request_header); default: return FALSE; } }