+ 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();
+ }
+ }