]> git.saurik.com Git - apple/xnu.git/blobdiff - iokit/Kernel/IOUserServer.cpp
xnu-6153.121.1.tar.gz
[apple/xnu.git] / iokit / Kernel / IOUserServer.cpp
index a436a8cb2a5836aa6d72fb94e5b893bbe50e8b16..52508a7611a58abd2984a027b12360c8948c5427 100644 (file)
@@ -60,6 +60,7 @@
 #include <DriverKit/IOBufferMemoryDescriptor.h>
 #include <DriverKit/IOMemoryMap.h>
 #include <DriverKit/IODataQueueDispatchSource.h>
 #include <DriverKit/IOBufferMemoryDescriptor.h>
 #include <DriverKit/IOMemoryMap.h>
 #include <DriverKit/IODataQueueDispatchSource.h>
+#include <DriverKit/IOServiceNotificationDispatchSource.h>
 #include <DriverKit/IOUserServer.h>
 
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
 #include <DriverKit/IOUserServer.h>
 
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
@@ -104,8 +105,6 @@ class IOUserService : public IOService
 
        virtual bool
        start(IOService * provider) APPLE_KEXT_OVERRIDE;
 
        virtual bool
        start(IOService * provider) APPLE_KEXT_OVERRIDE;
-       virtual IOReturn
-       setProperties(OSObject * props) APPLE_KEXT_OVERRIDE;
 };
 
 OSDefineMetaClassAndStructors(IOUserService, IOService)
 };
 
 OSDefineMetaClassAndStructors(IOUserService, IOService)
@@ -147,13 +146,6 @@ IOUserService::start(IOService * provider)
        return ok;
 }
 
        return ok;
 }
 
-IOReturn
-IOUserService::setProperties(OSObject * properties)
-{
-       setProperty("USER", properties);
-       return kIOReturnSuccess;
-}
-
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
 
 #undef super
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
 
 #undef super
@@ -285,10 +277,41 @@ IMPL(IOService, SetDispatchQueue)
 kern_return_t
 IMPL(IOService, SetProperties)
 {
 kern_return_t
 IMPL(IOService, SetProperties)
 {
-       IOReturn ret = kIOReturnUnsupported;
+       IOUserServer   * us;
+       OSDictionary   * dict;
+       IOReturn         ret;
 
        ret = setProperties(properties);
 
 
        ret = setProperties(properties);
 
+       if (kIOReturnUnsupported == ret) {
+               dict = OSDynamicCast(OSDictionary, properties);
+               us = (typeof(us))thread_iokit_tls_get(0);
+               if (dict && reserved->uvars && (reserved->uvars->userServer == us)) {
+                       ret = runPropertyActionBlock(^IOReturn (void) {
+                               OSDictionary   * userProps;
+                               IOReturn         ret;
+
+                               userProps = OSDynamicCast(OSDictionary, getProperty(gIOUserServicePropertiesKey));
+                               if (userProps) {
+                                       userProps = (typeof(userProps))userProps->copyCollection();
+                               } else {
+                                       userProps = OSDictionary::withCapacity(4);
+                               }
+                               if (!userProps) {
+                                       ret = kIOReturnNoMemory;
+                               } else {
+                                       bool ok = userProps->merge(dict);
+                                       if (ok) {
+                                               ok = setProperty(gIOUserServicePropertiesKey, userProps);
+                                       }
+                                       OSSafeReleaseNULL(userProps);
+                                       ret = ok ? kIOReturnSuccess : kIOReturnNotWritable;
+                               }
+                               return ret;
+                       });
+               }
+       }
+
        return ret;
 }
 
        return ret;
 }
 
@@ -585,6 +608,12 @@ IODispatchSource::free()
        super::free();
 }
 
        super::free();
 }
 
+kern_return_t
+IMPL(IODispatchSource, SetEnable)
+{
+       return SetEnableWithCompletion(enable, NULL);
+}
+
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
 
 struct IOInterruptDispatchSource_IVars {
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
 
 struct IOInterruptDispatchSource_IVars {
@@ -662,6 +691,10 @@ IOInterruptDispatchSource::free()
                assert(kIOReturnSuccess == ret);
        }
 
                assert(kIOReturnSuccess == ret);
        }
 
+       if (ivars && ivars->lock) {
+               IOSimpleLockFree(ivars->lock);
+       }
+
        IOSafeDeleteNULL(ivars, IOInterruptDispatchSource_IVars, 1);
 
        super::free();
        IOSafeDeleteNULL(ivars, IOInterruptDispatchSource_IVars, 1);
 
        super::free();
@@ -711,9 +744,9 @@ IMPL(IOInterruptDispatchSource, SetEnableWithCompletion)
 }
 
 kern_return_t
 }
 
 kern_return_t
-IMPL(IODispatchSource, SetEnable)
+IMPL(IOInterruptDispatchSource, Cancel)
 {
 {
-       return SetEnableWithCompletion(enable, NULL);
+       return kIOReturnUnsupported;
 }
 
 kern_return_t
 }
 
 kern_return_t
@@ -762,6 +795,266 @@ IMPL(IOInterruptDispatchSource, InterruptOccurred)
 
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
 
 
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
 
+enum {
+       kIOServiceNotificationTypeCount = kIOServiceNotificationTypeLast + 1,
+};
+
+struct IOServiceNotificationDispatchSource_IVars {
+       OSObject     * serverName;
+       OSAction     * action;
+       IOLock       * lock;
+       IONotifier   * notifier;
+       OSDictionary * interestNotifiers;
+       OSArray      * pending[kIOServiceNotificationTypeCount];
+       bool           enable;
+};
+
+kern_return_t
+IMPL(IOServiceNotificationDispatchSource, Create)
+{
+       IOUserServer * us;
+       IOReturn       ret;
+       IOServiceNotificationDispatchSource * inst;
+
+       inst = OSTypeAlloc(IOServiceNotificationDispatchSource);
+       if (!inst->init()) {
+               OSSafeReleaseNULL(inst);
+               return kIOReturnNoMemory;
+       }
+
+       us = (typeof(us))thread_iokit_tls_get(0);
+       assert(OSDynamicCast(IOUserServer, us));
+       if (!us) {
+               OSSafeReleaseNULL(inst);
+               return kIOReturnError;
+       }
+       inst->ivars->serverName = us->copyProperty(gIOUserServerNameKey);
+       if (!inst->ivars->serverName) {
+               OSSafeReleaseNULL(inst);
+               return kIOReturnNoMemory;
+       }
+
+       inst->ivars->lock    = IOLockAlloc();
+       if (!inst->ivars->lock) {
+               OSSafeReleaseNULL(inst);
+               return kIOReturnNoMemory;
+       }
+       for (uint32_t idx = 0; idx < kIOServiceNotificationTypeCount; idx++) {
+               inst->ivars->pending[idx] = OSArray::withCapacity(4);
+               if (!inst->ivars->pending[idx]) {
+                       OSSafeReleaseNULL(inst);
+                       return kIOReturnNoMemory;
+               }
+       }
+       inst->ivars->interestNotifiers = OSDictionary::withCapacity(4);
+       if (!inst->ivars->interestNotifiers) {
+               OSSafeReleaseNULL(inst);
+               return kIOReturnNoMemory;
+       }
+
+       inst->ivars->notifier = IOService::addMatchingNotification(gIOMatchedNotification, matching, 0 /*priority*/,
+           ^bool (IOService * newService, IONotifier * notifier) {
+               bool         notifyReady = false;
+               IONotifier * interest;
+               OSObject   * serverName;
+               bool         okToUse;
+
+               serverName = newService->copyProperty(gIOUserServerNameKey);
+               okToUse = (serverName && inst->ivars->serverName->isEqualTo(serverName));
+               OSSafeReleaseNULL(serverName);
+               if (!okToUse) {
+                       return false;
+               }
+
+               IOLockLock(inst->ivars->lock);
+               notifyReady = (0 == inst->ivars->pending[kIOServiceNotificationTypeMatched]->getCount());
+               inst->ivars->pending[kIOServiceNotificationTypeMatched]->setObject(newService);
+               IOLockUnlock(inst->ivars->lock);
+
+               interest = newService->registerInterest(gIOGeneralInterest,
+               ^IOReturn (uint32_t messageType, IOService * provider,
+               void * messageArgument, size_t argSize) {
+                       IONotifier * interest;
+                       bool         notifyReady = false;
+
+                       switch (messageType) {
+                       case kIOMessageServiceIsTerminated:
+                               IOLockLock(inst->ivars->lock);
+                               notifyReady = (0 == inst->ivars->pending[kIOServiceNotificationTypeTerminated]->getCount());
+                               inst->ivars->pending[kIOServiceNotificationTypeTerminated]->setObject(provider);
+                               interest = (typeof(interest))inst->ivars->interestNotifiers->getObject((const OSSymbol *) newService);
+                               assert(interest);
+                               interest->remove();
+                               inst->ivars->interestNotifiers->removeObject((const OSSymbol *) newService);
+                               IOLockUnlock(inst->ivars->lock);
+                               break;
+                       default:
+                               break;
+                       }
+                       if (notifyReady && inst->ivars->action) {
+                               inst->ServiceNotificationReady(inst->ivars->action);
+                       }
+                       return kIOReturnSuccess;
+               });
+               if (interest) {
+                       IOLockLock(inst->ivars->lock);
+                       inst->ivars->interestNotifiers->setObject((const OSSymbol *) newService, interest);
+                       IOLockUnlock(inst->ivars->lock);
+               }
+               if (notifyReady) {
+                       if (inst->ivars->action) {
+                               inst->ServiceNotificationReady(inst->ivars->action);
+                       }
+               }
+               return false;
+       });
+
+       if (!inst->ivars->notifier) {
+               OSSafeReleaseNULL(inst);
+               ret = kIOReturnError;
+       }
+
+       *notification = inst;
+       ret = kIOReturnSuccess;
+
+       return ret;
+}
+
+kern_return_t
+IMPL(IOServiceNotificationDispatchSource, CopyNextNotification)
+{
+       IOService * next;
+       uint32_t    idx;
+
+       IOLockLock(ivars->lock);
+       for (idx = 0; idx < kIOServiceNotificationTypeCount; idx++) {
+               next = (IOService *) ivars->pending[idx]->getObject(0);
+               if (next) {
+                       next->retain();
+                       ivars->pending[idx]->removeObject(0);
+                       break;
+               }
+       }
+       IOLockUnlock(ivars->lock);
+
+       if (idx == kIOServiceNotificationTypeCount) {
+               idx = kIOServiceNotificationTypeNone;
+       }
+       *type    = idx;
+       *service = next;
+       *options = 0;
+
+       return kIOReturnSuccess;
+}
+
+bool
+IOServiceNotificationDispatchSource::init()
+{
+       if (!super::init()) {
+               return false;
+       }
+       ivars = IONewZero(IOServiceNotificationDispatchSource_IVars, 1);
+       if (!ivars) {
+               return false;
+       }
+
+       return true;
+}
+
+void
+IOServiceNotificationDispatchSource::free()
+{
+       if (ivars) {
+               OSSafeReleaseNULL(ivars->serverName);
+               if (ivars->interestNotifiers) {
+                       ivars->interestNotifiers->iterateObjects(^bool (const OSSymbol * key, OSObject * object) {
+                               IONotifier * interest = (typeof(interest))object;
+                               interest->remove();
+                               return false;
+                       });
+                       OSSafeReleaseNULL(ivars->interestNotifiers);
+               }
+               for (uint32_t idx = 0; idx < kIOServiceNotificationTypeCount; idx++) {
+                       OSSafeReleaseNULL(ivars->pending[idx]);
+               }
+               if (ivars->lock) {
+                       IOLockFree(ivars->lock);
+                       ivars->lock = NULL;
+               }
+               if (ivars->notifier) {
+                       ivars->notifier->remove();
+                       ivars->notifier = NULL;
+               }
+               IOSafeDeleteNULL(ivars, IOServiceNotificationDispatchSource_IVars, 1);
+       }
+
+       super::free();
+}
+
+kern_return_t
+IMPL(IOServiceNotificationDispatchSource, SetHandler)
+{
+       IOReturn ret;
+       bool     notifyReady;
+
+       notifyReady = false;
+
+       IOLockLock(ivars->lock);
+       OSSafeReleaseNULL(ivars->action);
+       action->retain();
+       ivars->action = action;
+       if (action) {
+               for (uint32_t idx = 0; idx < kIOServiceNotificationTypeCount; idx++) {
+                       notifyReady = (ivars->pending[idx]->getCount());
+                       if (notifyReady) {
+                               break;
+                       }
+               }
+       }
+       IOLockUnlock(ivars->lock);
+
+       if (notifyReady) {
+               ServiceNotificationReady(action);
+       }
+       ret = kIOReturnSuccess;
+
+       return ret;
+}
+
+kern_return_t
+IMPL(IOServiceNotificationDispatchSource, SetEnableWithCompletion)
+{
+       if (enable == ivars->enable) {
+               return kIOReturnSuccess;
+       }
+
+       IOLockLock(ivars->lock);
+       ivars->enable = enable;
+       IOLockUnlock(ivars->lock);
+
+       return kIOReturnSuccess;
+}
+
+kern_return_t
+IMPL(IOServiceNotificationDispatchSource, Cancel)
+{
+       return kIOReturnUnsupported;
+}
+
+kern_return_t
+IMPL(IOServiceNotificationDispatchSource, CheckForWork)
+{
+       return kIOReturnNotReady;
+}
+
+kern_return_t
+IOServiceNotificationDispatchSource::DeliverNotifications(IOServiceNotificationBlock block)
+{
+       return kIOReturnUnsupported;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+
 kern_return_t
 IOUserServer::waitInterruptTrap(void * p1, void * p2, void * p3, void * p4, void * p5, void * p6)
 {
 kern_return_t
 IOUserServer::waitInterruptTrap(void * p1, void * p2, void * p3, void * p4, void * p5, void * p6)
 {
@@ -907,6 +1200,10 @@ IMPL(IODispatchQueue, Create)
 kern_return_t
 IMPL(IODispatchQueue, SetPort)
 {
 kern_return_t
 IMPL(IODispatchQueue, SetPort)
 {
+       if (MACH_PORT_NULL != ivars->serverPort) {
+               return kIOReturnNotReady;
+       }
+
        ivars->serverPort = port;
        return kIOReturnSuccess;
 }
        ivars->serverPort = port;
        return kIOReturnSuccess;
 }
@@ -926,6 +1223,10 @@ IODispatchQueue::init()
 void
 IODispatchQueue::free()
 {
 void
 IODispatchQueue::free()
 {
+       if (ivars && ivars->serverPort) {
+               ipc_port_release_send(ivars->serverPort);
+               ivars->serverPort = MACH_PORT_NULL;
+       }
        IOSafeDeleteNULL(ivars, IODispatchQueue_IVars, 1);
        super::free();
 }
        IOSafeDeleteNULL(ivars, IODispatchQueue_IVars, 1);
        super::free();
 }
@@ -1007,7 +1308,11 @@ struct IOPStrings {
 kern_return_t
 OSUserMetaClass::Dispatch(IORPC rpc)
 {
 kern_return_t
 OSUserMetaClass::Dispatch(IORPC rpc)
 {
-       return const_cast<OSMetaClass *>(meta)->Dispatch(rpc);
+       if (meta) {
+               return const_cast<OSMetaClass *>(meta)->Dispatch(rpc);
+       } else {
+               return kIOReturnUnsupported;
+       }
 }
 
 void
 }
 
 void
@@ -1290,7 +1595,7 @@ IOUserServer::copyInStringArray(const char * string, uint32_t userSize)
        array->count = 0;
        cstr = &array->strings[0];
        end =  &array->strings[array->dataSize];
        array->count = 0;
        cstr = &array->strings[0];
        end =  &array->strings[array->dataSize];
-       while ((len = cstr[0])) {
+       while ((len = (unsigned char)cstr[0])) {
                cstr++;
                if ((cstr + len) >= end) {
                        break;
                cstr++;
                if ((cstr + len) >= end) {
                        break;
@@ -1318,7 +1623,7 @@ IOUserServer::stringArrayIndex(IOPStrings * array, const char * look)
        cstr = &array->strings[0];
        end  =  &array->strings[array->dataSize];
        llen = strlen(look);
        cstr = &array->strings[0];
        end  =  &array->strings[array->dataSize];
        llen = strlen(look);
-       while ((len = cstr[0])) {
+       while ((len = (unsigned char)cstr[0])) {
                cstr++;
                if ((cstr + len) >= end) {
                        break;
                cstr++;
                if ((cstr + len) >= end) {
                        break;
@@ -1433,12 +1738,15 @@ IOUserServer::objectInstantiate(OSObject * obj, IORPC rpc, IORPCMessage * messag
                        resultFlags |= kOSObjectRPCRemote;
                }
                if (service->reserved->uvars && service->reserved->uvars->userServer) {
                        resultFlags |= kOSObjectRPCRemote;
                }
                if (service->reserved->uvars && service->reserved->uvars->userServer) {
+                       IOLockLock(service->reserved->uvars->userServer->fLock);
                        userMeta = (typeof(userMeta))service->reserved->uvars->userServer->fClasses->getObject(str);
                        userMeta = (typeof(userMeta))service->reserved->uvars->userServer->fClasses->getObject(str);
+                       IOLockUnlock(service->reserved->uvars->userServer->fLock);
                }
        }
        if (!str && !userMeta) {
                const OSMetaClass * meta;
                meta = obj->getMetaClass();
                }
        }
        if (!str && !userMeta) {
                const OSMetaClass * meta;
                meta = obj->getMetaClass();
+               IOLockLock(fLock);
                while (meta && !userMeta) {
                        str = (OSString *) meta->getClassNameSymbol();
                        userMeta = (typeof(userMeta))fClasses->getObject(str);
                while (meta && !userMeta) {
                        str = (OSString *) meta->getClassNameSymbol();
                        userMeta = (typeof(userMeta))fClasses->getObject(str);
@@ -1446,10 +1754,13 @@ IOUserServer::objectInstantiate(OSObject * obj, IORPC rpc, IORPCMessage * messag
                                meta = meta->getSuperClass();
                        }
                }
                                meta = meta->getSuperClass();
                        }
                }
+               IOLockUnlock(fLock);
        }
        if (str) {
                if (!userMeta) {
        }
        if (str) {
                if (!userMeta) {
+                       IOLockLock(fLock);
                        userMeta = (typeof(userMeta))fClasses->getObject(str);
                        userMeta = (typeof(userMeta))fClasses->getObject(str);
+                       IOLockUnlock(fLock);
                }
                if (kIODKLogSetup & gIODKDebug) {
                        DKLOG("userMeta %s %p\n", str->getCStringNoCopy(), userMeta);
                }
                if (kIODKLogSetup & gIODKDebug) {
                        DKLOG("userMeta %s %p\n", str->getCStringNoCopy(), userMeta);
@@ -1495,7 +1806,7 @@ IOUserServer::objectInstantiate(OSObject * obj, IORPC rpc, IORPCMessage * messag
                                idx = 0;
                                sendPort = NULL;
                                if (queue && (kIODispatchQueueStopped != queue)) {
                                idx = 0;
                                sendPort = NULL;
                                if (queue && (kIODispatchQueueStopped != queue)) {
-                                       sendPort = ipc_port_make_send(queue->ivars->serverPort);
+                                       sendPort = ipc_port_copy_send(queue->ivars->serverPort);
                                }
                                replySize = sizeof(OSObject_Instantiate_Rpl)
                                    + queueCount * sizeof(machReply->objects[0])
                                }
                                replySize = sizeof(OSObject_Instantiate_Rpl)
                                    + queueCount * sizeof(machReply->objects[0])
@@ -1530,7 +1841,7 @@ IOUserServer::objectInstantiate(OSObject * obj, IORPC rpc, IORPCMessage * messag
                                        queue = uvars->queueArray[idx];
                                        sendPort = NULL;
                                        if (queue) {
                                        queue = uvars->queueArray[idx];
                                        sendPort = NULL;
                                        if (queue) {
-                                               sendPort = ipc_port_make_send(queue->ivars->serverPort);
+                                               sendPort = ipc_port_copy_send(queue->ivars->serverPort);
                                        }
                                        machReply->objects[idx].type        = MACH_MSG_PORT_DESCRIPTOR;
                                        machReply->objects[idx].disposition = MACH_MSG_TYPE_MOVE_SEND;
                                        }
                                        machReply->objects[idx].type        = MACH_MSG_PORT_DESCRIPTOR;
                                        machReply->objects[idx].disposition = MACH_MSG_TYPE_MOVE_SEND;
@@ -1692,6 +2003,9 @@ IOUserServer::server(ipc_kmsg_t requestkmsg, ipc_kmsg_t * pReply)
        if (!message) {
                return kIOReturnIPCError;
        }
        if (!message) {
                return kIOReturnIPCError;
        }
+       if (message->objectRefs == 0) {
+               return kIOReturnIPCError;
+       }
        ret = copyInObjects(msgin, message, msgin->msgh.msgh_size, true, false);
        if (kIOReturnSuccess != ret) {
                if (kIODKLogIPC & gIODKDebug) {
        ret = copyInObjects(msgin, message, msgin->msgh.msgh_size, true, false);
        if (kIOReturnSuccess != ret) {
                if (kIODKLogIPC & gIODKDebug) {
@@ -1737,7 +2051,7 @@ IOUserServer::server(ipc_kmsg_t requestkmsg, ipc_kmsg_t * pReply)
                bzero((void *)msgout, replyAlloc);
        }
 
                bzero((void *)msgout, replyAlloc);
        }
 
-       IORPC rpc = { .message = msgin, .sendSize = msgin->msgh.msgh_size, .reply = msgout, .replySize = replyAlloc };
+       IORPC rpc = { .message = msgin, .reply = msgout, .sendSize = msgin->msgh.msgh_size, .replySize = replyAlloc };
 
        if (object) {
                thread_iokit_tls_set(0, this);
 
        if (object) {
                thread_iokit_tls_set(0, this);
@@ -1978,7 +2292,7 @@ IOUserServer::rpc(IORPC rpc)
                port = queue->ivars->serverPort;
        }
        if (port) {
                port = queue->ivars->serverPort;
        }
        if (port) {
-               sendPort = ipc_port_make_send(port);
+               sendPort = ipc_port_copy_send(port);
        }
        IOLockUnlock(gIOUserServerLock);
        if (!sendPort) {
        }
        IOLockUnlock(gIOUserServerLock);
        if (!sendPort) {
@@ -1990,46 +2304,63 @@ IOUserServer::rpc(IORPC rpc)
        ret = copyOutObjects(mach, message, sendSize, false);
 
        mach->msgh.msgh_bits = MACH_MSGH_BITS_COMPLEX |
        ret = copyOutObjects(mach, message, sendSize, false);
 
        mach->msgh.msgh_bits = MACH_MSGH_BITS_COMPLEX |
-           MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, (oneway ? 0 : MACH_MSG_TYPE_MAKE_SEND_ONCE));
+           MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, (oneway ? 0 : MACH_MSG_TYPE_MAKE_SEND_ONCE));
        mach->msgh.msgh_remote_port  = sendPort;
        mach->msgh.msgh_local_port   = (oneway ? MACH_PORT_NULL : mig_get_reply_port());
        mach->msgh.msgh_id           = kIORPCVersionCurrent;
        mach->msgh.msgh_reserved     = 0;
 
        mach->msgh.msgh_remote_port  = sendPort;
        mach->msgh.msgh_local_port   = (oneway ? MACH_PORT_NULL : mig_get_reply_port());
        mach->msgh.msgh_id           = kIORPCVersionCurrent;
        mach->msgh.msgh_reserved     = 0;
 
+       boolean_t message_moved;
+
        if (oneway) {
        if (oneway) {
-               ret = mach_msg_send_from_kernel(&mach->msgh, sendSize);
+               ret = kernel_mach_msg_send(&mach->msgh, sendSize,
+                   MACH_SEND_MSG | MACH_SEND_ALWAYS | MACH_SEND_NOIMPORTANCE,
+                   0, &message_moved);
        } else {
                assert(replySize >= (sizeof(IORPCMessageMach) + sizeof(IORPCMessage)));
        } else {
                assert(replySize >= (sizeof(IORPCMessageMach) + sizeof(IORPCMessage)));
-               ret = mach_msg_rpc_from_kernel(&mach->msgh, sendSize, replySize);
-               if (KERN_SUCCESS == ret) {
-                       if (kIORPCVersionCurrentReply != mach->msgh.msgh_id) {
-                               ret = (MACH_NOTIFY_SEND_ONCE == mach->msgh.msgh_id) ? MIG_SERVER_DIED : MIG_REPLY_MISMATCH;
-                       } else if ((replySize = mach->msgh.msgh_size) < (sizeof(IORPCMessageMach) + sizeof(IORPCMessage))) {
+               ret = kernel_mach_msg_rpc(&mach->msgh, sendSize, replySize, FALSE, &message_moved);
+       }
+
+       ipc_port_release_send(sendPort);
+
+       if (MACH_MSG_SUCCESS != ret) {
+               if (kIODKLogIPC & gIODKDebug) {
+                       DKLOG("mach_msg() failed 0x%x\n", ret);
+               }
+               if (!message_moved) {
+                       // release ports
+                       copyInObjects(mach, message, sendSize, false, true);
+               }
+       }
+
+       if ((KERN_SUCCESS == ret) && !oneway) {
+               if (kIORPCVersionCurrentReply != mach->msgh.msgh_id) {
+                       ret = (MACH_NOTIFY_SEND_ONCE == mach->msgh.msgh_id) ? MIG_SERVER_DIED : MIG_REPLY_MISMATCH;
+               } else if ((replySize = mach->msgh.msgh_size) < (sizeof(IORPCMessageMach) + sizeof(IORPCMessage))) {
 //                             printf("BAD REPLY SIZE\n");
 //                             printf("BAD REPLY SIZE\n");
+                       ret = MIG_BAD_ARGUMENTS;
+               } else {
+                       if (!(MACH_MSGH_BITS_COMPLEX & mach->msgh.msgh_bits)) {
+                               mach->msgh_body.msgh_descriptor_count = 0;
+                       }
+                       message = IORPCMessageFromMach(mach, true);
+                       if (!message) {
+                               ret = kIOReturnIPCError;
+                       } else if (message->msgid != msgid) {
+//                                     printf("BAD REPLY ID\n");
                                ret = MIG_BAD_ARGUMENTS;
                        } else {
                                ret = MIG_BAD_ARGUMENTS;
                        } else {
-                               if (!(MACH_MSGH_BITS_COMPLEX & mach->msgh.msgh_bits)) {
-                                       mach->msgh_body.msgh_descriptor_count = 0;
-                               }
-                               message = IORPCMessageFromMach(mach, true);
-                               if (!message) {
-                                       ret = kIOReturnIPCError;
-                               } else if (message->msgid != msgid) {
-//                                     printf("BAD REPLY ID\n");
-                                       ret = MIG_BAD_ARGUMENTS;
-                               } else {
-                                       bool isError = (0 != (kIORPCMessageError & message->flags));
-                                       ret = copyInObjects(mach, message, replySize, !isError, true);
-                                       if (kIOReturnSuccess != ret) {
-                                               if (kIODKLogIPC & gIODKDebug) {
-                                                       DKLOG("rpc copyin(0x%x) %x\n", ret, mach->msgh.msgh_id);
-                                               }
-                                               return KERN_NOT_SUPPORTED;
-                                       }
-                                       if (isError) {
-                                               IORPCMessageErrorReturnContent * errorMsg = (typeof(errorMsg))message;
-                                               ret = errorMsg->result;
+                               bool isError = (0 != (kIORPCMessageError & message->flags));
+                               ret = copyInObjects(mach, message, replySize, !isError, true);
+                               if (kIOReturnSuccess != ret) {
+                                       if (kIODKLogIPC & gIODKDebug) {
+                                               DKLOG("rpc copyin(0x%x) %x\n", ret, mach->msgh.msgh_id);
                                        }
                                        }
+                                       return KERN_NOT_SUPPORTED;
+                               }
+                               if (isError) {
+                                       IORPCMessageErrorReturnContent * errorMsg = (typeof(errorMsg))message;
+                                       ret = errorMsg->result;
                                }
                        }
                }
                                }
                        }
                }
@@ -2471,6 +2802,9 @@ IOUserClient * IOUserServer::withTask(task_t owningTask)
                }
        }
 
                }
        }
 
+       /* Mark the current task's space as eligible for uext object ports */
+       iokit_label_dext_task(inst->fOwningTask);
+
        inst->fLock     = IOLockAlloc();
        inst->fServices = OSArray::withCapacity(4);
        inst->fClasses  = OSDictionary::withCapacity(16);
        inst->fLock     = IOLockAlloc();
        inst->fServices = OSArray::withCapacity(4);
        inst->fClasses  = OSDictionary::withCapacity(16);
@@ -2622,12 +2956,21 @@ IOUserServer::registerClass(OSClassDescription * desc, uint32_t size, OSUserMeta
 
        cls->name = sym;
        cls->meta = OSMetaClass::copyMetaClassWithName(sym);
 
        cls->name = sym;
        cls->meta = OSMetaClass::copyMetaClassWithName(sym);
+       IOLockLock(fLock);
        cls->superMeta = OSDynamicCast(OSUserMetaClass, fClasses->getObject(desc->superName));
        cls->superMeta = OSDynamicCast(OSUserMetaClass, fClasses->getObject(desc->superName));
-       fClasses->setObject(sym, cls);
+       if (fClasses->getObject(sym) != NULL) {
+               /* class with this name exists */
+               ret = kIOReturnBadArgument;
+       } else {
+               if (fClasses->setObject(sym, cls)) {
+                       *pCls = cls;
+               } else {
+                       /* could not add class to fClasses */
+                       ret = kIOReturnNoMemory;
+               }
+       }
+       IOLockUnlock(fLock);
        cls->release();
        cls->release();
-
-       *pCls = cls;
-
        return ret;
 }
 
        return ret;
 }
 
@@ -2807,13 +3150,6 @@ IOUserServer::serviceNewUserClient(IOService * service, task_t owningTask, void
                }
        }
 
                }
        }
 
-       ret = userUC->Start(service);
-       if (kIOReturnSuccess != ret) {
-               userUC->detach(this);
-               userUC->release();
-               return ret;
-       }
-
        *handler = userUC;
 
        return ret;
        *handler = userUC;
 
        return ret;
@@ -2971,6 +3307,7 @@ IMPL(IOService, Create)
                        service = OSDynamicCast(IOService, inst);
                        if (service && service->init(properties) && service->attach(this)) {
                                reserved->uvars->userServer->serviceAttach(service, this);
                        service = OSDynamicCast(IOService, inst);
                        if (service && service->init(properties) && service->attach(this)) {
                                reserved->uvars->userServer->serviceAttach(service, this);
+                               service->reserved->uvars->started = true;
                                ret = kIOReturnSuccess;
                                *result = service;
                        }
                                ret = kIOReturnSuccess;
                                *result = service;
                        }
@@ -2986,6 +3323,25 @@ IMPL(IOService, Create)
        return ret;
 }
 
        return ret;
 }
 
+kern_return_t
+IMPL(IOService, Terminate)
+{
+       IOUserServer * us;
+
+       if (options) {
+               return kIOReturnUnsupported;
+       }
+
+       us = (typeof(us))thread_iokit_tls_get(0);
+       if (!reserved->uvars
+           || (reserved->uvars->userServer != us)) {
+               return kIOReturnNotPermitted;
+       }
+       terminate(kIOServiceTerminateNeedWillTerminate);
+
+       return kIOReturnSuccess;
+}
+
 kern_return_t
 IMPL(IOService, NewUserClient)
 {
 kern_return_t
 IMPL(IOService, NewUserClient)
 {
@@ -3009,6 +3365,80 @@ IMPL(IOService, SearchProperty)
        return object ? kIOReturnSuccess : kIOReturnNotFound;
 }
 
        return object ? kIOReturnSuccess : kIOReturnNotFound;
 }
 
+kern_return_t
+IMPL(IOService, CopyProviderProperties)
+{
+       IOReturn    ret;
+       OSArray   * result;
+       IOService * provider;
+
+       result = OSArray::withCapacity(8);
+       if (!result) {
+               return kIOReturnNoMemory;
+       }
+
+       ret = kIOReturnSuccess;
+       for (provider = this; provider; provider = provider->getProvider()) {
+               OSObject     * obj;
+               OSDictionary * props;
+
+               obj = provider->copyProperty(gIOSupportedPropertiesKey);
+               props = OSDynamicCast(OSDictionary, obj);
+               if (!props) {
+                       OSSafeReleaseNULL(obj);
+                       props = provider->dictionaryWithProperties();
+               }
+               if (!props) {
+                       ret = kIOReturnNoMemory;
+                       break;
+               }
+               bool __block addClass = true;
+               if (propertyKeys) {
+                       OSDictionary * retProps;
+                       retProps = OSDictionary::withCapacity(4);
+                       addClass = false;
+                       if (!retProps) {
+                               ret = kIOReturnNoMemory;
+                               break;
+                       }
+                       propertyKeys->iterateObjects(^bool (OSObject * _key) {
+                               OSString * key = OSDynamicCast(OSString, _key);
+                               if (gIOClassKey->isEqualTo(key)) {
+                                       addClass = true;
+                                       return false;
+                               }
+                               retProps->setObject(key, props->getObject(key));
+                               return false;
+                       });
+                       OSSafeReleaseNULL(props);
+                       props = retProps;
+               }
+               if (addClass) {
+                       OSArray * classes = OSArray::withCapacity(8);
+                       if (!classes) {
+                               ret = kIOReturnNoMemory;
+                               break;
+                       }
+                       for (const OSMetaClass * meta = provider->getMetaClass(); meta; meta = meta->getSuperClass()) {
+                               classes->setObject(meta->getClassNameSymbol());
+                       }
+                       props->setObject(gIOClassKey, classes);
+                       OSSafeReleaseNULL(classes);
+               }
+               bool ok = result->setObject(props);
+               props->release();
+               if (!ok) {
+                       ret = kIOReturnNoMemory;
+                       break;
+               }
+       }
+       if (kIOReturnSuccess != ret) {
+               OSSafeReleaseNULL(result);
+       }
+       *properties = result;
+       return ret;
+}
+
 void
 IOUserServer::systemPower(bool powerOff)
 {
 void
 IOUserServer::systemPower(bool powerOff)
 {
@@ -3182,8 +3612,6 @@ IOUserServer::serviceStop(IOService * service, IOService *)
                return kIOReturnSuccess;
        }
 
                return kIOReturnSuccess;
        }
 
-       IOMachPortDestroyUserReferences(service, IKOT_UEXT_OBJECT);
-
        if (uvars->queueArray && uvars->userMeta) {
                queueAlloc = 1;
                if (uvars->userMeta->queueNames) {
        if (uvars->queueArray && uvars->userMeta) {
                queueAlloc = 1;
                if (uvars->userMeta->queueNames) {
@@ -3303,12 +3731,6 @@ IMPL(IOService, Stop)
        return kIOReturnSuccess;
 }
 
        return kIOReturnSuccess;
 }
 
-kern_return_t
-IMPL(IOInterruptDispatchSource, Cancel)
-{
-       return kIOReturnUnsupported;
-}
-
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 #undef super
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 #undef super
@@ -3340,7 +3762,7 @@ IOUserUserClient::stop(IOService * provider)
 IOReturn
 IOUserUserClient::clientClose(void)
 {
 IOReturn
 IOUserUserClient::clientClose(void)
 {
-       terminate();
+       terminate(kIOServiceTerminateNeedWillTerminate);
        return kIOReturnSuccess;
 }
 
        return kIOReturnSuccess;
 }