]> git.saurik.com Git - apple/xnu.git/blobdiff - iokit/Kernel/IOUserServer.cpp
xnu-6153.11.26.tar.gz
[apple/xnu.git] / iokit / Kernel / IOUserServer.cpp
diff --git a/iokit/Kernel/IOUserServer.cpp b/iokit/Kernel/IOUserServer.cpp
new file mode 100644 (file)
index 0000000..4ad8eb5
--- /dev/null
@@ -0,0 +1,3462 @@
+/*
+ * Copyright (c) 1998-2014 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * 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.
+ *
+ * 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, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
+#include <IOKit/IORPC.h>
+#include <IOKit/IOKitServer.h>
+#include <IOKit/IOKitKeysPrivate.h>
+#include <IOKit/IOUserClient.h>
+#include <IOKit/IOService.h>
+#include <IOKit/IORegistryEntry.h>
+#include <IOKit/IOCatalogue.h>
+#include <IOKit/IOMemoryDescriptor.h>
+#include <IOKit/IOBufferMemoryDescriptor.h>
+#include <IOKit/IOLib.h>
+#include <IOKit/IOBSD.h>
+#include <IOKit/system.h>
+#include <IOKit/IOUserServer.h>
+#include <IOKit/IOInterruptEventSource.h>
+#include <IOKit/IOTimerEventSource.h>
+#include <IOKit/pwr_mgt/RootDomain.h>
+#include <libkern/c++/OSKext.h>
+#include <libkern/OSDebug.h>
+#include <libkern/Block.h>
+#include <sys/proc.h>
+#include "IOKitKernelInternal.h"
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+
+#include <DriverKit/IODispatchQueue.h>
+#include <DriverKit/OSObject.h>
+#include <DriverKit/OSAction.h>
+#include <DriverKit/IODispatchSource.h>
+#include <DriverKit/IOInterruptDispatchSource.h>
+#include <DriverKit/IOService.h>
+#include <DriverKit/IOMemoryDescriptor.h>
+#include <DriverKit/IOBufferMemoryDescriptor.h>
+#include <DriverKit/IOMemoryMap.h>
+#include <DriverKit/IODataQueueDispatchSource.h>
+#include <DriverKit/IOUserServer.h>
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+
+#include <System/IODataQueueDispatchSourceShared.h>
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+
+SInt64    gIODKDebug = kIODKEnable;
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+
+struct IOPStrings;
+
+class OSUserMetaClass : public OSObject
+{
+       OSDeclareDefaultStructors(OSUserMetaClass);
+public:
+       const OSSymbol    * name;
+       const OSMetaClass * meta;
+       OSUserMetaClass   * superMeta;
+
+       queue_chain_t       link;
+
+       OSClassDescription * description;
+       IOPStrings * queueNames;
+       uint32_t     methodCount;
+       uint64_t   * methods;
+
+       virtual void free() override;
+       virtual kern_return_t Dispatch(const IORPC rpc) APPLE_KEXT_OVERRIDE;
+};
+OSDefineMetaClassAndStructors(OSUserMetaClass, OSObject);
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+class IOUserService : public IOService
+{
+       friend class IOService;
+
+       OSDeclareDefaultStructors(IOUserService)
+
+       virtual bool
+       start(IOService * provider) APPLE_KEXT_OVERRIDE;
+       virtual IOReturn
+       setProperties(OSObject * props) APPLE_KEXT_OVERRIDE;
+};
+
+OSDefineMetaClassAndStructors(IOUserService, IOService)
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+class IOUserUserClient : public IOUserClient
+{
+       OSDeclareDefaultStructors(IOUserUserClient);
+public:
+       task_t          fTask;
+
+       IOReturn                   setTask(task_t task);
+       virtual void           stop(IOService * provider) APPLE_KEXT_OVERRIDE;
+       virtual IOReturn       clientClose(void) APPLE_KEXT_OVERRIDE;
+       virtual IOReturn       setProperties(OSObject * properties) APPLE_KEXT_OVERRIDE;
+       virtual IOReturn       externalMethod(uint32_t selector, IOExternalMethodArguments * args,
+           IOExternalMethodDispatch * dispatch, OSObject * target, void * reference) APPLE_KEXT_OVERRIDE;
+       virtual IOReturn           clientMemoryForType(UInt32 type,
+           IOOptionBits * options,
+           IOMemoryDescriptor ** memory) APPLE_KEXT_OVERRIDE;
+};
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+
+bool
+IOUserService::start(IOService * provider)
+{
+       bool     ok = true;
+       IOReturn ret;
+
+       ret = Start(provider);
+       if (kIOReturnSuccess != ret) {
+               return false;
+       }
+
+       return ok;
+}
+
+IOReturn
+IOUserService::setProperties(OSObject * properties)
+{
+       setProperty("USER", properties);
+       return kIOReturnSuccess;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+
+#undef super
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+
+struct IODispatchQueue_IVars {
+       IOUserServer * userServer;
+       IODispatchQueue   * queue;
+       queue_chain_t  link;
+       uint64_t       tid;
+
+       mach_port_t    serverPort;
+};
+
+struct OSAction_IVars {
+       OSObject             * target;
+       uint64_t               targetmsgid;
+       uint64_t               msgid;
+       OSActionAbortedHandler abortedHandler;
+       size_t                 referenceSize;
+       void                 * reference[0];
+};
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+
+kern_return_t
+IMPL(IOService, GetRegistryEntryID)
+{
+       IOReturn ret = kIOReturnSuccess;
+
+       *registryEntryID = getRegistryEntryID();
+
+       return ret;
+}
+
+kern_return_t
+IMPL(IOService, SetName)
+{
+       IOReturn ret = kIOReturnSuccess;
+
+       setName(name);
+
+       return ret;
+}
+
+kern_return_t
+IMPL(IOService, Start)
+{
+       IOReturn ret = kIOReturnSuccess;
+       return ret;
+}
+
+kern_return_t
+IMPL(IOService, RegisterService)
+{
+       IOReturn ret = kIOReturnSuccess;
+
+       registerService();
+
+       return ret;
+}
+
+kern_return_t
+IMPL(IOService, CopyDispatchQueue)
+{
+       IODispatchQueue * result;
+       IOService  * service;
+       IOReturn     ret;
+       uint32_t index;
+
+       ret = kIOReturnNotFound;
+       index = -1U;
+       if (!strcmp("Default", name)) {
+               index = 0;
+       } else if (reserved->uvars->userMeta
+           && reserved->uvars->userMeta->queueNames) {
+               index = reserved->uvars->userServer->stringArrayIndex(reserved->uvars->userMeta->queueNames, name);
+               if (index != -1U) {
+                       index++;
+               }
+       }
+       if (index == -1U) {
+               if ((service = getProvider())) {
+                       ret = service->CopyDispatchQueue(name, queue);
+               }
+       } else {
+               result = reserved->uvars->queueArray[index];
+               if (result) {
+                       result->retain();
+                       *queue = result;
+                       ret = kIOReturnSuccess;
+               }
+       }
+
+       return ret;
+}
+
+kern_return_t
+IMPL(IOService, SetDispatchQueue)
+{
+       IOReturn ret = kIOReturnSuccess;
+       uint32_t index;
+
+       if (kIODKLogSetup & gIODKDebug) {
+               DKLOG(DKS "::SetDispatchQueue(%s)\n", DKN(this), name);
+       }
+       queue->ivars->userServer = reserved->uvars->userServer;
+       index = -1U;
+       if (!strcmp("Default", name)) {
+               index = 0;
+       } else if (reserved->uvars->userMeta
+           && reserved->uvars->userMeta->queueNames) {
+               index = reserved->uvars->userServer->stringArrayIndex(reserved->uvars->userMeta->queueNames, name);
+               if (index != -1U) {
+                       index++;
+               }
+       }
+       if (index == -1U) {
+               ret = kIOReturnBadArgument;
+       } else {
+               reserved->uvars->queueArray[index] = queue;
+               queue->retain();
+       }
+
+       return ret;
+}
+
+kern_return_t
+IMPL(IOService, SetProperties)
+{
+       IOReturn ret = kIOReturnUnsupported;
+
+       ret = setProperties(properties);
+
+       return ret;
+}
+
+kern_return_t
+IMPL(IOService, CopyProperties)
+{
+       IOReturn ret = kIOReturnSuccess;
+       *properties = dictionaryWithProperties();
+       return ret;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+
+kern_return_t
+IMPL(IOMemoryDescriptor, _CopyState)
+{
+       IOReturn ret;
+
+       state->length = _length;
+       state->options = _flags;
+
+       ret = kIOReturnSuccess;
+
+       return ret;
+}
+
+kern_return_t
+IOMemoryDescriptor::GetLength(uint64_t * returnLength)
+{
+       *returnLength = getLength();
+
+       return kIOReturnSuccess;
+}
+
+kern_return_t
+IMPL(IOMemoryDescriptor, CreateMapping)
+{
+       IOReturn          ret;
+       IOMemoryMap     * resultMap;
+       IOOptionBits      koptions;
+       mach_vm_address_t atAddress;
+
+       ret       = kIOReturnSuccess;
+       koptions  = 0;
+       resultMap = NULL;
+
+       if (kIOMemoryMapFixedAddress & options) {
+               atAddress   = address;
+               koptions    = 0;
+       } else {
+               atAddress   = 0;
+               koptions   |= kIOMapAnywhere;
+       }
+
+       if (kIOMemoryMapReadOnly & options || (kIODirectionOut == getDirection())) {
+               if (!reserved || (current_task() != reserved->creator)) {
+                       koptions   |= kIOMapReadOnly;
+               }
+       }
+
+       switch (0xFF00 & options) {
+       case kIOMemoryMapCacheModeDefault:
+               koptions |= kIOMapDefaultCache;
+               break;
+       case kIOMemoryMapCacheModeInhibit:
+               koptions |= kIOMapInhibitCache;
+               break;
+       case kIOMemoryMapCacheModeCopyback:
+               koptions |= kIOMapCopybackCache;
+               break;
+       case kIOMemoryMapCacheModeWriteThrough:
+               koptions |= kIOMapWriteThruCache;
+               break;
+       default:
+               ret = kIOReturnBadArgument;
+       }
+
+       if (kIOReturnSuccess == ret) {
+               resultMap = createMappingInTask(current_task(), atAddress, koptions, offset, length);
+               if (!resultMap) {
+                       ret = kIOReturnError;
+               }
+       }
+
+       *map = resultMap;
+
+       return ret;
+}
+
+kern_return_t
+IMPL(IOMemoryDescriptor, PrepareForDMA)
+{
+       IOReturn    ret;
+       uint32_t    idx, count;
+       uint64_t    sumLength;
+       uint64_t    lflags;
+
+       if (!device) {
+               return kIOReturnBadArgument;
+       }
+
+       count = *segmentsCount;
+       sumLength = 0;
+       for (idx = 0; idx < count; idx++) {
+#ifdef __LP64__
+               segments[idx].address = getPhysicalSegment(offset, &segments[idx].length);
+#else
+               segments[idx].address = 0;
+#endif
+               if (!segments[idx].address) {
+                       break;
+               }
+               sumLength += segments[idx].length;
+               offset += segments[idx].length;
+       }
+       *returnLength = sumLength;
+       *segmentsCount = idx;
+
+       // !!translate flags
+       lflags = 0;
+       if (kIODirectionOut & _flags) {
+               lflags |= kIOMemoryDirectionOut;
+       }
+       if (kIODirectionIn  & _flags) {
+               lflags |= kIOMemoryDirectionIn;
+       }
+
+       *flags = lflags;
+       ret = kIOReturnSuccess;
+
+       return ret;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+
+kern_return_t
+IMPL(IOMemoryMap, _CopyState)
+{
+       IOReturn ret;
+
+       state->offset  = fOffset;
+       state->length  = getLength();
+       state->address = getAddress();
+       state->options = getMapOptions();
+
+       ret = kIOReturnSuccess;
+
+       return ret;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+
+kern_return_t
+IMPL(IOBufferMemoryDescriptor, Create)
+{
+       IOReturn ret;
+       IOBufferMemoryDescriptor   * bmd;
+       IOMemoryDescriptorReserved * reserved;
+
+       if (options & ~((uint64_t) kIOMemoryDirectionOutIn)) {
+               // no other options currently defined
+               return kIOReturnBadArgument;
+       }
+       options &= kIOMemoryDirectionOutIn;
+       bmd = IOBufferMemoryDescriptor::inTaskWithOptions(
+               kernel_task, options, capacity, alignment);
+
+       *memory = bmd;
+
+       if (!bmd) {
+               return kIOReturnNoMemory;
+       }
+
+       reserved = bmd->getKernelReserved();
+       reserved->creator = current_task();
+       task_reference(reserved->creator);
+
+       ret = kIOReturnSuccess;
+
+       return ret;
+}
+
+kern_return_t
+IMPL(IOBufferMemoryDescriptor, SetLength)
+{
+       setLength(length);
+       return kIOReturnSuccess;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+
+kern_return_t
+OSAction::Create(OSAction_Create_Args)
+{
+       kern_return_t ret;
+       ret = OSAction::Create_Call(target, targetmsgid, msgid, referenceSize, action);
+       return ret;
+}
+
+kern_return_t
+IMPL(OSAction, Create)
+{
+       OSAction * inst;
+       vm_size_t  allocsize;
+
+       if (os_add_overflow(referenceSize, sizeof(OSAction_IVars), &allocsize)) {
+               return kIOReturnBadArgument;
+       }
+       inst = OSTypeAlloc(OSAction);
+       if (!inst) {
+               return kIOReturnNoMemory;
+       }
+       inst->ivars = (typeof(inst->ivars))(uintptr_t) IONewZero(uint8_t, allocsize);
+       if (!inst->ivars) {
+               inst->release();
+               return kIOReturnNoMemory;
+       }
+       target->retain();
+       inst->ivars->target        = target;
+       inst->ivars->targetmsgid   = targetmsgid;
+       inst->ivars->msgid         = msgid;
+       inst->ivars->referenceSize = referenceSize;
+
+       *action = inst;
+
+       return kIOReturnSuccess;
+}
+
+void
+OSAction::free()
+{
+       if (ivars) {
+               if (ivars->abortedHandler) {
+                       Block_release(ivars->abortedHandler);
+                       ivars->abortedHandler = NULL;
+               }
+               OSSafeReleaseNULL(ivars->target);
+               IOSafeDeleteNULL(ivars, uint8_t, ivars->referenceSize + sizeof(OSAction_IVars));
+       }
+       return super::free();
+}
+
+void *
+OSAction::GetReference()
+{
+       assert(ivars && ivars->referenceSize);
+       return &ivars->reference[0];
+}
+
+kern_return_t
+OSAction::SetAbortedHandler(OSActionAbortedHandler handler)
+{
+       ivars->abortedHandler = Block_copy(handler);
+       return kIOReturnSuccess;
+}
+
+void
+OSAction::Aborted_Impl(void)
+{
+       if (ivars->abortedHandler) {
+               ivars->abortedHandler();
+       }
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+
+struct IODispatchSource_IVars {
+       queue_chain_t           link;
+       IODispatchSource      * source;
+       IOUserServer          * server;
+       IODispatchQueue_IVars * queue;
+       bool                    enabled;
+};
+
+bool
+IODispatchSource::init()
+{
+       if (!super::init()) {
+               return false;
+       }
+
+       ivars = IONewZero(IODispatchSource_IVars, 1);
+
+       ivars->source = this;
+
+       return true;
+}
+
+void
+IODispatchSource::free()
+{
+       IOSafeDeleteNULL(ivars, IODispatchSource_IVars, 1);
+       super::free();
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+
+struct IOInterruptDispatchSource_IVars {
+       IOService    * provider;
+       uint32_t       intIndex;
+       IOSimpleLock * lock;
+       thread_t       waiter;
+       uint64_t       count;
+       uint64_t       time;
+       OSAction     * action;
+       bool           enable;
+};
+
+static void
+IOInterruptDispatchSourceInterrupt(OSObject * target, void * refCon,
+    IOService * nub, int source )
+{
+       IOInterruptDispatchSource_IVars * ivars = (typeof(ivars))refCon;
+       IOInterruptState is;
+
+       is = IOSimpleLockLockDisableInterrupt(ivars->lock);
+       ivars->count++;
+       if (ivars->waiter) {
+               ivars->time = mach_absolute_time();
+               thread_wakeup_thread((event_t) ivars, ivars->waiter);
+               ivars->waiter = NULL;
+       }
+       IOSimpleLockUnlockEnableInterrupt(ivars->lock, is);
+}
+
+kern_return_t
+IMPL(IOInterruptDispatchSource, Create)
+{
+       IOReturn ret;
+       IOInterruptDispatchSource * inst;
+
+       inst = OSTypeAlloc(IOInterruptDispatchSource);
+       if (!inst->init()) {
+               inst->free();
+               return kIOReturnNoMemory;
+       }
+
+       inst->ivars->lock = IOSimpleLockAlloc();
+
+       ret = provider->registerInterrupt(index, inst, IOInterruptDispatchSourceInterrupt, inst->ivars);
+       if (kIOReturnSuccess == ret) {
+               inst->ivars->intIndex = index;
+               inst->ivars->provider = provider;
+               *source = inst;
+       }
+       return ret;
+}
+
+bool
+IOInterruptDispatchSource::init()
+{
+       if (!super::init()) {
+               return false;
+       }
+       ivars = IONewZero(IOInterruptDispatchSource_IVars, 1);
+       if (!ivars) {
+               return false;
+       }
+
+       return true;
+}
+
+void
+IOInterruptDispatchSource::free()
+{
+       IOReturn ret;
+
+       if (ivars && ivars->provider) {
+               ret = ivars->provider->unregisterInterrupt(ivars->intIndex);
+               assert(kIOReturnSuccess == ret);
+       }
+
+       IOSafeDeleteNULL(ivars, IOInterruptDispatchSource_IVars, 1);
+
+       super::free();
+}
+
+kern_return_t
+IMPL(IOInterruptDispatchSource, SetHandler)
+{
+       IOReturn ret;
+       OSAction * oldAction;
+
+       oldAction = (typeof(oldAction))ivars->action;
+       if (oldAction && OSCompareAndSwapPtr(oldAction, NULL, &ivars->action)) {
+               oldAction->release();
+       }
+       action->retain();
+       ivars->action = action;
+
+       ret = kIOReturnSuccess;
+
+       return ret;
+}
+
+kern_return_t
+IMPL(IOInterruptDispatchSource, SetEnableWithCompletion)
+{
+       IOReturn ret;
+       IOInterruptState is;
+
+       if (enable == ivars->enable) {
+               return kIOReturnSuccess;
+       }
+
+       if (enable) {
+               is = IOSimpleLockLockDisableInterrupt(ivars->lock);
+               ivars->enable = enable;
+               IOSimpleLockUnlockEnableInterrupt(ivars->lock, is);
+               ret = ivars->provider->enableInterrupt(ivars->intIndex);
+       } else {
+               ret = ivars->provider->disableInterrupt(ivars->intIndex);
+               is = IOSimpleLockLockDisableInterrupt(ivars->lock);
+               ivars->enable = enable;
+               IOSimpleLockUnlockEnableInterrupt(ivars->lock, is);
+       }
+
+       return ret;
+}
+
+kern_return_t
+IMPL(IODispatchSource, SetEnable)
+{
+       return SetEnableWithCompletion(enable, NULL);
+}
+
+kern_return_t
+IMPL(IOInterruptDispatchSource, CheckForWork)
+{
+       IOReturn         ret = kIOReturnNotReady;
+       IOInterruptState is;
+       wait_result_t    waitResult;
+       uint64_t         icount;
+       uint64_t         itime;
+       thread_t         self;
+
+       self = current_thread();
+       icount = 0;
+       do {
+               is = IOSimpleLockLockDisableInterrupt(ivars->lock);
+               if ((icount = ivars->count)) {
+                       itime = ivars->time;
+                       ivars->count = 0;
+                       waitResult = THREAD_AWAKENED;
+               } else if (synchronous) {
+                       assert(NULL == ivars->waiter);
+                       ivars->waiter = self;
+                       waitResult = assert_wait((event_t) ivars, THREAD_INTERRUPTIBLE);
+               }
+               IOSimpleLockUnlockEnableInterrupt(ivars->lock, is);
+               if (synchronous && (waitResult == THREAD_WAITING)) {
+                       waitResult = thread_block(THREAD_CONTINUE_NULL);
+                       if (THREAD_INTERRUPTED == waitResult) {
+                               break;
+                       }
+               }
+       } while (synchronous && !icount);
+
+       if (icount && ivars->action) {
+               ret = InterruptOccurred(rpc, ivars->action, icount, itime);
+       }
+
+       return ret;
+}
+
+void
+IMPL(IOInterruptDispatchSource, InterruptOccurred)
+{
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+
+kern_return_t
+IOUserServer::waitInterruptTrap(void * p1, void * p2, void * p3, void * p4, void * p5, void * p6)
+{
+       IOReturn         ret = kIOReturnBadArgument;
+       IOInterruptState is;
+       IOInterruptDispatchSource * interrupt;
+       IOInterruptDispatchSource_IVars * ivars;
+       IOInterruptDispatchSourcePayload payload;
+
+       wait_result_t    waitResult;
+       thread_t         self;
+
+       OSObject * object;
+
+       object = iokit_lookup_object_with_port_name((mach_port_name_t)(uintptr_t)p1, IKOT_UEXT_OBJECT, current_task());
+
+       if (!object) {
+               return kIOReturnBadArgument;
+       }
+       if (!(interrupt = OSDynamicCast(IOInterruptDispatchSource, object))) {
+               ret = kIOReturnBadArgument;
+       } else {
+               self = current_thread();
+               ivars = interrupt->ivars;
+               payload.count = 0;
+               do {
+                       is = IOSimpleLockLockDisableInterrupt(ivars->lock);
+                       if ((payload.count = ivars->count)) {
+                               payload.time = ivars->time;
+                               ivars->count = 0;
+                               waitResult = THREAD_AWAKENED;
+                       } else {
+                               assert(NULL == ivars->waiter);
+                               ivars->waiter = self;
+                               waitResult = assert_wait((event_t) ivars, THREAD_INTERRUPTIBLE);
+                       }
+                       IOSimpleLockUnlockEnableInterrupt(ivars->lock, is);
+                       if (waitResult == THREAD_WAITING) {
+                               waitResult = thread_block(THREAD_CONTINUE_NULL);
+                               if (THREAD_INTERRUPTED == waitResult) {
+                                       break;
+                               }
+                       }
+               } while (!payload.count);
+               ret = (payload.count ? kIOReturnSuccess : kIOReturnAborted);
+       }
+
+       if (kIOReturnSuccess == ret) {
+               int copyerr = copyout(&payload, (user_addr_t) p2, sizeof(payload));
+               if (copyerr) {
+                       ret = kIOReturnVMError;
+               }
+       }
+
+       object->release();
+
+       return ret;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+
+kern_return_t
+IMPL(IOUserServer, Create)
+{
+       IOReturn          ret;
+       IOUserServer    * us;
+       const OSSymbol  * sym;
+       OSNumber        * serverTag;
+       io_name_t         rname;
+
+       us = (typeof(us))thread_iokit_tls_get(0);
+       assert(OSDynamicCast(IOUserServer, us));
+       if (kIODKLogSetup & gIODKDebug) {
+               DKLOG(DKS "::Create(" DKS ") %p\n", DKN(us), name, tag, us);
+       }
+       if (!us) {
+               return kIOReturnError;
+       }
+
+       sym       = OSSymbol::withCString(name);
+       serverTag = OSNumber::withNumber(tag, 64);
+
+       us->setProperty(gIOUserServerNameKey, (OSObject *) sym);
+       us->setProperty(gIOUserServerTagKey, serverTag);
+
+       serverTag->release();
+       OSSafeReleaseNULL(sym);
+
+       snprintf(rname, sizeof(rname), "IOUserServer(%s-0x%qx)", name, tag);
+       us->setName(rname);
+
+       us->retain();
+       *server = us;
+       ret = kIOReturnSuccess;
+
+       return ret;
+}
+
+kern_return_t
+IMPL(IOUserServer, Exit)
+{
+       return kIOReturnUnsupported;
+}
+
+kern_return_t
+IMPL(IOUserServer, LoadModule)
+{
+       return kIOReturnUnsupported;
+}
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+
+kern_return_t
+IMPL(IODispatchQueue, Create)
+{
+       IODispatchQueue * result;
+       IOUserServer    * us;
+
+       result = OSTypeAlloc(IODispatchQueue);
+       if (!result) {
+               return kIOReturnNoMemory;
+       }
+       if (!result->init()) {
+               return kIOReturnNoMemory;
+       }
+
+       *queue = result;
+
+       if (!strcmp("Root", name)) {
+               us = (typeof(us))thread_iokit_tls_get(0);
+               assert(OSDynamicCast(IOUserServer, us));
+               us->setRootQueue(result);
+       }
+
+       if (kIODKLogSetup & gIODKDebug) {
+               DKLOG("IODispatchQueue::Create %s %p\n", name, result);
+       }
+
+       return kIOReturnSuccess;
+}
+
+kern_return_t
+IMPL(IODispatchQueue, SetPort)
+{
+       ivars->serverPort = port;
+       return kIOReturnSuccess;
+}
+
+bool
+IODispatchQueue::init()
+{
+       ivars = IONewZero(IODispatchQueue_IVars, 1);
+       if (!ivars) {
+               return false;
+       }
+       ivars->queue = this;
+
+       return true;
+}
+
+void
+IODispatchQueue::free()
+{
+       IOSafeDeleteNULL(ivars, IODispatchQueue_IVars, 1);
+       super::free();
+}
+
+bool
+IODispatchQueue::OnQueue()
+{
+       return false;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+
+kern_return_t
+OSMetaClassBase::Dispatch(IORPC rpc)
+{
+       return kIOReturnUnsupported;
+}
+
+kern_return_t
+OSMetaClassBase::Invoke(IORPC rpc)
+{
+       IOReturn          ret = kIOReturnUnsupported;
+       OSMetaClassBase * object;
+       OSAction        * action;
+       IOService       * service;
+       IOUserServer    * us;
+       IORPCMessage    * message;
+
+       assert(rpc.sendSize >= (sizeof(IORPCMessageMach) + sizeof(IORPCMessage)));
+       message = IORPCMessageFromMach(rpc.message, false);
+       if (!message) {
+               return kIOReturnIPCError;
+       }
+       message->flags |= kIORPCMessageKernel;
+
+       us = NULL;
+       if (!(kIORPCMessageLocalHost & message->flags)) {
+               us = OSDynamicCast(IOUserServer, this);
+               if (!us) {
+                       if ((action = OSDynamicCast(OSAction, this))) {
+                               object = IOUserServer::target(action, message);
+                       } else {
+                               object = this;
+                       }
+                       if ((service = OSDynamicCast(IOService, object))
+                           && service->reserved->uvars) {
+                               // xxx other classes
+                               us = service->reserved->uvars->userServer;
+                       }
+               }
+       }
+       if (us) {
+               message->flags |= kIORPCMessageRemote;
+               ret = us->rpc(rpc);
+               if (kIOReturnSuccess != ret) {
+                       if (kIODKLogIPC & gIODKDebug) {
+                               DKLOG("OSMetaClassBase::Invoke user 0x%x\n", ret);
+                       }
+               }
+       } else {
+               if (kIODKLogIPC & gIODKDebug) {
+                       DKLOG("OSMetaClassBase::Invoke kernel %s 0x%qx\n", getMetaClass()->getClassName(), message->msgid);
+               }
+               ret = Dispatch(rpc);
+       }
+
+       return ret;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+struct IOPStrings {
+       uint32_t     dataSize;
+       uint32_t     count;
+       const char   strings[0];
+};
+
+kern_return_t
+OSUserMetaClass::Dispatch(IORPC rpc)
+{
+       return const_cast<OSMetaClass *>(meta)->Dispatch(rpc);
+}
+
+void
+OSUserMetaClass::free()
+{
+       if (queueNames) {
+               IOFree(queueNames, sizeof(IOPStrings) + queueNames->dataSize * sizeof(char));
+               queueNames = NULL;
+       }
+       if (description) {
+               IOFree(description, description->descriptionSize);
+               description = NULL;
+       }
+       IOSafeDeleteNULL(methods, uint64_t, 2 * methodCount);
+       if (meta) {
+               meta->releaseMetaClass();
+       }
+       if (name) {
+               name->release();
+       }
+       OSObject::free();
+}
+
+/*
+ * Sets the loadTag of the associated OSKext
+ * in the dext task.
+ * NOTE: different instances of the same OSKext
+ * (so same BounleID but different tasks)
+ * will have the same loadTag.
+ */
+void
+IOUserServer::setTaskLoadTag(OSKext *kext)
+{
+       task_t owningTask;
+       uint32_t loadTag, prev_taskloadTag;
+
+       owningTask = this->fOwningTask;
+       if (!owningTask) {
+               printf("%s: fOwningTask not found\n", __FUNCTION__);
+               return;
+       }
+
+       loadTag = kext->getLoadTag();
+       prev_taskloadTag = set_task_loadTag(owningTask, loadTag);
+       if (prev_taskloadTag) {
+               printf("%s: found the task loadTag already set to %u (set to %u)\n",
+                   __FUNCTION__, prev_taskloadTag, loadTag);
+       }
+}
+
+/*
+ * Sets the OSKext uuid as the uuid of the userspace
+ * dext executable.
+ */
+void
+IOUserServer::setDriverKitUUID(OSKext *kext)
+{
+       task_t task;
+       proc_t p;
+       uuid_t p_uuid, k_uuid;
+       OSData *k_data_uuid;
+       OSData *new_uuid;
+       uuid_string_t       uuid_string = "";
+
+       task = this->fOwningTask;
+       if (!task) {
+               printf("%s: fOwningTask not found\n", __FUNCTION__);
+               return;
+       }
+
+       p = (proc_t)(get_bsdtask_info(task));
+       if (!p) {
+               printf("%s: proc not found\n", __FUNCTION__);
+               return;
+       }
+       proc_getexecutableuuid(p, p_uuid, sizeof(p_uuid));
+
+       k_data_uuid = kext->copyUUID();
+       if (k_data_uuid) {
+               memcpy(&k_uuid, k_data_uuid->getBytesNoCopy(), sizeof(k_uuid));
+               OSSafeReleaseNULL(k_data_uuid);
+               if (uuid_compare(k_uuid, p_uuid) != 0) {
+                       printf("%s: uuid not matching\n", __FUNCTION__);
+               }
+               return;
+       }
+
+       uuid_unparse(p_uuid, uuid_string);
+       new_uuid = OSData::withBytes(p_uuid, sizeof(p_uuid));
+       kext->setDriverKitUUID(new_uuid);
+}
+
+bool
+IOUserServer::serviceMatchesCDHash(IOService *service)
+{
+       OSObject   *obj               = NULL;
+       bool        result            = false;
+       OSString   *requiredCDHashStr = NULL;
+       const char *requiredCDHash    = NULL;
+       char        taskCDHash[CS_CDHASH_LEN];
+
+       task_t owningTask = this->fOwningTask;
+       if (!owningTask) {
+               printf("%s: fOwningTask not found\n", __FUNCTION__);
+               goto out;
+       }
+
+       obj = service->copyProperty(gIOUserServerCDHashKey);
+       requiredCDHashStr = OSDynamicCast(OSString, obj);
+       if (!requiredCDHashStr) {
+               printf("%s: required cdhash not found as property of personality\n", __FUNCTION__);
+               goto out;
+       }
+
+       requiredCDHash = requiredCDHashStr->getCStringNoCopy();
+       if (!requiredCDHash) {
+               printf("%s: required cdhash unable to be read as string\n", __FUNCTION__);
+               goto out;
+       }
+
+       if (strlen(requiredCDHash) != CS_CDHASH_LEN * 2) {
+               printf("%s: required cdhash string has incorrect length\n", __FUNCTION__);
+               goto out;
+       }
+
+       get_task_cdhash(owningTask, taskCDHash);
+       for (int i = 0; i < (int)CS_CDHASH_LEN * 2; i++) {
+               uint8_t which  = (i + 1) & 0x1; /* 1 for upper nibble, 0 for lower */
+               uint8_t nibble = requiredCDHash[i];
+               uint8_t byte   = taskCDHash[i / 2];
+               if ('0' <= nibble && nibble <= '9') {
+                       nibble -= '0';
+               } else if ('a' <= nibble && nibble <= 'f') {
+                       nibble -= 'a' - 10;
+               } else if ('A' <= nibble && nibble <= 'F') {
+                       nibble -= 'A' - 10;
+               } else {
+                       printf("%s: required cdhash contains invalid token '%c'\n", __FUNCTION__, nibble);
+                       goto out;
+               }
+
+               /*
+                * Decide which half of the byte to compare
+                */
+               if (nibble != (which ? (byte >> 4) : (byte & 0x0f))) {
+                       printf("%s: required cdhash %s in personality does not match service\n", __FUNCTION__, requiredCDHash);
+                       goto out;
+               }
+       }
+
+       result = true;
+out:
+       OSSafeReleaseNULL(obj);
+       return result;
+}
+
+bool
+IOUserServer::checkEntitlements(
+       OSDictionary * entitlements, OSObject * prop,
+       IOService * provider, IOService * dext)
+{
+       OSDictionary * matching;
+
+       if (!prop) {
+               return true;
+       }
+       if (!entitlements) {
+               return false;
+       }
+
+       matching = NULL;
+       if (dext) {
+               matching = dext->dictionaryWithProperties();
+               if (!matching) {
+                       return false;
+               }
+       }
+
+       bool allPresent __block;
+       prop->iterateObjects(^bool (OSObject * object) {
+               allPresent = false;
+               object->iterateObjects(^bool (OSObject * object) {
+                       OSString * string;
+                       OSObject * value;
+                       string = OSDynamicCast(OSString, object);
+                       value = entitlements->getObject(string);
+                       if (matching && value) {
+                               matching->setObject(string, value);
+                       }
+                       allPresent = (NULL != value);
+                       return !allPresent;
+               });
+               return allPresent;
+       });
+
+       if (allPresent && matching && provider) {
+               allPresent = provider->matchPropertyTable(matching);
+       }
+
+       OSSafeReleaseNULL(matching);
+       OSSafeReleaseNULL(prop);
+
+       return allPresent;
+}
+
+bool
+IOUserServer::checkEntitlements(IOService * provider, IOService * dext)
+{
+       OSObject     * prop;
+       bool           ok;
+
+       if (!fOwningTask) {
+               return false;
+       }
+
+       prop = provider->copyProperty(gIOServiceDEXTEntitlementsKey);
+       ok = checkEntitlements(fEntitlements, prop, provider, dext);
+       if (!ok) {
+               DKLOG(DKS ": provider entitlements check failed\n", DKN(dext));
+       }
+       if (ok) {
+               prop = dext->copyProperty(gIOServiceDEXTEntitlementsKey);
+               ok = checkEntitlements(fEntitlements, prop, NULL, NULL);
+               if (!ok) {
+                       DKLOG(DKS ": family entitlements check failed\n", DKN(dext));
+               }
+       }
+
+       return ok;
+}
+
+IOReturn
+IOUserServer::exit(const char * reason)
+{
+       DKLOG("%s::exit(%s)\n", getName(), reason);
+       Exit(reason);
+       return kIOReturnSuccess;
+}
+
+OSObjectUserVars *
+IOUserServer::varsForObject(OSObject * obj)
+{
+       IOService * service;
+
+       if ((service = OSDynamicCast(IOService, obj))) {
+               return service->reserved->uvars;
+       }
+
+       return NULL;
+}
+
+IOPStrings *
+IOUserServer::copyInStringArray(const char * string, uint32_t userSize)
+{
+       IOPStrings * array;
+       vm_size_t    alloc;
+       size_t       len;
+       const char * cstr;
+       const char * end;
+
+       if (userSize <= 1) {
+               return NULL;
+       }
+
+       if (os_add_overflow(sizeof(IOPStrings), userSize, &alloc)) {
+               assert(false);
+               return NULL;
+       }
+       if (alloc > 16384) {
+               assert(false);
+               return NULL;
+       }
+       array = (typeof(array))IOMalloc(alloc);
+       if (!array) {
+               return NULL;
+       }
+       array->dataSize = userSize;
+       bcopy(string, (void *) &array->strings[0], userSize);
+
+       array->count = 0;
+       cstr = &array->strings[0];
+       end =  &array->strings[array->dataSize];
+       while ((len = cstr[0])) {
+               cstr++;
+               if ((cstr + len) >= end) {
+                       break;
+               }
+               cstr += len;
+               array->count++;
+       }
+       if (len) {
+               IOFree(array, alloc);
+               array = NULL;
+       }
+
+       return array;
+}
+
+uint32_t
+IOUserServer::stringArrayIndex(IOPStrings * array, const char * look)
+{
+       uint32_t     idx;
+       size_t       len, llen;
+       const char * cstr;
+       const char * end;
+
+       idx  = 0;
+       cstr = &array->strings[0];
+       end  =  &array->strings[array->dataSize];
+       llen = strlen(look);
+       while ((len = cstr[0])) {
+               cstr++;
+               if ((cstr + len) >= end) {
+                       break;
+               }
+               if ((len == llen) && !strncmp(cstr, look, len)) {
+                       return idx;
+               }
+               cstr += len;
+               idx++;
+       }
+
+       return -1U;
+}
+#define kIODispatchQueueStopped ((IODispatchQueue *) -1L)
+
+IODispatchQueue *
+IOUserServer::queueForObject(OSObject * obj, uint64_t msgid)
+{
+       IODispatchQueue  * queue;
+       OSObjectUserVars * uvars;
+       uint64_t           option;
+
+       uvars = varsForObject(obj);
+       if (!uvars) {
+               return NULL;
+       }
+       if (!uvars->queueArray) {
+               if (uvars->stopped) {
+                       return kIODispatchQueueStopped;
+               }
+               return NULL;
+       }
+       queue = uvars->queueArray[0];
+
+       if (uvars->userMeta
+           && uvars->userMeta->methods) {
+               uint32_t idx, baseIdx;
+               uint32_t lim;
+               // bsearch
+               for (baseIdx = 0, lim = uvars->userMeta->methodCount; lim; lim >>= 1) {
+                       idx = baseIdx + (lim >> 1);
+                       if (msgid == uvars->userMeta->methods[idx]) {
+                               option = uvars->userMeta->methods[uvars->userMeta->methodCount + idx];
+                               option &= 0xFF;
+                               if (option < uvars->userMeta->queueNames->count) {
+                                       queue = uvars->queueArray[option + 1];
+                               }
+                               break;
+                       } else if (msgid > uvars->userMeta->methods[idx]) {
+                               // move right
+                               baseIdx += (lim >> 1) + 1;
+                               lim--;
+                       }
+                       // else move left
+               }
+       }
+       return queue;
+}
+
+IOReturn
+IOUserServer::objectInstantiate(OSObject * obj, IORPC rpc, IORPCMessage * message)
+{
+       IOReturn         ret;
+       OSString       * str;
+       OSObject       * prop;
+       IOService      * service;
+
+       OSAction       * action;
+       OSObject       * target;
+       uint32_t         queueCount, queueAlloc;
+       const char     * resultClassName;
+       uint64_t         resultFlags;
+
+       size_t             replySize;
+       uint32_t           methodCount;
+       const uint64_t   * methods;
+       IODispatchQueue  * queue;
+       OSUserMetaClass  * userMeta;
+       OSObjectUserVars * uvars;
+       uint32_t           idx;
+       ipc_port_t         sendPort;
+
+       OSObject_Instantiate_Rpl_Content * reply;
+
+       queueCount      = 0;
+       methodCount     = 0;
+       methods         = NULL;
+       str             = NULL;
+       prop            = NULL;
+       userMeta        = NULL;
+       resultClassName = NULL;
+       resultFlags     = 0;
+       ret = kIOReturnUnsupportedMode;
+
+       service = OSDynamicCast(IOService, obj);
+       if (!service) {
+               // xxx other classes hosted
+               resultFlags |= kOSObjectRPCKernel;
+               resultFlags |= kOSObjectRPCRemote;
+       } else {
+               if (service->isInactive()) {
+                       DKLOG(DKS "::instantiate inactive\n", DKN(service));
+                       return kIOReturnOffline;
+               }
+               prop = service->copyProperty(gIOUserClassKey);
+               str = OSDynamicCast(OSString, prop);
+               if (!service->reserved->uvars) {
+                       resultFlags |= kOSObjectRPCRemote;
+                       resultFlags |= kOSObjectRPCKernel;
+               } else if (this != service->reserved->uvars->userServer) {
+                       // remote, use base class
+                       resultFlags |= kOSObjectRPCRemote;
+               }
+               if (service->reserved->uvars && service->reserved->uvars->userServer) {
+                       userMeta = (typeof(userMeta))service->reserved->uvars->userServer->fClasses->getObject(str);
+               }
+       }
+       if (!str && !userMeta) {
+               const OSMetaClass * meta;
+               meta = obj->getMetaClass();
+               while (meta && !userMeta) {
+                       str = (OSString *) meta->getClassNameSymbol();
+                       userMeta = (typeof(userMeta))fClasses->getObject(str);
+                       if (!userMeta) {
+                               meta = meta->getSuperClass();
+                       }
+               }
+       }
+       if (str) {
+               if (!userMeta) {
+                       userMeta = (typeof(userMeta))fClasses->getObject(str);
+               }
+               if (kIODKLogSetup & gIODKDebug) {
+                       DKLOG("userMeta %s %p\n", str->getCStringNoCopy(), userMeta);
+               }
+               if (userMeta) {
+                       if (kOSObjectRPCRemote & resultFlags) {
+                               while (userMeta && !(kOSClassCanRemote & userMeta->description->flags)) {
+                                       userMeta = userMeta->superMeta;
+                               }
+                               if (userMeta) {
+                                       resultClassName = userMeta->description->name;
+                                       ret = kIOReturnSuccess;
+                               }
+                       } else {
+                               service->reserved->uvars->userMeta = userMeta;
+                               queueAlloc = 1;
+                               if (userMeta->queueNames) {
+                                       queueAlloc += userMeta->queueNames->count;
+                               }
+                               service->reserved->uvars->queueArray =
+                                   IONewZero(IODispatchQueue *, queueAlloc);
+                               resultClassName = str->getCStringNoCopy();
+                               ret = kIOReturnSuccess;
+                       }
+               }
+       }
+       OSSafeReleaseNULL(prop);
+
+       IORPCMessageMach * machReply = rpc.reply;
+       replySize = sizeof(OSObject_Instantiate_Rpl);
+
+       if ((kIOReturnSuccess == ret) && (kOSObjectRPCRemote & resultFlags)) {
+               target = obj;
+               if ((action = OSDynamicCast(OSAction, obj))) {
+                       if (action->ivars->referenceSize) {
+                               resultFlags |= kOSObjectRPCKernel;
+                       } else {
+                               resultFlags &= ~kOSObjectRPCKernel;
+                               target = action->ivars->target;
+
+                               queueCount = 1;
+                               queue = queueForObject(target, action->ivars->targetmsgid);
+                               idx = 0;
+                               sendPort = NULL;
+                               if (queue && (kIODispatchQueueStopped != queue)) {
+                                       sendPort = ipc_port_make_send(queue->ivars->serverPort);
+                               }
+                               replySize = sizeof(OSObject_Instantiate_Rpl)
+                                   + queueCount * sizeof(machReply->objects[0])
+                                   + 2 * methodCount * sizeof(reply->methods[0]);
+                               if (replySize > rpc.replySize) {
+                                       assert(false);
+                                       return kIOReturnIPCError;
+                               }
+                               machReply->objects[idx].type        = MACH_MSG_PORT_DESCRIPTOR;
+                               machReply->objects[idx].disposition = MACH_MSG_TYPE_MOVE_SEND;
+                               machReply->objects[idx].name        = sendPort;
+                               machReply->objects[idx].pad2        = 0;
+                               machReply->objects[idx].pad_end     = 0;
+                       }
+               } else {
+                       uvars = varsForObject(target);
+                       if (uvars && uvars->userMeta) {
+                               queueCount = 1;
+                               if (uvars->userMeta->queueNames) {
+                                       queueCount += uvars->userMeta->queueNames->count;
+                               }
+                               methods = &uvars->userMeta->methods[0];
+                               methodCount = uvars->userMeta->methodCount;
+                               replySize = sizeof(OSObject_Instantiate_Rpl)
+                                   + queueCount * sizeof(machReply->objects[0])
+                                   + 2 * methodCount * sizeof(reply->methods[0]);
+                               if (replySize > rpc.replySize) {
+                                       assert(false);
+                                       return kIOReturnIPCError;
+                               }
+                               for (idx = 0; idx < queueCount; idx++) {
+                                       queue = uvars->queueArray[idx];
+                                       sendPort = NULL;
+                                       if (queue) {
+                                               sendPort = ipc_port_make_send(queue->ivars->serverPort);
+                                       }
+                                       machReply->objects[idx].type        = MACH_MSG_PORT_DESCRIPTOR;
+                                       machReply->objects[idx].disposition = MACH_MSG_TYPE_MOVE_SEND;
+                                       machReply->objects[idx].name        = sendPort;
+                                       machReply->objects[idx].pad2        = 0;
+                                       machReply->objects[idx].pad_end     = 0;
+                               }
+                       }
+               }
+       }
+
+       if (kIODKLogIPC & gIODKDebug) {
+               DKLOG("instantiate %s\n", obj->getMetaClass()->getClassName());
+       }
+
+       if (kIOReturnSuccess != ret) {
+               DKLOG("%s: no user class found\n", str ? str->getCStringNoCopy() : obj->getMetaClass()->getClassName());
+               resultClassName = "unknown";
+       }
+
+       machReply->msgh.msgh_id                    = kIORPCVersionCurrentReply;
+       machReply->msgh.msgh_size                  = replySize;
+       machReply->msgh_body.msgh_descriptor_count = queueCount;
+
+       reply = (typeof(reply))IORPCMessageFromMach(machReply, true);
+       if (!reply) {
+               return kIOReturnIPCError;
+       }
+       if (methodCount) {
+               bcopy(methods, &reply->methods[0], methodCount * 2 * sizeof(reply->methods[0]));
+       }
+       reply->__hdr.msgid       = OSObject_Instantiate_ID;
+       reply->__hdr.flags       = kIORPCMessageOneway;
+       reply->__hdr.objectRefs  = 0;
+       reply->__pad             = 0;
+       reply->flags             = resultFlags;
+       strlcpy(reply->classname, resultClassName, sizeof(reply->classname));
+       reply->__result          = ret;
+
+       ret = kIOReturnSuccess;
+
+       return ret;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+
+IOReturn
+IOUserServer::kernelDispatch(OSObject * obj, IORPC rpc)
+{
+       IOReturn       ret;
+       IORPCMessage * message;
+
+       message = IORPCMessageFromMach(rpc.message, false);
+       if (!message) {
+               return kIOReturnIPCError;
+       }
+
+       if (OSObject_Instantiate_ID == message->msgid) {
+               ret = objectInstantiate(obj, rpc, message);
+               if (kIOReturnSuccess != ret) {
+                       DKLOG("%s: instantiate failed 0x%x\n", obj->getMetaClass()->getClassName(), ret);
+               }
+       } else {
+               if (kIODKLogIPC & gIODKDebug) {
+                       DKLOG("%s::Dispatch kernel 0x%qx\n", obj->getMetaClass()->getClassName(), message->msgid);
+               }
+               ret = obj->Dispatch(rpc);
+               if (kIODKLogIPC & gIODKDebug) {
+                       DKLOG("%s::Dispatch kernel 0x%qx result 0x%x\n", obj->getMetaClass()->getClassName(), message->msgid, ret);
+               }
+       }
+
+       return ret;
+}
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+
+OSObject *
+IOUserServer::target(OSAction * action, IORPCMessage * message)
+{
+       OSObject * object;
+
+       if (message->msgid != action->ivars->msgid) {
+               return action;
+       }
+       object              = action->ivars->target;
+       message->msgid      = action->ivars->targetmsgid;
+       message->objects[0] = (OSObjectRef) object;
+       if (kIORPCMessageRemote & message->flags) {
+               object->retain();
+               action->release();
+       }
+       if (kIODKLogIPC & gIODKDebug) {
+               DKLOG("TARGET %s msg 0x%qx from 0x%qx\n", object->getMetaClass()->getClassName(), message->msgid, action->ivars->msgid);
+       }
+
+       return object;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+
+kern_return_t
+uext_server(ipc_kmsg_t requestkmsg, ipc_kmsg_t * pReply)
+{
+       kern_return_t      ret;
+       IORPCMessageMach * msgin;
+       OSObject         * object;
+       IOUserServer     * server;
+
+       msgin   = (typeof(msgin))ipc_kmsg_msg_header(requestkmsg);
+
+       object = IOUserServer::copyObjectForSendRight(msgin->msgh.msgh_remote_port, IKOT_UEXT_OBJECT);
+       server = OSDynamicCast(IOUserServer, object);
+       if (!server) {
+               OSSafeReleaseNULL(object);
+               return KERN_INVALID_NAME;
+       }
+       ret = server->server(requestkmsg, pReply);
+       object->release();
+
+       return ret;
+}
+
+#define MAX_UEXT_REPLY_SIZE     0x17c0
+
+kern_return_t
+IOUserServer::server(ipc_kmsg_t requestkmsg, ipc_kmsg_t * pReply)
+{
+       kern_return_t      ret;
+       mach_msg_size_t    replyAlloc;
+       ipc_kmsg_t         replykmsg;
+       IORPCMessageMach * msgin;
+       IORPCMessage     * message;
+       IORPCMessageMach * msgout;
+       IORPCMessage     * reply;
+       uint32_t           replySize;
+       OSObject         * object;
+       OSAction         * action;
+       bool               oneway;
+       uint64_t           msgid;
+
+       msgin   = (typeof(msgin))ipc_kmsg_msg_header(requestkmsg);
+       replyAlloc = 0;
+       msgout = NULL;
+       replykmsg = NULL;
+
+       if (msgin->msgh.msgh_size < (sizeof(IORPCMessageMach) + sizeof(IORPCMessage))) {
+               if (kIODKLogIPC & gIODKDebug) {
+                       DKLOG("UEXT notify %o\n", msgin->msgh.msgh_id);
+               }
+               return KERN_NOT_SUPPORTED;
+       }
+
+       if (!(MACH_MSGH_BITS_COMPLEX & msgin->msgh.msgh_bits)) {
+               msgin->msgh_body.msgh_descriptor_count = 0;
+       }
+       message = IORPCMessageFromMach(msgin, false);
+       if (!message) {
+               return kIOReturnIPCError;
+       }
+       ret = copyInObjects(msgin, message, msgin->msgh.msgh_size, true, false);
+       if (kIOReturnSuccess != ret) {
+               if (kIODKLogIPC & gIODKDebug) {
+                       DKLOG("UEXT copyin(0x%x) %x\n", ret, msgin->msgh.msgh_id);
+               }
+               return KERN_NOT_SUPPORTED;
+       }
+
+       if (msgin->msgh_body.msgh_descriptor_count < 1) {
+               return KERN_NOT_SUPPORTED;
+       }
+       object = (OSObject *) message->objects[0];
+       msgid = message->msgid;
+       message->flags &= ~kIORPCMessageKernel;
+       message->flags |= kIORPCMessageRemote;
+
+       if ((action = OSDynamicCast(OSAction, object))) {
+               object = target(action, message);
+               msgid  = message->msgid;
+       }
+
+       oneway = (0 != (kIORPCMessageOneway & message->flags));
+       assert(oneway || (MACH_PORT_NULL != msgin->msgh.msgh_local_port));
+
+       // includes trailer size
+       replyAlloc = oneway ? 0 : MAX_UEXT_REPLY_SIZE;
+       if (replyAlloc) {
+               replykmsg = ipc_kmsg_alloc(replyAlloc);
+               if (replykmsg == NULL) {
+//                     printf("uext_server: dropping request\n");
+                       //      ipc_kmsg_trace_send(request, option);
+                       consumeObjects(message, msgin->msgh.msgh_size);
+                       ipc_kmsg_destroy(requestkmsg);
+                       return KERN_MEMORY_FAILURE;
+               }
+
+               msgout = (typeof(msgout))ipc_kmsg_msg_header(replykmsg);
+               /*
+                * MIG should really assure no data leakage -
+                * but until it does, pessimistically zero the
+                * whole reply buffer.
+                */
+               bzero((void *)msgout, replyAlloc);
+       }
+
+       IORPC rpc = { .message = msgin, .sendSize = msgin->msgh.msgh_size, .reply = msgout, .replySize = replyAlloc };
+
+       if (object) {
+               thread_iokit_tls_set(0, this);
+               ret = kernelDispatch(object, rpc);
+               thread_iokit_tls_set(0, NULL);
+       } else {
+               ret = kIOReturnBadArgument;
+       }
+
+       // release objects
+       consumeObjects(message, msgin->msgh.msgh_size);
+
+       // release ports
+       copyInObjects(msgin, message, msgin->msgh.msgh_size, false, true);
+
+       if (!oneway) {
+               if (kIOReturnSuccess == ret) {
+                       replySize = msgout->msgh.msgh_size;
+                       reply = IORPCMessageFromMach(msgout, true);
+                       if (!reply) {
+                               ret = kIOReturnIPCError;
+                       } else {
+                               ret = copyOutObjects(msgout, reply, replySize, (kIORPCVersionCurrentReply == msgout->msgh.msgh_id) /* =>!InvokeReply */);
+                       }
+               }
+               if (kIOReturnSuccess != ret) {
+                       IORPCMessageErrorReturnContent * errorMsg;
+
+                       msgout->msgh_body.msgh_descriptor_count = 0;
+                       msgout->msgh.msgh_id                    = kIORPCVersionCurrentReply;
+                       errorMsg = (typeof(errorMsg))IORPCMessageFromMach(msgout, true);
+                       errorMsg->hdr.msgid      = message->msgid;
+                       errorMsg->hdr.flags      = kIORPCMessageOneway | kIORPCMessageError;
+                       errorMsg->hdr.objectRefs = 0;
+                       errorMsg->result         = ret;
+                       errorMsg->pad            = 0;
+                       replySize                = sizeof(IORPCMessageErrorReturn);
+               }
+
+               msgout->msgh.msgh_bits = MACH_MSGH_BITS_COMPLEX |
+                   MACH_MSGH_BITS_SET(MACH_MSGH_BITS_LOCAL(msgin->msgh.msgh_bits) /*remote*/, 0 /*local*/, 0, 0);
+
+               msgout->msgh.msgh_remote_port  = msgin->msgh.msgh_local_port;
+               msgout->msgh.msgh_local_port   = MACH_PORT_NULL;
+               msgout->msgh.msgh_voucher_port = (mach_port_name_t) 0;
+               msgout->msgh.msgh_reserved     = 0;
+               msgout->msgh.msgh_size         = replySize;
+       }
+
+       *pReply = replykmsg;
+
+       return oneway ? MIG_NO_REPLY : KERN_SUCCESS;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+
+#define MAX_OBJECT_COUNT(mach, size, message) \
+       ((((size) + ((uintptr_t) (mach))) - ((uintptr_t) (&message->objects[0]))) / sizeof(OSObjectRef))
+
+kern_return_t
+IOUserServerUEXTTrap(OSObject * object, void * p1, void * p2, void * p3, void * p4, void * p5, void * p6)
+{
+       const user_addr_t msg              = (uintptr_t) p1;
+       size_t            inSize           = (uintptr_t) p2;
+       user_addr_t       out              = (uintptr_t) p3;
+       size_t            outSize          = (uintptr_t) p4;
+       mach_port_name_t  objectName1      = (uintptr_t) p5;
+       size_t            totalSize;
+       OSObject        * objectArg1;
+
+       IORPCMessageMach *  mach;
+       mach_msg_port_descriptor_t * descs;
+
+#pragma pack(4)
+       struct {
+               uint32_t                   pad;
+               IORPCMessageMach           mach;
+               mach_msg_port_descriptor_t objects[2];
+               IOTrapMessageBuffer        buffer;
+       } buffer;
+#pragma pack()
+
+       IOReturn           ret;
+       OSAction         * action;
+       int                copyerr;
+       IORPCMessage     * message;
+       IORPCMessage     * reply;
+       IORPC              rpc;
+       uint64_t           refs;
+       uint32_t           maxObjectCount;
+       size_t             copySize;
+       uint64_t         * replyHdr;
+       uintptr_t          p;
+
+       bzero(&buffer, sizeof(buffer));
+
+       p = (typeof(p)) & buffer.buffer[0];
+       if (os_add_overflow(inSize, outSize, &totalSize)) {
+               return kIOReturnMessageTooLarge;
+       }
+       if (totalSize > sizeof(buffer.buffer)) {
+               return kIOReturnMessageTooLarge;
+       }
+       if (inSize < sizeof(IORPCMessage)) {
+               return kIOReturnIPCError;
+       }
+       copyerr = copyin(msg, &buffer.buffer[0], inSize);
+       if (copyerr) {
+               return kIOReturnVMError;
+       }
+
+       message = (typeof(message))p;
+       refs    = message->objectRefs;
+       if ((refs > 2) || !refs) {
+               return kIOReturnUnsupported;
+       }
+       if (!(kIORPCMessageSimpleReply & message->flags)) {
+               return kIOReturnUnsupported;
+       }
+
+       descs = (typeof(descs))(p - refs * sizeof(*descs));
+       mach  = (typeof(mach))(p - refs * sizeof(*descs) - sizeof(*mach));
+
+       mach->msgh.msgh_id   = kIORPCVersionCurrent;
+       mach->msgh.msgh_size = sizeof(IORPCMessageMach) + refs * sizeof(*descs) + inSize;
+       mach->msgh_body.msgh_descriptor_count = refs;
+
+       rpc.message   = mach;
+       rpc.sendSize  = mach->msgh.msgh_size;
+       rpc.reply     = (IORPCMessageMach *) (p + inSize);
+       rpc.replySize = sizeof(buffer.buffer) - inSize;
+
+       message->objects[0] = 0;
+       if ((action = OSDynamicCast(OSAction, object))) {
+               maxObjectCount = MAX_OBJECT_COUNT(rpc.message, rpc.sendSize, message);
+               if (refs > maxObjectCount) {
+                       return kIOReturnBadArgument;
+               }
+               object = IOUserServer::target(action, message);
+               message->objects[1] = (OSObjectRef) action;
+               if (kIODKLogIPC & gIODKDebug) {
+                       DKLOG("%s::Dispatch(trap) kernel 0x%qx\n", object->getMetaClass()->getClassName(), message->msgid);
+               }
+               ret = object->Dispatch(rpc);
+       } else {
+               objectArg1 = NULL;
+               if (refs > 1) {
+                       objectArg1 = iokit_lookup_uext_ref_current_task(objectName1);
+                       if (!objectArg1) {
+                               return kIOReturnIPCError;
+                       }
+                       message->objects[1] = (OSObjectRef) objectArg1;
+               }
+               if (kIODKLogIPC & gIODKDebug) {
+                       DKLOG("%s::Dispatch(trap) kernel 0x%qx\n", object->getMetaClass()->getClassName(), message->msgid);
+               }
+               ret = object->Dispatch(rpc);
+               if (kIODKLogIPC & gIODKDebug) {
+                       DKLOG("%s::Dispatch(trap) kernel 0x%qx 0x%x\n", object->getMetaClass()->getClassName(), message->msgid, ret);
+               }
+               OSSafeReleaseNULL(objectArg1);
+
+               if (kIOReturnSuccess == ret) {
+                       if (rpc.reply->msgh_body.msgh_descriptor_count) {
+                               return kIOReturnIPCError;
+                       }
+                       reply = IORPCMessageFromMach(rpc.reply, rpc.reply->msgh.msgh_size);
+                       if (!reply) {
+                               return kIOReturnIPCError;
+                       }
+                       copySize = rpc.reply->msgh.msgh_size - (((uintptr_t) reply) - ((uintptr_t) rpc.reply)) + sizeof(uint64_t);
+                       if (copySize > outSize) {
+                               return kIOReturnIPCError;
+                       }
+                       replyHdr = (uint64_t *) reply;
+                       replyHdr--;
+                       replyHdr[0] = copySize;
+                       copyerr = copyout(replyHdr, out, copySize);
+                       if (copyerr) {
+                               return kIOReturnVMError;
+                       }
+               }
+       }
+
+       return ret;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+
+IOReturn
+IOUserServer::rpc(IORPC rpc)
+{
+       if (isInactive() && !fRootQueue) {
+               return kIOReturnOffline;
+       }
+
+       IOReturn           ret;
+       IORPCMessage     * message;
+       IORPCMessageMach * mach;
+       mach_msg_id_t      machid;
+       uint32_t           sendSize, replySize;
+       bool               oneway;
+       uint64_t           msgid;
+       IODispatchQueue  * queue;
+       IOService        * service;
+       ipc_port_t         port;
+       ipc_port_t         sendPort;
+
+       queue    = NULL;
+       port     = NULL;
+       sendPort = NULL;
+
+       mach      = rpc.message;
+       sendSize  = rpc.sendSize;
+       replySize = rpc.replySize;
+
+       assert(sendSize >= (sizeof(IORPCMessageMach) + sizeof(IORPCMessage)));
+
+       message = IORPCMessageFromMach(mach, false);
+       if (!message) {
+               ret = kIOReturnIPCError;
+       }
+       msgid   = message->msgid;
+       machid  = (msgid >> 32);
+
+       if (mach->msgh_body.msgh_descriptor_count < 1) {
+               return kIOReturnNoMedia;
+       }
+
+       IOLockLock(gIOUserServerLock);
+       if ((service = OSDynamicCast(IOService, (OSObject *) message->objects[0]))) {
+               queue = queueForObject(service, msgid);
+       }
+       if (!queue) {
+               queue = fRootQueue;
+       }
+       if (queue && (kIODispatchQueueStopped != queue)) {
+               port = queue->ivars->serverPort;
+       }
+       if (port) {
+               sendPort = ipc_port_make_send(port);
+       }
+       IOLockUnlock(gIOUserServerLock);
+       if (!sendPort) {
+               return kIOReturnNotReady;
+       }
+
+       oneway = (0 != (kIORPCMessageOneway & message->flags));
+
+       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.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;
+
+       if (oneway) {
+               ret = mach_msg_send_from_kernel(&mach->msgh, sendSize);
+       } 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))) {
+//                             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 {
+                                       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;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       return ret;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+IORPCMessage *
+IORPCMessageFromMach(IORPCMessageMach * msg, bool reply)
+{
+       mach_msg_size_t              idx, count;
+       mach_msg_port_descriptor_t * desc;
+       mach_msg_port_descriptor_t * maxDesc;
+       size_t                       size, msgsize;
+       bool                         upgrade;
+
+       msgsize = msg->msgh.msgh_size;
+       count   = msg->msgh_body.msgh_descriptor_count;
+       desc    = &msg->objects[0];
+       maxDesc = (typeof(maxDesc))(((uintptr_t) msg) + msgsize);
+       upgrade = (msg->msgh.msgh_id != (reply ? kIORPCVersionCurrentReply : kIORPCVersionCurrent));
+
+       if (upgrade) {
+               OSReportWithBacktrace("obsolete message");
+               return NULL;
+       }
+
+       for (idx = 0; idx < count; idx++) {
+               if (desc >= maxDesc) {
+                       return NULL;
+               }
+               switch (desc->type) {
+               case MACH_MSG_PORT_DESCRIPTOR:
+                       size = sizeof(mach_msg_port_descriptor_t);
+                       break;
+               case MACH_MSG_OOL_DESCRIPTOR:
+                       size = sizeof(mach_msg_ool_descriptor_t);
+                       break;
+               default:
+                       return NULL;
+               }
+               desc = (typeof(desc))(((uintptr_t) desc) + size);
+       }
+       return (IORPCMessage *)(uintptr_t) desc;
+}
+
+ipc_port_t
+IOUserServer::copySendRightForObject(OSObject * object, ipc_kobject_type_t type)
+{
+       ipc_port_t port;
+       ipc_port_t sendPort = NULL;
+
+       port = iokit_port_for_object(object, type);
+       if (port) {
+               sendPort = ipc_port_make_send(port);
+               iokit_release_port(port);
+       }
+
+       return sendPort;
+}
+
+OSObject *
+IOUserServer::copyObjectForSendRight(ipc_port_t port, ipc_kobject_type_t type)
+{
+       OSObject * object;
+       object = iokit_lookup_io_object(port, type);
+       return object;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+// Create a vm_map_copy_t or kalloc'ed data for memory
+// to be copied out. ipc will free after the copyout.
+
+static kern_return_t
+copyoutkdata(const void * data, vm_size_t len, void ** buf)
+{
+       kern_return_t       err;
+       vm_map_copy_t       copy;
+
+       err = vm_map_copyin( kernel_map, CAST_USER_ADDR_T(data), len,
+           false /* src_destroy */, &copy);
+
+       assert( err == KERN_SUCCESS );
+       if (err == KERN_SUCCESS) {
+               *buf = (char *) copy;
+       }
+
+       return err;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+IOReturn
+IOUserServer::copyOutObjects(IORPCMessageMach * mach, IORPCMessage * message,
+    size_t size, bool consume)
+{
+       uint64_t           refs;
+       uint32_t           idx, maxObjectCount;
+       ipc_port_t         port;
+       OSObject         * object;
+       size_t             descsize;
+       mach_msg_port_descriptor_t * desc;
+       mach_msg_ool_descriptor_t  * ool;
+       vm_map_copy_t                copy;
+       void                       * address;
+       mach_msg_size_t              length;
+       kern_return_t                kr;
+       OSSerialize                * s;
+
+       refs           = message->objectRefs;
+       maxObjectCount = MAX_OBJECT_COUNT(mach, size, message);
+//     assert(refs <= mach->msgh_body.msgh_descriptor_count);
+//     assert(refs <= maxObjectCount);
+       if (refs > mach->msgh_body.msgh_descriptor_count) {
+               return kIOReturnBadArgument;
+       }
+       if (refs > maxObjectCount) {
+               return kIOReturnBadArgument;
+       }
+
+       desc = &mach->objects[0];
+       for (idx = 0; idx < refs; idx++) {
+               object = (OSObject *) message->objects[idx];
+
+               switch (desc->type) {
+               case MACH_MSG_PORT_DESCRIPTOR:
+                       descsize = sizeof(mach_msg_port_descriptor_t);
+                       port = NULL;
+                       if (object) {
+                               port = copySendRightForObject(object, IKOT_UEXT_OBJECT);
+                               if (!port) {
+                                       break;
+                               }
+                               if (consume) {
+                                       object->release();
+                               }
+                               message->objects[idx] = 0;
+                       }
+//                 desc->type        = MACH_MSG_PORT_DESCRIPTOR;
+                       desc->disposition = MACH_MSG_TYPE_MOVE_SEND;
+                       desc->name        = port;
+                       desc->pad2        = 0;
+                       desc->pad_end     = 0;
+                       break;
+
+               case MACH_MSG_OOL_DESCRIPTOR:
+                       descsize = sizeof(mach_msg_ool_descriptor_t);
+
+                       length = 0;
+                       address = NULL;
+                       if (object) {
+                               s = OSSerialize::binaryWithCapacity(4096);
+                               assert(s);
+                               if (!s) {
+                                       break;
+                               }
+                               s->setIndexed(true);
+                               if (!object->serialize(s)) {
+                                       assert(false);
+                                       descsize = -1UL;
+                                       s->release();
+                                       break;
+                               }
+                               length = s->getLength();
+                               kr = copyoutkdata(s->text(), length, &address);
+                               s->release();
+                               if (KERN_SUCCESS != kr) {
+                                       descsize = -1UL;
+                                       address = NULL;
+                                       length = 0;
+                               }
+                               if (consume) {
+                                       object->release();
+                               }
+                               message->objects[idx] = 0;
+                       }
+                       ool = (typeof(ool))desc;
+//                 ool->type        = MACH_MSG_OOL_DESCRIPTOR;
+                       ool->deallocate  = false;
+                       ool->copy        = MACH_MSG_PHYSICAL_COPY;
+                       ool->size        = length;
+                       ool->address     = address;
+                       break;
+
+               default:
+                       descsize = -1UL;
+                       break;
+               }
+               if (-1UL == descsize) {
+                       break;
+               }
+               desc = (typeof(desc))(((uintptr_t) desc) + descsize);
+       }
+
+       if (idx >= refs) {
+               return kIOReturnSuccess;
+       }
+
+       desc = &mach->objects[0];
+       while (idx--) {
+               switch (desc->type) {
+               case MACH_MSG_PORT_DESCRIPTOR:
+                       descsize = sizeof(mach_msg_port_descriptor_t);
+                       port = desc->name;
+                       if (port) {
+                               ipc_port_release_send(port);
+                       }
+                       break;
+
+               case MACH_MSG_OOL_DESCRIPTOR:
+                       descsize = sizeof(mach_msg_ool_descriptor_t);
+                       ool = (typeof(ool))desc;
+                       copy = (vm_map_copy_t) ool->address;
+                       if (copy) {
+                               vm_map_copy_discard(copy);
+                       }
+                       break;
+
+               default:
+                       descsize = -1UL;
+                       break;
+               }
+               if (-1UL == descsize) {
+                       break;
+               }
+               desc = (typeof(desc))(((uintptr_t) desc) + descsize);
+       }
+
+       return kIOReturnBadArgument;
+}
+
+IOReturn
+IOUserServer::copyInObjects(IORPCMessageMach * mach, IORPCMessage * message,
+    size_t size, bool copyObjects, bool consumePorts)
+{
+       uint64_t           refs;
+       uint32_t           idx, maxObjectCount;
+       ipc_port_t         port;
+       OSObject         * object;
+       size_t                       descsize;
+       mach_msg_port_descriptor_t * desc;
+       mach_msg_ool_descriptor_t  * ool;
+       vm_map_address_t             copyoutdata;
+       kern_return_t                kr;
+
+       refs           = message->objectRefs;
+       maxObjectCount = MAX_OBJECT_COUNT(mach, size, message);
+//     assert(refs <= mach->msgh_body.msgh_descriptor_count);
+//     assert(refs <= maxObjectCount);
+       if (refs > mach->msgh_body.msgh_descriptor_count) {
+               return kIOReturnBadArgument;
+       }
+       if (refs > maxObjectCount) {
+               return kIOReturnBadArgument;
+       }
+
+       desc = &mach->objects[0];
+       for (idx = 0; idx < refs; idx++) {
+               switch (desc->type) {
+               case MACH_MSG_PORT_DESCRIPTOR:
+                       descsize = sizeof(mach_msg_port_descriptor_t);
+
+                       object = NULL;
+                       port = desc->name;
+                       if (port) {
+                               if (copyObjects) {
+                                       object = copyObjectForSendRight(port, IKOT_UEXT_OBJECT);
+                                       if (!object) {
+                                               descsize = -1UL;
+                                               break;
+                                       }
+                               }
+                               if (consumePorts) {
+                                       ipc_port_release_send(port);
+                               }
+                       }
+                       break;
+
+               case MACH_MSG_OOL_DESCRIPTOR:
+                       descsize = sizeof(mach_msg_ool_descriptor_t);
+                       ool = (typeof(ool))desc;
+
+                       object = NULL;
+                       if (copyObjects && ool->size && ool->address) {
+                               kr = vm_map_copyout(kernel_map, &copyoutdata, (vm_map_copy_t) ool->address);
+                               if (KERN_SUCCESS == kr) {
+                                       object = OSUnserializeXML((const char *) copyoutdata, ool->size);
+                                       // vm_map_copyout() has consumed the vm_map_copy_t in the message
+                                       ool->size = 0;
+                                       ool->address = NULL;
+                                       kr = vm_deallocate(kernel_map, copyoutdata, ool->size);
+                                       assert(KERN_SUCCESS == kr);
+                               }
+                               if (!object) {
+                                       descsize = -1UL;
+                                       break;
+                               }
+                       }
+                       break;
+
+               default:
+                       descsize = -1UL;
+                       break;
+               }
+               if (-1UL == descsize) {
+                       break;
+               }
+               if (copyObjects) {
+                       message->objects[idx] = (OSObjectRef) object;
+               }
+               desc = (typeof(desc))(((uintptr_t) desc) + descsize);
+       }
+
+       if (idx >= refs) {
+               return kIOReturnSuccess;
+       }
+
+       while (idx--) {
+               object = (OSObject *) message->objects[idx];
+               object->release();
+               message->objects[idx] = 0;
+       }
+
+       return kIOReturnBadArgument;
+}
+
+IOReturn
+IOUserServer::consumeObjects(IORPCMessage * message, size_t messageSize)
+{
+       uint64_t    refs, idx;
+       OSObject  * object;
+
+       refs   = message->objectRefs;
+       for (idx = 0; idx < refs; idx++) {
+               object = (OSObject *) message->objects[idx];
+               if (object) {
+                       object->release();
+                       message->objects[idx] = 0;
+               }
+       }
+
+       return kIOReturnSuccess;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+bool
+IOUserServer::finalize(IOOptionBits options)
+{
+       OSArray   * services;
+
+       if (kIODKLogSetup & gIODKDebug) {
+               DKLOG("%s::finalize(%p)\n", getName(), this);
+       }
+
+       IOLockLock(gIOUserServerLock);
+       OSSafeReleaseNULL(fRootQueue);
+       IOLockUnlock(gIOUserServerLock);
+
+       services = NULL;
+       IOLockLock(fLock);
+       if (fServices) {
+               services = OSArray::withArray(fServices);
+       }
+       IOLockUnlock(fLock);
+
+       if (services) {
+               services->iterateObjects(^bool (OSObject * obj) {
+                       IOService * service;
+                       IOService * provider;
+                       bool        started = false;
+
+                       service = (IOService *) obj;
+                       if (kIODKLogSetup & gIODKDebug) {
+                               DKLOG("%s::terminate(" DKS ")\n", getName(), DKN(service));
+                       }
+                       if (service->reserved->uvars) {
+                               started = service->reserved->uvars->started;
+                               service->reserved->uvars->serverDied = true;
+                               if (started) {
+                                       provider = service->getProvider();
+                                       serviceDidStop(service, provider);
+                                       service->terminate(kIOServiceTerminateNeedWillTerminate | kIOServiceTerminateWithRematch);
+                               }
+                       }
+                       if (!started) {
+                               DKLOG("%s::terminate(" DKS ") server exit before start()\n", getName(), DKN(service));
+                               serviceStop(service, NULL);
+                       }
+                       return false;
+               });
+               services->release();
+       }
+
+       return IOUserClient::finalize(options);
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#undef super
+#define super IOUserClient
+
+OSDefineMetaClassAndStructors(IOUserServer, IOUserClient)
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+IOUserClient * IOUserServer::withTask(task_t owningTask)
+{
+       IOUserServer * inst;
+
+       inst = new IOUserServer;
+       if (inst && !inst->init()) {
+               inst->release();
+               inst = NULL;
+               return inst;
+       }
+       inst->PMinit();
+
+       inst->fOwningTask = current_task();
+       inst->fEntitlements = IOUserClient::copyClientEntitlements(inst->fOwningTask);
+
+       if (!(kIODKDisableEntitlementChecking & gIODKDebug)) {
+               if (!inst->fEntitlements || !inst->fEntitlements->getObject(gIODriverKitEntitlementKey)) {
+                       proc_t p;
+                       pid_t  pid;
+
+                       p = (proc_t)get_bsdtask_info(inst->fOwningTask);
+                       if (p) {
+                               pid = proc_pid(p);
+                               IOLog(kIODriverKitEntitlementKey " entitlement check failed for %s[%d]\n", proc_best_name(p), pid);
+                       }
+                       inst->release();
+                       inst = NULL;
+                       return inst;
+               }
+       }
+
+       inst->fLock     = IOLockAlloc();
+       inst->fServices = OSArray::withCapacity(4);
+       inst->fClasses  = OSDictionary::withCapacity(16);
+       inst->fClasses->setOptions(OSCollection::kSort, OSCollection::kSort);
+
+       return inst;
+}
+
+IOReturn
+IOUserServer::clientClose(void)
+{
+       terminate();
+       return kIOReturnSuccess;
+}
+
+IOReturn
+IOUserServer::setProperties(OSObject * properties)
+{
+       IOReturn kr = kIOReturnUnsupported;
+       return kr;
+}
+
+void
+IOUserServer::stop(IOService * provider)
+{
+       fOwningTask = TASK_NULL;
+
+       PMstop();
+
+       IOServicePH::serverRemove(this);
+
+       OSSafeReleaseNULL(fRootQueue);
+
+       if (fInterruptLock) {
+               IOSimpleLockFree(fInterruptLock);
+       }
+}
+
+void
+IOUserServer::free()
+{
+       OSSafeReleaseNULL(fEntitlements);
+       OSSafeReleaseNULL(fClasses);
+       if (fLock) {
+               IOLockFree(fLock);
+       }
+       OSSafeReleaseNULL(fServices);
+       IOUserClient::free();
+}
+
+IOReturn
+IOUserServer::registerClass(OSClassDescription * desc, uint32_t size, OSUserMetaClass ** pCls)
+{
+       OSUserMetaClass * cls;
+       const OSSymbol  * sym;
+       uint64_t        * methodOptions;
+       const char      * queueNames;
+       uint32_t          methodOptionsEnd, queueNamesEnd;
+       IOReturn          ret = kIOReturnSuccess;
+
+       if (size < sizeof(OSClassDescription)) {
+               assert(false);
+               return kIOReturnBadArgument;
+       }
+
+       if (kIODKLogSetup & gIODKDebug) {
+               DKLOG("%s::registerClass %s, %d, %d\n", getName(), desc->name, desc->queueNamesSize, desc->methodNamesSize);
+       }
+
+       if (desc->descriptionSize != size) {
+               assert(false);
+               return kIOReturnBadArgument;
+       }
+       if (os_add_overflow(desc->queueNamesOffset, desc->queueNamesSize, &queueNamesEnd)) {
+               assert(false);
+               return kIOReturnBadArgument;
+       }
+       if (queueNamesEnd > size) {
+               assert(false);
+               return kIOReturnBadArgument;
+       }
+       if (os_add_overflow(desc->methodOptionsOffset, desc->methodOptionsSize, &methodOptionsEnd)) {
+               assert(false);
+               return kIOReturnBadArgument;
+       }
+       if (methodOptionsEnd > size) {
+               assert(false);
+               return kIOReturnBadArgument;
+       }
+       // overlaps?
+       if ((desc->queueNamesOffset >= desc->methodOptionsOffset) && (desc->queueNamesOffset < methodOptionsEnd)) {
+               assert(false);
+               return kIOReturnBadArgument;
+       }
+       if ((queueNamesEnd >= desc->methodOptionsOffset) && (queueNamesEnd < methodOptionsEnd)) {
+               assert(false);
+               return kIOReturnBadArgument;
+       }
+
+       if (desc->methodOptionsSize & ((2 * sizeof(uint64_t)) - 1)) {
+               assert(false);
+               return kIOReturnBadArgument;
+       }
+       if (sizeof(desc->name) == strnlen(desc->name, sizeof(desc->name))) {
+               assert(false);
+               return kIOReturnBadArgument;
+       }
+       if (sizeof(desc->superName) == strnlen(desc->superName, sizeof(desc->superName))) {
+               assert(false);
+               return kIOReturnBadArgument;
+       }
+
+       cls = OSTypeAlloc(OSUserMetaClass);
+       assert(cls);
+       if (!cls) {
+               return kIOReturnNoMemory;
+       }
+
+       cls->description = (typeof(cls->description))IOMalloc(size);
+       assert(cls->description);
+       if (!cls->description) {
+               assert(false);
+               cls->release();
+               return kIOReturnNoMemory;
+       }
+       bcopy(desc, cls->description, size);
+
+       cls->methodCount = desc->methodOptionsSize / (2 * sizeof(uint64_t));
+       cls->methods = IONew(uint64_t, 2 * cls->methodCount);
+       if (!cls->methods) {
+               assert(false);
+               cls->release();
+               return kIOReturnNoMemory;
+       }
+
+       methodOptions = (typeof(methodOptions))(((uintptr_t) desc) + desc->methodOptionsOffset);
+       bcopy(methodOptions, cls->methods, 2 * cls->methodCount * sizeof(uint64_t));
+
+       queueNames = (typeof(queueNames))(((uintptr_t) desc) + desc->queueNamesOffset);
+       cls->queueNames = copyInStringArray(queueNames, desc->queueNamesSize);
+
+       sym = OSSymbol::withCString(desc->name);
+       assert(sym);
+       if (!sym) {
+               assert(false);
+               cls->release();
+               return kIOReturnNoMemory;
+       }
+
+       cls->name = sym;
+       cls->meta = OSMetaClass::copyMetaClassWithName(sym);
+       cls->superMeta = OSDynamicCast(OSUserMetaClass, fClasses->getObject(desc->superName));
+       fClasses->setObject(sym, cls);
+       cls->release();
+
+       *pCls = cls;
+
+       return ret;
+}
+
+IOReturn
+IOUserServer::setRootQueue(IODispatchQueue * queue)
+{
+       assert(!fRootQueue);
+       if (fRootQueue) {
+               return kIOReturnStillOpen;
+       }
+       queue->retain();
+       fRootQueue = queue;
+
+       return kIOReturnSuccess;
+}
+
+IOReturn
+IOUserServer::externalMethod(uint32_t selector, IOExternalMethodArguments * args,
+    IOExternalMethodDispatch * dispatch, OSObject * target, void * reference)
+{
+       IOReturn ret = kIOReturnBadArgument;
+       mach_port_name_t portname;
+
+       switch (selector) {
+       case kIOUserServerMethodRegisterClass:
+       {
+               OSUserMetaClass * cls;
+               if (!args->structureInputSize) {
+                       return kIOReturnBadArgument;
+               }
+               if (args->scalarOutputCount != 2) {
+                       return kIOReturnBadArgument;
+               }
+               ret = registerClass((OSClassDescription *) args->structureInput, args->structureInputSize, &cls);
+               if (kIOReturnSuccess == ret) {
+                       portname = iokit_make_send_right(fOwningTask, cls, IKOT_UEXT_OBJECT);
+                       assert(portname);
+                       args->scalarOutput[0] = portname;
+                       args->scalarOutput[1] = kOSObjectRPCRemote;
+               }
+               break;
+       }
+       case kIOUserServerMethodStart:
+       {
+               if (args->scalarOutputCount != 1) {
+                       return kIOReturnBadArgument;
+               }
+               portname = iokit_make_send_right(fOwningTask, this, IKOT_UEXT_OBJECT);
+               assert(portname);
+               args->scalarOutput[0] = portname;
+               ret = kIOReturnSuccess;
+               break;
+       }
+       default:
+               break;
+       }
+
+       return ret;
+}
+
+IOExternalTrap *
+IOUserServer::getTargetAndTrapForIndex( IOService **targetP, UInt32 index )
+{
+       static const IOExternalTrap trapTemplate[] = {
+               { NULL, (IOTrap) & IOUserServer::waitInterruptTrap},
+       };
+       if (index >= (sizeof(trapTemplate) / sizeof(IOExternalTrap))) {
+               return NULL;
+       }
+       *targetP = this;
+       return (IOExternalTrap *)&trapTemplate[index];
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+IOReturn
+IOUserServer::serviceAttach(IOService * service, IOService * provider)
+{
+       IOReturn           ret;
+       OSObjectUserVars * vars;
+       OSObject         * prop;
+       OSString         * str;
+       OSSymbolConstPtr   bundleID;
+       char               execPath[1024];
+
+       vars = IONewZero(OSObjectUserVars, 1);
+       service->reserved->uvars = vars;
+
+       vars->userServer = this;
+       vars->userServer->retain();
+       IOLockLock(fLock);
+       if (-1U == fServices->getNextIndexOfObject(service, 0)) {
+               fServices->setObject(service);
+       }
+       IOLockUnlock(fLock);
+
+       prop = service->copyProperty(gIOUserClassKey);
+       str = OSDynamicCast(OSString, prop);
+       if (str) {
+               service->setName(str);
+       }
+       OSSafeReleaseNULL(prop);
+
+       prop = service->copyProperty(gIOModuleIdentifierKey);
+       bundleID = OSDynamicCast(OSSymbol, prop);
+       if (bundleID) {
+               execPath[0] = 0;
+               bool ok = OSKext::copyUserExecutablePath(bundleID, execPath, sizeof(execPath));
+               if (ok) {
+                       ret = LoadModule(execPath);
+                       if (kIODKLogSetup & gIODKDebug) {
+                               DKLOG("%s::LoadModule 0x%x %s\n", getName(), ret, execPath);
+                       }
+               }
+       }
+       OSSafeReleaseNULL(prop);
+
+       ret = kIOReturnSuccess;
+
+       return ret;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define kDriverKitUCPrefix "com.apple.developer.driverkit.userclient-access."
+
+IOReturn
+IOUserServer::serviceNewUserClient(IOService * service, task_t owningTask, void * securityID,
+    uint32_t type, OSDictionary * properties, IOUserClient ** handler)
+{
+       IOReturn           ret;
+       IOUserClient     * uc;
+       IOUserUserClient * userUC;
+       OSDictionary     * entitlements;
+       OSObject         * prop;
+       OSObject         * bundleID;
+       bool               ok;
+
+       *handler = NULL;
+       ret = service->NewUserClient(type, &uc);
+       if (kIOReturnSuccess != ret) {
+               return ret;
+       }
+       userUC = OSDynamicCast(IOUserUserClient, uc);
+       if (!userUC) {
+               uc->terminate();
+               OSSafeReleaseNULL(uc);
+               return kIOReturnUnsupported;
+       }
+       userUC->setTask(owningTask);
+
+       if (!(kIODKDisableEntitlementChecking & gIODKDebug)) {
+               entitlements = IOUserClient::copyClientEntitlements(owningTask);
+               bundleID = service->copyProperty(gIOModuleIdentifierKey);
+               ok = (entitlements
+                   && bundleID
+                   && (prop = entitlements->getObject(gIODriverKitUserClientEntitlementsKey)));
+               if (ok) {
+                       bool found __block = false;
+                       ok = prop->iterateObjects(^bool (OSObject * object) {
+                               found = object->isEqualTo(bundleID);
+                               return found;
+                       });
+                       ok = found;
+               }
+               if (ok) {
+                       prop = userUC->copyProperty(gIOServiceDEXTEntitlementsKey);
+                       ok = checkEntitlements(entitlements, prop, NULL, NULL);
+               }
+               OSSafeReleaseNULL(bundleID);
+               OSSafeReleaseNULL(entitlements);
+               if (!ok) {
+                       DKLOG(DKS ":UC entitlements check failed\n", DKN(userUC));
+                       uc->terminate();
+                       OSSafeReleaseNULL(uc);
+                       return kIOReturnNotPermitted;
+               }
+       }
+
+       ret = userUC->Start(service);
+       if (kIOReturnSuccess != ret) {
+               userUC->detach(this);
+               userUC->release();
+               return ret;
+       }
+
+       *handler = userUC;
+
+       return ret;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static IOPMPowerState
+    sPowerStates[] = {
+       {   .version                = kIOPMPowerStateVersion1,
+           .capabilityFlags        = 0,
+           .outputPowerCharacter   = 0,
+           .inputPowerRequirement  = 0},
+       {   .version                = kIOPMPowerStateVersion1,
+           .capabilityFlags        = kIOPMLowPower,
+           .outputPowerCharacter   = kIOPMLowPower,
+           .inputPowerRequirement  = kIOPMLowPower},
+       {   .version                = kIOPMPowerStateVersion1,
+           .capabilityFlags        = kIOPMPowerOn,
+           .outputPowerCharacter   = kIOPMPowerOn,
+           .inputPowerRequirement  = kIOPMPowerOn},
+};
+
+IOReturn
+IOUserServer::setPowerState(unsigned long state, IOService * service)
+{
+       if (kIODKLogPM & gIODKDebug) {
+               DKLOG(DKS "::setPowerState(%ld) %d\n", DKN(service), state, fSystemPowerAck);
+       }
+       return kIOPMAckImplied;
+}
+
+IOReturn
+IOUserServer::powerStateWillChangeTo(IOPMPowerFlags flags, unsigned long state, IOService * service)
+{
+       IOReturn ret;
+
+       if (service->reserved->uvars) {
+               if (!fSystemOff && !(kIODKDisablePM & gIODKDebug)) {
+                       service->reserved->uvars->willPower = true;
+                       if (kIODKLogPM & gIODKDebug) {
+                               DKLOG(DKS "::powerStateWillChangeTo(%ld) 0x%qx, %d\n", DKN(service), state, fPowerStates, fSystemPowerAck);
+                       }
+                       ret = service->SetPowerState(flags);
+                       if (kIOReturnSuccess == ret) {
+                               return 20 * 1000 * 1000;
+                       }
+               }
+               service->reserved->uvars->willPower = false;
+       }
+
+       return kIOPMAckImplied;
+}
+
+IOReturn
+IOUserServer::powerStateDidChangeTo(IOPMPowerFlags flags, unsigned long state, IOService * service)
+{
+       unsigned int idx;
+       bool         pmAck;
+
+       pmAck = false;
+       IOLockLock(fLock);
+       idx = fServices->getNextIndexOfObject(service, 0);
+       if (-1U == idx) {
+               IOLockUnlock(fLock);
+               return kIOPMAckImplied;
+       }
+       assert(idx <= 63);
+
+       if (state) {
+               fPowerStates |= (1ULL << idx);
+       } else {
+               fPowerStates &= ~(1ULL << idx);
+       }
+       if (kIODKLogPM & gIODKDebug) {
+               DKLOG(DKS "::powerStateDidChangeTo(%ld) 0x%qx, %d\n", DKN(service), state, fPowerStates, fSystemPowerAck);
+       }
+       if (!fPowerStates && (pmAck = fSystemPowerAck)) {
+               fSystemPowerAck = false;
+               fSystemOff      = true;
+       }
+       IOLockUnlock(fLock);
+
+       if (pmAck) {
+               IOServicePH::serverAck(this);
+       }
+
+       return kIOPMAckImplied;
+}
+
+kern_return_t
+IMPL(IOService, SetPowerState)
+{
+       if (kIODKLogPM & gIODKDebug) {
+               DKLOG(DKS "::SetPowerState(%d), %d\n", DKN(this), powerFlags, reserved->uvars->willPower);
+       }
+       if (reserved->uvars
+           && reserved->uvars->userServer
+           && reserved->uvars->willPower) {
+               reserved->uvars->willPower = false;
+               acknowledgePowerChange(reserved->uvars->userServer);
+               return kIOReturnSuccess;
+       }
+       return kIOReturnNotReady;
+}
+
+kern_return_t
+IMPL(IOService, ChangePowerState)
+{
+       switch (powerFlags) {
+       case kIOServicePowerCapabilityOff:
+               changePowerStateToPriv(0);
+               break;
+       case kIOServicePowerCapabilityLow:
+               changePowerStateToPriv(1);
+               break;
+       case kIOServicePowerCapabilityOn:
+               changePowerStateToPriv(2);
+               break;
+       default:
+               return kIOReturnBadArgument;
+       }
+
+       return kIOReturnSuccess;
+}
+
+kern_return_t
+IMPL(IOService, Create)
+{
+       OSObject       * inst;
+       IOService      * service;
+       OSString       * str;
+       const OSSymbol * sym;
+       OSObject       * prop;
+       OSDictionary   * properties;
+       kern_return_t    ret;
+
+       if (provider != this) {
+               return kIOReturnUnsupported;
+       }
+
+       ret = kIOReturnUnsupported;
+       inst = NULL;
+       service = NULL;
+
+       prop = copyProperty(propertiesKey);
+       properties = OSDynamicCast(OSDictionary, prop);
+       assert(properties);
+       if (properties) {
+               str = OSDynamicCast(OSString, properties->getObject(gIOClassKey));
+               assert(str);
+               sym = OSSymbol::withString(str);
+               if (sym) {
+                       inst = OSMetaClass::allocClassWithName(sym);
+                       service = OSDynamicCast(IOService, inst);
+                       if (service && service->init(properties) && service->attach(this)) {
+                               reserved->uvars->userServer->serviceAttach(service, this);
+                               ret = kIOReturnSuccess;
+                               *result = service;
+                       }
+                       OSSafeReleaseNULL(sym);
+               }
+       }
+
+       OSSafeReleaseNULL(prop);
+       if (kIOReturnSuccess != ret) {
+               OSSafeReleaseNULL(inst);
+       }
+
+       return ret;
+}
+
+kern_return_t
+IMPL(IOService, NewUserClient)
+{
+       return kIOReturnError;
+}
+
+kern_return_t
+IMPL(IOService, SearchProperty)
+{
+       OSObject * object;
+
+       if (kIOServiceSearchPropertyParents & options) {
+               options = kIORegistryIterateParents | kIORegistryIterateRecursively;
+       } else {
+               options = 0;
+       }
+
+       object = copyProperty(name, IORegistryEntry::getPlane(plane), options);
+       *property = object;
+
+       return object ? kIOReturnSuccess : kIOReturnNotFound;
+}
+
+void
+IOUserServer::systemPower(bool powerOff)
+{
+       OSArray * services;
+
+       if (kIODKLogPM & gIODKDebug) {
+               DKLOG("%s::powerOff(%d) 0x%qx\n", getName(), powerOff, fPowerStates);
+       }
+
+       IOLockLock(fLock);
+       services = OSArray::withArray(fServices);
+
+       if (powerOff) {
+               fSystemPowerAck = (0 != fPowerStates);
+               if (!fSystemPowerAck) {
+                       fSystemOff = true;
+               }
+               IOLockUnlock(fLock);
+
+               if (!fSystemPowerAck) {
+                       IOServicePH::serverAck(this);
+               } else {
+                       if (services) {
+                               services->iterateObjects(^bool (OSObject * obj) {
+                                       IOService * service;
+                                       service = (IOService *) obj;
+                                       if (kIODKLogPM & gIODKDebug) {
+                                               DKLOG("changePowerStateWithOverrideTo(" DKS ", %d)\n", DKN(service), 0);
+                                       }
+                                       service->reserved->uvars->powerOverride = service->getPowerState();
+                                       service->changePowerStateWithOverrideTo(0, 0);
+                                       return false;
+                               });
+                       }
+               }
+       } else {
+               fSystemOff = false;
+               IOLockUnlock(fLock);
+               if (services) {
+                       services->iterateObjects(^bool (OSObject * obj) {
+                               IOService * service;
+                               service = (IOService *) obj;
+                               if (-1U != service->reserved->uvars->powerOverride) {
+                                       if (kIODKLogPM & gIODKDebug) {
+                                               DKLOG("changePowerStateWithOverrideTo(" DKS ", %d)\n", DKN(service), service->reserved->uvars->powerOverride);
+                                       }
+                                       service->changePowerStateWithOverrideTo(service->reserved->uvars->powerOverride, 0);
+                                       service->reserved->uvars->powerOverride = -1U;
+                               }
+                               return false;
+                       });
+               }
+       }
+       OSSafeReleaseNULL(services);
+}
+
+
+
+IOReturn
+IOUserServer::serviceStarted(IOService * service, IOService * provider, bool result)
+{
+       IOReturn    ret;
+       IOService * pmProvider;
+
+       DKLOG(DKS "::start(" DKS ") %s\n", DKN(service), DKN(provider), result ? "ok" : "fail");
+
+       if (!result) {
+               ret = kIOReturnSuccess;
+               return ret;
+       }
+
+       if (!fRootNotifier) {
+               ret = registerPowerDriver(this, sPowerStates, sizeof(sPowerStates) / sizeof(sPowerStates[0]));
+               assert(kIOReturnSuccess == ret);
+               IOServicePH::serverAdd(this);
+               fRootNotifier = true;
+       }
+
+       if (!(kIODKDisablePM & gIODKDebug) && !service->pm_vars) {
+               service->PMinit();
+               ret = service->registerPowerDriver(this, sPowerStates, sizeof(sPowerStates) / sizeof(sPowerStates[0]));
+               assert(kIOReturnSuccess == ret);
+
+               pmProvider = service;
+               while (pmProvider && !pmProvider->inPlane(gIOPowerPlane)) {
+                       pmProvider = pmProvider->getProvider();
+               }
+               if (pmProvider) {
+                       OSObject  * prop;
+                       OSString  * str;
+                       prop = pmProvider->copyProperty("non-removable");
+                       if (prop) {
+                               str = OSDynamicCast(OSString, prop);
+                               if (str && str->isEqualTo("yes")) {
+                                       pmProvider = NULL;
+                               }
+                               prop->release();
+                       }
+               }
+               if (pmProvider) {
+                       IOLockLock(fLock);
+                       unsigned int idx = fServices->getNextIndexOfObject(service, 0);
+                       assert(idx <= 63);
+                       fPowerStates |= (1ULL << idx);
+                       IOLockUnlock(fLock);
+
+                       pmProvider->joinPMtree(service);
+                       service->reserved->uvars->userServerPM = true;
+               }
+       }
+
+       service->registerInterestedDriver(this);
+       service->reserved->uvars->started = true;
+
+       return kIOReturnSuccess;
+}
+
+
+IOReturn
+IOUserServer::serviceOpen(IOService * provider, IOService * client)
+{
+       OSObjectUserVars * uvars;
+
+       uvars = client->reserved->uvars;
+       if (!uvars->openProviders) {
+               uvars->openProviders = OSArray::withObjects((const OSObject **) &provider, 1);
+       } else if (-1U == uvars->openProviders->getNextIndexOfObject(client, 0)) {
+               uvars->openProviders->setObject(provider);
+       }
+
+       return kIOReturnSuccess;
+}
+
+IOReturn
+IOUserServer::serviceClose(IOService * provider, IOService * client)
+{
+       OSObjectUserVars * uvars;
+       unsigned int       idx;
+
+       uvars = client->reserved->uvars;
+       if (!uvars->openProviders) {
+               return kIOReturnNotOpen;
+       }
+       idx = uvars->openProviders->getNextIndexOfObject(client, 0);
+       if (-1U == idx) {
+               return kIOReturnNotOpen;
+       }
+       uvars->openProviders->removeObject(idx);
+
+       return kIOReturnSuccess;
+}
+
+
+IOReturn
+IOUserServer::serviceStop(IOService * service, IOService *)
+{
+       IOReturn           ret;
+       uint32_t           idx, queueAlloc;
+       OSObjectUserVars * uvars;
+
+       IOLockLock(fLock);
+       idx = fServices->getNextIndexOfObject(service, 0);
+       if (-1U != idx) {
+               fServices->removeObject(idx);
+               uvars = service->reserved->uvars;
+               uvars->stopped = true;
+       }
+       IOLockUnlock(fLock);
+
+       if (-1U == idx) {
+               return kIOReturnSuccess;
+       }
+
+       IOMachPortDestroyUserReferences(service, IKOT_UEXT_OBJECT);
+
+       if (uvars->queueArray && uvars->userMeta) {
+               queueAlloc = 1;
+               if (uvars->userMeta->queueNames) {
+                       queueAlloc += uvars->userMeta->queueNames->count;
+               }
+               for (idx = 0; idx < queueAlloc; idx++) {
+                       OSSafeReleaseNULL(uvars->queueArray[idx]);
+               }
+               IOSafeDeleteNULL(uvars->queueArray, IODispatchQueue *, queueAlloc);
+       }
+
+       (void) service->deRegisterInterestedDriver(this);
+       if (uvars->userServerPM) {
+               service->PMstop();
+       }
+
+       ret = kIOReturnSuccess;
+       return ret;
+}
+
+void
+IOUserServer::serviceFree(IOService * service)
+{
+       OSObjectUserVars * uvars;
+
+       uvars = service->reserved->uvars;
+       if (!uvars) {
+               return;
+       }
+       OSSafeReleaseNULL(uvars->userServer);
+       IOSafeDeleteNULL(service->reserved->uvars, OSObjectUserVars, 1);
+}
+
+void
+IOUserServer::serviceWillTerminate(IOService * client, IOService * provider, IOOptionBits options)
+{
+       IOReturn ret;
+       bool     willTerminate;
+
+       willTerminate = false;
+       if (client->lockForArbitration(true)) {
+               if (!client->reserved->uvars->serverDied
+                   && !client->reserved->uvars->willTerminate) {
+                       client->reserved->uvars->willTerminate = true;
+                       willTerminate = true;
+               }
+               client->unlockForArbitration();
+       }
+
+       if (willTerminate) {
+               ret = client->Stop(provider);
+               if (kIOReturnSuccess != ret) {
+                       ret = client->IOService::Stop(provider);
+               }
+       }
+}
+
+void
+IOUserServer::serviceDidTerminate(IOService * client, IOService * provider, IOOptionBits options, bool * defer)
+{
+       if (client->lockForArbitration(true)) {
+               client->reserved->uvars->didTerminate = true;
+               if (!client->reserved->uvars->serverDied
+                   && !client->reserved->uvars->stopped) {
+                       *defer = true;
+               }
+               client->unlockForArbitration();
+       }
+}
+
+void
+IOUserServer::serviceDidStop(IOService * client, IOService * provider)
+{
+       bool complete;
+       OSArray * closeArray;
+
+       complete = false;
+       closeArray = NULL;
+
+       if (client->lockForArbitration(true)) {
+               if (client->reserved->uvars
+                   && client->reserved->uvars->willTerminate
+                   && !client->reserved->uvars->stopped) {
+                       client->reserved->uvars->stopped = true;
+                       complete = client->reserved->uvars->didTerminate;
+               }
+
+               if (client->reserved->uvars) {
+                       closeArray = client->reserved->uvars->openProviders;
+                       client->reserved->uvars->openProviders = NULL;
+               }
+               client->unlockForArbitration();
+               if (closeArray) {
+                       closeArray->iterateObjects(^bool (OSObject * obj) {
+                               IOService * toClose;
+                               toClose = OSDynamicCast(IOService, obj);
+                               if (toClose) {
+                                       DKLOG(DKS ":force close (" DKS ")\n", DKN(client), DKN(toClose));
+                                       toClose->close(client);
+                               }
+                               return false;
+                       });
+                       closeArray->release();
+               }
+       }
+       if (complete) {
+               bool defer = false;
+               client->didTerminate(provider, 0, &defer);
+       }
+}
+
+kern_return_t
+IMPL(IOService, Stop)
+{
+       IOUserServer::serviceDidStop(this, provider);
+
+       return kIOReturnSuccess;
+}
+
+kern_return_t
+IMPL(IOInterruptDispatchSource, Cancel)
+{
+       return kIOReturnUnsupported;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#undef super
+#define super IOUserClient
+
+OSDefineMetaClassAndStructors(IOUserUserClient, IOUserClient)
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+IOReturn
+IOUserUserClient::setTask(task_t task)
+{
+       task_reference(task);
+       fTask = task;
+
+       return kIOReturnSuccess;
+}
+
+void
+IOUserUserClient::stop(IOService * provider)
+{
+       if (fTask) {
+               task_deallocate(fTask);
+               fTask = NULL;
+       }
+       super::stop(provider);
+}
+
+IOReturn
+IOUserUserClient::clientClose(void)
+{
+       terminate();
+       return kIOReturnSuccess;
+}
+
+IOReturn
+IOUserUserClient::setProperties(OSObject * properties)
+{
+       IOReturn ret = kIOReturnUnsupported;
+       return ret;
+}
+
+struct IOUserUserClientActionRef {
+       OSAsyncReference64 asyncRef;
+};
+
+void
+IMPL(IOUserClient, KernelCompletion)
+{
+       IOUserUserClientActionRef * ref;
+
+       ref = (typeof(ref))action->GetReference();
+
+       IOUserClient::sendAsyncResult64(ref->asyncRef, status, (io_user_reference_t *) asyncData, asyncDataCount);
+}
+
+kern_return_t
+IMPL(IOUserClient, _ExternalMethod)
+{
+       return kIOReturnUnsupported;
+}
+
+IOReturn
+IOUserUserClient::clientMemoryForType(UInt32 type,
+    IOOptionBits * koptions,
+    IOMemoryDescriptor ** kmemory)
+{
+       IOReturn             kr;
+       uint64_t             options;
+       IOMemoryDescriptor * memory;
+
+       kr = CopyClientMemoryForType(type, &options, &memory);
+
+       *koptions = 0;
+       *kmemory  = NULL;
+       if (kIOReturnSuccess != kr) {
+               return kr;
+       }
+
+       if (kIOUserClientMemoryReadOnly & options) {
+               *koptions |= kIOMapReadOnly;
+       }
+       *kmemory = memory;
+
+       return kr;
+}
+
+IOReturn
+IOUserUserClient::externalMethod(uint32_t selector, IOExternalMethodArguments * args,
+    IOExternalMethodDispatch * dispatch, OSObject * target, void * reference)
+{
+       IOReturn   kr;
+       OSData   * structureInput;
+       OSData   * structureOutput;
+       size_t     copylen;
+       uint64_t   structureOutputSize;
+       OSAction                  * action;
+       IOUserUserClientActionRef * ref;
+
+       kr             = kIOReturnUnsupported;
+       structureInput = NULL;
+       action         = NULL;
+
+       if (args->structureInputSize) {
+               structureInput = OSData::withBytesNoCopy((void *) args->structureInput, args->structureInputSize);
+       }
+
+       if (MACH_PORT_NULL != args->asyncWakePort) {
+               kr = CreateActionKernelCompletion(sizeof(IOUserUserClientActionRef), &action);
+               assert(KERN_SUCCESS == kr);
+               ref = (typeof(ref))action->GetReference();
+               bcopy(args->asyncReference, &ref->asyncRef[0], args->asyncReferenceCount * sizeof(ref->asyncRef[0]));
+       }
+
+       if (args->structureVariableOutputData) {
+               structureOutputSize = kIOUserClientVariableStructureSize;
+       } else if (args->structureOutputDescriptor) {
+               structureOutputSize = args->structureOutputDescriptor->getLength();
+       } else {
+               structureOutputSize = args->structureOutputSize;
+       }
+
+       kr = _ExternalMethod(selector, &args->scalarInput[0], args->scalarInputCount,
+           structureInput, args->structureInputDescriptor,
+           args->scalarOutput, &args->scalarOutputCount,
+           structureOutputSize, &structureOutput, args->structureOutputDescriptor,
+           action);
+
+       OSSafeReleaseNULL(structureInput);
+       OSSafeReleaseNULL(action);
+
+       if (kIOReturnSuccess != kr) {
+               return kr;
+       }
+       if (structureOutput) {
+               if (args->structureVariableOutputData) {
+                       *args->structureVariableOutputData = structureOutput;
+               } else {
+                       copylen = structureOutput->getLength();
+                       if (copylen > args->structureOutputSize) {
+                               kr = kIOReturnBadArgument;
+                       } else {
+                               bcopy((const void *) structureOutput->getBytesNoCopy(), args->structureOutput, copylen);
+                       }
+                       OSSafeReleaseNULL(structureOutput);
+               }
+       }
+
+       return kr;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */