+ 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());
+ }
+
+ waitForService( table );
+
+ if (gIOKitDebug & kIOLogConfig) {
+ LOG("config(%p): waking\n", IOSERVICE_OBFUSCATE(IOThreadSelf()));
+ }
+
+ return true;
+}
+
+bool
+IOService::checkResources( void )
+{
+ OSObject * resourcesProp;
+ OSSet * set;
+ OSObject * obj;
+ OSIterator * iter;
+ bool ok;
+
+ resourcesProp = copyProperty( gIOResourceMatchKey );
+ if (NULL == resourcesProp) {
+ return true;
+ }
+
+ if ((set = OSDynamicCast( OSSet, resourcesProp ))) {
+ iter = OSCollectionIterator::withCollection( set );
+ ok = (NULL != iter);
+ while (ok && (obj = iter->getNextObject())) {
+ ok = checkResource( obj );
+ }
+ if (iter) {
+ iter->release();
+ }
+ } else {
+ ok = checkResource( resourcesProp );
+ }
+
+ OSSafeReleaseNULL(resourcesProp);
+
+ return ok;
+}
+
+
+void
+_IOConfigThread::configThread( const char * name )
+{
+ _IOConfigThread * inst;
+
+ do {
+ if (!(inst = new _IOConfigThread)) {
+ continue;
+ }
+ if (!inst->init()) {
+ continue;
+ }
+ thread_t thread;
+ if (KERN_SUCCESS != kernel_thread_start(&_IOConfigThread::main, inst, &thread)) {
+ continue;
+ }
+
+ char threadName[MAXTHREADNAMESIZE];
+ snprintf(threadName, sizeof(threadName), "IOConfigThread_'%s'", name);
+ thread_set_thread_name(thread, threadName);
+ thread_deallocate(thread);
+
+ return;
+ } while (false);
+
+ if (inst) {
+ inst->release();
+ }
+
+ return;
+}
+
+void
+IOService::doServiceMatch( IOOptionBits options )
+{
+ _IOServiceNotifier * notify;
+ OSIterator * iter;
+ OSOrderedSet * matches;
+ OSArray * resourceKeys = NULL;
+ SInt32 catalogGeneration;
+ bool keepGuessing = true;
+ bool reRegistered = true;
+ bool didRegister;
+ OSArray * notifiers[2] = {NULL};
+
+// job->nub->deliverNotification( gIOPublishNotification,
+// kIOServiceRegisteredState, 0xffffffff );
+
+ while (keepGuessing) {
+ matches = gIOCatalogue->findDrivers( this, &catalogGeneration );
+ // the matches list should always be created by findDrivers()
+ if (matches) {
+ lockForArbitration();
+ if (0 == (__state[0] & kIOServiceFirstPublishState)) {
+ getMetaClass()->addInstance(this);
+ notifiers[0] = copyNotifiers(gIOFirstPublishNotification,
+ kIOServiceFirstPublishState, 0xffffffff );
+ }
+ LOCKREADNOTIFY();
+ __state[1] &= ~kIOServiceNeedConfigState;
+ __state[1] |= kIOServiceConfigState | kIOServiceConfigRunning;
+ didRegister = (0 == (kIOServiceRegisteredState & __state[0]));
+ __state[0] |= kIOServiceRegisteredState;
+
+ keepGuessing &= (0 == (__state[0] & kIOServiceInactiveState));
+ if (reRegistered && keepGuessing) {
+ iter = OSCollectionIterator::withCollection((OSOrderedSet *)
+ gNotifications->getObject( gIOPublishNotification ));
+ if (iter) {
+ while ((notify = (_IOServiceNotifier *)
+ iter->getNextObject())) {
+ if (matchPassive(notify->matching, 0)
+ && (kIOServiceNotifyEnable & notify->state)) {
+ matches->setObject( notify );
+ }
+ }
+ iter->release();
+ }
+ }
+
+ UNLOCKNOTIFY();
+ unlockForArbitration();
+ invokeNotifiers(¬ifiers[0]);
+
+ if (keepGuessing && matches->getCount() && (kIOReturnSuccess == getResources())) {
+ if ((this == gIOResources) || (this == gIOUserResources)) {
+ if (resourceKeys) {
+ resourceKeys->release();
+ }
+ resourceKeys = copyPropertyKeys();
+ }
+ probeCandidates( matches );
+ } else {
+ matches->release();
+ }
+ }
+
+ lockForArbitration();
+ reRegistered = (0 != (__state[1] & kIOServiceNeedConfigState));
+ keepGuessing =
+ (reRegistered || (catalogGeneration !=
+ gIOCatalogue->getGenerationCount()))
+ && (0 == (__state[0] & kIOServiceInactiveState));
+
+ if (keepGuessing) {
+ unlockForArbitration();
+ }
+ }
+
+ if ((0 == (__state[0] & kIOServiceInactiveState))
+ && (0 == (__state[1] & kIOServiceModuleStallState))) {
+ if (resourceKeys) {
+ setProperty(gIOResourceMatchedKey, resourceKeys);
+ }
+
+ notifiers[0] = copyNotifiers(gIOMatchedNotification,
+ kIOServiceMatchedState, 0xffffffff);
+ if (0 == (__state[0] & kIOServiceFirstMatchState)) {
+ notifiers[1] = copyNotifiers(gIOFirstMatchNotification,
+ kIOServiceFirstMatchState, 0xffffffff);
+ }
+ }
+
+ __state[1] &= ~kIOServiceConfigRunning;
+ unlockForArbitration();
+
+ if (resourceKeys) {
+ resourceKeys->release();
+ }
+
+ invokeNotifiers(¬ifiers[0]);
+ invokeNotifiers(¬ifiers[1]);
+
+ lockForArbitration();
+ __state[1] &= ~kIOServiceConfigState;
+ scheduleTerminatePhase2();
+
+ _adjustBusy( -1 );
+ unlockForArbitration();
+}
+
+UInt32
+IOService::_adjustBusy( SInt32 delta )
+{
+ IOService * next;
+ UInt32 count;
+ UInt32 result;
+ bool wasQuiet, nowQuiet, needWake;
+
+ next = this;
+ result = __state[1] & kIOServiceBusyStateMask;
+
+ if (delta) {
+ do {
+ if (next != this) {
+ next->lockForArbitration();
+ }
+ count = next->__state[1] & kIOServiceBusyStateMask;
+ wasQuiet = (0 == count);
+ if (((delta < 0) && wasQuiet) || ((delta > 0) && (kIOServiceBusyMax == count))) {
+ OSReportWithBacktrace("%s: bad busy count (%d,%d)\n", next->getName(), count, delta);
+ } else {
+ count += delta;
+ }
+ next->__state[1] = (next->__state[1] & ~kIOServiceBusyStateMask) | count;
+ nowQuiet = (0 == count);
+ needWake = (0 != (kIOServiceBusyWaiterState & next->__state[1]));
+
+ if (needWake) {
+ next->__state[1] &= ~kIOServiceBusyWaiterState;
+ IOLockLock( gIOServiceBusyLock );
+ thread_wakeup((event_t) next);
+ IOLockUnlock( gIOServiceBusyLock );
+ }
+ if (next != this) {
+ next->unlockForArbitration();
+ }
+
+ if ((wasQuiet || nowQuiet)) {
+ uint64_t regID = next->getRegistryEntryID();
+ IOServiceTrace(
+ ((wasQuiet /*nowBusy*/) ? IOSERVICE_BUSY : IOSERVICE_NONBUSY),
+ (uintptr_t) regID,
+ (uintptr_t) (regID >> 32),
+ (uintptr_t) next,
+ 0);
+
+ if (wasQuiet) {
+ next->__timeBusy = mach_absolute_time();
+ } else {
+ next->__accumBusy += mach_absolute_time() - next->__timeBusy;
+ next->__timeBusy = 0;
+ }
+
+ MessageClientsContext context;
+
+ context.service = next;
+ context.type = kIOMessageServiceBusyStateChange;
+ context.argument = (void *) wasQuiet; /*nowBusy*/
+ context.argSize = 0;
+
+ applyToInterestNotifiers( next, gIOBusyInterest,
+ &messageClientsApplier, &context );
+
+#if !NO_KEXTD
+ if (nowQuiet && (next == gIOServiceRoot)) {
+ OSKext::considerUnloads();
+ IOServiceTrace(IOSERVICE_REGISTRY_QUIET, 0, 0, 0, 0);
+ }
+#endif
+ }
+
+ delta = nowQuiet ? -1 : +1;
+ } while ((wasQuiet || nowQuiet) && (next = next->getProvider()));
+ }
+
+ return result;
+}
+
+void
+IOService::adjustBusy( SInt32 delta )
+{
+ lockForArbitration();
+ _adjustBusy( delta );
+ unlockForArbitration();
+}
+
+uint64_t
+IOService::getAccumulatedBusyTime( void )
+{
+ uint64_t accumBusy = __accumBusy;
+ uint64_t timeBusy = __timeBusy;
+ uint64_t nano;
+
+ do{
+ accumBusy = __accumBusy;
+ timeBusy = __timeBusy;
+ if (timeBusy) {
+ accumBusy += mach_absolute_time() - timeBusy;
+ }
+ }while (timeBusy != __timeBusy);
+
+ absolutetime_to_nanoseconds(*(AbsoluteTime *)&accumBusy, &nano);
+
+ return nano;
+}
+
+UInt32
+IOService::getBusyState( void )
+{
+ return __state[1] & kIOServiceBusyStateMask;
+}
+
+IOReturn
+IOService::waitForState( UInt32 mask, UInt32 value,
+ mach_timespec_t * timeout )
+{
+ panic("waitForState");
+ return kIOReturnUnsupported;
+}
+
+IOReturn
+IOService::waitForState( UInt32 mask, UInt32 value,
+ uint64_t timeout )
+{
+ bool wait;
+ int waitResult = THREAD_AWAKENED;
+ bool computeDeadline = true;
+ AbsoluteTime abstime;
+
+ do {
+ lockForArbitration();
+ IOLockLock( gIOServiceBusyLock );
+ wait = (value != (__state[1] & mask));
+ if (wait) {
+ __state[1] |= kIOServiceBusyWaiterState;
+ unlockForArbitration();
+ if (timeout != UINT64_MAX) {
+ if (computeDeadline) {
+ AbsoluteTime nsinterval;
+ nanoseconds_to_absolutetime(timeout, &nsinterval );
+ clock_absolutetime_interval_to_deadline(nsinterval, &abstime);
+ computeDeadline = false;
+ }
+ assert_wait_deadline((event_t)this, THREAD_UNINT, __OSAbsoluteTime(abstime));
+ } else {
+ assert_wait((event_t)this, THREAD_UNINT );
+ }
+ } else {
+ unlockForArbitration();
+ }
+ IOLockUnlock( gIOServiceBusyLock );
+ if (wait) {
+ waitResult = thread_block(THREAD_CONTINUE_NULL);
+ }
+ } while (wait && (waitResult != THREAD_TIMED_OUT));
+
+ if (waitResult == THREAD_TIMED_OUT) {
+ return kIOReturnTimeout;
+ } else {
+ return kIOReturnSuccess;
+ }
+}
+
+IOReturn
+IOService::waitQuiet( uint64_t timeout )
+{
+ IOReturn ret;
+ uint32_t loops;
+ char * string = NULL;
+ char * panicString = NULL;
+ size_t len;
+ size_t panicStringLen;
+ uint64_t time;
+ uint64_t nano;
+ bool pendingRequests;
+ bool dopanic = false;
+
+#if KASAN
+ /*
+ * On kasan kernels, everything takes longer, so double the number of
+ * timeout extensions. This should help with issues like 41259215
+ * where WindowServer was timing out waiting for kextd to get all the
+ * kasan kexts loaded and started.
+ */
+ enum { kTimeoutExtensions = 8 };
+#define WITH_IOWAITQUIET_EXTENSIONS 1
+#elif XNU_TARGET_OS_OSX && defined(__arm64__)
+ enum { kTimeoutExtensions = 1 };
+#define WITH_IOWAITQUIET_EXTENSIONS 0
+#else
+ enum { kTimeoutExtensions = 4 };
+#define WITH_IOWAITQUIET_EXTENSIONS 1
+#endif
+
+ time = mach_absolute_time();
+ pendingRequests = false;
+ for (loops = 0; loops < kTimeoutExtensions; loops++) {
+ ret = waitForState( kIOServiceBusyStateMask, 0, timeout );
+
+ if (loops && (kIOReturnSuccess == ret)) {
+ time = mach_absolute_time() - time;
+ absolutetime_to_nanoseconds(*(AbsoluteTime *)&time, &nano);
+ IOLog("busy extended ok[%d], (%llds, %llds)\n",
+ loops, timeout / 1000000000ULL, nano / 1000000000ULL);
+ break;
+ } else if (kIOReturnTimeout != ret) {
+ break;
+ } else if (timeout < (41ull * NSEC_PER_SEC)) {
+ break;
+ }
+
+ {
+ IORegistryIterator * iter;
+ OSOrderedSet * set;
+ OSOrderedSet * leaves;
+ IOService * next;
+ IOService * nextParent;
+ char * s;
+ size_t l;
+
+ len = 256;
+ panicStringLen = 256;
+ if (!string) {
+ string = IONew(char, len);
+ }
+ if (!panicString) {
+ panicString = IONew(char, panicStringLen);
+ }
+ set = NULL;
+ pendingRequests = OSKext::pendingIOKitDaemonRequests();
+ iter = IORegistryIterator::iterateOver(this, gIOServicePlane, kIORegistryIterateRecursively);
+ leaves = OSOrderedSet::withCapacity(4);
+ if (iter) {
+ set = iter->iterateAll();
+ }
+ if (string && panicString && leaves && set) {
+ string[0] = panicString[0] = 0;
+ set->setObject(this);
+ while ((next = (IOService *) set->getLastObject())) {
+ if (next->getBusyState()) {
+ if (kIOServiceModuleStallState & next->__state[1]) {
+ pendingRequests = true;
+ }
+ leaves->setObject(next);
+ nextParent = next;
+ while ((nextParent = nextParent->getProvider())) {
+ set->removeObject(nextParent);
+ leaves->removeObject(nextParent);
+ }
+ }
+ set->removeObject(next);
+ }
+ s = string;
+ while ((next = (IOService *) leaves->getLastObject())) {
+ l = snprintf(s, len, "%s'%s'", ((s == string) ? "" : ", "), next->getName());
+ if (l >= len) {
+ break;
+ }
+ s += l;
+ len -= l;
+ leaves->removeObject(next);
+ }
+ }
+ OSSafeReleaseNULL(leaves);
+ OSSafeReleaseNULL(set);
+ OSSafeReleaseNULL(iter);
+ }
+
+ dopanic = (kIOWaitQuietPanics & gIOKitDebug);
+#if WITH_IOWAITQUIET_EXTENSIONS
+ dopanic = (dopanic && (loops >= (kTimeoutExtensions - 1)));
+#endif
+ snprintf(panicString, panicStringLen,
+ "%s[%d], (%llds): %s",
+ pendingRequests ? "IOKit Daemon (" kIOKitDaemonName ") stall" : "busy timeout",
+ loops, timeout / 1000000000ULL,
+ string ? string : "");
+ IOLog("%s\n", panicString);
+ if (dopanic) {
+ panic("%s", panicString);
+ } else if (!loops) {
+ getPMRootDomain()->startSpinDump(1);
+ }
+ }
+
+ if (string) {
+ IODelete(string, char, 256);
+ }
+ if (panicString) {
+ IODelete(panicString, char, panicStringLen);
+ }
+
+ return ret;
+}
+
+IOReturn
+IOService::waitQuiet( mach_timespec_t * timeout )
+{
+ uint64_t timeoutNS;
+
+ if (timeout) {
+ timeoutNS = timeout->tv_sec;
+ timeoutNS *= kSecondScale;
+ timeoutNS += timeout->tv_nsec;
+ } else {
+ timeoutNS = UINT64_MAX;
+ }
+
+ return waitQuiet(timeoutNS);
+}
+
+bool
+IOService::serializeProperties( OSSerialize * s ) const
+{
+#if 0
+ ((IOService *)this)->setProperty(((IOService *)this)->__state,
+ sizeof(__state), "__state");
+#endif
+ return super::serializeProperties(s);
+}
+
+void
+IOService::resetRematchProperties()
+{
+ removeProperty(gIORematchCountKey);
+ removeProperty(gIORematchPersonalityKey);
+}
+
+
+void
+_IOConfigThread::main(void * arg, wait_result_t result)
+{
+ _IOConfigThread * self = (_IOConfigThread *) arg;
+ _IOServiceJob * job;
+ IOService * nub;
+ bool alive = true;
+ kern_return_t kr;
+ thread_precedence_policy_data_t precedence = { -1 };
+
+ kr = thread_policy_set(current_thread(),
+ THREAD_PRECEDENCE_POLICY,
+ (thread_policy_t) &precedence,
+ THREAD_PRECEDENCE_POLICY_COUNT);
+ if (KERN_SUCCESS != kr) {
+ IOLog("thread_policy_set(%d)\n", kr);
+ }
+
+ do {
+// randomDelay();
+
+ semaphore_wait( gJobsSemaphore );
+
+ IOTakeLock( gJobsLock );
+ job = (_IOServiceJob *) gJobs->getFirstObject();
+ job->retain();
+ gJobs->removeObject(job);
+ if (job) {
+ gOutstandingJobs--;
+// gNumConfigThreads--; // we're out of service
+ gNumWaitingThreads--; // we're out of service
+ }
+ IOUnlock( gJobsLock );
+
+ if (job) {
+ nub = job->nub;
+
+ if (gIOKitDebug & kIOLogConfig) {
+ LOG("config(%p): starting on %s, %d\n",
+ IOSERVICE_OBFUSCATE(IOThreadSelf()), job->nub->getName(), job->type);
+ }
+
+ switch (job->type) {
+ case kMatchNubJob:
+ nub->doServiceMatch( job->options );
+ break;
+
+ default:
+ LOG("config(%p): strange type (%d)\n",
+ IOSERVICE_OBFUSCATE(IOThreadSelf()), job->type );
+ break;
+ }
+
+ nub->release();
+ job->release();
+
+ IOTakeLock( gJobsLock );
+ alive = (gOutstandingJobs > gNumWaitingThreads);
+ if (alive) {
+ gNumWaitingThreads++; // back in service
+ }
+// gNumConfigThreads++;
+ else {
+ if (0 == --gNumConfigThreads) {
+// IOLog("MATCH IDLE\n");
+ IOLockWakeup( gJobsLock, (event_t) &gNumConfigThreads, /* one-thread */ false );
+ }
+ }
+ IOUnlock( gJobsLock );
+ }
+ } while (alive);
+
+ if (gIOKitDebug & kIOLogConfig) {
+ LOG("config(%p): terminating\n", IOSERVICE_OBFUSCATE(IOThreadSelf()));
+ }
+
+ self->release();
+}
+
+IOReturn
+IOService::waitMatchIdle( UInt32 msToWait )
+{
+ bool wait;
+ int waitResult = THREAD_AWAKENED;
+ bool computeDeadline = true;
+ AbsoluteTime deadline;
+
+ IOLockLock( gJobsLock );
+ do {
+ wait = (0 != gNumConfigThreads);
+ if (wait) {
+ if (msToWait) {
+ if (computeDeadline) {
+ clock_interval_to_deadline(
+ msToWait, kMillisecondScale, &deadline );
+ computeDeadline = false;
+ }
+ waitResult = IOLockSleepDeadline( gJobsLock, &gNumConfigThreads,
+ deadline, THREAD_UNINT );
+ } else {
+ waitResult = IOLockSleep( gJobsLock, &gNumConfigThreads,
+ THREAD_UNINT );
+ }
+ }
+ } while (wait && (waitResult != THREAD_TIMED_OUT));
+ IOLockUnlock( gJobsLock );
+
+ if (waitResult == THREAD_TIMED_OUT) {
+ return kIOReturnTimeout;
+ } else {
+ return kIOReturnSuccess;
+ }
+}
+
+void
+IOService::cpusRunning(void)
+{
+ gCPUsRunning = true;
+}
+
+void
+_IOServiceJob::pingConfig( _IOServiceJob * job )
+{
+ int count;
+ bool create;
+ IOService * nub;
+
+ assert( job );
+ nub = job->nub;
+
+ IOTakeLock( gJobsLock );
+
+ gOutstandingJobs++;
+ if (nub == gIOResources) {
+ gJobs->setFirstObject( job );
+ } else {
+ gJobs->setLastObject( job );
+ }
+
+ count = gNumWaitingThreads;
+// if( gNumConfigThreads) count++;// assume we're called from a config thread
+
+ create = ((gOutstandingJobs > count)
+ && ((gNumConfigThreads < gMaxConfigThreads)
+ || (nub == gIOResources)
+ || !gCPUsRunning));
+ if (create) {
+ gNumConfigThreads++;
+ gNumWaitingThreads++;
+ if (gNumConfigThreads > gHighNumConfigThreads) {
+ gHighNumConfigThreads = gNumConfigThreads;
+ }
+ }
+
+ IOUnlock( gJobsLock );
+
+ job->release();
+
+ if (create) {
+ if (gIOKitDebug & kIOLogConfig) {
+ LOG("config(%d): creating\n", gNumConfigThreads - 1);
+ }
+ _IOConfigThread::configThread(nub->getName());
+ }
+
+ semaphore_signal( gJobsSemaphore );
+}
+
+struct IOServiceMatchContext {
+ OSDictionary * table;
+ OSObject * result;
+ uint32_t options;
+ uint32_t state;
+ uint32_t count;
+ uint32_t done;
+};
+
+bool
+IOService::instanceMatch(const OSObject * entry, void * context)
+{
+ IOServiceMatchContext * ctx = (typeof(ctx))context;
+ IOService * service = (typeof(service))entry;
+ OSDictionary * table = ctx->table;
+ uint32_t options = ctx->options;
+ uint32_t state = ctx->state;
+ uint32_t done;
+ bool match;
+
+ done = 0;
+ do{
+ match = ((state == (state & service->__state[0]))
+ && (0 == (service->__state[0] & kIOServiceInactiveState)));
+ if (!match) {
+ break;
+ }
+ match = service->matchInternal(table, options, &done);
+ if (match) {
+ ctx->count += table->getCount();
+ ctx->done += done;
+ }
+ }while (false);
+ if (!match) {
+ return false;
+ }
+
+ if ((kIONotifyOnce & options) && (ctx->done == ctx->count)) {
+ service->retain();
+ ctx->result = service;
+ return true;
+ } else if (!ctx->result) {
+ ctx->result = OSSet::withObjects((const OSObject **) &service, 1, 1);
+ } else {
+ ((OSSet *)ctx->result)->setObject(service);
+ }
+ return false;
+}
+
+// internal - call with gNotificationLock
+OSObject *
+IOService::copyExistingServices( OSDictionary * matching,
+ IOOptionBits inState, IOOptionBits options )
+{
+ OSObject * current = NULL;
+ OSIterator * iter;
+ IOService * service;
+ OSObject * obj;
+ OSString * str;
+
+ if (!matching) {
+ return NULL;
+ }
+
+#if MATCH_DEBUG
+ OSSerialize * s = OSSerialize::withCapacity(128);
+ matching->serialize(s);
+#endif
+
+ if ((obj = matching->getObject(gIOProviderClassKey))
+ && gIOResourcesKey
+ && gIOResourcesKey->isEqualTo(obj)
+ && (service = gIOResources)) {
+ if ((inState == (service->__state[0] & inState))
+ && (0 == (service->__state[0] & kIOServiceInactiveState))
+ && service->matchPassive(matching, options)) {
+ if (options & kIONotifyOnce) {
+ service->retain();
+ current = service;
+ } else {
+ current = OSSet::withObjects((const OSObject **) &service, 1, 1 );
+ }
+ }
+ } else {
+ IOServiceMatchContext ctx;
+
+ options |= kIOServiceClassDone;
+ ctx.table = matching;
+ ctx.state = inState;
+ ctx.count = 0;
+ ctx.done = 0;
+ ctx.options = options;
+ ctx.result = NULL;
+
+ if ((str = OSDynamicCast(OSString, obj))) {
+ const OSSymbol * sym = OSSymbol::withString(str);
+ OSMetaClass::applyToInstancesOfClassName(sym, instanceMatch, &ctx);
+ sym->release();
+ } else {
+ IOService::gMetaClass.applyToInstances(instanceMatch, &ctx);
+ }
+
+ if (((!(options & kIONotifyOnce) || !ctx.result))
+ && matching->getObject(gIOCompatibilityMatchKey)) {
+ IOServiceCompatibility::gMetaClass.applyToInstances(instanceMatch, &ctx);
+ }
+
+ current = ctx.result;
+ options |= kIOServiceInternalDone;
+ if (current && (ctx.done != ctx.count)) {
+ OSSet * source = OSDynamicCast(OSSet, current);
+ current = NULL;
+ while ((service = (IOService *) source->getAnyObject())) {
+ if (service->matchPassive(matching, options)) {
+ if (options & kIONotifyOnce) {
+ service->retain();
+ current = service;
+ break;
+ }
+ if (current) {
+ ((OSSet *)current)->setObject( service );
+ } else {
+ current = OSSet::withObjects(
+ (const OSObject **) &service, 1, 1 );
+ }
+ }
+ source->removeObject(service);
+ }
+ source->release();
+ }
+ }
+
+#if MATCH_DEBUG
+ {
+ OSObject * _current = 0;
+
+ iter = IORegistryIterator::iterateOver( gIOServicePlane,
+ kIORegistryIterateRecursively );
+ if (iter) {
+ do {
+ iter->reset();
+ while ((service = (IOService *) iter->getNextObject())) {
+ if ((inState == (service->__state[0] & inState))
+ && (0 == (service->__state[0] & kIOServiceInactiveState))
+ && service->matchPassive(matching, 0)) {
+ if (options & kIONotifyOnce) {
+ service->retain();
+ _current = service;
+ break;
+ }
+ if (_current) {
+ ((OSSet *)_current)->setObject( service );
+ } else {
+ _current = OSSet::withObjects(
+ (const OSObject **) &service, 1, 1 );
+ }
+ }
+ }
+ } while (!service && !iter->isValid());
+ iter->release();
+ }
+
+ if (((current != 0) != (_current != 0))
+ || (current && _current && !current->isEqualTo(_current))) {
+ OSSerialize * s1 = OSSerialize::withCapacity(128);
+ OSSerialize * s2 = OSSerialize::withCapacity(128);
+ current->serialize(s1);
+ _current->serialize(s2);
+ kprintf("**mismatch** %p %p\n%s\n%s\n%s\n", IOSERVICE_OBFUSCATE(current),
+ IOSERVICE_OBFUSCATE(_current), s->text(), s1->text(), s2->text());
+ s1->release();
+ s2->release();
+ }
+
+ if (_current) {
+ _current->release();
+ }
+ }
+
+ s->release();
+#endif
+
+ if (current && (0 == (options & (kIONotifyOnce | kIOServiceExistingSet)))) {
+ iter = OSCollectionIterator::withCollection((OSSet *)current );
+ current->release();
+ current = iter;
+ }
+
+ return current;
+}
+
+// public version
+OSIterator *
+IOService::getMatchingServices( OSDictionary * matching )
+{
+ OSIterator * iter;
+
+ // is a lock even needed?
+ LOCKWRITENOTIFY();
+
+ iter = (OSIterator *) copyExistingServices( matching,
+ kIOServiceMatchedState );
+
+ UNLOCKNOTIFY();
+
+ return iter;
+}
+
+IOService *
+IOService::copyMatchingService( OSDictionary * matching )
+{
+ IOService * service;
+
+ // is a lock even needed?
+ LOCKWRITENOTIFY();
+
+ service = (IOService *) copyExistingServices( matching,
+ kIOServiceMatchedState, kIONotifyOnce );
+
+ UNLOCKNOTIFY();
+
+ return service;
+}
+
+struct _IOServiceMatchingNotificationHandlerRef {
+ IOServiceNotificationHandler handler;
+ void * ref;
+};
+
+static bool
+_IOServiceMatchingNotificationHandler( void * target, void * refCon,
+ IOService * newService,
+ IONotifier * notifier )
+{
+ return (*((_IOServiceNotifier *) notifier)->compatHandler)(target, refCon, newService);
+}
+
+// internal - call with gNotificationLock
+IONotifier *
+IOService::setNotification(
+ const OSSymbol * type, OSDictionary * matching,
+ IOServiceMatchingNotificationHandler handler, void * target, void * ref,
+ SInt32 priority )
+{
+ _IOServiceNotifier * notify = NULL;
+ OSOrderedSet * set;
+
+ if (!matching) {
+ return NULL;
+ }
+
+ notify = new _IOServiceNotifier;
+ if (notify && !notify->init()) {
+ notify->release();
+ notify = NULL;
+ }
+
+ if (notify) {
+ notify->handler = handler;
+ notify->target = target;
+ notify->type = type;
+ notify->matching = matching;
+ matching->retain();
+ if (handler == &_IOServiceMatchingNotificationHandler) {
+ notify->compatHandler = ((_IOServiceMatchingNotificationHandlerRef *)ref)->handler;
+ notify->ref = ((_IOServiceMatchingNotificationHandlerRef *)ref)->ref;
+ } else {
+ notify->ref = ref;
+ }
+ notify->priority = priority;
+ notify->state = kIOServiceNotifyEnable;
+ queue_init( ¬ify->handlerInvocations );
+
+ ////// queue
+
+ if (NULL == (set = (OSOrderedSet *) gNotifications->getObject( type ))) {
+ set = OSOrderedSet::withCapacity( 1,
+ IONotifyOrdering, NULL );
+ if (set) {
+ gNotifications->setObject( type, set );
+ set->release();
+ }
+ }
+ notify->whence = set;
+ if (set) {
+ set->setObject( notify );
+ }
+ }
+
+ return notify;
+}
+
+// internal - call with gNotificationLock
+IONotifier *
+IOService::doInstallNotification(
+ const OSSymbol * type, OSDictionary * matching,
+ IOServiceMatchingNotificationHandler handler,
+ void * target, void * ref,
+ SInt32 priority, OSIterator ** existing )
+{
+ OSIterator * exist;
+ IONotifier * notify;
+ IOOptionBits inState;
+
+ if (!matching) {
+ return NULL;
+ }
+
+ if (type == gIOPublishNotification) {
+ inState = kIOServiceRegisteredState;
+ } else if (type == gIOFirstPublishNotification) {
+ inState = kIOServiceFirstPublishState;
+ } else if (type == gIOMatchedNotification) {
+ inState = kIOServiceMatchedState;
+ } else if (type == gIOFirstMatchNotification) {
+ inState = kIOServiceFirstMatchState;
+ } else if ((type == gIOTerminatedNotification) || (type == gIOWillTerminateNotification)) {
+ inState = 0;
+ } else {
+ return NULL;
+ }
+
+ notify = setNotification( type, matching, handler, target, ref, priority );
+
+ if (inState) {
+ // get the current set
+ exist = (OSIterator *) copyExistingServices( matching, inState );
+ } else {
+ exist = NULL;
+ }
+
+ *existing = exist;
+
+ return notify;
+}
+
+#if !defined(__LP64__)
+IONotifier *
+IOService::installNotification(const OSSymbol * type, OSDictionary * matching,
+ IOServiceNotificationHandler handler,
+ void * target, void * refCon,
+ SInt32 priority, OSIterator ** existing )
+{
+ IONotifier * result;
+ _IOServiceMatchingNotificationHandlerRef ref;
+ ref.handler = handler;
+ ref.ref = refCon;
+
+ result = (_IOServiceNotifier *) installNotification( type, matching,
+ &_IOServiceMatchingNotificationHandler,
+ target, &ref, priority, existing );
+ if (result) {
+ matching->release();
+ }
+
+ return result;
+}
+
+#endif /* !defined(__LP64__) */
+
+
+IONotifier *
+IOService::installNotification(
+ const OSSymbol * type, OSDictionary * matching,
+ IOServiceMatchingNotificationHandler handler,
+ void * target, void * ref,
+ SInt32 priority, OSIterator ** existing )
+{
+ IONotifier * notify;
+
+ LOCKWRITENOTIFY();
+
+ notify = doInstallNotification( type, matching, handler, target, ref,
+ priority, existing );
+
+ // in case handler remove()s
+ if (notify) {
+ notify->retain();
+ }
+
+ UNLOCKNOTIFY();
+
+ return notify;
+}
+
+IONotifier *
+IOService::addNotification(
+ const OSSymbol * type, OSDictionary * matching,
+ IOServiceNotificationHandler handler,
+ void * target, void * refCon,
+ SInt32 priority )
+{
+ IONotifier * result;
+ _IOServiceMatchingNotificationHandlerRef ref;
+
+ ref.handler = handler;
+ ref.ref = refCon;
+
+ result = addMatchingNotification(type, matching, &_IOServiceMatchingNotificationHandler,
+ target, &ref, priority);
+
+ if (result) {
+ matching->release();
+ }
+
+ return result;
+}
+
+IONotifier *
+IOService::addMatchingNotification(
+ const OSSymbol * type, OSDictionary * matching,
+ IOServiceMatchingNotificationHandler handler,
+ void * target, void * ref,
+ SInt32 priority )
+{
+ OSIterator * existing = NULL;
+ IONotifier * ret;
+ _IOServiceNotifier * notify;
+ IOService * next;
+
+ ret = notify = (_IOServiceNotifier *) installNotification( type, matching,
+ handler, target, ref, priority, &existing );
+ if (!ret) {
+ return NULL;
+ }
+
+ // send notifications for existing set
+ if (existing) {
+ while ((next = (IOService *) existing->getNextObject())) {
+ if (0 == (next->__state[0] & kIOServiceInactiveState)) {
+ next->invokeNotifier( notify );
+ }
+ }
+ existing->release();
+ }
+
+ LOCKWRITENOTIFY();
+ bool removed = (NULL == notify->whence);
+ notify->release();
+ if (removed) {
+ ret = gIOServiceNullNotifier;
+ }
+ UNLOCKNOTIFY();
+
+ return ret;
+}
+
+static bool
+IOServiceMatchingNotificationHandlerToBlock( void * target __unused, void * refCon,
+ IOService * newService,
+ IONotifier * notifier )
+{
+ return ((IOServiceMatchingNotificationHandlerBlock) refCon)(newService, notifier);
+}
+
+IONotifier *
+IOService::addMatchingNotification(
+ const OSSymbol * type, OSDictionary * matching,
+ SInt32 priority,
+ IOServiceMatchingNotificationHandlerBlock handler)
+{
+ IONotifier * notify;
+ void * block;
+
+ block = Block_copy(handler);
+ if (!block) {
+ return NULL;
+ }
+
+ notify = addMatchingNotification(type, matching,
+ &IOServiceMatchingNotificationHandlerToBlock, NULL, block, priority);
+
+ if (!notify) {
+ Block_release(block);
+ }
+
+ return notify;
+}
+
+void
+IOService::userServerCheckInTokenNotificationHandler(
+ __unused IOUserServerCheckInToken *token,
+ void *ref)
+{
+ LOCKWRITENOTIFY();
+ WAKEUPNOTIFY(ref);
+ UNLOCKNOTIFY();
+}
+
+bool
+IOService::syncNotificationHandler(
+ void * /* target */, void * ref,
+ IOService * newService,
+ IONotifier * notifier )
+{
+ LOCKWRITENOTIFY();
+ if (!*((IOService **) ref)) {
+ newService->retain();
+ (*(IOService **) ref) = newService;
+ WAKEUPNOTIFY(ref);
+ }
+ UNLOCKNOTIFY();
+
+ return false;
+}
+
+IOService *
+IOService::waitForMatchingServiceWithToken( OSDictionary * matching,
+ uint64_t timeout,
+ IOUserServerCheckInToken * checkInToken)
+{
+ IONotifier * notify = NULL;
+ // priority doesn't help us much since we need a thread wakeup
+ SInt32 priority = 0;
+ IOService * result;
+
+ if (!matching) {
+ return NULL;
+ }
+
+ result = NULL;
+
+#if DEBUG || DEVELOPMENT
+ char currentName[MAXTHREADNAMESIZE];
+ char newName[MAXTHREADNAMESIZE];
+ OSObject * obj;
+ OSString * str;
+ OSDictionary * dict;
+
+ currentName[0] = '\0';
+ if (thread_has_thread_name(current_thread())) {
+ dict = matching;
+ obj = matching->getObject(gIOPropertyMatchKey);
+ if ((dict = OSDynamicCast(OSDictionary, obj))) {
+ OSObject * result __block = NULL;
+ dict->iterateObjects(^bool (const OSSymbol * sym, OSObject * value) {
+ result = __DECONST(OSObject *, sym);
+ return true;
+ });
+ obj = result;
+ }
+ if (!obj) {
+ obj = matching->getObject(gIOResourceMatchKey);
+ }
+ if (!obj) {
+ obj = matching->getObject(gIONameMatchKey);
+ }
+ if (!obj) {
+ obj = matching->getObject(gIOProviderClassKey);
+ }
+ if ((str = OSDynamicCast(OSString, obj))) {
+ thread_get_thread_name(current_thread(), currentName);
+ snprintf(newName, sizeof(newName), "Waiting_'%s'", str->getCStringNoCopy());
+ thread_set_thread_name(current_thread(), newName);
+ }
+ }
+#endif /* DEBUG || DEVELOPMENT */
+
+ LOCKWRITENOTIFY();
+ do{
+ if (checkInToken) {
+ checkInToken->setNoSendersNotification(&IOService::userServerCheckInTokenNotificationHandler,
+ &result);
+ }
+ result = (IOService *) copyExistingServices( matching,
+ kIOServiceMatchedState, kIONotifyOnce );
+ if (result) {
+ break;
+ }
+ notify = IOService::setNotification( gIOMatchedNotification, matching,
+ &IOService::syncNotificationHandler, (void *) NULL,
+ &result, priority );
+ if (!notify) {
+ break;
+ }
+ if (UINT64_MAX != timeout) {
+ AbsoluteTime deadline;
+ nanoseconds_to_absolutetime(timeout, &deadline);
+ clock_absolutetime_interval_to_deadline(deadline, &deadline);
+ SLEEPNOTIFYTO(&result, deadline);
+ } else {
+ SLEEPNOTIFY(&result);
+ }
+ }while (false);
+
+ UNLOCKNOTIFY();
+
+#if DEBUG || DEVELOPMENT
+ if (currentName[0]) {
+ thread_set_thread_name(current_thread(), currentName);
+ }
+#endif /* DEBUG || DEVELOPMENT */
+
+ if (notify) {
+ notify->remove(); // dequeues
+ }
+
+ if (checkInToken) {
+ checkInToken->clearNotification();
+ }
+
+ return result;
+}
+
+IOService *
+IOService::waitForMatchingService( OSDictionary * matching,
+ uint64_t timeout)
+{
+ return IOService::waitForMatchingServiceWithToken(matching, timeout, NULL);
+}
+
+IOService *
+IOService::waitForService( OSDictionary * matching,
+ mach_timespec_t * timeout )
+{
+ IOService * result;
+ uint64_t timeoutNS;
+
+ if (timeout) {
+ timeoutNS = timeout->tv_sec;
+ timeoutNS *= kSecondScale;
+ timeoutNS += timeout->tv_nsec;
+ } else {
+ timeoutNS = UINT64_MAX;
+ }
+
+ result = waitForMatchingService(matching, timeoutNS);
+
+ matching->release();
+ if (result) {
+ result->release();
+ }
+
+ return result;
+}
+
+__dead2
+void
+IOService::deliverNotification( const OSSymbol * type,
+ IOOptionBits orNewState, IOOptionBits andNewState )
+{
+ panic("deliverNotification");
+}
+
+OSArray *
+IOService::copyNotifiers(const OSSymbol * type,
+ IOOptionBits orNewState, IOOptionBits andNewState )
+{
+ _IOServiceNotifier * notify;
+ OSIterator * iter;
+ OSArray * willSend = NULL;
+
+ lockForArbitration();
+
+ if ((0 == (__state[0] & kIOServiceInactiveState))
+ || (type == gIOTerminatedNotification)
+ || (type == gIOWillTerminateNotification)) {
+ LOCKREADNOTIFY();
+
+ iter = OSCollectionIterator::withCollection((OSOrderedSet *)
+ gNotifications->getObject( type ));
+
+ if (iter) {
+ while ((notify = (_IOServiceNotifier *) iter->getNextObject())) {
+ if (matchPassive(notify->matching, 0)
+ && (kIOServiceNotifyEnable & notify->state)) {
+ if (NULL == willSend) {
+ willSend = OSArray::withCapacity(8);
+ }
+ if (willSend) {
+ willSend->setObject( notify );
+ }
+ }
+ }
+ iter->release();
+ }
+ __state[0] = (__state[0] | orNewState) & andNewState;
+ UNLOCKNOTIFY();
+ }
+
+ unlockForArbitration();
+
+ return willSend;
+}
+
+IOOptionBits
+IOService::getState( void ) const
+{
+ return __state[0];
+}
+
+/*
+ * Helpers to make matching objects for simple cases
+ */
+
+OSDictionary *
+IOService::serviceMatching( const OSString * name,
+ OSDictionary * table )
+{
+ const OSString * str;
+
+ str = OSSymbol::withString(name);
+ if (!str) {
+ return NULL;
+ }
+
+ if (!table) {
+ table = OSDictionary::withCapacity( 2 );
+ }
+ if (table) {
+ table->setObject(gIOProviderClassKey, (OSObject *)str );
+ }
+ str->release();
+
+ return table;
+}
+
+OSDictionary *
+IOService::serviceMatching( const char * name,
+ OSDictionary * table )
+{
+ const OSString * str;
+
+ str = OSSymbol::withCString( name );
+ if (!str) {
+ return NULL;
+ }
+
+ table = serviceMatching( str, table );
+ str->release();
+ return table;
+}
+
+OSDictionary *
+IOService::nameMatching( const OSString * name,
+ OSDictionary * table )
+{
+ if (!table) {
+ table = OSDictionary::withCapacity( 2 );
+ }
+ if (table) {
+ table->setObject( gIONameMatchKey, (OSObject *)name );
+ }
+
+ return table;
+}
+
+OSDictionary *
+IOService::nameMatching( const char * name,
+ OSDictionary * table )
+{
+ const OSString * str;
+
+ str = OSSymbol::withCString( name );
+ if (!str) {
+ return NULL;
+ }
+
+ table = nameMatching( str, table );
+ str->release();
+ return table;
+}
+
+OSDictionary *
+IOService::resourceMatching( const OSString * str,
+ OSDictionary * table )
+{
+ table = serviceMatching( gIOResourcesKey, table );
+ if (table) {
+ table->setObject( gIOResourceMatchKey, (OSObject *) str );
+ }
+
+ return table;
+}
+
+OSDictionary *
+IOService::resourceMatching( const char * name,
+ OSDictionary * table )
+{
+ const OSSymbol * str;
+
+ str = OSSymbol::withCString( name );
+ if (!str) {
+ return NULL;
+ }
+
+ table = resourceMatching( str, table );
+ str->release();
+
+ return table;
+}
+
+OSDictionary *
+IOService::propertyMatching( const OSSymbol * key, const OSObject * value,
+ OSDictionary * table )
+{
+ OSDictionary * properties;
+
+ properties = OSDictionary::withCapacity( 2 );
+ if (!properties) {
+ return NULL;
+ }
+ properties->setObject( key, value );
+
+ if (!table) {
+ table = OSDictionary::withCapacity( 2 );
+ }
+ if (table) {
+ table->setObject( gIOPropertyMatchKey, properties );
+ }
+
+ properties->release();
+
+ return table;
+}
+
+OSDictionary *
+IOService::registryEntryIDMatching( uint64_t entryID,
+ OSDictionary * table )
+{
+ OSNumber * num;
+
+ num = OSNumber::withNumber( entryID, 64 );
+ if (!num) {
+ return NULL;
+ }
+
+ if (!table) {
+ table = OSDictionary::withCapacity( 2 );
+ }
+ if (table) {
+ table->setObject( gIORegistryEntryIDKey, num );
+ }
+
+ if (num) {
+ num->release();
+ }
+
+ return table;
+}
+
+
+/*
+ * _IOServiceNotifier
+ */
+
+// wait for all threads, other than the current one,
+// to exit the handler
+
+void
+_IOServiceNotifier::wait()
+{
+ _IOServiceNotifierInvocation * next;
+ bool doWait;
+
+ do {
+ doWait = false;
+ queue_iterate( &handlerInvocations, next,
+ _IOServiceNotifierInvocation *, link) {
+ if (next->thread != current_thread()) {
+ doWait = true;
+ break;
+ }
+ }
+ if (doWait) {
+ state |= kIOServiceNotifyWaiter;
+ SLEEPNOTIFY(this);
+ }
+ } while (doWait);