]> git.saurik.com Git - apple/xnu.git/blobdiff - iokit/Kernel/IOUserServer.cpp
xnu-7195.50.7.100.1.tar.gz
[apple/xnu.git] / iokit / Kernel / IOUserServer.cpp
index df4172f3c81587c0de8a678bb95c2c3df7e51b26..88fd179e5dad23ba1361d26860d25edd8c10d13a 100644 (file)
@@ -35,6 +35,8 @@
 #include <IOKit/IOCatalogue.h>
 #include <IOKit/IOMemoryDescriptor.h>
 #include <IOKit/IOBufferMemoryDescriptor.h>
+#include <IOKit/IOSubMemoryDescriptor.h>
+#include <IOKit/IOMultiMemoryDescriptor.h>
 #include <IOKit/IOMapper.h>
 #include <IOKit/IOLib.h>
 #include <IOKit/IOBSD.h>
 #include <IOKit/IOTimerEventSource.h>
 #include <IOKit/pwr_mgt/RootDomain.h>
 #include <libkern/c++/OSKext.h>
+#include <libkern/c++/OSSharedPtr.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/IOServiceNotificationDispatchSource.h>
 #include <DriverKit/IOUserServer.h>
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 #include <System/IODataQueueDispatchSourceShared.h>
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
-SInt64    gIODKDebug = kIODKEnable;
+SECURITY_READ_ONLY_LATE(SInt64)    gIODKDebug = kIODKEnable;
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 struct IOPStrings;
 
@@ -129,6 +132,7 @@ public:
            IOMemoryDescriptor ** memory) APPLE_KEXT_OVERRIDE;
 };
 
