X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/1c79356b52d46aa6b508fb032f5ae709b1f2897b..21362eb3e66fd2c787aee132bce100a44d71a99c:/osfmk/kern/ipc_mig.c diff --git a/osfmk/kern/ipc_mig.c b/osfmk/kern/ipc_mig.c index 928832f10..e68fa9f06 100644 --- a/osfmk/kern/ipc_mig.c +++ b/osfmk/kern/ipc_mig.c @@ -1,23 +1,29 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * @OSF_COPYRIGHT@ @@ -50,23 +56,21 @@ /* */ -#include -#include - #include #include -#include +#include #include #include #include -#include + #include #include +#include #include #include #include #include -#include + #include #include #include @@ -75,10 +79,7 @@ #include #include #include - -/* Default (zeroed) template for qos */ - -static mach_port_qos_t qos_template; +#include /* * Routine: mach_msg_send_from_kernel @@ -151,13 +152,9 @@ mach_msg_rpc_from_kernel( if (mr != MACH_MSG_SUCCESS) return mr; - rpc_lock(self); - reply = self->ith_rpc_reply; if (reply == IP_NULL) { - rpc_unlock(self); reply = ipc_port_alloc_reply(); - rpc_lock(self); if ((reply == IP_NULL) || (self->ith_rpc_reply != IP_NULL)) panic("mach_msg_rpc_from_kernel"); @@ -165,12 +162,11 @@ mach_msg_rpc_from_kernel( } /* insert send-once right for the reply port */ - kmsg->ikm_header.msgh_local_port = reply; - kmsg->ikm_header.msgh_bits |= + kmsg->ikm_header->msgh_local_port = reply; + kmsg->ikm_header->msgh_bits |= MACH_MSGH_BITS(0, MACH_MSG_TYPE_MAKE_SEND_ONCE); ipc_port_reference(reply); - rpc_unlock(self); ipc_kmsg_copyin_from_kernel(kmsg); @@ -185,7 +181,7 @@ mach_msg_rpc_from_kernel( ipc_port_release(reply); return MACH_RCV_PORT_DIED; } - if (!self->top_act || !self->top_act->active) { + if (!self->active) { ip_unlock(reply); ipc_port_release(reply); return MACH_RCV_INTERRUPTED; @@ -214,7 +210,7 @@ mach_msg_rpc_from_kernel( assert(mr == MACH_RCV_INTERRUPTED); - if (self->top_act && self->top_act->handlers) { + if (self->handlers) { ipc_port_release(reply); return(mr); } @@ -232,9 +228,9 @@ mach_msg_rpc_from_kernel( } *****/ - if (rcv_size < kmsg->ikm_header.msgh_size) { + if (rcv_size < kmsg->ikm_header->msgh_size) { ipc_kmsg_copyout_dest(kmsg, ipc_space_reply); - ipc_kmsg_put_to_kernel(msg, kmsg, kmsg->ikm_header.msgh_size); + ipc_kmsg_put_to_kernel(msg, kmsg, kmsg->ikm_header->msgh_size); return MACH_RCV_TOO_LARGE; } @@ -245,20 +241,15 @@ mach_msg_rpc_from_kernel( */ ipc_kmsg_copyout_to_kernel(kmsg, ipc_space_reply); - ipc_kmsg_put_to_kernel(msg, kmsg, kmsg->ikm_header.msgh_size); + ipc_kmsg_put_to_kernel(msg, kmsg, kmsg->ikm_header->msgh_size); return MACH_MSG_SUCCESS; } -/************** These Calls are set up for kernel-loaded tasks **************/ -/************** Apple does not plan on supporting that. These **************/ -/************** need to be reworked to deal with the kernel **************/ -/************** proper to eliminate the kernel specific code MIG **************/ -/************** must generate. **************/ - +/************** These Calls are set up for kernel-loaded tasks/threads **************/ /* - * Routine: mach_msg + * Routine: mach_msg_overwrite * Purpose: * Like mach_msg_overwrite_trap except that message buffers * live in kernel space. Doesn't handle any options. @@ -273,15 +264,15 @@ mach_msg_rpc_from_kernel( mach_msg_return_t mach_msg_overwrite( - mach_msg_header_t *msg, - mach_msg_option_t option, + mach_msg_header_t *msg, + mach_msg_option_t option, mach_msg_size_t send_size, mach_msg_size_t rcv_size, - mach_port_name_t rcv_name, - mach_msg_timeout_t timeout, - mach_port_name_t notify, - mach_msg_header_t *rcv_msg, - mach_msg_size_t rcv_msg_size) + mach_port_name_t rcv_name, + __unused mach_msg_timeout_t msg_timeout, + __unused mach_port_name_t notify, + __unused mach_msg_header_t *rcv_msg, + __unused mach_msg_size_t rcv_msg_size) { ipc_space_t space = current_space(); vm_map_t map = current_map(); @@ -291,10 +282,37 @@ mach_msg_overwrite( mach_msg_format_0_trailer_t *trailer; if (option & MACH_SEND_MSG) { - mr = ipc_kmsg_get_from_kernel(msg, send_size, &kmsg); - if (mr != MACH_MSG_SUCCESS) - panic("mach_msg"); + mach_msg_size_t msg_and_trailer_size; + mach_msg_max_trailer_t *max_trailer; + + if ((send_size < sizeof(mach_msg_header_t)) || (send_size & 3)) + return MACH_SEND_MSG_TOO_SMALL; + + if (send_size > MACH_MSG_SIZE_MAX - MAX_TRAILER_SIZE) + return MACH_SEND_TOO_LARGE; + msg_and_trailer_size = send_size + MAX_TRAILER_SIZE; + kmsg = ipc_kmsg_alloc(msg_and_trailer_size); + + if (kmsg == IKM_NULL) + return MACH_SEND_NO_BUFFER; + + (void) memcpy((void *) kmsg->ikm_header, (const void *) msg, send_size); + + kmsg->ikm_header->msgh_size = send_size; + + /* + * Reserve for the trailer the largest space (MAX_TRAILER_SIZE) + * However, the internal size field of the trailer (msgh_trailer_size) + * is initialized to the minimum (sizeof(mach_msg_trailer_t)), to optimize + * the cases where no implicit data is requested. + */ + max_trailer = (mach_msg_max_trailer_t *) ((vm_offset_t)kmsg->ikm_header + send_size); + max_trailer->msgh_sender = current_thread()->task->sec_token; + max_trailer->msgh_audit = current_thread()->task->audit_token; + max_trailer->msgh_trailer_type = MACH_MSG_TRAILER_FORMAT_0; + max_trailer->msgh_trailer_size = MACH_MSG_TRAILER_MINIMUM_SIZE; + mr = ipc_kmsg_copyin(kmsg, space, map, MACH_PORT_NULL); if (mr != MACH_MSG_SUCCESS) { ipc_kmsg_free(kmsg); @@ -338,15 +356,16 @@ mach_msg_overwrite( return mr; trailer = (mach_msg_format_0_trailer_t *) - ((vm_offset_t)&kmsg->ikm_header + kmsg->ikm_header.msgh_size); + ((vm_offset_t)kmsg->ikm_header + kmsg->ikm_header->msgh_size); if (option & MACH_RCV_TRAILER_MASK) { trailer->msgh_seqno = seqno; trailer->msgh_trailer_size = REQUESTED_TRAILER_SIZE(option); } - if (rcv_size < (kmsg->ikm_header.msgh_size + trailer->msgh_trailer_size)) { + if (rcv_size < (kmsg->ikm_header->msgh_size + trailer->msgh_trailer_size)) { ipc_kmsg_copyout_dest(kmsg, space); - ipc_kmsg_put_to_kernel(msg, kmsg, sizeof *msg); + (void) memcpy((void *) msg, (const void *) kmsg->ikm_header, sizeof *msg); + ipc_kmsg_free(kmsg); return MACH_RCV_TOO_LARGE; } @@ -355,17 +374,19 @@ mach_msg_overwrite( if (mr != MACH_MSG_SUCCESS) { if ((mr &~ MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) { ipc_kmsg_put_to_kernel(msg, kmsg, - kmsg->ikm_header.msgh_size + trailer->msgh_trailer_size); + kmsg->ikm_header->msgh_size + trailer->msgh_trailer_size); } else { ipc_kmsg_copyout_dest(kmsg, space); - ipc_kmsg_put_to_kernel(msg, kmsg, sizeof *msg); + (void) memcpy((void *) msg, (const void *) kmsg->ikm_header, sizeof *msg); + ipc_kmsg_free(kmsg); } return mr; } - ipc_kmsg_put_to_kernel(msg, kmsg, - kmsg->ikm_header.msgh_size + trailer->msgh_trailer_size); + (void) memcpy((void *) msg, (const void *) kmsg->ikm_header, + kmsg->ikm_header->msgh_size + trailer->msgh_trailer_size); + ipc_kmsg_free(kmsg); } return MACH_MSG_SUCCESS; @@ -375,39 +396,23 @@ mach_msg_overwrite( * Routine: mig_get_reply_port * Purpose: * Called by client side interfaces living in the kernel - * to get a reply port. This port is used for - * mach_msg() calls which are kernel calls. + * to get a reply port. */ mach_port_t mig_get_reply_port(void) { - thread_t self = current_thread(); - - assert(self->ith_mig_reply == (mach_port_t)0); - - /* - * JMM - for now we have no real clients of this under the kernel - * loaded server model because we only have one of those. In order - * to avoid MIG changes, we just return null here - and return] - * references to ipc_port_t's instead of names. - * - * if (self->ith_mig_reply == MACH_PORT_NULL) - * self->ith_mig_reply = mach_reply_port(); - */ - return self->ith_mig_reply; + return (MACH_PORT_NULL); } /* * Routine: mig_dealloc_reply_port * Purpose: * Called by client side interfaces to get rid of a reply port. - * Shouldn't ever be called inside the kernel, because - * kernel calls shouldn't prompt Mig to call it. */ void mig_dealloc_reply_port( - mach_port_t reply_port) + __unused mach_port_t reply_port) { panic("mig_dealloc_reply_port"); } @@ -420,7 +425,7 @@ mig_dealloc_reply_port( */ void mig_put_reply_port( - mach_port_t reply_port) + __unused mach_port_t reply_port) { } @@ -442,9 +447,9 @@ mig_put_reply_port( */ int mig_strncpy( - char *dest, - char *src, - int len) + char *dest, + const char *src, + int len) { int i = 0; @@ -471,6 +476,252 @@ mig_user_deallocate( char *data, vm_size_t size) { - kfree((vm_offset_t)data, size); + kfree(data, size); } +/* + * Routine: mig_object_init + * Purpose: + * Initialize the base class portion of a MIG object. We + * will lazy init the port, so just clear it for now. + */ +kern_return_t +mig_object_init( + mig_object_t mig_object, + const IMIGObject *interface) +{ + if (mig_object == MIG_OBJECT_NULL) + return KERN_INVALID_ARGUMENT; + mig_object->pVtbl = (const IMIGObjectVtbl *)interface; + mig_object->port = MACH_PORT_NULL; + return KERN_SUCCESS; +} + +/* + * Routine: mig_object_destroy + * Purpose: + * The object is being freed. This call lets us clean + * up any state we have have built up over the object's + * lifetime. + * Conditions: + * Since notifications and the port hold references on + * on the object, neither can exist when this is called. + * This is a good place to assert() that condition. + */ +void +mig_object_destroy( + __assert_only mig_object_t mig_object) +{ + assert(mig_object->port == MACH_PORT_NULL); + return; +} + +/* + * Routine: mig_object_reference + * Purpose: + * Pure virtual helper to invoke the MIG object's AddRef + * method. + * Conditions: + * MIG object port may be locked. + */ +void +mig_object_reference( + mig_object_t mig_object) +{ + assert(mig_object != MIG_OBJECT_NULL); + mig_object->pVtbl->AddRef((IMIGObject *)mig_object); +} + +/* + * Routine: mig_object_deallocate + * Purpose: + * Pure virtual helper to invoke the MIG object's Release + * method. + * Conditions: + * Nothing locked. + */ +void +mig_object_deallocate( + mig_object_t mig_object) +{ + assert(mig_object != MIG_OBJECT_NULL); + mig_object->pVtbl->Release((IMIGObject *)mig_object); +} + +/* + * Routine: convert_mig_object_to_port [interface] + * Purpose: + * Base implementation of MIG outtrans routine to convert from + * a mig object reference to a new send right on the object's + * port. The object reference is consumed. + * Returns: + * IP_NULL - Null MIG object supplied + * Otherwise, a newly made send right for the port + * Conditions: + * Nothing locked. + */ +ipc_port_t +convert_mig_object_to_port( + mig_object_t mig_object) +{ + ipc_port_t port; + boolean_t deallocate = TRUE; + + if (mig_object == MIG_OBJECT_NULL) + return IP_NULL; + + port = mig_object->port; + while ((port == IP_NULL) || + ((port = ipc_port_make_send(port)) == IP_NULL)) { + ipc_port_t previous; + + /* + * Either the port was never set up, or it was just + * deallocated out from under us by the no-senders + * processing. In either case, we must: + * Attempt to make one + * Arrange for no senders + * Try to atomically register it with the object + * Destroy it if we are raced. + */ + port = ipc_port_alloc_kernel(); + ip_lock(port); + ipc_kobject_set_atomically(port, + (ipc_kobject_t) mig_object, + IKOT_MIG); + + /* make a sonce right for the notification */ + port->ip_sorights++; + ip_reference(port); + + ipc_port_nsrequest(port, 1, port, &previous); + /* port unlocked */ + + assert(previous == IP_NULL); + + if (hw_compare_and_store((uint32_t)IP_NULL, (uint32_t)port, + (uint32_t *)&mig_object->port)) { + deallocate = FALSE; + } else { + ipc_port_dealloc_kernel(port); + port = mig_object->port; + } + } + + if (deallocate) + mig_object->pVtbl->Release((IMIGObject *)mig_object); + + return (port); +} + + +/* + * Routine: convert_port_to_mig_object [interface] + * Purpose: + * Base implementation of MIG intrans routine to convert from + * an incoming port reference to a new reference on the + * underlying object. A new reference must be created, because + * the port's reference could go away asynchronously. + * Returns: + * NULL - Not an active MIG object port or iid not supported + * Otherwise, a reference to the underlying MIG interface + * Conditions: + * Nothing locked. + */ +mig_object_t +convert_port_to_mig_object( + ipc_port_t port, + const MIGIID *iid) +{ + mig_object_t mig_object; + void *ppv; + + if (!IP_VALID(port)) + return NULL; + + ip_lock(port); + if (!ip_active(port) || (ip_kotype(port) != IKOT_MIG)) { + ip_unlock(port); + return NULL; + } + + /* + * Our port points to some MIG object interface. Now + * query it to get a reference to the desired interface. + */ + ppv = NULL; + mig_object = (mig_object_t)port->ip_kobject; + mig_object->pVtbl->QueryInterface((IMIGObject *)mig_object, iid, &ppv); + ip_unlock(port); + return (mig_object_t)ppv; +} + +/* + * Routine: mig_object_no_senders [interface] + * Purpose: + * Base implementation of a no-senders notification handler + * for MIG objects. If there truly are no more senders, must + * destroy the port and drop its reference on the object. + * Returns: + * TRUE - port deallocate and reference dropped + * FALSE - more senders arrived, re-registered for notification + * Conditions: + * Nothing locked. + */ + +boolean_t +mig_object_no_senders( + ipc_port_t port, + mach_port_mscount_t mscount) +{ + mig_object_t mig_object; + + ip_lock(port); + if (port->ip_mscount > mscount) { + ipc_port_t previous; + + /* + * Somebody created new send rights while the + * notification was in-flight. Just create a + * new send-once right and re-register with + * the new (higher) mscount threshold. + */ + /* make a sonce right for the notification */ + port->ip_sorights++; + ip_reference(port); + ipc_port_nsrequest(port, mscount, port, &previous); + /* port unlocked */ + + assert(previous == IP_NULL); + return (FALSE); + } + + /* + * Clear the port pointer while we have it locked. + */ + mig_object = (mig_object_t)port->ip_kobject; + mig_object->port = IP_NULL; + + /* + * Bring the sequence number and mscount in + * line with ipc_port_destroy assertion. + */ + port->ip_mscount = 0; + port->ip_messages.imq_seqno = 0; + ipc_port_destroy(port); /* releases lock */ + + /* + * Release the port's reference on the object. + */ + mig_object->pVtbl->Release((IMIGObject *)mig_object); + return (TRUE); +} + +/* + * Kernel implementation of the notification chain for MIG object + * is kept separate from the actual objects, since there are expected + * to be much fewer of them than actual objects. + * + * The implementation of this part of MIG objects is coming + * "Real Soon Now"(TM). + */