+ IOServiceTrace(
+ (defer ? IOSERVICE_TERMINATE_DID_DEFER
+ : IOSERVICE_TERMINATE_DID),
+ (uintptr_t) regID1,
+ (uintptr_t) (regID1 >> 32),
+ (uintptr_t) regID2,
+ (uintptr_t) (regID2 >> 32));
+
+ TLOG("%s[0x%qx]::didTerminate(%s[0x%qx], defer %d)\n",
+ client->getName(), regID1,
+ victim->getName(), regID2, defer);
+ }
+ iter->release();
+ }
+}
+
+
+void
+IOService::actionWillStop( IOService * victim, IOOptionBits options,
+ void *unused1 __unused, void *unused2 __unused,
+ void *unused3 __unused )
+{
+ OSIterator * iter;
+ IOService * provider;
+ bool ok;
+ uint64_t regID1, regID2 = victim->getRegistryEntryID();
+
+ iter = victim->getProviderIterator();
+ if (iter) {
+ while ((provider = (IOService *) iter->getNextObject())) {
+ regID1 = provider->getRegistryEntryID();
+ TLOG("%s[0x%qx]::willTerminate(%s[0x%qx], %08llx)\n",
+ victim->getName(), regID2,
+ provider->getName(), regID1, (long long)options);
+ IOServiceTrace(
+ IOSERVICE_TERMINATE_WILL,
+ (uintptr_t) regID2,
+ (uintptr_t) (regID2 >> 32),
+ (uintptr_t) regID1,
+ (uintptr_t) (regID1 >> 32));
+
+ ok = victim->willTerminate( provider, options );
+ }
+ iter->release();
+ }
+}
+
+void
+IOService::actionDidStop( IOService * victim, IOOptionBits options,
+ void *unused1 __unused, void *unused2 __unused,
+ void *unused3 __unused )
+{
+ OSIterator * iter;
+ IOService * provider;
+ bool defer = false;
+ uint64_t regID1, regID2 = victim->getRegistryEntryID();
+
+ iter = victim->getProviderIterator();
+ if (iter) {
+ while ((provider = (IOService *) iter->getNextObject())) {
+ regID1 = provider->getRegistryEntryID();
+ TLOG("%s[0x%qx]::didTerminate(%s[0x%qx], %08llx)\n",
+ victim->getName(), regID2,
+ provider->getName(), regID1, (long long)options);
+ victim->didTerminate( provider, options, &defer );
+
+ IOServiceTrace(
+ (defer ? IOSERVICE_TERMINATE_DID_DEFER
+ : IOSERVICE_TERMINATE_DID),
+ (uintptr_t) regID2,
+ (uintptr_t) (regID2 >> 32),
+ (uintptr_t) regID1,
+ (uintptr_t) (regID1 >> 32));
+
+ TLOG("%s[0x%qx]::didTerminate(%s[0x%qx], defer %d)\n",
+ victim->getName(), regID2,
+ provider->getName(), regID1, defer);
+ }
+ iter->release();
+ }
+}
+
+
+void
+IOService::actionFinalize( IOService * victim, IOOptionBits options,
+ void *unused1 __unused, void *unused2 __unused,
+ void *unused3 __unused )
+{
+ uint64_t regID1 = victim->getRegistryEntryID();
+ TLOG("%s[0x%qx]::finalize(%08llx)\n", victim->getName(), regID1, (long long)options);
+ IOServiceTrace(
+ IOSERVICE_TERMINATE_FINALIZE,
+ (uintptr_t) regID1,
+ (uintptr_t) (regID1 >> 32),
+ 0, 0);
+
+ victim->finalize( options );
+}
+
+void
+IOService::actionStop( IOService * provider, IOService * client,
+ void *unused1 __unused, void *unused2 __unused,
+ void *unused3 __unused )
+{
+ uint64_t regID1 = provider->getRegistryEntryID();
+ uint64_t regID2 = client->getRegistryEntryID();
+
+ TLOG("%s[0x%qx]::stop(%s[0x%qx])\n", client->getName(), regID2, provider->getName(), regID1);
+ IOServiceTrace(
+ IOSERVICE_TERMINATE_STOP,
+ (uintptr_t) regID1,
+ (uintptr_t) (regID1 >> 32),
+ (uintptr_t) regID2,
+ (uintptr_t) (regID2 >> 32));
+
+ client->stop( provider );
+ if (provider->isOpen( client )) {
+ provider->close( client );
+ }
+
+ TLOG("%s[0x%qx]::detach(%s[0x%qx])\n", client->getName(), regID2, provider->getName(), regID1);
+ client->detach( provider );
+}
+
+void
+IOService::terminateWorker( IOOptionBits options )
+{
+ OSArray * doPhase2List;
+ OSArray * didPhase2List;
+ OSSet * freeList;
+ OSIterator * iter;
+ UInt32 workDone;
+ IOService * victim;
+ IOService * client;
+ IOService * provider;
+ unsigned int idx;
+ bool moreToDo;
+ bool doPhase2;
+ bool doPhase3;
+
+ options |= kIOServiceRequired;
+
+ doPhase2List = OSArray::withCapacity( 16 );
+ didPhase2List = OSArray::withCapacity( 16 );
+ freeList = OSSet::withCapacity( 16 );
+ if ((NULL == doPhase2List) || (NULL == didPhase2List) || (NULL == freeList)) {
+ return;
+ }
+
+ do {
+ workDone = gIOTerminateWork;
+
+ while ((victim = (IOService *) gIOTerminatePhase2List->getObject(0))) {
+ victim->retain();
+ gIOTerminatePhase2List->removeObject(0);
+ IOLockUnlock( gJobsLock );
+
+ uint64_t regID1 = victim->getRegistryEntryID();
+ IOServiceTrace(
+ IOSERVICE_TERM_START_PHASE2,
+ (uintptr_t) regID1,
+ (uintptr_t) (regID1 >> 32),
+ (uintptr_t) 0,
+ (uintptr_t) 0);
+
+ while (victim) {
+ doPhase2 = victim->lockForArbitration( true );
+ if (doPhase2) {
+ doPhase2 = (0 != (kIOServiceInactiveState & victim->__state[0]));
+ if (doPhase2) {
+ uint64_t regID1 = victim->getRegistryEntryID();
+ IOServiceTrace(
+ IOSERVICE_TERM_TRY_PHASE2,
+ (uintptr_t) regID1,
+ (uintptr_t) (regID1 >> 32),
+ (uintptr_t) victim->__state[1],
+ (uintptr_t) 0);
+
+ doPhase2 = (0 == (victim->__state[1] &
+ (kIOServiceTermPhase1State
+ | kIOServiceTermPhase2State
+ | kIOServiceConfigState)));
+
+ if (doPhase2 && (iter = victim->getClientIterator())) {
+ while (doPhase2 && (client = (IOService *) iter->getNextObject())) {
+ doPhase2 = (0 == (client->__state[1] & kIOServiceStartState));
+ if (!doPhase2) {
+ uint64_t regID1 = client->getRegistryEntryID();
+ IOServiceTrace(
+ IOSERVICE_TERM_UC_DEFER,
+ (uintptr_t) regID1,
+ (uintptr_t) (regID1 >> 32),
+ (uintptr_t) client->__state[1],
+ (uintptr_t) 0);
+ TLOG("%s[0x%qx]::defer phase2(%s[0x%qx])\n",
+ victim->getName(), victim->getRegistryEntryID(),
+ client->getName(), client->getRegistryEntryID());
+ }
+ }
+ iter->release();
+ }
+ if (doPhase2) {
+ victim->__state[1] |= kIOServiceTermPhase2State;
+ }
+ }
+ victim->unlockForArbitration();
+ }
+ if (doPhase2) {
+ if (kIOServiceNeedWillTerminate & victim->__state[1]) {
+ if (NULL == victim->reserved->uvars) {
+ _workLoopAction((IOWorkLoop::Action) &actionWillStop,
+ victim, (void *)(uintptr_t) options);
+ } else {
+ actionWillStop(victim, options, NULL, NULL, NULL);
+ }
+ }
+
+ OSArray * notifiers;
+ notifiers = victim->copyNotifiers(gIOWillTerminateNotification, 0, 0xffffffff);
+ victim->invokeNotifiers(¬ifiers);
+
+ _workLoopAction((IOWorkLoop::Action) &actionWillTerminate,
+ victim,
+ (void *)(uintptr_t) options,
+ (void *)(uintptr_t) doPhase2List,
+ (void *)(uintptr_t) false);
+
+ actionWillTerminate(
+ victim, options, doPhase2List, true, NULL);
+
+ didPhase2List->headQ( victim );
+ }
+ victim->release();
+ victim = (IOService *) doPhase2List->getObject(0);
+ if (victim) {
+ victim->retain();
+ doPhase2List->removeObject(0);
+ }
+ }
+
+ while ((victim = (IOService *) didPhase2List->getObject(0))) {
+ bool scheduleFinalize = false;
+ if (victim->lockForArbitration( true )) {
+ victim->__state[1] |= kIOServiceTermPhase3State;
+ scheduleFinalize = (NULL == victim->getClient());
+ victim->unlockForArbitration();
+ }
+ _workLoopAction((IOWorkLoop::Action) &actionDidTerminate,
+ victim, (void *)(uintptr_t) options );
+ if (kIOServiceNeedWillTerminate & victim->__state[1]) {
+ _workLoopAction((IOWorkLoop::Action) &actionDidStop,
+ victim, (void *)(uintptr_t) options, NULL );
+ }
+ // no clients - will go to finalize
+ if (scheduleFinalize) {
+ victim->scheduleFinalize(false);
+ }
+ didPhase2List->removeObject(0);
+ }
+ IOLockLock( gJobsLock );
+ }
+
+ // phase 3
+ do {
+ doPhase3 = false;
+ // finalize leaves
+ while ((victim = (IOService *) gIOFinalizeList->getObject(0))) {
+ bool sendFinal = false;
+ IOLockUnlock( gJobsLock );
+ if (victim->lockForArbitration(true)) {
+ sendFinal = (0 == (victim->__state[1] & kIOServiceFinalized));
+ if (sendFinal) {
+ victim->__state[1] |= kIOServiceFinalized;
+ }
+ victim->unlockForArbitration();
+ }
+ if (sendFinal) {
+ _workLoopAction((IOWorkLoop::Action) &actionFinalize,
+ victim, (void *)(uintptr_t) options );
+ }
+ IOLockLock( gJobsLock );
+ // hold off free
+ freeList->setObject( victim );
+ // safe if finalize list is append only
+ gIOFinalizeList->removeObject(0);
+ }
+
+ for (idx = 0;
+ (!doPhase3) && (client = (IOService *) gIOStopList->getObject(idx));) {
+ provider = (IOService *) gIOStopProviderList->getObject(idx);
+ assert( provider );
+
+ uint64_t regID1 = provider->getRegistryEntryID();
+ uint64_t regID2 = client->getRegistryEntryID();
+
+ if (!provider->isChild( client, gIOServicePlane )) {
+ // may be multiply queued - nop it
+ TLOG("%s[0x%qx]::nop stop(%s[0x%qx])\n", client->getName(), regID2, provider->getName(), regID1);
+ IOServiceTrace(
+ IOSERVICE_TERMINATE_STOP_NOP,
+ (uintptr_t) regID1,
+ (uintptr_t) (regID1 >> 32),
+ (uintptr_t) regID2,
+ (uintptr_t) (regID2 >> 32));
+ } else {
+ // a terminated client is not ready for stop if it has clients, skip it
+ bool deferStop = (0 != (kIOServiceInactiveState & client->__state[0]));
+ IOLockUnlock( gJobsLock );
+ if (deferStop && client->lockForArbitration(true)) {
+ deferStop = (0 == (client->__state[1] & kIOServiceFinalized));
+ //deferStop = (!deferStop && (0 != client->getClient()));
+ //deferStop = (0 != client->getClient());
+ client->unlockForArbitration();
+ if (deferStop) {
+ TLOG("%s[0x%qx]::defer stop()\n", client->getName(), regID2);
+ IOServiceTrace(IOSERVICE_TERMINATE_STOP_DEFER,
+ (uintptr_t) regID1,
+ (uintptr_t) (regID1 >> 32),
+ (uintptr_t) regID2,
+ (uintptr_t) (regID2 >> 32));
+
+ idx++;
+ IOLockLock( gJobsLock );
+ continue;
+ }
+ }
+ _workLoopAction((IOWorkLoop::Action) &actionStop,
+ provider, (void *) client );
+ IOLockLock( gJobsLock );
+ // check the finalize list now
+ doPhase3 = true;
+ }
+ // hold off free
+ freeList->setObject( client );
+ freeList->setObject( provider );
+
+ // safe if stop list is append only
+ gIOStopList->removeObject( idx );
+ gIOStopProviderList->removeObject( idx );
+ idx = 0;
+ }
+ } while (doPhase3);
+
+ gIOTerminateWork -= workDone;
+ moreToDo = (gIOTerminateWork != 0);
+
+ if (!moreToDo) {
+ TLOG("iokit terminate done, %d stops remain\n", gIOStopList->getCount());
+ IOServiceTrace(
+ IOSERVICE_TERMINATE_DONE,
+ (uintptr_t) gIOStopList->getCount(), 0, 0, 0);
+ }
+ } while (moreToDo);
+
+ IOLockUnlock( gJobsLock );
+
+ freeList->release();
+ doPhase2List->release();
+ didPhase2List->release();
+
+ IOLockLock( gJobsLock );
+}
+
+bool
+IOService::finalize( IOOptionBits options )
+{
+ OSIterator * iter;
+ IOService * provider;
+ uint64_t regID1, regID2 = getRegistryEntryID();
+
+ iter = getProviderIterator();
+ assert( iter );
+
+ if (iter) {
+ while ((provider = (IOService *) iter->getNextObject())) {
+ // -- compat
+ if (0 == (__state[1] & kIOServiceTermPhase3State)) {
+ /* we come down here on programmatic terminate */
+
+ regID1 = provider->getRegistryEntryID();
+ TLOG("%s[0x%qx]::stop1(%s[0x%qx])\n", getName(), regID2, provider->getName(), regID1);
+ IOServiceTrace(
+ IOSERVICE_TERMINATE_STOP,
+ (uintptr_t) regID1,
+ (uintptr_t) (regID1 >> 32),
+ (uintptr_t) regID2,
+ (uintptr_t) (regID2 >> 32));
+
+ stop( provider );
+ if (provider->isOpen( this )) {
+ provider->close( this );
+ }
+ detach( provider );
+ } else {
+ //--
+ if (provider->lockForArbitration( true )) {
+ if (0 == (provider->__state[1] & kIOServiceTermPhase3State)) {
+ scheduleStop( provider );
+ }
+ provider->unlockForArbitration();
+ }
+ }
+ }
+ iter->release();
+ }
+
+ return true;
+}
+
+#undef tailQ
+#undef headQ
+
+/*
+ * Terminate
+ */
+
+void
+IOService::doServiceTerminate( IOOptionBits options )
+{
+}
+
+// a method in case someone needs to override it
+bool
+IOService::terminateClient( IOService * client, IOOptionBits options )
+{
+ bool ok;
+
+ if (client->isParent( this, gIOServicePlane, true)) {
+ // we are the clients only provider
+ ok = client->terminate( options );
+ } else {
+ ok = true;
+ }
+
+ return ok;
+}
+
+bool
+IOService::terminate( IOOptionBits options )
+{
+ options |= kIOServiceTerminate;
+
+ return terminatePhase1( options );
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*
+ * Open & close
+ */
+
+struct ServiceOpenMessageContext {
+ IOService * service;
+ UInt32 type;
+ IOService * excludeClient;
+ IOOptionBits options;
+};
+
+static void
+serviceOpenMessageApplier( OSObject * object, void * ctx )
+{
+ ServiceOpenMessageContext * context = (ServiceOpenMessageContext *) ctx;
+
+ if (object != context->excludeClient) {
+ context->service->messageClient( context->type, object, (void *)(uintptr_t) context->options );
+ }
+}
+
+bool
+IOService::open( IOService * forClient,
+ IOOptionBits options,
+ void * arg )
+{
+ bool ok;
+ ServiceOpenMessageContext context;
+
+ context.service = this;
+ context.type = kIOMessageServiceIsAttemptingOpen;
+ context.excludeClient = forClient;
+ context.options = options;
+
+ applyToInterested( gIOGeneralInterest,
+ &serviceOpenMessageApplier, &context );
+
+ if (false == lockForArbitration(false)) {
+ return false;
+ }
+
+ ok = (0 == (__state[0] & kIOServiceInactiveState));
+ if (ok) {
+ ok = handleOpen( forClient, options, arg );
+ }
+
+ if (ok && forClient && forClient->reserved->uvars && forClient->reserved->uvars->userServer) {
+ forClient->reserved->uvars->userServer->serviceOpen(this, forClient);
+ }
+
+ unlockForArbitration();
+
+ return ok;
+}
+
+void
+IOService::close( IOService * forClient,
+ IOOptionBits options )
+{
+ bool wasClosed;
+ bool last = false;
+
+ lockForArbitration();
+
+ wasClosed = handleIsOpen( forClient );
+ if (wasClosed) {
+ handleClose( forClient, options );
+ last = (__state[1] & kIOServiceTermPhase3State);
+
+ if (forClient && forClient->reserved->uvars && forClient->reserved->uvars->userServer) {
+ forClient->reserved->uvars->userServer->serviceClose(this, forClient);
+ }
+ }
+
+ unlockForArbitration();
+
+ if (last) {
+ forClient->scheduleStop( this );
+ } else if (wasClosed) {
+ ServiceOpenMessageContext context;
+
+ context.service = this;
+ context.type = kIOMessageServiceWasClosed;
+ context.excludeClient = forClient;
+ context.options = options;
+
+ applyToInterested( gIOGeneralInterest,
+ &serviceOpenMessageApplier, &context );
+ }
+}
+
+bool
+IOService::isOpen( const IOService * forClient ) const
+{
+ IOService * self = (IOService *) this;
+ bool ok;
+
+ self->lockForArbitration();
+
+ ok = handleIsOpen( forClient );
+
+ self->unlockForArbitration();
+
+ return ok;
+}
+
+bool
+IOService::handleOpen( IOService * forClient,
+ IOOptionBits options,
+ void * arg )
+{
+ bool ok;
+
+ ok = (NULL == __owner);
+ if (ok) {
+ __owner = forClient;
+ } else if (options & kIOServiceSeize) {
+ ok = (kIOReturnSuccess == messageClient( kIOMessageServiceIsRequestingClose,
+ __owner, (void *)(uintptr_t) options ));
+ if (ok && (NULL == __owner)) {
+ __owner = forClient;
+ } else {
+ ok = false;
+ }
+ }
+ return ok;
+}
+
+void
+IOService::handleClose( IOService * forClient,
+ IOOptionBits options )
+{
+ if (__owner == forClient) {
+ __owner = NULL;
+ }
+}
+
+bool
+IOService::handleIsOpen( const IOService * forClient ) const
+{
+ if (forClient) {
+ return __owner == forClient;
+ } else {
+ return __owner != forClient;
+ }
+}
+
+/*
+ * Probing & starting
+ */
+static SInt32
+IONotifyOrdering( const OSMetaClassBase * inObj1, const OSMetaClassBase * inObj2, void * ref )
+{
+ const _IOServiceNotifier * obj1 = (const _IOServiceNotifier *) inObj1;
+ const _IOServiceNotifier * obj2 = (const _IOServiceNotifier *) inObj2;
+ SInt32 val1;
+ SInt32 val2;
+
+ val1 = 0;
+ val2 = 0;
+ if (obj1) {
+ val1 = obj1->priority;
+ }
+ if (obj2) {
+ val2 = obj2->priority;
+ }
+ if (val1 > val2) {
+ return 1;
+ }
+ if (val1 < val2) {
+ return -1;
+ }
+ return 0;
+}
+
+static SInt32
+IOServiceObjectOrder( const OSObject * entry, void * ref)
+{
+ OSDictionary * dict;
+ IOService * service;
+ _IOServiceNotifier * notify;
+ OSSymbol * key = (OSSymbol *) ref;
+ OSNumber * offset;
+ OSObject * prop;
+ SInt32 result;
+
+ prop = NULL;
+ result = kIODefaultProbeScore;
+ if ((dict = OSDynamicCast( OSDictionary, entry))) {
+ offset = OSDynamicCast(OSNumber, dict->getObject( key ));
+ } else if ((notify = OSDynamicCast( _IOServiceNotifier, entry))) {
+ return notify->priority;
+ } else if ((service = OSDynamicCast( IOService, entry))) {
+ prop = service->copyProperty(key);
+ offset = OSDynamicCast(OSNumber, prop);
+ } else {
+ assert( false );
+ offset = NULL;
+ }
+
+ if (offset) {
+ result = offset->unsigned32BitValue();
+ }
+
+ OSSafeReleaseNULL(prop);
+
+ return result;
+}
+
+SInt32
+IOServiceOrdering( const OSMetaClassBase * inObj1, const OSMetaClassBase * inObj2, void * ref )
+{
+ const OSObject * obj1 = (const OSObject *) inObj1;
+ const OSObject * obj2 = (const OSObject *) inObj2;
+ SInt32 val1;
+ SInt32 val2;
+
+ val1 = 0;
+ val2 = 0;
+
+ if (obj1) {
+ val1 = IOServiceObjectOrder( obj1, ref );
+ }
+
+ if (obj2) {
+ val2 = IOServiceObjectOrder( obj2, ref );
+ }
+
+ return val1 - val2;
+}
+
+IOService *
+IOService::copyClientWithCategory( const OSSymbol * category )
+{
+ IOService * service = NULL;
+ OSIterator * iter;
+ const OSSymbol * nextCat;
+
+ iter = getClientIterator();
+ if (iter) {
+ while ((service = (IOService *) iter->getNextObject())) {
+ if (kIOServiceInactiveState & service->__state[0]) {
+ continue;
+ }
+ nextCat = (const OSSymbol *) OSDynamicCast( OSSymbol,
+ service->getProperty( gIOMatchCategoryKey ));
+ if (category == nextCat) {
+ service->retain();
+ break;
+ }
+ }
+ iter->release();
+ }
+ return service;
+}
+
+IOService *
+IOService::getClientWithCategory( const OSSymbol * category )
+{
+ IOService *
+ service = copyClientWithCategory(category);
+ if (service) {
+ service->release();
+ }
+ return service;
+}
+
+bool
+IOService::invokeNotifier( _IOServiceNotifier * notify )
+{
+ _IOServiceNotifierInvocation invocation;
+ bool willNotify;
+ bool ret = true;
+ invocation.thread = current_thread();
+
+#if DEBUG_NOTIFIER_LOCKED
+ uint32_t count;
+ if ((count = isLockedForArbitration(0))) {
+ IOLog("[%s, 0x%x]\n", notify->type->getCStringNoCopy(), count);
+ panic("[%s, 0x%x]\n", notify->type->getCStringNoCopy(), count);
+ }
+#endif /* DEBUG_NOTIFIER_LOCKED */
+
+ LOCKWRITENOTIFY();
+ willNotify = (0 != (kIOServiceNotifyEnable & notify->state));
+
+ if (willNotify) {
+ queue_enter( ¬ify->handlerInvocations, &invocation,
+ _IOServiceNotifierInvocation *, link );
+ }
+ UNLOCKNOTIFY();
+
+ if (willNotify) {
+ ret = (*notify->handler)(notify->target, notify->ref, this, notify);
+
+ LOCKWRITENOTIFY();
+ queue_remove( ¬ify->handlerInvocations, &invocation,
+ _IOServiceNotifierInvocation *, link );
+ if (kIOServiceNotifyWaiter & notify->state) {
+ notify->state &= ~kIOServiceNotifyWaiter;
+ WAKEUPNOTIFY( notify );
+ }
+ UNLOCKNOTIFY();
+ }
+
+ return ret;
+}
+
+bool
+IOService::invokeNotifiers(OSArray * willSend[])
+{
+ OSArray * array;
+ _IOServiceNotifier * notify;
+ bool ret = true;
+
+ array = *willSend;
+ if (!array) {
+ return true;
+ }
+ *willSend = NULL;
+
+ for (unsigned int idx = 0;
+ (notify = (_IOServiceNotifier *) array->getObject(idx));
+ idx++) {
+ ret &= invokeNotifier(notify);
+ }
+ array->release();
+
+ return ret;
+}
+
+/*
+ * Alloc and probe matching classes,
+ * called on the provider instance
+ */
+
+void
+IOService::probeCandidates( OSOrderedSet * matches )
+{
+ OSDictionary * match = NULL;
+ OSSymbol * symbol;
+ IOService * inst;
+ IOService * newInst;
+ OSDictionary * props;
+ SInt32 score;
+ OSNumber * newPri;
+ OSOrderedSet * familyMatches = NULL;
+ OSOrderedSet * startList;
+ OSSet * kexts = NULL;
+ OSObject * kextRef;
+
+ OSDictionary * startDict = NULL;
+ const OSSymbol * category;
+ OSIterator * iter;
+ _IOServiceNotifier * notify;
+ OSObject * nextMatch = NULL;
+ bool started;
+ bool needReloc = false;
+ bool matchDeferred = false;
+#if IOMATCHDEBUG
+ SInt64 debugFlags;
+#endif
+ IOService * client = NULL;
+ OSObject * prop1;
+ OSObject * prop2;
+ OSDictionary * rematchPersonality;
+ OSNumber * num;
+ uint32_t count;
+ uint32_t dextCount;
+ bool isDext;
+ bool categoryConsumed;
+
+ prop2 = NULL;
+ count = 0;
+ prop1 = copyProperty(gIORematchPersonalityKey);
+ rematchPersonality = OSDynamicCast(OSDictionary, prop1);
+ if (rematchPersonality) {
+ prop2 = copyProperty(gIORematchCountKey);
+ num = OSDynamicCast(OSNumber, prop2);
+ if (num) {
+ count = num->unsigned32BitValue();
+ }
+ OSSafeReleaseNULL(prop2);
+ }
+ dextCount = 0;
+
+ assert( matches );
+ while (!needReloc
+ && (nextMatch = matches->getFirstObject())) {
+ nextMatch->retain();
+ matches->removeObject(nextMatch);
+
+ if ((notify = OSDynamicCast( _IOServiceNotifier, nextMatch ))) {
+ if (0 == (__state[0] & kIOServiceInactiveState)) {
+ invokeNotifier( notify );
+ }
+ nextMatch->release();
+ nextMatch = NULL;
+ continue;
+ } else if (!(match = OSDynamicCast( OSDictionary, nextMatch ))) {
+ nextMatch->release();
+ nextMatch = NULL;
+ continue;
+ }
+
+ props = NULL;
+#if IOMATCHDEBUG
+ debugFlags = getDebugFlags( match );
+#endif
+
+ do {
+ isDext = (NULL != match->getObject(gIOUserServerNameKey));
+ if (isDext && !(kIODKEnable & gIODKDebug)) {
+ continue;
+ }
+
+ category = OSDynamicCast( OSSymbol,
+ match->getObject( gIOMatchCategoryKey ));
+ if (NULL == category) {
+ category = gIODefaultMatchCategoryKey;
+ }
+ client = copyClientWithCategory(category);
+
+ categoryConsumed = (client != NULL);
+ if (categoryConsumed) {
+#if IOMATCHDEBUG
+ if ((debugFlags & kIOLogMatch) && (this != gIOResources)) {
+ LOG("%s: match category %s exists\n", getName(),
+ category->getCStringNoCopy());
+ }
+#endif
+ OSSafeReleaseNULL(client);
+ if (!isDext) {
+ break;
+ }
+ }
+
+ // create a copy now in case its modified during matching
+ props = OSDictionary::withDictionary(match, match->getCount());
+ if (NULL == props) {
+ break;
+ }
+ props->setCapacityIncrement(1);
+
+ // check the nub matches
+ if (false == matchPassive(props, kIOServiceChangesOK | kIOServiceClassDone)) {
+ break;
+ }
+ if (isDext) {
+ dextCount++;
+ if (categoryConsumed) {
+ break;
+ }
+ }
+
+ if (rematchPersonality) {
+ bool personalityMatch = match->isEqualTo(rematchPersonality);
+ if (count > gIODextRelaunchMax) {
+ personalityMatch = !personalityMatch;
+ }
+ if (!personalityMatch) {
+ break;
+ }
+ }
+
+ // Check to see if driver reloc has been loaded.
+ needReloc = (false == gIOCatalogue->isModuleLoaded( match, &kextRef ));
+ if (needReloc) {
+#if IOMATCHDEBUG
+ if (debugFlags & kIOLogCatalogue) {
+ LOG("%s: stalling for module\n", getName());
+ }
+#endif
+ // If reloc hasn't been loaded, exit;
+ // reprobing will occur after reloc has been loaded.
+ break;
+ }
+ if (kextRef) {
+ if (NULL == kexts) {
+ kexts = OSSet::withCapacity(1);
+ }
+ if (kexts) {
+ kexts->setObject(kextRef);
+ kextRef->release();
+ }
+ }
+ if (isDext) {
+ // copy saved for rematchng
+ props->setObject(gIOMatchedPersonalityKey, match);
+ }
+ // reorder on family matchPropertyTable score.
+ if (NULL == familyMatches) {
+ familyMatches = OSOrderedSet::withCapacity( 1,
+ IOServiceOrdering, (void *) gIOProbeScoreKey );
+ }
+ if (familyMatches) {
+ familyMatches->setObject( props );
+ }
+ } while (false);
+
+ OSSafeReleaseNULL(nextMatch);
+ OSSafeReleaseNULL(props);
+ }
+ matches->release();
+ matches = NULL;
+
+ if (familyMatches) {
+ while (!needReloc
+ && (props = (OSDictionary *) familyMatches->getFirstObject())) {
+ props->retain();
+ familyMatches->removeObject( props );
+
+ inst = NULL;
+ newInst = NULL;
+#if IOMATCHDEBUG
+ debugFlags = getDebugFlags( props );
+#endif
+ do {
+ symbol = OSDynamicCast( OSSymbol,
+ props->getObject( gIOClassKey));
+ if (!symbol) {
+ continue;
+ }
+
+ //IOLog("%s alloc (symbol %p props %p)\n", symbol->getCStringNoCopy(), IOSERVICE_OBFUSCATE(symbol), IOSERVICE_OBFUSCATE(props));
+
+ // alloc the driver instance
+ inst = (IOService *) OSMetaClass::allocClassWithName( symbol);
+
+ if (!inst || !OSDynamicCast(IOService, inst)) {
+ IOLog("Couldn't alloc class \"%s\"\n",
+ symbol->getCStringNoCopy());
+ continue;
+ }
+
+ // init driver instance
+ if (!(inst->init( props ))) {
+#if IOMATCHDEBUG
+ if (debugFlags & kIOLogStart) {
+ IOLog("%s::init fails\n", symbol->getCStringNoCopy());
+ }
+#endif
+ continue;
+ }
+ if (__state[1] & kIOServiceSynchronousState) {
+ inst->__state[1] |= kIOServiceSynchronousState;
+ }
+
+ // give the driver the default match category if not specified
+ category = OSDynamicCast( OSSymbol,
+ props->getObject( gIOMatchCategoryKey ));
+ if (NULL == category) {
+ category = gIODefaultMatchCategoryKey;
+ }
+ inst->setProperty( gIOMatchCategoryKey, (OSObject *) category );
+ // attach driver instance
+ if (!(inst->attach( this ))) {
+ continue;
+ }
+
+ // pass in score from property table
+ score = familyMatches->orderObject( props );
+
+ // & probe the new driver instance
+#if IOMATCHDEBUG
+ if (debugFlags & kIOLogProbe) {
+ LOG("%s::probe(%s)\n",
+ inst->getMetaClass()->getClassName(), getName());
+ }
+#endif
+
+ newInst = inst->probe( this, &score );
+ inst->detach( this );
+ if (NULL == newInst) {
+#if IOMATCHDEBUG
+ if (debugFlags & kIOLogProbe) {
+ IOLog("%s::probe fails\n", symbol->getCStringNoCopy());
+ }
+#endif
+ continue;
+ }
+
+ // save the score
+ newPri = OSNumber::withNumber( score, 32 );
+ if (newPri) {
+ newInst->setProperty( gIOProbeScoreKey, newPri );
+ newPri->release();
+ }
+
+ // add to start list for the match category
+ if (NULL == startDict) {
+ startDict = OSDictionary::withCapacity( 1 );
+ }
+ assert( startDict );
+ startList = (OSOrderedSet *)
+ startDict->getObject( category );
+ if (NULL == startList) {
+ startList = OSOrderedSet::withCapacity( 1,
+ IOServiceOrdering, (void *) gIOProbeScoreKey );
+ if (startDict && startList) {
+ startDict->setObject( category, startList );
+ startList->release();
+ }
+ }
+ assert( startList );
+ if (startList) {
+ startList->setObject( newInst );
+ }
+ } while (false);
+
+ props->release();
+ if (inst) {
+ inst->release();
+ }
+ }
+ familyMatches->release();
+ familyMatches = NULL;
+ }
+
+ // start the best (until success) of each category
+
+ iter = OSCollectionIterator::withCollection( startDict );
+ if (iter) {
+ while ((category = (const OSSymbol *) iter->getNextObject())) {
+ startList = (OSOrderedSet *) startDict->getObject( category );
+ assert( startList );
+ if (!startList) {
+ continue;
+ }
+
+ started = false;
+ while (true // (!started)
+ && !matchDeferred
+ && (inst = (IOService *)startList->getFirstObject())) {
+ inst->retain();
+ startList->removeObject(inst);
+
+#if IOMATCHDEBUG
+ debugFlags = getDebugFlags( inst );
+
+ if (debugFlags & kIOLogStart) {
+ if (started) {
+ LOG( "match category exists, skipping " );
+ }
+ LOG( "%s::start(%s) <%d>\n", inst->getName(),
+ getName(), inst->getRetainCount());
+ }
+#endif
+ if (false == started) {
+#if !NO_KEXTD
+ IOLockLock(gJobsLock);
+ matchDeferred = (gIOMatchDeferList
+ && (kOSBooleanTrue == inst->getProperty(gIOMatchDeferKey) || gInUserspaceReboot));
+ if (matchDeferred && (-1U == gIOMatchDeferList->getNextIndexOfObject(this, 0))) {
+ gIOMatchDeferList->setObject(this);
+ }
+ IOLockUnlock(gJobsLock);
+ if (matchDeferred) {
+ symbol = OSDynamicCast(OSSymbol, inst->getProperty(gIOClassKey));
+ IOLog("%s(0x%qx): matching deferred by %s\n",
+ getName(), getRegistryEntryID(),
+ symbol ? symbol->getCStringNoCopy() : "");
+ // rematching will occur after the IOKit daemon loads all plists
+ }
+#endif
+ if (!matchDeferred) {
+ started = startCandidate( inst );
+#if IOMATCHDEBUG
+ if ((debugFlags & kIOLogStart) && (false == started)) {
+ LOG( "%s::start(%s) <%d> failed\n", inst->getName(), getName(),
+ inst->getRetainCount());
+ }
+#endif
+ }
+ }
+ inst->release();
+ }
+ }
+ iter->release();
+ }
+
+ OSSafeReleaseNULL(prop1);
+
+ if (dextCount) {
+ num = OSNumber::withNumber(dextCount, 32);
+ setProperty(gIODEXTMatchCountKey, num);
+ OSSafeReleaseNULL(num);
+ } else if (rematchPersonality) {
+ removeProperty(gIODEXTMatchCountKey);
+ }
+
+ // now that instances are created, drop the refs on any kexts allowing unload
+ if (kexts) {
+ OSKext::dropMatchingReferences(kexts);
+ OSSafeReleaseNULL(kexts);
+ }
+
+ // adjust the busy count by +1 if matching is stalled for a module,
+ // or -1 if a previously stalled matching is complete.
+ lockForArbitration();
+ SInt32 adjBusy = 0;
+ uint64_t regID = getRegistryEntryID();
+
+ if (needReloc) {
+ adjBusy = (__state[1] & kIOServiceModuleStallState) ? 0 : 1;
+ if (adjBusy) {
+ IOServiceTrace(
+ IOSERVICE_MODULESTALL,
+ (uintptr_t) regID,
+ (uintptr_t) (regID >> 32),
+ (uintptr_t) this,
+ 0);
+
+ __state[1] |= kIOServiceModuleStallState;
+ }
+ } else if (__state[1] & kIOServiceModuleStallState) {
+ IOServiceTrace(
+ IOSERVICE_MODULEUNSTALL,
+ (uintptr_t) regID,
+ (uintptr_t) (regID >> 32),
+ (uintptr_t) this,
+ 0);
+
+ __state[1] &= ~kIOServiceModuleStallState;
+ adjBusy = -1;
+ }
+ if (adjBusy) {
+ _adjustBusy( adjBusy );
+ }
+ unlockForArbitration();
+
+ if (startDict) {
+ startDict->release();
+ }
+}
+
+/*
+ * Wait for a IOUserServer to check in
+ */
+
+static
+__attribute__((noinline, not_tail_called))
+IOService *
+__WAITING_FOR_USER_SERVER__(OSDictionary * matching, IOUserServerCheckInToken * token)
+{
+ IOService * server;
+ server = IOService::waitForMatchingServiceWithToken(matching, kIOUserServerCheckInTimeoutSecs * NSEC_PER_SEC, token);
+ return server;
+}
+
+void
+IOService::willShutdown()
+{
+ gIOKitWillTerminate = true;
+#if !NO_KEXTD
+ getPlatform()->waitQuiet(30 * NSEC_PER_SEC);
+#endif
+ OSKext::willShutdown();
+}
+
+void
+IOService::userSpaceWillReboot()
+{
+ IOLockLock(gJobsLock);
+#if !NO_KEXTD
+ // Recreate the defer list if it does not exist
+ if (!gIOMatchDeferList) {
+ gIOMatchDeferList = OSArray::withCapacity( 16 );
+ }
+#endif
+ gInUserspaceReboot = true;
+ IOLockUnlock(gJobsLock);
+}
+
+void
+IOService::userSpaceDidReboot()
+{
+ IOLockLock(gJobsLock);
+ gInUserspaceReboot = false;
+ IOLockUnlock(gJobsLock);
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+void
+IOServicePH::init(IOPMrootDomain * root)
+{
+ fUserServers = OSArray::withCapacity(4);
+ fMatchingWork = OSArray::withCapacity(4);
+
+ assert(fUserServers && fMatchingWork);
+
+ fRootNotifier = root->registerInterest(
+ gIOPriorityPowerStateInterest, &IOServicePH::systemPowerChange, NULL, NULL);
+
+ assert(fRootNotifier);
+}
+
+void
+IOServicePH::lock()
+{
+ IOLockLock(gJobsLock);
+}
+
+void
+IOServicePH::unlock()
+{
+ IOLockUnlock(gJobsLock);
+}
+
+void
+IOServicePH::serverAdd(IOUserServer * server)
+{
+ uint32_t idx;
+
+ lock();
+ idx = fUserServers->getNextIndexOfObject(server, 0);
+ if (idx == -1U) {
+ fUserServers->setObject(server);
+ }
+ unlock();
+}
+
+void
+IOServicePH::serverRemove(IOUserServer * server)
+{
+ uint32_t idx;
+
+ lock();
+ idx = fUserServers->getNextIndexOfObject(server, 0);
+ if (idx != -1U) {
+ fUserServers->removeObject(idx);
+ }
+
+ if (fWaitingUserServers) {
+ fWaitingUserServers = false;
+ IOLockWakeup(gJobsLock, &fWaitingUserServers, /* one-thread */ false);
+ }
+
+ unlock();
+}
+
+void
+IOServicePH::serverAck(IOUserServer * server)
+{
+ uint32_t idx;
+ IOService * ackTo;
+ uint32_t ackToRef;
+
+ ackTo = NULL;
+ lock();
+ if (server && fUserServersWait) {
+ idx = fUserServersWait->getNextIndexOfObject(server, 0);
+ if (idx != -1U) {
+ fUserServersWait->removeObject(idx);
+ if (0 == fUserServersWait->getCount()) {
+ OSSafeReleaseNULL(fUserServersWait);
+ }
+ }
+ }
+ if (!fUserServersWait && !fMatchingWork->getCount()) {
+ ackTo = fSystemPowerAckTo;
+ ackToRef = fSystemPowerAckRef;
+ fSystemPowerAckTo = NULL;
+ }
+ unlock();
+
+ if (ackTo) {
+ DKLOG("allowPowerChange\n");
+ ackTo->allowPowerChange((uintptr_t) ackToRef);
+ }
+}
+
+bool
+IOServicePH::matchingStart(IOService * service)
+{
+ uint32_t idx;
+ bool ok;
+
+ lock();
+ ok = !fSystemOff;
+ if (ok) {
+ idx = fMatchingWork->getNextIndexOfObject(service, 0);
+ if (idx == -1U) {
+ fMatchingWork->setObject(service);
+ }
+ } else {
+ if (!fMatchingDelayed) {
+ fMatchingDelayed = OSArray::withObjects((const OSObject **) &service, 1, 1);
+ } else {
+ idx = fMatchingDelayed->getNextIndexOfObject(service, 0);
+ if (idx == -1U) {
+ fMatchingDelayed->setObject(service);
+ }
+ }
+ }
+ unlock();
+
+ return ok;
+}
+
+void
+IOServicePH::matchingEnd(IOService * service)
+{
+ uint32_t idx;
+ OSArray * notifyServers;
+ OSArray * deferredMatches;
+
+ notifyServers = NULL;
+ deferredMatches = NULL;
+
+ lock();
+
+ if (service) {
+ idx = fMatchingWork->getNextIndexOfObject(service, 0);
+ if (idx != -1U) {
+ fMatchingWork->removeObject(idx);
+ }
+ }
+
+
+ if ((fUserServerOff != fSystemOff) && fUserServers->getCount()) {
+ if (fSystemOff) {
+ if (0 == fMatchingWork->getCount()) {
+ fUserServersWait = OSArray::withArray(fUserServers);
+ notifyServers = OSArray::withArray(fUserServers);
+ fUserServerOff = fSystemOff;
+ }
+ } else {
+ notifyServers = OSArray::withArray(fUserServers);
+ fUserServerOff = fSystemOff;
+ }
+ }
+
+ if (!fSystemOff && fMatchingDelayed) {
+ deferredMatches = fMatchingDelayed;
+ fMatchingDelayed = NULL;
+ }
+
+ unlock();
+
+ if (notifyServers) {
+ notifyServers->iterateObjects(^bool (OSObject * obj) {
+ IOUserServer * us;
+ us = (typeof(us))obj;
+ us->systemPower(fSystemOff);
+ return false;
+ });
+ OSSafeReleaseNULL(notifyServers);
+ }
+
+ if (deferredMatches) {
+ DKLOG("sleep deferred rematching count %d\n", deferredMatches->getCount());
+ deferredMatches->iterateObjects(^bool (OSObject * obj)
+ {
+ ((IOService *)obj)->startMatching(kIOServiceAsynchronous);
+ return false;
+ });
+ deferredMatches->release();
+ }
+
+ serverAck(NULL);
+}
+
+
+void
+IOServicePH::systemHalt(void)
+{
+ OSArray * notifyServers;
+ uint64_t deadline;
+
+ lock();
+ notifyServers = OSArray::withArray(fUserServers);
+ unlock();
+
+ if (notifyServers) {
+ notifyServers->iterateObjects(^bool (OSObject * obj) {
+ IOUserServer * us;
+ us = (typeof(us))obj;
+ us->systemHalt();
+ return false;
+ });
+ OSSafeReleaseNULL(notifyServers);
+ }
+
+ lock();
+ clock_interval_to_deadline(1000, kMillisecondScale, &deadline);
+ while (0 < fUserServers->getCount()) {
+ fWaitingUserServers = true;
+ __assert_only int waitResult =
+ IOLockSleepDeadline(gJobsLock, &fWaitingUserServers, deadline, THREAD_UNINT);
+ assert((THREAD_AWAKENED == waitResult) || (THREAD_TIMED_OUT == waitResult));
+ if (THREAD_TIMED_OUT == waitResult) {
+ break;
+ }
+ }
+ unlock();
+}
+
+bool
+IOServicePH::serverSlept(void)
+{
+ bool ret;
+
+ lock();
+ ret = (kIOMessageSystemWillSleep == sSystemPower)
+ || (kIOMessageSystemPagingOff == sSystemPower);
+ unlock();
+
+ return ret;
+}
+
+IOReturn
+IOServicePH::systemPowerChange(
+ void * target,
+ void * refCon,
+ UInt32 messageType, IOService * service,
+ void * messageArgument, vm_size_t argSize)
+{
+ IOReturn ret;
+ IOUserServer * us;
+ IOPMSystemCapabilityChangeParameters * params;
+
+ us = NULL;
+
+ switch (messageType) {
+ case kIOMessageSystemCapabilityChange:
+
+ params = (typeof params)messageArgument;
+
+ if (kIODKLogPM & gIODKDebug) {
+ IOLog("IOServicePH::kIOMessageSystemCapabilityChange: %s%s 0x%x->0x%x\n",
+ params->changeFlags & kIOPMSystemCapabilityWillChange ? "will" : "",
+ params->changeFlags & kIOPMSystemCapabilityDidChange ? "did" : "",
+ params->fromCapabilities,
+ params->toCapabilities);
+ }
+
+ if ((params->changeFlags & kIOPMSystemCapabilityWillChange) &&
+ (params->fromCapabilities & kIOPMSystemCapabilityCPU) &&
+ ((params->toCapabilities & kIOPMSystemCapabilityCPU) == 0)) {
+ lock();
+ fSystemOff = true;
+ fSystemPowerAckRef = params->notifyRef;
+ fSystemPowerAckTo = service;
+ unlock();
+
+ matchingEnd(NULL);
+
+ params->maxWaitForReply = 60 * 1000 * 1000;
+ ret = kIOReturnSuccess;
+ } else if ((params->changeFlags & kIOPMSystemCapabilityWillChange) &&
+ ((params->fromCapabilities & kIOPMSystemCapabilityCPU) == 0) &&
+ (params->toCapabilities & kIOPMSystemCapabilityCPU)) {
+ lock();
+ fSystemOff = false;
+ unlock();
+
+ matchingEnd(NULL);
+
+ params->maxWaitForReply = 0;
+ ret = kIOReturnSuccess;
+ } else {
+ params->maxWaitForReply = 0;
+ ret = kIOReturnSuccess;
+ }
+ break;
+
+ default:
+ ret = kIOReturnUnsupported;
+ break;
+ }
+
+ return ret;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*
+ * Start a previously attached & probed instance,
+ * called on exporting object instance
+ */
+
+bool
+IOService::startCandidate( IOService * service )
+{
+ bool ok;
+ OSObject * obj;
+ OSObject * prop;
+ IOUserServer * userServer;
+ bool ph;
+
+ userServer = NULL;
+ obj = service->copyProperty(gIOUserServerNameKey);
+
+ if (obj && (this == gIOResources)) {
+ ok = false;
+ } else {
+ ok = service->attach( this );
+ }
+ if (!ok) {
+ return false;
+ }
+
+ if ((this != gIOResources) && (this != gIOUserResources)) {
+ // stall for any nub resources
+ checkResources();
+ // stall for any driver resources
+ service->checkResources();
+ }
+ ph = false;
+ {
+ OSString * bundleID;
+ OSString * serverName;
+ OSString * str;
+ const OSSymbol * sym;
+ OSDictionary * matching;
+ IOService * server;
+ OSNumber * serverTag;
+ uint64_t entryID;
+ IOUserServerCheckInToken * token;
+
+ if ((serverName = OSDynamicCast(OSString, obj))) {
+ obj = service->copyProperty(gIOModuleIdentifierKey);
+ bundleID = OSDynamicCast(OSString, obj);
+ entryID = service->getRegistryEntryID();
+ serverTag = OSNumber::withNumber(entryID, 64);
+ token = NULL;
+
+ if (gIOKitWillTerminate) {
+ DKLOG("%s disabled in shutdown\n", serverName->getCStringNoCopy());
+ service->detach(this);
+ OSSafeReleaseNULL(obj);
+ return false;
+ }
+
+ ph = IOServicePH::matchingStart(this);
+ if (!ph) {
+ DKLOG("%s deferred in sleep\n", serverName->getCStringNoCopy());
+ service->detach(this);
+ OSSafeReleaseNULL(obj);
+ return false;
+ }
+
+ prop = service->copyProperty(gIOUserClassKey);
+ str = OSDynamicCast(OSString, prop);
+ if (str) {
+ service->setName(str);
+ }
+ OSSafeReleaseNULL(prop);
+
+ if (!(kIODKDisableDextLaunch & gIODKDebug)) {
+ OSKext::requestDaemonLaunch(bundleID, serverName, serverTag, &token);
+ }
+ if (!token) {
+ DKLOG("%s failed to create check in token\n", serverName->getCStringNoCopy());
+ service->detach(this);
+ OSSafeReleaseNULL(obj);
+ return false;
+ }
+ sym = OSSymbol::withString(serverName);
+ matching = serviceMatching(gIOUserServerClassKey);
+ propertyMatching(gIOUserServerNameKey, sym, matching);
+ if (!(kIODKDisableDextTag & gIODKDebug)) {
+ propertyMatching(gIOUserServerTagKey, serverTag, matching);
+ }
+
+ server = __WAITING_FOR_USER_SERVER__(matching, token);
+ matching->release();
+ OSSafeReleaseNULL(serverTag);
+ OSSafeReleaseNULL(serverName);
+
+ userServer = OSDynamicCast(IOUserServer, server);
+ if (!userServer) {
+ token->release();
+ service->detach(this);
+ IOServicePH::matchingEnd(this);
+ OSSafeReleaseNULL(obj);
+ DKLOG(DKS " user server timeout\n", DKN(service));
+#if DEVELOPMENT || DEBUG
+ driverkit_checkin_timed_out = mach_absolute_time();
+#endif
+ return false;
+ }
+
+ if (!(kIODKDisableCheckInTokenVerification & gIODKDebug)) {
+ if (!userServer->serviceMatchesCheckInToken(token)) {
+ token->release();
+ service->detach(this);
+ IOServicePH::matchingEnd(this);
+ OSSafeReleaseNULL(obj);
+ userServer->exit("Check In Token verification failed");
+ userServer->release();
+ return false;
+ }
+ }
+ token->release();
+
+ OSKext *kext = OSKext::lookupKextWithIdentifier(bundleID);
+ if (!kext) {
+ const char *name = bundleID->getCStringNoCopy();
+ IOLog("%s Could not find OSKext for %s\n", __func__, name);
+ goto skip_log;
+ }
+
+ /*
+ * Used for logging
+ */
+ userServer->setTaskLoadTag(kext);
+ userServer->setDriverKitUUID(kext);
+ OSKext::OSKextLogDriverKitInfoLoad(kext);
+skip_log:
+ OSSafeReleaseNULL(bundleID);
+ OSSafeReleaseNULL(kext);
+
+ if (!(kIODKDisableEntitlementChecking & gIODKDebug)) {
+ if (!userServer->checkEntitlements(this, service)) {
+ service->detach(this);
+ IOServicePH::matchingEnd(this);
+ userServer->exit("Entitlements check failed");
+ userServer->release();
+ return false;
+ }
+ }
+
+ userServer->serviceAttach(service, this);
+ }
+ }
+
+ AbsoluteTime startTime;
+ AbsoluteTime endTime;
+ UInt64 nano;
+
+ if (kIOLogStart & gIOKitDebug) {
+ clock_get_uptime(&startTime);
+ }
+
+ ok = service->start(this);
+
+ if (kIOLogStart & gIOKitDebug) {
+ clock_get_uptime(&endTime);
+
+ if (CMP_ABSOLUTETIME(&endTime, &startTime) > 0) {
+ SUB_ABSOLUTETIME(&endTime, &startTime);
+ absolutetime_to_nanoseconds(endTime, &nano);
+ if (nano > 500000000ULL) {
+ IOLog("%s::start took %ld ms\n", service->getName(), (long)(UInt32)(nano / 1000000ULL));
+ }
+ }
+ }
+ if (userServer) {
+ userServer->serviceStarted(service, this, ok);
+ userServer->release();
+ }
+
+ if (ok) {
+ IOInstallServiceSleepPlatformActions(service);
+ }
+
+ if (!ok) {
+ service->detach( this );
+ }
+
+ if (ph) {
+ IOServicePH::matchingEnd(this);
+ }
+
+ return ok;
+}
+
+void
+IOService::publishResource( const char * key, OSObject * value )
+{
+ const OSSymbol * sym;
+
+ if ((sym = OSSymbol::withCString( key))) {
+ publishResource( sym, value);
+ sym->release();
+ }
+}
+
+void
+IOService::publishResource( const OSSymbol * key, OSObject * value )
+{
+ if (NULL == value) {
+ value = (OSObject *) gIOServiceKey;
+ }
+
+ gIOResources->setProperty( key, value);
+
+ if (IORecursiveLockHaveLock( gNotificationLock)) {
+ return;
+ }
+
+ gIOResourceGenerationCount++;
+ gIOResources->registerService();
+}
+
+void
+IOService::publishUserResource( const OSSymbol * key, OSObject * value )
+{
+ if (NULL == value) {
+ value = (OSObject *) gIOServiceKey;
+ }
+
+ gIOUserResources->setProperty( key, value);
+
+ if (IORecursiveLockHaveLock( gNotificationLock)) {
+ return;
+ }
+
+ gIOResourceGenerationCount++;
+ gIOUserResources->registerService();
+}
+
+bool
+IOService::addNeededResource( const char * key )
+{
+ OSObject * resourcesProp;
+ OSSet * set;
+ OSString * newKey;
+ bool ret;
+
+ resourcesProp = copyProperty( gIOResourceMatchKey );
+ if (!resourcesProp) {
+ return false;
+ }
+
+ newKey = OSString::withCString( key );
+ if (!newKey) {
+ resourcesProp->release();
+ return false;
+ }
+
+ set = OSDynamicCast( OSSet, resourcesProp );
+ if (!set) {
+ set = OSSet::withCapacity( 1 );
+ if (set) {
+ set->setObject( resourcesProp );
+ }
+ } else {
+ set->retain();
+ }
+
+ set->setObject( newKey );
+ newKey->release();
+ ret = setProperty( gIOResourceMatchKey, set );
+ set->release();
+ resourcesProp->release();
+
+ return ret;
+}
+
+bool
+IOService::checkResource( OSObject * matching )
+{
+ OSString * str;
+ OSDictionary * table;
+
+ if ((str = OSDynamicCast( OSString, matching ))) {
+ if (gIOResources->getProperty( str )) {
+ return true;
+ }
+ }
+
+ if (str) {
+ table = resourceMatching( str );
+ } else if ((table = OSDynamicCast( OSDictionary, matching ))) {
+ table->retain();
+ } else {
+ IOLog("%s: Can't match using: %s\n", getName(),
+ matching->getMetaClass()->getClassName());
+ /* false would stall forever */
+ return true;
+ }
+
+ if (gIOKitDebug & kIOLogConfig) {
+ LOG("config(%p): stalling %s\n", IOSERVICE_OBFUSCATE(IOThreadSelf()), getName());
+ }