]> git.saurik.com Git - apple/xnu.git/blobdiff - iokit/Kernel/IOService.cpp
xnu-2050.18.24.tar.gz
[apple/xnu.git] / iokit / Kernel / IOService.cpp
index 6ef0b34138181b876437b65961257fda79d1fb1f..0eabf83f9d70ebd488ce9ff04b5d4ebd2964723c 100644 (file)
@@ -35,6 +35,7 @@
 #include <libkern/c++/OSUnserialize.h>
 #include <IOKit/IOCatalogue.h>
 #include <IOKit/IOCommand.h>
+#include <IOKit/IODeviceTreeSupport.h>
 #include <IOKit/IODeviceMemory.h>
 #include <IOKit/IOInterrupts.h>
 #include <IOKit/IOInterruptController.h>
@@ -46,6 +47,7 @@
 #include <IOKit/IOUserClient.h>
 #include <IOKit/IOWorkLoop.h>
 #include <IOKit/IOTimeStamp.h>
+#include <IOKit/IOHibernatePrivate.h>
 #include <mach/sync_policy.h>
 #include <IOKit/assert.h>
 #include <sys/errno.h>
@@ -54,6 +56,7 @@
 
 #define LOG kprintf
 //#define LOG IOLog
+#define MATCH_DEBUG    0
 
 #include "IOServicePrivate.h"
 #include "IOKitKernelInternal.h"
@@ -115,10 +118,14 @@ const OSSymbol *          gIOConsoleSessionUIDKey;
 const OSSymbol *               gIOConsoleSessionAuditIDKey;
 const OSSymbol *               gIOConsoleUsersSeedKey;
 const OSSymbol *               gIOConsoleSessionOnConsoleKey;
+const OSSymbol *               gIOConsoleSessionLoginDoneKey;
 const OSSymbol *               gIOConsoleSessionSecureInputPIDKey;
 const OSSymbol *               gIOConsoleSessionScreenLockedTimeKey;
 
-static clock_sec_t             gIOConsoleLockTime;
+clock_sec_t                    gIOConsoleLockTime;
+static bool                    gIOConsoleLoggedIn;
+static uint32_t                        gIOScreenLockState;
+static IORegistryEntry *        gIOChosenEntry;
 
 static int                     gIOResourceGenerationCount;
 
@@ -224,7 +231,6 @@ static IOLock *     gArbitrationLockQueueLock;
 bool IOService::isInactive( void ) const
     { return( 0 != (kIOServiceInactiveState & getState())); }
 
-
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 #if defined(__i386__) || defined(__x86_64__)
@@ -322,6 +328,7 @@ void IOService::initialize( void )
 
     gIOConsoleUsersSeedKey              = OSSymbol::withCStringNoCopy(kIOConsoleUsersSeedKey);
     gIOConsoleSessionOnConsoleKey        = OSSymbol::withCStringNoCopy(kIOConsoleSessionOnConsoleKey);
+    gIOConsoleSessionLoginDoneKey        = OSSymbol::withCStringNoCopy(kIOConsoleSessionLoginDoneKey);
     gIOConsoleSessionSecureInputPIDKey   = OSSymbol::withCStringNoCopy(kIOConsoleSessionSecureInputPIDKey);
     gIOConsoleSessionScreenLockedTimeKey = OSSymbol::withCStringNoCopy(kIOConsoleSessionScreenLockedTimeKey);
 
@@ -361,6 +368,8 @@ void IOService::initialize( void )
 
     gIOConsoleLockCallout = thread_call_allocate(&IOService::consoleLockTimer, NULL);
 
+    IORegistryEntry::getRegistryRoot()->setProperty(gIOConsoleLockedKey, kOSBooleanTrue);
+
     assert( gIOServiceBusyLock && gJobs && gJobsLock && gIOConsoleUsersLock
                && gIOConsoleLockCallout && (err == KERN_SUCCESS) );
 
@@ -456,6 +465,8 @@ bool IOService::attach( IOService * provider )
        ok = attachToParent( getRegistryRoot(), gIOServicePlane);
     }
 
+    if (ok && !__provider) (void) getProvider();
+
     return( ok );
 }
 
@@ -486,6 +497,10 @@ void IOService::detach( IOService * provider )
             _adjustBusy( -busy );
     }
 
+    if (kIOServiceInactiveState & __state[0]) {
+       getMetaClass()->removeInstance(this);
+    }
+
     unlockForArbitration();
 
     if( newProvider) {
@@ -624,7 +639,9 @@ void IOService::startMatching( IOOptionBits options )
             lockForArbitration();
             IOLockLock( gIOServiceBusyLock );
 
-            waitAgain = (prevBusy < (__state[1] & kIOServiceBusyStateMask));
+            waitAgain = ((prevBusy < (__state[1] & kIOServiceBusyStateMask))
+                                      && (0 == (__state[0] & kIOServiceInactiveState)));
+
             if( waitAgain)
                 __state[1] |= kIOServiceSyncPubState | kIOServiceBusyWaiterState;
             else
@@ -657,37 +674,37 @@ IOReturn IOService::catalogNewDrivers( OSOrderedSet * newTables )
     
     while( (table = (OSDictionary *) newTables->getFirstObject())) {
 
-       LOCKWRITENOTIFY();
+        LOCKWRITENOTIFY();
         set = (OSSet *) copyExistingServices( table, 
                                                kIOServiceRegisteredState,
                                                kIOServiceExistingSet);
-       UNLOCKNOTIFY();
-       if( set) {
+        UNLOCKNOTIFY();
+        if( set) {
 
 #if IOMATCHDEBUG
-           count += set->getCount();
+            count += set->getCount();
 #endif
-           if (allSet) {
-               allSet->merge((const OSSet *) set);
-               set->release();
-           }
-           else
-               allSet = set;
-       }
+            if (allSet) {
+                allSet->merge((const OSSet *) set);
+                set->release();
+            }
+            else
+                allSet = set;
+        }
 
 #if IOMATCHDEBUG
-       if( getDebugFlags( table ) & kIOLogMatch)
-           LOG("Matching service count = %ld\n", (long)count);
+        if( getDebugFlags( table ) & kIOLogMatch)
+            LOG("Matching service count = %ld\n", (long)count);
 #endif
-       newTables->removeObject(table);
+        newTables->removeObject(table);
     }
 
     if (allSet) {
-       while( (service = (IOService *) allSet->getAnyObject())) {
-           service->startMatching(kIOServiceAsynchronous);
-           allSet->removeObject(service);
-       }
-       allSet->release();
+        while( (service = (IOService *) allSet->getAnyObject())) {
+            service->startMatching(kIOServiceAsynchronous);
+            allSet->removeObject(service);
+        }
+        allSet->release();
     }
 
     newTables->release();
@@ -752,10 +769,9 @@ IOService * IOService::getProvider( void ) const
     IOService *        parent;
     SInt32     generation;
 
-    parent = __provider;
     generation = getGenerationCount();
     if( __providerGeneration == generation)
-       return( parent );
+       return( __provider );
 
     parent = (IOService *) getParentEntry( gIOServicePlane);
     if( parent == IORegistryEntry::getRegistryRoot())
@@ -763,7 +779,8 @@ IOService * IOService::getProvider( void ) const
        parent = 0;
 
     self->__provider = parent;
-    // save the count before getParentEntry()
+    OSMemoryBarrier();
+    // save the count from before call to getParentEntry()
     self->__providerGeneration = generation;
 
     return( parent );
@@ -2471,13 +2488,13 @@ static SInt32 IOServiceObjectOrder( const OSObject * entry, void * ref)
     OSSymbol *         key = (OSSymbol *) ref;
     OSNumber *         offset;
 
-    if( (notify = OSDynamicCast( _IOServiceNotifier, entry)))
+    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)))
         offset = OSDynamicCast(OSNumber, service->getProperty( key ));
-    else if( (dict = OSDynamicCast( OSDictionary, entry)))
-        offset = OSDynamicCast(OSNumber, dict->getObject( key ));
     else {
        assert( false );
        offset = 0;
@@ -2598,10 +2615,6 @@ void IOService::probeCandidates( OSOrderedSet * matches )
     OSObject           *       nextMatch = 0;
     bool                       started;
     bool                       needReloc = false;
-#if CONFIG_MACF_KEXT
-    OSBoolean          *       isSandbox = 0;
-    bool                       useSandbox = false;
-#endif
 #if IOMATCHDEBUG
     SInt64                     debugFlags;
 #endif
@@ -2663,7 +2676,7 @@ void IOService::probeCandidates( OSOrderedSet * matches )
            props->setCapacityIncrement(1);             
 
            // check the nub matches
-           if( false == passiveMatch( props, true ))
+           if( false == matchPassive(props, kIOServiceChangesOK | kIOServiceClassDone))
                continue;
 
             // Check to see if driver reloc has been loaded.
@@ -2744,10 +2757,6 @@ void IOService::probeCandidates( OSOrderedSet * matches )
                 if( 0 == category)
                     category = gIODefaultMatchCategoryKey;
                 inst->setProperty( gIOMatchCategoryKey, (OSObject *) category );
-#if CONFIG_MACF_KEXT
-               isSandbox = OSDynamicCast(OSBoolean,
-                            props->getObject("IOKitForceMatch"));
-#endif
                 // attach driver instance
                 if( !(inst->attach( this )))
                         continue;
@@ -2764,21 +2773,6 @@ void IOService::probeCandidates( OSOrderedSet * matches )
     
                 newInst = inst->probe( this, &score );
                 inst->detach( this );
-#if CONFIG_MACF_KEXT
-               /*
-                * If this is the Sandbox driver and it matched, this is a
-                * disallowed device; toss any drivers that were already
-                * matched.
-                */
-               if (isSandbox && isSandbox->isTrue() && newInst != 0) {
-                   if (startDict != 0) {
-                       startDict->flushCollection();
-                       startDict->release();
-                       startDict = 0;
-                   }
-                   useSandbox = true;
-               }
-#endif
                 if( 0 == newInst) {
 #if IOMATCHDEBUG
                     if( debugFlags & kIOLogProbe)
@@ -2817,13 +2811,6 @@ void IOService::probeCandidates( OSOrderedSet * matches )
             props->release();
             if( inst)
                 inst->release();
-#if CONFIG_MACF_KEXT
-           /*
-            * If we're forcing the sandbox, drop out of the loop.
-            */
-           if (isSandbox && isSandbox->isTrue() && useSandbox)
-                   break;
-#endif
         }
         familyMatches->release();
         familyMatches = 0;
@@ -3109,6 +3096,7 @@ void IOService::doServiceMatch( IOOptionBits options )
     SInt32             catalogGeneration;
     bool               keepGuessing = true;
     bool               reRegistered = true;
+    bool               didRegister;
 
 //    job->nub->deliverNotification( gIOPublishNotification,
 //                             kIOServiceRegisteredState, 0xffffffff );
@@ -3126,6 +3114,7 @@ void IOService::doServiceMatch( IOOptionBits options )
            LOCKREADNOTIFY();
             __state[1] &= ~kIOServiceNeedConfigState;
             __state[1] |= kIOServiceConfigState;
+            didRegister = (0 == (kIOServiceRegisteredState & __state[0]));
             __state[0] |= kIOServiceRegisteredState;
 
            keepGuessing &= (0 == (__state[0] & kIOServiceInactiveState));
@@ -3136,7 +3125,7 @@ void IOService::doServiceMatch( IOOptionBits options )
                     while((notify = (_IOServiceNotifier *)
                            iter->getNextObject())) {
 
-                        if( passiveMatch( notify->matching )
+                        if( matchPassive(notify->matching, 0)
                          && (kIOServiceNotifyEnable & notify->state))
                             matches->setObject( notify );
                     }
@@ -3145,6 +3134,9 @@ void IOService::doServiceMatch( IOOptionBits options )
             }
 
            UNLOCKNOTIFY();
+           if (didRegister) {
+               getMetaClass()->addInstance(this);
+           }
             unlockForArbitration();
 
             if (keepGuessing && matches->getCount() && (kIOReturnSuccess == getResources()))
@@ -3514,27 +3506,83 @@ void _IOServiceJob::pingConfig( _IOServiceJob * job )
     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;
+       ctx->count += table->getCount();
+        match = service->matchInternal(table, options, &done);
+       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 = 0;
-    OSIterator *       iter;
-    IOService *                service;
-    OSObject *         obj;
+    OSObject *  current = 0;
+    OSIterator * iter;
+    IOService *         service;
+    OSObject *  obj;
+    OSString *   str;
 
     if( !matching)
        return( 0 );
 
-    if(true 
-      && (obj = matching->getObject(gIOProviderClassKey))
+#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->passiveMatch( matching ))
+         &&  service->matchPassive(matching, options))
        {
            if( options & kIONotifyOnce)
            {
@@ -3542,12 +3590,69 @@ OSObject * IOService::copyExistingServices( OSDictionary * matching,
                current = service;
            }
            else
-               current = OSSet::withObjects(
-                               (const OSObject **) &service, 1, 1 );
+               current = OSSet::withObjects((const OSObject **) &service, 1, 1 );
        }
     }
     else
     {
+       IOServiceMatchContext ctx;
+       ctx.table   = matching;
+       ctx.state   = inState;
+       ctx.count   = 0;
+       ctx.done    = 0;
+       ctx.options = options;
+       ctx.result  = 0;
+
+       if ((str = OSDynamicCast(OSString, obj)))
+       {
+           const OSSymbol * sym = OSSymbol::withString(str);
+           OSMetaClass::applyToInstancesOfClassName(sym, instanceMatch, &ctx);
+           sym->release();
+       }
+       else
+       {
+           IOService::gMetaClass.applyToInstances(instanceMatch, &ctx);
+       }
+
+
+       current = ctx.result;
+
+       options |= kIOServiceInternalDone | kIOServiceClassDone;
+       if (current && (ctx.done != ctx.count))
+       {
+           OSSet *
+           source = OSDynamicCast(OSSet, current);
+           current = 0;
+           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) {
@@ -3556,24 +3661,42 @@ OSObject * IOService::copyExistingServices( OSDictionary * matching,
                while( (service = (IOService *) iter->getNextObject())) {
                    if( (inState == (service->__state[0] & inState))
                    && (0 == (service->__state[0] & kIOServiceInactiveState))
-                   &&  service->passiveMatch( matching )) {
+                   &&  service->matchPassive(matching, 0)) {
     
                        if( options & kIONotifyOnce) {
                            service->retain();
-                           current = service;
+                           _current = service;
                            break;
                        }
-                       if( current)
-                           ((OSSet *)current)->setObject( service );
+                       if( _current)
+                           ((OSSet *)_current)->setObject( service );
                        else
-                           current = OSSet::withObjects(
+                           _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", current, _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 );
@@ -3600,6 +3723,21 @@ OSIterator * IOService::getMatchingServices( OSDictionary * matching )
     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;
@@ -3907,7 +4045,7 @@ void IOService::deliverNotification( const OSSymbol * type,
         if( iter) {
             while( (notify = (_IOServiceNotifier *) iter->getNextObject())) {
 
-                if( passiveMatch( notify->matching)
+                if( matchPassive(notify->matching, 0)
                   && (kIOServiceNotifyEnable & notify->state)) {
                     if( 0 == willSend)
                         willSend = OSArray::withCapacity(8);
@@ -3946,10 +4084,18 @@ IOOptionBits IOService::getState( void ) const
 OSDictionary * IOService::serviceMatching( const OSString * name,
                        OSDictionary * table )
 {
+
+    const OSString *   str;
+
+    str = OSSymbol::withString(name);
+    if( !str)
+       return( 0 );
+
     if( !table)
        table = OSDictionary::withCapacity( 2 );
     if( table)
-        table->setObject(gIOProviderClassKey, (OSObject *)name );
+        table->setObject(gIOProviderClassKey, (OSObject *)str );
+    str->release();
 
     return( table );
 }
@@ -4240,44 +4386,61 @@ void IOService::updateConsoleUsers(OSArray * consoleUsers, IOMessage systemMessa
 
     regEntry = IORegistryEntry::getRegistryRoot();
 
+    if (!gIOChosenEntry)
+       gIOChosenEntry = IORegistryEntry::fromPath("/chosen", gIODTPlane);
+
     IOLockLock(gIOConsoleUsersLock);
 
     if (systemMessage)
     {
         sSystemPower = systemMessage;
+#if HIBERNATION
+       if ((kIOMessageSystemHasPoweredOn == systemMessage) && IOHibernateWasScreenLocked())
+       {
+           locked = kOSBooleanTrue;
+       }
+#endif /* HIBERNATION */
     }
+
     if (consoleUsers)
     {
         OSNumber * num = 0;
+       gIOConsoleLoggedIn = false;
        for (idx = 0; 
-             (!num) && (user = OSDynamicCast(OSDictionary, consoleUsers->getObject(idx))); 
+             (user = OSDynamicCast(OSDictionary, consoleUsers->getObject(idx))); 
              idx++)
        {
-           num = OSDynamicCast(OSNumber, user->getObject(gIOConsoleSessionScreenLockedTimeKey));
+           gIOConsoleLoggedIn |= ((kOSBooleanTrue == user->getObject(gIOConsoleSessionOnConsoleKey))
+                       && (kOSBooleanTrue == user->getObject(gIOConsoleSessionLoginDoneKey)));
+           if (!num)
+           {
+               num = OSDynamicCast(OSNumber, user->getObject(gIOConsoleSessionScreenLockedTimeKey));
+           }
        }
         gIOConsoleLockTime = num ? num->unsigned32BitValue() : 0;
     }
 
-    if (gIOConsoleLockTime)
+    if (!gIOConsoleLoggedIn 
+     || (kIOMessageSystemWillSleep == sSystemPower)
+     || (kIOMessageSystemPagingOff == sSystemPower))
     {
-       if (kIOMessageSystemWillSleep == sSystemPower)
-           locked = kOSBooleanTrue;
+       locked = kOSBooleanTrue;
+    }
+    else if (gIOConsoleLockTime)
+    {
+       clock_sec_t  now;
+       clock_usec_t microsecs;
+
+       clock_get_calendar_microtime(&now, &microsecs);
+       if (gIOConsoleLockTime > now)
+       {
+           AbsoluteTime deadline;
+           clock_interval_to_deadline(gIOConsoleLockTime - now, kSecondScale, &deadline);
+           thread_call_enter_delayed(gIOConsoleLockCallout, deadline);
+       }
        else
        {
-           clock_sec_t  now;
-           clock_usec_t microsecs;
-
-           clock_get_calendar_microtime(&now, &microsecs);
-           if (gIOConsoleLockTime > now)
-           {
-               AbsoluteTime deadline;
-               clock_interval_to_deadline(gIOConsoleLockTime - now, kSecondScale, &deadline);
-               thread_call_enter_delayed(gIOConsoleLockCallout, deadline);
-           }
-           else
-           {
-               locked = kOSBooleanTrue;
-           }
+           locked = kOSBooleanTrue;
        }
     }
 
@@ -4292,6 +4455,20 @@ void IOService::updateConsoleUsers(OSArray * consoleUsers, IOMessage systemMessa
        OSIncrementAtomic( &gIOConsoleUsersSeed );
     }
 
+#if HIBERNATION
+    if (gIOChosenEntry)
+    {
+       uint32_t screenLockState;
+
+       if (locked == kOSBooleanTrue) screenLockState = kIOScreenLockLocked;
+       else if (gIOConsoleLockTime)  screenLockState = kIOScreenLockUnlocked;
+       else                          screenLockState = kIOScreenLockNoLock;
+
+       if (screenLockState != gIOScreenLockState) gIOChosenEntry->setProperty(kIOScreenLockStateKey, &screenLockState, sizeof(screenLockState));
+       gIOScreenLockState = screenLockState;
+    }
+#endif /* HIBERNATION */
+
     IOLockUnlock(gIOConsoleUsersLock);
 
     if (publish)
@@ -4443,144 +4620,188 @@ IOService * IOService::matchLocation( IOService * /* client */ )
     return( parent );
 }
 
-bool IOService::passiveMatch( OSDictionary * table, bool changesOK )
+bool IOService::matchInternal(OSDictionary * table, uint32_t options, uint32_t * did)
 {
-    IOService *                where;
     OSString *         matched;
     OSObject *         obj;
     OSString *         str;
     IORegistryEntry *  entry;
     OSNumber *         num;
-    SInt32             score;
-    OSNumber *         newPri;
     bool               match = true;
-    bool               matchParent = false;
-    UInt32             done;
+    bool                changesOK = (0 != (kIOServiceChangesOK & options));
+    uint32_t            count;
+    uint32_t            done;
 
-    assert( table );
-
-    where = this;
+    do
+    {
+       count = table->getCount();
+       done = 0;
+       str = OSDynamicCast(OSString, table->getObject(gIOProviderClassKey));
+       if (str) {
+           done++;
+           match = ((kIOServiceClassDone & options) || (0 != metaCast(str)));
+#if MATCH_DEBUG
+           match = (0 != metaCast( str ));
+           if ((kIOServiceClassDone & options) && !match) panic("classDone");
+#endif
+           if ((!match) || (done == count)) break;
+       }
 
-    do {
-        do {
-            done = 0;
-
-            str = OSDynamicCast( OSString, table->getObject( gIOProviderClassKey));
-            if( str) {
-                done++;
-                match = (0 != where->metaCast( str ));
-                if( !match)
-                    break;
-            }
+       obj = table->getObject( gIONameMatchKey );
+       if( obj) {
+           done++;
+           match = compareNames( obj, changesOK ? &matched : 0 );
+           if (!match) break;
+           if( changesOK && matched) {
+               // leave a hint as to which name matched
+               table->setObject( gIONameMatchedKey, matched );
+               matched->release();
+           }
+           if (done == count) break;
+       }
 
-            obj = table->getObject( gIONameMatchKey );
-            if( obj) {
-                done++;
-                match = where->compareNames( obj, changesOK ? &matched : 0 );
-                if( !match)
-                    break;
-                if( changesOK && matched) {
-                    // leave a hint as to which name matched
-                    table->setObject( gIONameMatchedKey, matched );
-                    matched->release();
-                }
-            }
+       str = OSDynamicCast( OSString, table->getObject( gIOLocationMatchKey ));
+       if (str)
+       {
+           const OSSymbol * sym;
+           done++;
+           match = false;
+           sym = copyLocation();
+           if (sym) {
+               match = sym->isEqualTo( str );
+               sym->release();
+           }
+           if ((!match) || (done == count)) break;
+       }
 
-            str = OSDynamicCast( OSString, table->getObject( gIOLocationMatchKey ));
-            if( str) {
+       obj = table->getObject( gIOPropertyMatchKey );
+       if( obj)
+       {
+           OSDictionary * dict;
+           OSDictionary * nextDict;
+           OSIterator *   iter;
+           done++;
+           match = false;
+           dict = dictionaryWithProperties();
+           if( dict) {
+               nextDict = OSDynamicCast( OSDictionary, obj);
+               if( nextDict)
+                   iter = 0;
+               else
+                   iter = OSCollectionIterator::withCollection(
+                               OSDynamicCast(OSCollection, obj));
+
+               while( nextDict
+                   || (iter && (0 != (nextDict = OSDynamicCast(OSDictionary,
+                                           iter->getNextObject()))))) {
+                   match = dict->isEqualTo( nextDict, nextDict);
+                   if( match)
+                       break;
+                   nextDict = 0;
+               }
+               dict->release();
+               if( iter)
+                   iter->release();
+           }
+           if ((!match) || (done == count)) break;
+       }
 
-                const OSSymbol * sym;
+       str = OSDynamicCast( OSString, table->getObject( gIOPathMatchKey ));
+       if( str) {
+           done++;
+           entry = IORegistryEntry::fromPath( str->getCStringNoCopy() );
+           match = (this == entry);
+           if( entry)
+               entry->release();
+           if ((!match) || (done == count)) break;
+       }
 
-                done++;
-                match = false;
-                sym = where->copyLocation();
-                if( sym) {
-                    match = sym->isEqualTo( str );
-                    sym->release();
-                }
-                if( !match)
-                    break;
-            }
+       num = OSDynamicCast( OSNumber, table->getObject( gIORegistryEntryIDKey ));
+       if (num) {
+           done++;
+           match = (getRegistryEntryID() == num->unsigned64BitValue());
+           if ((!match) || (done == count)) break;
+       }
 
-            obj = table->getObject( gIOPropertyMatchKey );
-            if( obj) {
+       num = OSDynamicCast( OSNumber, table->getObject( gIOMatchedServiceCountKey ));
+       if( num)
+       {
+           OSIterator *        iter;
+           IOService *         service = 0;
+           UInt32              serviceCount = 0;
 
-                OSDictionary * dict;
-                OSDictionary * nextDict;
-                OSIterator *   iter;
+           done++;
+           iter = getClientIterator();
+           if( iter) {
+               while( (service = (IOService *) iter->getNextObject())) {
+                   if( kIOServiceInactiveState & service->__state[0])
+                       continue;
+                   if( 0 == service->getProperty( gIOMatchCategoryKey ))
+                       continue;
+                   ++serviceCount;
+               }
+               iter->release();
+           }
+           match = (serviceCount == num->unsigned32BitValue());
+           if ((!match) || (done == count)) break;
+       }
 
-                done++;
-                match = false;
-                dict = where->dictionaryWithProperties();
-                if( dict) {
-                    nextDict = OSDynamicCast( OSDictionary, obj);
-                    if( nextDict)
-                        iter = 0;
-                    else
-                        iter = OSCollectionIterator::withCollection(
-                                    OSDynamicCast(OSCollection, obj));
-
-                    while( nextDict
-                        || (iter && (0 != (nextDict = OSDynamicCast(OSDictionary,
-                                                iter->getNextObject()))))) {
-                        match = dict->isEqualTo( nextDict, nextDict);
-                        if( match)
-                            break;
-                        nextDict = 0;
-                    }
-                    dict->release();
-                    if( iter)
-                        iter->release();
-                }
-                if( !match)
-                    break;
-            }
+#define propMatch(key)                                 \
+       obj = table->getObject(key);                    \
+       if (obj)                                        \
+       {                                               \
+           OSObject * prop;                            \
+           done++;                                     \
+           prop = copyProperty(key);                   \
+           match = obj->isEqualTo(prop);               \
+            if (prop) prop->release();                 \
+           if ((!match) || (done == count)) break;     \
+       }
+       propMatch(kIOBSDNameKey)
+       propMatch(kIOBSDMajorKey)
+       propMatch(kIOBSDMinorKey)
+       propMatch(kIOBSDUnitKey)
+#undef propMatch
+    }
+    while (false);
 
-            str = OSDynamicCast( OSString, table->getObject( gIOPathMatchKey ));
-            if( str) {
-                done++;
-                entry = IORegistryEntry::fromPath( str->getCStringNoCopy() );
-                match = (where == entry);
-                if( entry)
-                    entry->release();
-                if( !match)
-                    break;
-            }
+    if (did) *did = done;
+    return (match);
+}
 
-            num = OSDynamicCast( OSNumber, table->getObject( gIORegistryEntryIDKey ));
-            if( num) {
-               done++;
-                match = (getRegistryEntryID() == num->unsigned64BitValue());
-           }
+bool IOService::passiveMatch( OSDictionary * table, bool changesOK )
+{
+    return (matchPassive(table, changesOK ? kIOServiceChangesOK : 0));
+}
 
-            num = OSDynamicCast( OSNumber, table->getObject( gIOMatchedServiceCountKey ));
-            if( num) {
+bool IOService::matchPassive(OSDictionary * table, uint32_t options)
+{
+    IOService *                where;
+    OSDictionary *      nextTable;
+    SInt32             score;
+    OSNumber *         newPri;
+    bool               match = true;
+    bool               matchParent = false;
+    uint32_t           count;
+    uint32_t           done;
 
-                OSIterator *   iter;
-                IOService *            service = 0;
-                UInt32         serviceCount = 0;
+    assert( table );
 
-                done++;
-                iter = where->getClientIterator();
-                if( iter) {
-                    while( (service = (IOService *) iter->getNextObject())) {
-                        if( kIOServiceInactiveState & service->__state[0])
-                            continue;
-                        if( 0 == service->getProperty( gIOMatchCategoryKey ))
-                            continue;
-                        ++serviceCount;
-                    }
-                    iter->release();
-                }
-                match = (serviceCount == num->unsigned32BitValue());
-                if( !match)
-                    break;
-            }
+#if MATCH_DEBUG 
+    OSDictionary * root = table;
+#endif
 
-            if( done == table->getCount()) {
-                // don't call family if we've done all the entries in the table
-                matchParent = false;
-                break;
+    where = this;
+    do
+    {
+        do
+        {
+           count = table->getCount();
+           if (!(kIOServiceInternalDone & options))
+           {
+               match = where->matchInternal(table, options, &done);
+               // don't call family if we've done all the entries in the table
+               if ((!match) || (done == count)) break;
             }
 
             // pass in score from property table
@@ -4597,7 +4818,7 @@ bool IOService::passiveMatch( OSDictionary * table, bool changesOK )
                 break;
             }
 
-            if( changesOK) {
+            if (kIOServiceChangesOK & options) {
                 // save the score
                 newPri = OSNumber::withNumber( score, 32 );
                 if( newPri) {
@@ -4606,43 +4827,42 @@ bool IOService::passiveMatch( OSDictionary * table, bool changesOK )
                 }
             }
 
-            if( !(match = where->compareProperty( table, kIOBSDNameKey )))
-                break;
-            if( !(match = where->compareProperty( table, kIOBSDMajorKey )))
-                break;
-            if( !(match = where->compareProperty( table, kIOBSDMinorKey )))
-                break;
-            if( !(match = where->compareProperty( table, kIOBSDUnitKey )))
-                break;
-
+           options = 0;
             matchParent = false;
 
-            obj = OSDynamicCast( OSDictionary,
+            nextTable = OSDynamicCast(OSDictionary,
                   table->getObject( gIOParentMatchKey ));
-            if( obj) {
+            if( nextTable) {
+               // look for a matching entry anywhere up to root
                 match = false;
                 matchParent = true;
-                table = (OSDictionary *) obj;
+               table = nextTable;
                 break;
             }
 
-            table = OSDynamicCast( OSDictionary,
+            table = OSDynamicCast(OSDictionary,
                     table->getObject( gIOLocationMatchKey ));
-            if( table) {
+            if (table) {
+               // look for a matching entry at matchLocation()
                 match = false;
                 where = where->getProvider();
-                if( where)
-                    where = where->matchLocation( where );
+                if (where && (where = where->matchLocation(where))) continue;
             }
+            break;
+        }
+        while (true);
+    }
+    while( matchParent && (!match) && (where = where->getProvider()) );
 
-        } while( table && where );
-
-    } while( matchParent && (where = where->getProvider()) );
-
-    if( kIOLogMatch & gIOKitDebug)
-        if( where && (where != this) )
-            LOG("match parent @ %s = %d\n",
-                        where->getName(), match );
+#if MATCH_DEBUG
+    if (where != this) 
+    {
+       OSSerialize * s = OSSerialize::withCapacity(128);
+       root->serialize(s);
+       kprintf("parent match 0x%llx, %d,\n%s\n", getRegistryEntryID(), match, s->text());
+       s->release();
+    }
+#endif
 
     return( match );
 }