+OSDefineMetaClassAndStructors(IOUserServerCheckInToken, OSObject);
 
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
@@ -147,11 +151,11 @@ IOUserService::start(IOService * provider)
        return ok;
 }
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 #undef super
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 struct IODispatchQueue_IVars {
        IOUserServer * userServer;
@@ -168,13 +172,15 @@ struct OSAction_IVars {
        uint64_t               msgid;
        OSActionAbortedHandler abortedHandler;
        size_t                 referenceSize;
+       OSString             * typeName;
        void                 * reference[0];
 };
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 kern_return_t
-IMPL(IOService, GetRegistryEntryID)
+IOService::GetRegistryEntryID_Impl(
+       uint64_t * registryEntryID)
 {
        IOReturn ret = kIOReturnSuccess;
 
@@ -184,7 +190,8 @@ IMPL(IOService, GetRegistryEntryID)
 }
 
 kern_return_t
-IMPL(IOService, SetName)
+IOService::SetName_Impl(
+       const char * name)
 {
        IOReturn ret = kIOReturnSuccess;
 
@@ -194,14 +201,15 @@ IMPL(IOService, SetName)
 }
 
 kern_return_t
-IMPL(IOService, Start)
+IOService::Start_Impl(
+       IOService * provider)
 {
        IOReturn ret = kIOReturnSuccess;
        return ret;
 }
 
 kern_return_t
-IMPL(IOService, RegisterService)
+IOService::RegisterService_Impl()
 {
        IOReturn ret = kIOReturnSuccess;
 
@@ -211,13 +219,19 @@ IMPL(IOService, RegisterService)
 }
 
 kern_return_t
-IMPL(IOService, CopyDispatchQueue)
+IOService::CopyDispatchQueue_Impl(
+       const char * name,
+       IODispatchQueue ** queue)
 {
        IODispatchQueue * result;
        IOService  * service;
        IOReturn     ret;
        uint32_t index;
 
+       if (!reserved->uvars) {
+               return kIOReturnError;
+       }
+
        ret = kIOReturnNotFound;
        index = -1U;
        if (!strcmp("Default", name)) {
@@ -246,11 +260,17 @@ IMPL(IOService, CopyDispatchQueue)
 }
 
 kern_return_t
-IMPL(IOService, SetDispatchQueue)
+IOService::SetDispatchQueue_Impl(
+       const char * name,
+       IODispatchQueue * queue)
 {
        IOReturn ret = kIOReturnSuccess;
        uint32_t index;
 
+       if (!reserved->uvars) {
+               return kIOReturnError;
+       }
+
        if (kIODKLogSetup & gIODKDebug) {
                DKLOG(DKS "::SetDispatchQueue(%s)\n", DKN(this), name);
        }
@@ -276,7 +296,8 @@ IMPL(IOService, SetDispatchQueue)
 }
 
 kern_return_t
-IMPL(IOService, SetProperties)
+IOService::SetProperties_Impl(
+       OSDictionary * properties)
 {
        IOUserServer   * us;
        OSDictionary   * dict;
@@ -317,17 +338,34 @@ IMPL(IOService, SetProperties)
 }
 
 kern_return_t
-IMPL(IOService, CopyProperties)
+IOService::CopyProperties_Impl(
+       OSDictionary ** properties)
 {
        IOReturn ret = kIOReturnSuccess;
        *properties = dictionaryWithProperties();
        return ret;
 }
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+kern_return_t
+IOService::RequireMaxBusStall_Impl(
+       uint64_t u64ns)
+{
+       IOReturn ret;
+       UInt32   ns;
+
+       if (os_convert_overflow(u64ns, &ns)) {
+               return kIOReturnBadArgument;
+       }
+       ret = requireMaxBusStall(ns);
+
+       return kIOReturnSuccess;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 kern_return_t
-IMPL(IOMemoryDescriptor, _CopyState)
+IOMemoryDescriptor::_CopyState_Impl(
+       _IOMDPrivateState * state)
 {
        IOReturn ret;
 
@@ -348,7 +386,13 @@ IOMemoryDescriptor::GetLength(uint64_t * returnLength)
 }
 
 kern_return_t
-IMPL(IOMemoryDescriptor, CreateMapping)
+IOMemoryDescriptor::CreateMapping_Impl(
+       uint64_t options,
+       uint64_t address,
+       uint64_t offset,
+       uint64_t length,
+       uint64_t alignment,
+       IOMemoryMap ** map)
 {
        IOReturn          ret;
        IOMemoryMap     * resultMap;
@@ -403,53 +447,148 @@ IMPL(IOMemoryDescriptor, CreateMapping)
 }
 
 kern_return_t
-IMPL(IOMemoryDescriptor, PrepareForDMA)
+IOMemoryDescriptor::CreateSubMemoryDescriptor_Impl(
+       uint64_t memoryDescriptorCreateOptions,
+       uint64_t offset,
+       uint64_t length,
+       IOMemoryDescriptor * ofDescriptor,
+       IOMemoryDescriptor ** memory)
+{
+       IOReturn             ret;
+       IOMemoryDescriptor * iomd;
+       IOByteCount          mdOffset;
+       IOByteCount          mdLength;
+       IOByteCount          mdEnd;
+
+       if (!ofDescriptor) {
+               return kIOReturnBadArgument;
+       }
+       if (memoryDescriptorCreateOptions & ~kIOMemoryDirectionOutIn) {
+               return kIOReturnBadArgument;
+       }
+       if (os_convert_overflow(offset, &mdOffset)) {
+               return kIOReturnBadArgument;
+       }
+       if (os_convert_overflow(length, &mdLength)) {
+               return kIOReturnBadArgument;
+       }
+       if (os_add_overflow(mdOffset, mdLength, &mdEnd)) {
+               return kIOReturnBadArgument;
+       }
+       if (mdEnd > ofDescriptor->getLength()) {
+               return kIOReturnBadArgument;
+       }
+
+       iomd = IOSubMemoryDescriptor::withSubRange(
+               ofDescriptor, mdOffset, mdLength, (IOOptionBits) memoryDescriptorCreateOptions);
+
+       if (iomd) {
+               ret = kIOReturnSuccess;
+               *memory = iomd;
+       } else {
+               ret = kIOReturnNoMemory;
+               *memory = NULL;
+       }
+
+       return ret;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+kern_return_t
+IOMemoryDescriptor::CreateWithMemoryDescriptors_Impl(
+       uint64_t memoryDescriptorCreateOptions,
+       uint32_t withDescriptorsCount,
+       IOMemoryDescriptor ** const withDescriptors,
+       IOMemoryDescriptor ** memory)
 {
-       IOReturn    ret;
-       uint32_t    idx, count;
-       uint64_t    sumLength;
-       uint64_t    lflags;
+       IOReturn             ret;
+       IOMemoryDescriptor * iomd;
 
-       if (!device) {
+       if (!withDescriptors) {
+               return kIOReturnBadArgument;
+       }
+       if (!withDescriptorsCount) {
+               return kIOReturnBadArgument;
+       }
+       if (memoryDescriptorCreateOptions & ~kIOMemoryDirectionOutIn) {
                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;
+       for (unsigned int idx = 0; idx < withDescriptorsCount; idx++) {
+               if (NULL == withDescriptors[idx]) {
+                       return kIOReturnBadArgument;
                }
-               sumLength += segments[idx].length;
-               offset += segments[idx].length;
        }
-       *returnLength = sumLength;
-       *segmentsCount = idx;
 
-       // !!translate flags
-       lflags = 0;
-       if (kIODirectionOut & _flags) {
-               lflags |= kIOMemoryDirectionOut;
+       iomd = IOMultiMemoryDescriptor::withDescriptors(withDescriptors, withDescriptorsCount,
+           (IODirection) memoryDescriptorCreateOptions, false);
+
+       if (iomd) {
+               ret = kIOReturnSuccess;
+               *memory = iomd;
+       } else {
+               ret = kIOReturnNoMemory;
+               *memory = NULL;
        }
-       if (kIODirectionIn  & _flags) {
-               lflags |= kIOMemoryDirectionIn;
+
+       return ret;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+
+kern_return_t
+IOUserClient::CreateMemoryDescriptorFromClient_Impl(
+       uint64_t memoryDescriptorCreateOptions,
+       uint32_t segmentsCount,
+       const IOAddressSegment segments[32],
+       IOMemoryDescriptor ** memory)
+{
+       IOReturn             ret;
+       IOMemoryDescriptor * iomd;
+       IOOptionBits         mdOptions;
+       IOUserUserClient   * me;
+       IOAddressRange     * ranges;
+
+       me = OSDynamicCast(IOUserUserClient, this);
+       if (!me) {
+               return kIOReturnBadArgument;
        }
 
-       *flags = lflags;
-       ret = kIOReturnSuccess;
+       mdOptions = 0;
+       if (kIOMemoryDirectionOut & memoryDescriptorCreateOptions) {
+               mdOptions |= kIODirectionOut;
+       }
+       if (kIOMemoryDirectionIn & memoryDescriptorCreateOptions) {
+               mdOptions |= kIODirectionIn;
+       }
+       if (!(kIOMemoryDisableCopyOnWrite & memoryDescriptorCreateOptions)) {
+               mdOptions |= kIOMemoryMapCopyOnWrite;
+       }
+
+       static_assert(sizeof(IOAddressRange) == sizeof(IOAddressSegment));
+       ranges = __DECONST(IOAddressRange *, &segments[0]);
+
+       iomd = IOMemoryDescriptor::withAddressRanges(
+               ranges, segmentsCount,
+               mdOptions, me->fTask);
+
+       if (iomd) {
+               ret = kIOReturnSuccess;
+               *memory = iomd;
+       } else {
+               ret = kIOReturnNoMemory;
+               *memory = NULL;
+       }
 
        return ret;
 }
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 kern_return_t
-IMPL(IOMemoryMap, _CopyState)
+IOMemoryMap::_CopyState_Impl(
+       _IOMemoryMapPrivateState * state)
 {
        IOReturn ret;
 
@@ -463,12 +602,17 @@ IMPL(IOMemoryMap, _CopyState)
        return ret;
 }
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 kern_return_t
-IMPL(IOBufferMemoryDescriptor, Create)
+IOBufferMemoryDescriptor::Create_Impl(
+       uint64_t options,
+       uint64_t capacity,
+       uint64_t alignment,
+       IOBufferMemoryDescriptor ** memory)
 {
        IOReturn ret;
+       IOOptionBits                 bmdOptions;
        IOBufferMemoryDescriptor   * bmd;
        IOMemoryDescriptorReserved * reserved;
 
@@ -476,10 +620,9 @@ IMPL(IOBufferMemoryDescriptor, Create)
                // no other options currently defined
                return kIOReturnBadArgument;
        }
-       options &= kIOMemoryDirectionOutIn;
-       options |= kIOMemoryKernelUserShared;
+       bmdOptions = (options & kIOMemoryDirectionOutIn) | kIOMemoryKernelUserShared;
        bmd = IOBufferMemoryDescriptor::inTaskWithOptions(
-               kernel_task, options, capacity, alignment);
+               kernel_task, bmdOptions, capacity, alignment);
 
        *memory = bmd;
 
@@ -497,17 +640,22 @@ IMPL(IOBufferMemoryDescriptor, Create)
 }
 
 kern_return_t
-IMPL(IOBufferMemoryDescriptor, SetLength)
+IOBufferMemoryDescriptor::SetLength_Impl(
+       uint64_t length)
 {
        setLength(length);
        return kIOReturnSuccess;
 }
 
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 kern_return_t
-IMPL(IODMACommand, Create)
+IODMACommand::Create_Impl(
+       IOService * device,
+       uint64_t options,
+       const IODMACommandSpecification * specification,
+       IODMACommand ** command)
 {
        IOReturn ret;
        IODMACommand   * dma;
@@ -550,7 +698,14 @@ IMPL(IODMACommand, Create)
 }
 
 kern_return_t
-IMPL(IODMACommand, PrepareForDMA)
+IODMACommand::PrepareForDMA_Impl(
+       uint64_t options,
+       IOMemoryDescriptor * memory,
+       uint64_t offset,
+       uint64_t length,
+       uint64_t * flags,
+       uint32_t * segmentsCount,
+       IOAddressSegment * segments)
 {
        IOReturn ret;
        uint64_t lflags, mdFlags;
@@ -562,14 +717,22 @@ IMPL(IODMACommand, PrepareForDMA)
                return kIOReturnBadArgument;
        }
 
+       // uses IOMD direction
+       ret = memory->prepare();
+       if (kIOReturnSuccess != ret) {
+               return ret;
+       }
+
        ret = setMemoryDescriptor(memory, false);
        if (kIOReturnSuccess != ret) {
+               memory->complete();
                return ret;
        }
 
        ret = prepare(offset, length);
        if (kIOReturnSuccess != ret) {
                clearMemoryDescriptor(false);
+               memory->complete();
                return ret;
        }
 
@@ -580,9 +743,7 @@ IMPL(IODMACommand, PrepareForDMA)
        ret = genIOVMSegments(&genOffset, segments, &numSegments);
 
        if (kIOReturnSuccess == ret) {
-               IOMemoryDescriptor * mem;
-               mem = __IODEQUALIFY(IOMemoryDescriptor *, fMemory);
-               mdFlags = mem->getFlags();
+               mdFlags = fMemory->getFlags();
                lflags  = 0;
                if (kIODirectionOut & mdFlags) {
                        lflags |= kIOMemoryDirectionOut;
@@ -598,22 +759,43 @@ IMPL(IODMACommand, PrepareForDMA)
 }
 
 kern_return_t
-IMPL(IODMACommand, CompleteDMA)
+IODMACommand::CompleteDMA_Impl(
+       uint64_t options)
 {
-       IOReturn ret;
+       IOReturn ret, completeRet;
+       IOMemoryDescriptor * md;
 
        if (options & ~((uint64_t) kIODMACommandCompleteDMANoOptions)) {
                // no other options currently defined
                return kIOReturnBadArgument;
        }
+       if (!fActive) {
+               return kIOReturnNotReady;
+       }
+
+       md = __DECONST(IOMemoryDescriptor *, fMemory);
+       if (md) {
+               md->retain();
+       }
 
        ret = clearMemoryDescriptor(true);
 
+       if (md) {
+               completeRet = md->complete();
+               OSSafeReleaseNULL(md);
+               if (kIOReturnSuccess == ret) {
+                       ret = completeRet;
+               }
+       }
+
        return ret;
 }
 
 kern_return_t
-IMPL(IODMACommand, GetPreparation)
+IODMACommand::GetPreparation_Impl(
+       uint64_t * offset,
+       uint64_t * length,
+       IOMemoryDescriptor ** memory)
 {
        IOReturn ret;
        IOMemoryDescriptor * md;
@@ -640,7 +822,12 @@ IMPL(IODMACommand, GetPreparation)
 }
 
 kern_return_t
-IMPL(IODMACommand, PerformOperation)
+IODMACommand::PerformOperation_Impl(
+       uint64_t options,
+       uint64_t dmaOffset,
+       uint64_t length,
+       uint64_t dataOffset,
+       IOMemoryDescriptor * data)
 {
        IOReturn ret;
        void * buffer;
@@ -730,43 +917,109 @@ IMPL(IODMACommand, PerformOperation)
 }
 
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
-
-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)
+static kern_return_t
+OSActionCreateWithTypeNameInternal(OSObject * target, uint64_t targetmsgid, uint64_t msgid, size_t referenceSize, OSString * typeName, bool fromKernel, OSAction ** action)
 {
-       OSAction * inst;
+       OSAction * inst = NULL;
        vm_size_t  allocsize;
+       const OSSymbol *sym = NULL; // must release
+       OSObject *obj = NULL; // must release
+       const OSMetaClass *actionMetaClass = NULL; // do not release
+       kern_return_t ret;
 
        if (os_add_overflow(referenceSize, sizeof(OSAction_IVars), &allocsize)) {
-               return kIOReturnBadArgument;
+               ret = kIOReturnBadArgument;
+               goto finish;
        }
-       inst = OSTypeAlloc(OSAction);
-       if (!inst) {
-               return kIOReturnNoMemory;
+
+       if (fromKernel && typeName) {
+               /* The action is being constructed in the kernel with a type name */
+               sym = OSSymbol::withString(typeName);
+               actionMetaClass = OSMetaClass::getMetaClassWithName(sym);
+               if (actionMetaClass && actionMetaClass->getSuperClass() == OSTypeID(OSAction)) {
+                       obj = actionMetaClass->alloc();
+                       if (!obj) {
+                               ret = kIOReturnNoMemory;
+                               goto finish;
+                       }
+                       inst = OSDynamicCast(OSAction, obj);
+                       obj = NULL; // prevent release
+                       assert(inst); // obj is a subclass of OSAction so the dynamic cast should always work
+               } else {
+                       DKLOG("Attempted to create action object with type \"%s\" which does not inherit from OSAction\n", typeName->getCStringNoCopy());
+                       ret = kIOReturnBadArgument;
+                       goto finish;
+               }
+       } else {
+               inst = OSTypeAlloc(OSAction);
+               if (!inst) {
+                       ret = kIOReturnNoMemory;
+                       goto finish;
+               }
        }
+
        inst->ivars = (typeof(inst->ivars))(uintptr_t) IONewZero(uint8_t, allocsize);
        if (!inst->ivars) {
-               inst->release();
-               return kIOReturnNoMemory;
+               ret = kIOReturnNoMemory;
+               goto finish;
        }
        target->retain();
        inst->ivars->target        = target;
        inst->ivars->targetmsgid   = targetmsgid;
        inst->ivars->msgid         = msgid;
        inst->ivars->referenceSize = referenceSize;
+       if (typeName) {
+               typeName->retain();
+       }
+       inst->ivars->typeName      = typeName;
 
        *action = inst;
+       inst = NULL; // prevent release
+       ret = kIOReturnSuccess;
 
-       return kIOReturnSuccess;
+finish:
+       OSSafeReleaseNULL(obj);
+       OSSafeReleaseNULL(sym);
+       OSSafeReleaseNULL(inst);
+
+       return ret;
+}
+
+kern_return_t
+OSAction::Create(OSAction_Create_Args)
+{
+       return OSAction::CreateWithTypeName(target, targetmsgid, msgid, referenceSize, NULL, action);
+}
+
+kern_return_t
+OSAction::CreateWithTypeName(OSAction_CreateWithTypeName_Args)
+{
+       return OSActionCreateWithTypeNameInternal(target, targetmsgid, msgid, referenceSize, typeName, true, action);
+}
+
+kern_return_t
+OSAction::Create_Impl(
+       OSObject * target,
+       uint64_t targetmsgid,
+       uint64_t msgid,
+       size_t referenceSize,
+       OSAction ** action)
+{
+       return OSAction::CreateWithTypeName_Impl(target, targetmsgid, msgid, referenceSize, NULL, action);
+}
+
+kern_return_t
+OSAction::CreateWithTypeName_Impl(
+       OSObject * target,
+       uint64_t targetmsgid,
+       uint64_t msgid,
+       size_t referenceSize,
+       OSString * typeName,
+       OSAction ** action)
+{
+       return OSActionCreateWithTypeNameInternal(target, targetmsgid, msgid, referenceSize, typeName, false, action);
 }
 
 void
@@ -778,6 +1031,7 @@ OSAction::free()
                        ivars->abortedHandler = NULL;
                }
                OSSafeReleaseNULL(ivars->target);
+               OSSafeReleaseNULL(ivars->typeName);
                IOSafeDeleteNULL(ivars, uint8_t, ivars->referenceSize + sizeof(OSAction_IVars));
        }
        return super::free();
@@ -805,7 +1059,7 @@ OSAction::Aborted_Impl(void)
        }
 }
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 struct IODispatchSource_IVars {
        queue_chain_t           link;
@@ -837,16 +1091,18 @@ IODispatchSource::free()
 }
 
 kern_return_t
-IMPL(IODispatchSource, SetEnable)
+IODispatchSource::SetEnable_Impl(
+       bool enable)
 {
        return SetEnableWithCompletion(enable, NULL);
 }
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 struct IOInterruptDispatchSource_IVars {
        IOService    * provider;
        uint32_t       intIndex;
+       int            interruptType;
        IOSimpleLock * lock;
        thread_t       waiter;
        uint64_t       count;
@@ -869,11 +1125,18 @@ IOInterruptDispatchSourceInterrupt(OSObject * target, void * refCon,
                thread_wakeup_thread((event_t) ivars, ivars->waiter);
                ivars->waiter = NULL;
        }
+       if (kIOInterruptTypeLevel & ivars->interruptType) {
+               ivars->provider->disableInterrupt(ivars->intIndex);
+       }
        IOSimpleLockUnlockEnableInterrupt(ivars->lock, is);
 }
 
 kern_return_t
-IMPL(IOInterruptDispatchSource, Create)
+IOInterruptDispatchSource::Create_Impl(
+       IOService * provider,
+       uint32_t index,
+       IODispatchQueue * queue,
+       IOInterruptDispatchSource ** source)
 {
        IOReturn ret;
        IOInterruptDispatchSource * inst;
@@ -886,17 +1149,26 @@ IMPL(IOInterruptDispatchSource, Create)
 
        inst->ivars->lock = IOSimpleLockAlloc();
 
+       ret = provider->getInterruptType(index, &inst->ivars->interruptType);
+       if (kIOReturnSuccess != ret) {
+               OSSafeReleaseNULL(inst);
+               return ret;
+       }
        ret = provider->registerInterrupt(index, inst, IOInterruptDispatchSourceInterrupt, inst->ivars);
        if (kIOReturnSuccess == ret) {
                inst->ivars->intIndex = index;
                inst->ivars->provider = provider;
+               inst->ivars->provider->retain();
                *source = inst;
        }
        return ret;
 }
 
 kern_return_t
-IMPL(IOInterruptDispatchSource, GetInterruptType)
+IOInterruptDispatchSource::GetInterruptType_Impl(
+       IOService * provider,
+       uint32_t index,
+       uint64_t * interruptType)
 {
        IOReturn ret;
        int      type;
@@ -932,6 +1204,7 @@ IOInterruptDispatchSource::free()
        if (ivars && ivars->provider) {
                ret = ivars->provider->unregisterInterrupt(ivars->intIndex);
                assert(kIOReturnSuccess == ret);
+               ivars->provider->release();
        }
 
        if (ivars && ivars->lock) {
@@ -944,7 +1217,8 @@ IOInterruptDispatchSource::free()
 }
 
 kern_return_t
-IMPL(IOInterruptDispatchSource, SetHandler)
+IOInterruptDispatchSource::SetHandler_Impl(
+       OSAction * action)
 {
        IOReturn ret;
        OSAction * oldAction;
@@ -962,7 +1236,9 @@ IMPL(IOInterruptDispatchSource, SetHandler)
 }
 
 kern_return_t
-IMPL(IOInterruptDispatchSource, SetEnableWithCompletion)
+IOInterruptDispatchSource::SetEnableWithCompletion_Impl(
+       bool enable,
+       IODispatchSourceCancelHandler handler)
 {
        IOReturn ret;
        IOInterruptState is;
@@ -987,16 +1263,20 @@ IMPL(IOInterruptDispatchSource, SetEnableWithCompletion)
 }
 
 kern_return_t
-IMPL(IOInterruptDispatchSource, Cancel)
+IOInterruptDispatchSource::Cancel_Impl(
+       IODispatchSourceCancelHandler handler)
 {
        return kIOReturnUnsupported;
 }
 
 kern_return_t
-IMPL(IOInterruptDispatchSource, CheckForWork)
+IOInterruptDispatchSource::CheckForWork_Impl(
+       const IORPC rpc,
+       bool synchronous)
 {
        IOReturn         ret = kIOReturnNotReady;
        IOInterruptState is;
+       bool             willWait;
        wait_result_t    waitResult;
        uint64_t         icount;
        uint64_t         itime;
@@ -1015,10 +1295,17 @@ IMPL(IOInterruptDispatchSource, CheckForWork)
                        ivars->waiter = self;
                        waitResult = assert_wait((event_t) ivars, THREAD_INTERRUPTIBLE);
                }
+               willWait = (synchronous && (waitResult == THREAD_WAITING));
+               if (willWait && (kIOInterruptTypeLevel & ivars->interruptType) && ivars->enable) {
+                       ivars->provider->enableInterrupt(ivars->intIndex);
+               }
                IOSimpleLockUnlockEnableInterrupt(ivars->lock, is);
-               if (synchronous && (waitResult == THREAD_WAITING)) {
+               if (willWait) {
                        waitResult = thread_block(THREAD_CONTINUE_NULL);
                        if (THREAD_INTERRUPTED == waitResult) {
+                               is = IOSimpleLockLockDisableInterrupt(ivars->lock);
+                               ivars->waiter = NULL;
+                               IOSimpleLockUnlockEnableInterrupt(ivars->lock, is);
                                break;
                        }
                }
@@ -1032,11 +1319,14 @@ IMPL(IOInterruptDispatchSource, CheckForWork)
 }
 
 void
-IMPL(IOInterruptDispatchSource, InterruptOccurred)
+IOInterruptDispatchSource::InterruptOccurred_Impl(
+       OSAction * action,
+       uint64_t count,
+       uint64_t time)
 {
 }
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 enum {
        kIOServiceNotificationTypeCount = kIOServiceNotificationTypeLast + 1,
@@ -1053,7 +1343,11 @@ struct IOServiceNotificationDispatchSource_IVars {
 };
 
 kern_return_t
-IMPL(IOServiceNotificationDispatchSource, Create)
+IOServiceNotificationDispatchSource::Create_Impl(
+       OSDictionary * matching,
+       uint64_t options,
+       IODispatchQueue * queue,
+       IOServiceNotificationDispatchSource ** notification)
 {
        IOUserServer * us;
        IOReturn       ret;
@@ -1164,7 +1458,10 @@ IMPL(IOServiceNotificationDispatchSource, Create)
 }
 
 kern_return_t
-IMPL(IOServiceNotificationDispatchSource, CopyNextNotification)
+IOServiceNotificationDispatchSource::CopyNextNotification_Impl(
+       uint64_t * type,
+       IOService ** service,
+       uint64_t * options)
 {
        IOService * next;
        uint32_t    idx;
@@ -1235,7 +1532,8 @@ IOServiceNotificationDispatchSource::free()
 }
 
 kern_return_t
-IMPL(IOServiceNotificationDispatchSource, SetHandler)
+IOServiceNotificationDispatchSource::SetHandler_Impl(
+       OSAction * action)
 {
        IOReturn ret;
        bool     notifyReady;
@@ -1265,7 +1563,9 @@ IMPL(IOServiceNotificationDispatchSource, SetHandler)
 }
 
 kern_return_t
-IMPL(IOServiceNotificationDispatchSource, SetEnableWithCompletion)
+IOServiceNotificationDispatchSource::SetEnableWithCompletion_Impl(
+       bool enable,
+       IODispatchSourceCancelHandler handler)
 {
        if (enable == ivars->enable) {
                return kIOReturnSuccess;
@@ -1279,13 +1579,16 @@ IMPL(IOServiceNotificationDispatchSource, SetEnableWithCompletion)
 }
 
 kern_return_t
-IMPL(IOServiceNotificationDispatchSource, Cancel)
+IOServiceNotificationDispatchSource::Cancel_Impl(
+       IODispatchSourceCancelHandler handler)
 {
        return kIOReturnUnsupported;
 }
 
 kern_return_t
-IMPL(IOServiceNotificationDispatchSource, CheckForWork)
+IOServiceNotificationDispatchSource::CheckForWork_Impl(
+       const IORPC rpc,
+       bool synchronous)
 {
        return kIOReturnNotReady;
 }
@@ -1296,7 +1599,7 @@ IOServiceNotificationDispatchSource::DeliverNotifications(IOServiceNotificationB
        return kIOReturnUnsupported;
 }
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 kern_return_t
 IOUserServer::waitInterruptTrap(void * p1, void * p2, void * p3, void * p4, void * p5, void * p6)
@@ -1307,6 +1610,7 @@ IOUserServer::waitInterruptTrap(void * p1, void * p2, void * p3, void * p4, void
        IOInterruptDispatchSource_IVars * ivars;
        IOInterruptDispatchSourcePayload payload;
 
+       bool             willWait;
        wait_result_t    waitResult;
        thread_t         self;
 
@@ -1334,10 +1638,17 @@ IOUserServer::waitInterruptTrap(void * p1, void * p2, void * p3, void * p4, void
                                ivars->waiter = self;
                                waitResult = assert_wait((event_t) ivars, THREAD_INTERRUPTIBLE);
                        }
+                       willWait = (waitResult == THREAD_WAITING);
+                       if (willWait && (kIOInterruptTypeLevel & ivars->interruptType) && ivars->enable) {
+                               ivars->provider->enableInterrupt(ivars->intIndex);
+                       }
                        IOSimpleLockUnlockEnableInterrupt(ivars->lock, is);
-                       if (waitResult == THREAD_WAITING) {
+                       if (willWait) {
                                waitResult = thread_block(THREAD_CONTINUE_NULL);
                                if (THREAD_INTERRUPTED == waitResult) {
+                                       is = IOSimpleLockLockDisableInterrupt(ivars->lock);
+                                       ivars->waiter = NULL;
+                                       IOSimpleLockUnlockEnableInterrupt(ivars->lock, is);
                                        break;
                                }
                        }
@@ -1357,10 +1668,14 @@ IOUserServer::waitInterruptTrap(void * p1, void * p2, void * p3, void * p4, void
        return ret;
 }
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 kern_return_t
-IMPL(IOUserServer, Create)
+IOUserServer::Create_Impl(
+       const char * name,
+       uint64_t tag,
+       uint64_t options,
+       IOUserServer ** server)
 {
        IOReturn          ret;
        IOUserServer    * us;
@@ -1397,22 +1712,28 @@ IMPL(IOUserServer, Create)
 }
 
 kern_return_t
-IMPL(IOUserServer, Exit)
+IOUserServer::Exit_Impl(
+       const char * reason)
 {
        return kIOReturnUnsupported;
 }
 
 kern_return_t
-IMPL(IOUserServer, LoadModule)
+IOUserServer::LoadModule_Impl(
+       const char * path)
 {
        return kIOReturnUnsupported;
 }
 
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 kern_return_t
-IMPL(IODispatchQueue, Create)
+IODispatchQueue::Create_Impl(
+       const char * name,
+       uint64_t options,
+       uint64_t priority,
+       IODispatchQueue ** queue)
 {
        IODispatchQueue * result;
        IOUserServer    * us;
@@ -1441,7 +1762,8 @@ IMPL(IODispatchQueue, Create)
 }
 
 kern_return_t
-IMPL(IODispatchQueue, SetPort)
+IODispatchQueue::SetPort_Impl(
+       mach_port_t port)
 {
        if (MACH_PORT_NULL != ivars->serverPort) {
                return kIOReturnNotReady;
@@ -1648,68 +1970,26 @@ IOUserServer::setDriverKitUUID(OSKext *kext)
        kext->setDriverKitUUID(new_uuid);
 }
 
-bool
-IOUserServer::serviceMatchesCDHash(IOService *service)
+void
+IOUserServer::setCheckInToken(IOUserServerCheckInToken *token)
 {
-       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;
+       if (token != NULL && fCheckInToken == NULL) {
+               token->retain();
+               fCheckInToken = token;
+       } else {
+               printf("%s: failed to set check in token. token=%p, fCheckInToken=%p\n", __FUNCTION__, token, fCheckInToken);
        }
+}
 
-       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;
-               }
+bool
+IOUserServer::serviceMatchesCheckInToken(IOUserServerCheckInToken *token)
+{
+       if (token != NULL) {
+               return token == fCheckInToken;
+       } else {
+               printf("%s: null check in token\n", __FUNCTION__);
+               return false;
        }
-
-       result = true;
-out:
-       OSSafeReleaseNULL(obj);
-       return result;
 }
 
 bool
@@ -1940,7 +2220,7 @@ IOUserServer::objectInstantiate(OSObject * obj, IORPC rpc, IORPCMessage * messag
        const char     * resultClassName;
        uint64_t         resultFlags;
 
-       size_t             replySize;
+       mach_msg_size_t    replySize;
        uint32_t           methodCount;
        const uint64_t   * methods;
        IODispatchQueue  * queue;
@@ -1962,6 +2242,7 @@ IOUserServer::objectInstantiate(OSObject * obj, IORPC rpc, IORPCMessage * messag
        ret = kIOReturnUnsupportedMode;
 
        service = OSDynamicCast(IOService, obj);
+       action = OSDynamicCast(OSAction, obj);
        if (!service) {
                // xxx other classes hosted
                resultFlags |= kOSObjectRPCKernel;
@@ -1990,6 +2271,12 @@ IOUserServer::objectInstantiate(OSObject * obj, IORPC rpc, IORPCMessage * messag
                const OSMetaClass * meta;
                meta = obj->getMetaClass();
                IOLockLock(fLock);
+               if (action) {
+                       str = action->ivars->typeName;
+                       if (str) {
+                               userMeta = (typeof(userMeta))fClasses->getObject(str);
+                       }
+               }
                while (meta && !userMeta) {
                        str = (OSString *) meta->getClassNameSymbol();
                        userMeta = (typeof(userMeta))fClasses->getObject(str);
@@ -2010,8 +2297,11 @@ IOUserServer::objectInstantiate(OSObject * obj, IORPC rpc, IORPCMessage * messag
                }
                if (userMeta) {
                        if (kOSObjectRPCRemote & resultFlags) {
-                               while (userMeta && !(kOSClassCanRemote & userMeta->description->flags)) {
-                                       userMeta = userMeta->superMeta;
+                               if (!action) {
+                                       /* Special case: For OSAction subclasses, do not use the superclass */
+                                       while (userMeta && !(kOSClassCanRemote & userMeta->description->flags)) {
+                                               userMeta = userMeta->superMeta;
+                                       }
                                }
                                if (userMeta) {
                                        resultClassName = userMeta->description->name;
@@ -2028,6 +2318,14 @@ IOUserServer::objectInstantiate(OSObject * obj, IORPC rpc, IORPCMessage * messag
                                resultClassName = str->getCStringNoCopy();
                                ret = kIOReturnSuccess;
                        }
+               } else if (kIODKLogSetup & gIODKDebug) {
+                       DKLOG("userMeta %s was not found in fClasses\n", str->getCStringNoCopy());
+                       IOLockLock(fLock);
+                       fClasses->iterateObjects(^bool (const OSSymbol * key, OSObject * val) {
+                               DKLOG(" fClasses[\"%s\"] => %p\n", key->getCStringNoCopy(), val);
+                               return false;
+                       });
+                       IOLockUnlock(fLock);
                }
        }
        OSSafeReleaseNULL(prop);
@@ -2037,7 +2335,7 @@ IOUserServer::objectInstantiate(OSObject * obj, IORPC rpc, IORPCMessage * messag
 
        if ((kIOReturnSuccess == ret) && (kOSObjectRPCRemote & resultFlags)) {
                target = obj;
-               if ((action = OSDynamicCast(OSAction, obj))) {
+               if (action) {
                        if (action->ivars->referenceSize) {
                                resultFlags |= kOSObjectRPCKernel;
                        } else {
@@ -2097,7 +2395,7 @@ IOUserServer::objectInstantiate(OSObject * obj, IORPC rpc, IORPCMessage * messag
        }
 
        if (kIODKLogIPC & gIODKDebug) {
-               DKLOG("instantiate %s\n", obj->getMetaClass()->getClassName());
+               DKLOG("instantiate object %s with user class %s\n", obj->getMetaClass()->getClassName(), str ? str->getCStringNoCopy() : "(null)");
        }
 
        if (kIOReturnSuccess != ret) {
@@ -2129,7 +2427,7 @@ IOUserServer::objectInstantiate(OSObject * obj, IORPC rpc, IORPCMessage * messag
        return ret;
 }
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 IOReturn
 IOUserServer::kernelDispatch(OSObject * obj, IORPC rpc)
@@ -2161,7 +2459,7 @@ IOUserServer::kernelDispatch(OSObject * obj, IORPC rpc)
 }
 
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 OSObject *
 IOUserServer::target(OSAction * action, IORPCMessage * message)
@@ -2176,7 +2474,13 @@ IOUserServer::target(OSAction * action, IORPCMessage * message)
        message->objects[0] = (OSObjectRef) object;
        if (kIORPCMessageRemote & message->flags) {
                object->retain();
+#ifndef __clang_analyzer__
+               // Hide the release of 'action' from the clang static analyzer to suppress
+               // an overrelease diagnostic. The analyzer doesn't have a way to express the
+               // non-standard contract of this method, which is that it releases 'action' when
+               // the message flags have kIORPCMessageRemote set.
                action->release();
+#endif
        }
        if (kIODKLogIPC & gIODKDebug) {
                DKLOG("TARGET %s msg 0x%qx from 0x%qx\n", object->getMetaClass()->getClassName(), message->msgid, action->ivars->msgid);
@@ -2185,7 +2489,7 @@ IOUserServer::target(OSAction * action, IORPCMessage * message)
        return object;
 }
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 kern_return_t
 uext_server(ipc_kmsg_t requestkmsg, ipc_kmsg_t * pReply)
@@ -2349,10 +2653,10 @@ IOUserServer::server(ipc_kmsg_t requestkmsg, ipc_kmsg_t * pReply)
        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))
+       ((uint32_t)(((((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)
@@ -2361,7 +2665,7 @@ IOUserServerUEXTTrap(OSObject * object, void * p1, void * p2, void * p3, void *
        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;
+       mach_port_name_t  objectName1      = (mach_port_name_t)(uintptr_t) p5;
        size_t            totalSize;
        OSObject        * objectArg1;
 
@@ -2419,13 +2723,13 @@ IOUserServerUEXTTrap(OSObject * object, void * p1, void * p2, void * p3, void *
        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;
+       mach->msgh.msgh_size = (mach_msg_size_t) (sizeof(IORPCMessageMach) + refs * sizeof(*descs) + inSize); // totalSize was checked
+       mach->msgh_body.msgh_descriptor_count = ((mach_msg_size_t) refs);
 
        rpc.message   = mach;
        rpc.sendSize  = mach->msgh.msgh_size;
        rpc.reply     = (IORPCMessageMach *) (p + inSize);
-       rpc.replySize = sizeof(buffer.buffer) - inSize;
+       rpc.replySize = ((uint32_t) (sizeof(buffer.buffer) - inSize));    // inSize was checked
 
        message->objects[0] = 0;
        if ((action = OSDynamicCast(OSAction, object))) {
@@ -2484,7 +2788,7 @@ IOUserServerUEXTTrap(OSObject * object, void * p1, void * p2, void * p3, void *
        return ret;
 }
 
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 IOReturn
 IOUserServer::rpc(IORPC rpc)
@@ -2517,7 +2821,7 @@ IOUserServer::rpc(IORPC rpc)
 
        message = IORPCMessageFromMach(mach, false);
        if (!message) {
-               ret = kIOReturnIPCError;
+               return kIOReturnIPCError;
        }
        msgid   = message->msgid;
        machid  = (msgid >> 32);
@@ -3097,6 +3401,7 @@ IOUserServer::free()
                IOLockFree(fLock);
        }
        OSSafeReleaseNULL(fServices);
+       OSSafeReleaseNULL(fCheckInToken);
        IOUserClient::free();
 }
 
@@ -3219,6 +3524,17 @@ IOUserServer::registerClass(OSClassDescription * desc, uint32_t size, OSUserMeta
        return ret;
 }
 
+IOReturn
+IOUserServer::registerClass(OSClassDescription * desc, uint32_t size, OSSharedPtr<OSUserMetaClass>& pCls)
+{
+       OSUserMetaClass* pClsRaw = NULL;
+       IOReturn result = registerClass(desc, size, &pClsRaw);
+       if (result == kIOReturnSuccess) {
+               pCls.reset(pClsRaw, OSRetain);
+       }
+       return result;
+}
+
 IOReturn
 IOUserServer::setRootQueue(IODispatchQueue * queue)
 {
@@ -3263,6 +3579,21 @@ IOUserServer::externalMethod(uint32_t selector, IOExternalMethodArguments * args
                if (args->scalarOutputCount != 1) {
                        return kIOReturnBadArgument;
                }
+               if (!(kIODKDisableCheckInTokenVerification & gIODKDebug)) {
+                       if (args->scalarInputCount != 1) {
+                               return kIOReturnBadArgument;
+                       }
+                       mach_port_name_t checkInPortName = ((typeof(checkInPortName))args->scalarInput[0]);
+                       OSObject * obj = iokit_lookup_object_with_port_name(checkInPortName, IKOT_IOKIT_IDENT, fOwningTask);
+                       IOUserServerCheckInToken * retrievedToken = OSDynamicCast(IOUserServerCheckInToken, obj);
+                       if (retrievedToken != NULL) {
+                               setCheckInToken(retrievedToken);
+                       } else {
+                               OSSafeReleaseNULL(obj);
+                               return kIOReturnBadArgument;
+                       }
+                       OSSafeReleaseNULL(obj);
+               }
                portname = iokit_make_send_right(fOwningTask, this, IKOT_UEXT_OBJECT);
                assert(portname);
                args->scalarOutput[0] = portname;
@@ -3298,7 +3629,7 @@ IOUserServer::serviceAttach(IOService * service, IOService * provider)
        OSObjectUserVars * vars;
        OSObject         * prop;
        OSString         * str;
-       OSSymbolConstPtr   bundleID;
+       OSSymbol const*   bundleID;
        char               execPath[1024];
 
        vars = IONewZero(OSObjectUserVars, 1);
@@ -3406,6 +3737,16 @@ IOUserServer::serviceNewUserClient(IOService * service, task_t owningTask, void
        return ret;
 }
 
+IOReturn
+IOUserServer::serviceNewUserClient(IOService * service, task_t owningTask, void * securityID,
+    uint32_t type, OSDictionary * properties, OSSharedPtr<IOUserClient>& handler)
+{
+       IOUserClient* handlerRaw = NULL;
+       IOReturn result = serviceNewUserClient(service, owningTask, securityID, type, properties, &handlerRaw);
+       handler.reset(handlerRaw, OSNoRetain);
+       return result;
+}
+
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 static IOPMPowerState
@@ -3434,17 +3775,19 @@ IOUserServer::setPowerState(unsigned long state, IOService * service)
 }
 
 IOReturn
-IOUserServer::powerStateWillChangeTo(IOPMPowerFlags flags, unsigned long state, IOService * service)
+IOUserServer::serviceSetPowerState(IOService * controllingDriver, IOService * service, IOPMPowerFlags flags, unsigned long state)
 {
        IOReturn ret;
 
        if (service->reserved->uvars) {
                if (!fSystemOff && !(kIODKDisablePM & gIODKDebug)) {
                        service->reserved->uvars->willPower = true;
+                       service->reserved->uvars->willPowerState = state;
+                       service->reserved->uvars->controllingDriver = controllingDriver;
                        if (kIODKLogPM & gIODKDebug) {
-                               DKLOG(DKS "::powerStateWillChangeTo(%ld) 0x%qx, %d\n", DKN(service), state, fPowerStates, fSystemPowerAck);
+                               DKLOG(DKS "::serviceSetPowerState(%ld) 0x%qx, %d\n", DKN(service), state, fPowerStates, fSystemPowerAck);
                        }
-                       ret = service->SetPowerState(flags);
+                       ret = service->SetPowerState((uint32_t) flags);
                        if (kIOReturnSuccess == ret) {
                                return 20 * 1000 * 1000;
                        }
@@ -3455,6 +3798,12 @@ IOUserServer::powerStateWillChangeTo(IOPMPowerFlags flags, unsigned long state,
        return kIOPMAckImplied;
 }
 
+IOReturn
+IOUserServer::powerStateWillChangeTo(IOPMPowerFlags flags, unsigned long state, IOService * service)
+{
+       return kIOPMAckImplied;
+}
+
 IOReturn
 IOUserServer::powerStateDidChangeTo(IOPMPowerFlags flags, unsigned long state, IOService * service)
 {
@@ -3492,7 +3841,8 @@ IOUserServer::powerStateDidChangeTo(IOPMPowerFlags flags, unsigned long state, I
 }
 
 kern_return_t
-IMPL(IOService, SetPowerState)
+IOService::SetPowerState_Impl(
+       uint32_t powerFlags)
 {
        if (kIODKLogPM & gIODKDebug) {
                DKLOG(DKS "::SetPowerState(%d), %d\n", DKN(this), powerFlags, reserved->uvars->willPower);
@@ -3500,15 +3850,20 @@ IMPL(IOService, SetPowerState)
        if (reserved->uvars
            && reserved->uvars->userServer
            && reserved->uvars->willPower) {
+               IOReturn ret;
                reserved->uvars->willPower = false;
-               acknowledgePowerChange(reserved->uvars->userServer);
+               ret = reserved->uvars->controllingDriver->setPowerState(reserved->uvars->willPowerState, this);
+               if (kIOPMAckImplied == ret) {
+                       acknowledgeSetPowerState();
+               }
                return kIOReturnSuccess;
        }
        return kIOReturnNotReady;
 }
 
 kern_return_t
-IMPL(IOService, ChangePowerState)
+IOService::ChangePowerState_Impl(
+       uint32_t powerFlags)
 {
        switch (powerFlags) {
        case kIOServicePowerCapabilityOff:
@@ -3528,7 +3883,10 @@ IMPL(IOService, ChangePowerState)
 }
 
 kern_return_t
-IMPL(IOService, Create)
+IOService::Create_Impl(
+       IOService * provider,
+       const char * propertiesKey,
+       IOService ** result)
 {
        OSObject       * inst;
        IOService      * service;
@@ -3575,7 +3933,8 @@ IMPL(IOService, Create)
 }
 
 kern_return_t
-IMPL(IOService, Terminate)
+IOService::Terminate_Impl(
+       uint64_t options)
 {
        IOUserServer * us;
 
@@ -3594,30 +3953,39 @@ IMPL(IOService, Terminate)
 }
 
 kern_return_t
-IMPL(IOService, NewUserClient)
+IOService::NewUserClient_Impl(
+       uint32_t type,
+       IOUserClient ** userClient)
 {
        return kIOReturnError;
 }
 
 kern_return_t
-IMPL(IOService, SearchProperty)
+IOService::SearchProperty_Impl(
+       const char * name,
+       const char * plane,
+       uint64_t options,
+       OSContainer ** property)
 {
-       OSObject * object;
+       OSObject   * object;
+       IOOptionBits regOptions;
 
        if (kIOServiceSearchPropertyParents & options) {
-               options = kIORegistryIterateParents | kIORegistryIterateRecursively;
+               regOptions = kIORegistryIterateParents | kIORegistryIterateRecursively;
        } else {
-               options = 0;
+               regOptions = 0;
        }
 
-       object = copyProperty(name, IORegistryEntry::getPlane(plane), options);
+       object = copyProperty(name, IORegistryEntry::getPlane(plane), regOptions);
        *property = object;
 
        return object ? kIOReturnSuccess : kIOReturnNotFound;
 }
 
 kern_return_t
-IMPL(IOService, CopyProviderProperties)
+IOService::CopyProviderProperties_Impl(
+       OSArray * propertyKeys,
+       OSArray ** properties)
 {
        IOReturn    ret;
        OSArray   * result;
@@ -3753,6 +4121,7 @@ IOUserServer::serviceStarted(IOService * service, IOService * provider, bool res
 {
        IOReturn    ret;
        IOService * pmProvider;
+       bool        joinTree;
 
        DKLOG(DKS "::start(" DKS ") %s\n", DKN(service), DKN(provider), result ? "ok" : "fail");
 
@@ -3768,34 +4137,39 @@ IOUserServer::serviceStarted(IOService * service, IOService * provider, bool res
                fRootNotifier = true;
        }
 
+       joinTree = false;
        if (!(kIODKDisablePM & gIODKDebug) && !service->pm_vars) {
                service->PMinit();
                ret = service->registerPowerDriver(this, sPowerStates, sizeof(sPowerStates) / sizeof(sPowerStates[0]));
                assert(kIOReturnSuccess == ret);
+               joinTree = true;
+       }
 
-               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();
+       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);
+       }
 
+       if (!(kIODKDisablePM & gIODKDebug) && pmProvider) {
+               IOLockLock(fLock);
+               unsigned int idx = fServices->getNextIndexOfObject(service, 0);
+               assert(idx <= 63);
+               fPowerStates |= (1ULL << idx);
+               IOLockUnlock(fLock);
+
+               if (joinTree) {
                        pmProvider->joinPMtree(service);
                        service->reserved->uvars->userServerPM = true;
                }
@@ -3913,9 +4287,15 @@ IOUserServer::serviceWillTerminate(IOService * client, IOService * provider, IOO
        }
 
        if (willTerminate) {
-               ret = client->Stop(provider);
+               if (IOServicePH::serverSlept()) {
+                       client->Stop_async(provider);
+                       ret = kIOReturnOffline;
+               } else {
+                       ret = client->Stop(provider);
+               }
                if (kIOReturnSuccess != ret) {
-                       ret = client->IOService::Stop(provider);
+                       IOUserServer::serviceDidStop(client, provider);
+                       ret = kIOReturnSuccess;
                }
        }
 }
@@ -3975,13 +4355,20 @@ IOUserServer::serviceDidStop(IOService * client, IOService * provider)
 }
 
 kern_return_t
-IMPL(IOService, Stop)
+IOService::Stop_Impl(
+       IOService * provider)
 {
        IOUserServer::serviceDidStop(this, provider);
 
        return kIOReturnSuccess;
 }
 
+void
+IOService::Stop_async_Impl(
+       IOService * provider)
+{
+}
+
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 #undef super
@@ -4029,7 +4416,11 @@ struct IOUserUserClientActionRef {
 };
 
 void
-IMPL(IOUserClient, KernelCompletion)
+IOUserClient::KernelCompletion_Impl(
+       OSAction * action,
+       IOReturn status,
+       const unsigned long long * asyncData,
+       uint32_t asyncDataCount)
 {
        IOUserUserClientActionRef * ref;
 
@@ -4039,7 +4430,18 @@ IMPL(IOUserClient, KernelCompletion)
 }
 
 kern_return_t
-IMPL(IOUserClient, _ExternalMethod)
+IOUserClient::_ExternalMethod_Impl(
+       uint64_t selector,
+       const unsigned long long * scalarInput,
+       uint32_t scalarInputCount,
+       OSData * structureInput,
+       IOMemoryDescriptor * structureInputDescriptor,
+       unsigned long long * scalarOutput,
+       uint32_t * scalarOutputCount,
+       uint64_t structureOutputMaximumSize,
+       OSData ** structureOutput,
+       IOMemoryDescriptor * structureOutputDescriptor,
+       OSAction * completion)
 {
        return kIOReturnUnsupported;
 }
@@ -4084,6 +4486,7 @@ IOUserUserClient::externalMethod(uint32_t selector, IOExternalMethodArguments *
        kr             = kIOReturnUnsupported;
        structureInput = NULL;
        action         = NULL;
+       ref            = NULL;
 
        if (args->structureInputSize) {
                structureInput = OSData::withBytesNoCopy((void *) args->structureInput, args->structureInputSize);
@@ -4094,6 +4497,17 @@ IOUserUserClient::externalMethod(uint32_t selector, IOExternalMethodArguments *
                assert(KERN_SUCCESS == kr);
                ref = (typeof(ref))action->GetReference();
                bcopy(args->asyncReference, &ref->asyncRef[0], args->asyncReferenceCount * sizeof(ref->asyncRef[0]));
+
+               kr = action->SetAbortedHandler(^(void) {
+                       IOUserUserClientActionRef * ref;
+                       IOReturn ret;
+
+                       ref = (typeof(ref))action->GetReference();
+                       ret = releaseAsyncReference64(ref->asyncRef);
+                       assert(kIOReturnSuccess == ret);
+                       bzero(&ref->asyncRef[0], sizeof(ref->asyncRef));
+               });
+               assert(KERN_SUCCESS == kr);
        }
 
        if (args->structureVariableOutputData) {
@@ -4114,6 +4528,10 @@ IOUserUserClient::externalMethod(uint32_t selector, IOExternalMethodArguments *
        OSSafeReleaseNULL(action);
 
        if (kIOReturnSuccess != kr) {
+               if (ref) {
+                       // mig will destroy any async port, remove our pointer to it
+                       bzero(&ref->asyncRef[0], sizeof(ref->asyncRef));
+               }
                return kr;
        }
        if (structureOutput) {
@@ -4134,3 +4552,40 @@ IOUserUserClient::externalMethod(uint32_t selector, IOExternalMethodArguments *
 }
 
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+void
+IOUserServerCheckInToken::setNoSendersNotification(IOUserServerCheckInNotificationHandler handler,
+    void* handlerArgs)
+{
+       this->handler = handler;
+       this->handlerArgs = handlerArgs;
+}
+
+void
+IOUserServerCheckInToken::notifyNoSenders(IOUserServerCheckInToken *token)
+{
+       if (token->handler) {
+               token->handler(token, token->handlerArgs);
+       }
+}
+
+void
+IOUserServerCheckInToken::clearNotification()
+{
+       this->handler = NULL;
+       this->handlerArgs = NULL;
+}
+
+IOUserServerCheckInToken *
+IOUserServerCheckInToken::create()
+{
+       IOUserServerCheckInToken *me = new IOUserServerCheckInToken;
+       if (me && !me->init()) {
+               me->release();
+               return NULL;
+       }
+       me->clearNotification();
+       return me;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */