X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/1c79356b52d46aa6b508fb032f5ae709b1f2897b..490019cf9519204c5fb36b2fba54ceb983bb6b72:/iokit/Kernel/IOService.cpp diff --git a/iokit/Kernel/IOService.cpp b/iokit/Kernel/IOService.cpp index 51a544089..189f008fa 100644 --- a/iokit/Kernel/IOService.cpp +++ b/iokit/Kernel/IOService.cpp @@ -1,62 +1,74 @@ /* - * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 1998-2014 Apple Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ - */ -/* - * Copyright (c) 1991-1999 Apple Computer, Inc. All rights reserved. - * - * HISTORY - * - * 29-Jan-91 Portions from IODevice.m, Doug Mitchell at NeXT, Created. - * 18-Jun-98 start IOKit objc - * 10-Nov-98 start iokit cpp - * 25-Feb-99 sdouglas, add threads and locks to ensure deadlock - * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ #include #include +#include #include +#include #include #include +#include +#include #include #include #include #include #include #include -#include +#include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -//#define LESS_THREAD_CREATE -//#define LOG kprintf -#define LOG IOLog +#include -#include "IOServicePrivate.h" -#include +#define LOG kprintf +//#define LOG IOLog +#define MATCH_DEBUG 0 +#define OBFUSCATE(x) ((void *)(VM_KERNEL_ADDRPERM(x))) -#include +#include "IOServicePrivate.h" +#include "IOKitKernelInternal.h" -#include +// take lockForArbitration before LOCKNOTIFY /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ @@ -81,6 +93,7 @@ OSDefineMetaClassAndAbstractStructors(IONotifier, OSObject) /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static IOPlatformExpert * gIOPlatform; +static class IOPMrootDomain * gIOPMRootDomain; const IORegistryPlane * gIOServicePlane; const IORegistryPlane * gIOPowerPlane; const OSSymbol * gIODeviceMemoryKey; @@ -94,16 +107,35 @@ const OSSymbol * gIONameMatchKey; const OSSymbol * gIONameMatchedKey; const OSSymbol * gIOPropertyMatchKey; const OSSymbol * gIOLocationMatchKey; +const OSSymbol * gIOParentMatchKey; const OSSymbol * gIOPathMatchKey; const OSSymbol * gIOMatchCategoryKey; const OSSymbol * gIODefaultMatchCategoryKey; const OSSymbol * gIOMatchedServiceCountKey; +const OSSymbol * gIOMapperIDKey; const OSSymbol * gIOUserClientClassKey; const OSSymbol * gIOKitDebugKey; const OSSymbol * gIOCommandPoolSizeKey; +const OSSymbol * gIOConsoleLockedKey; +const OSSymbol * gIOConsoleUsersKey; +const OSSymbol * gIOConsoleSessionUIDKey; +const OSSymbol * gIOConsoleSessionAuditIDKey; +const OSSymbol * gIOConsoleUsersSeedKey; +const OSSymbol * gIOConsoleSessionOnConsoleKey; +const OSSymbol * gIOConsoleSessionLoginDoneKey; +const OSSymbol * gIOConsoleSessionSecureInputPIDKey; +const OSSymbol * gIOConsoleSessionScreenLockedTimeKey; + +clock_sec_t gIOConsoleLockTime; +static bool gIOConsoleLoggedIn; +#if HIBERNATION +static uint32_t gIOScreenLockState; +#endif +static IORegistryEntry * gIOChosenEntry; + static int gIOResourceGenerationCount; const OSSymbol * gIOServiceKey; @@ -116,6 +148,13 @@ const OSSymbol * gIOTerminatedNotification; const OSSymbol * gIOGeneralInterest; const OSSymbol * gIOBusyInterest; const OSSymbol * gIOAppPowerStateInterest; +const OSSymbol * gIOPriorityPowerStateInterest; +const OSSymbol * gIOConsoleSecurityInterest; + +const OSSymbol * gAKSGetKey; +#if defined(__i386__) || defined(__x86_64__) +const OSSymbol * gIOCreateEFIDevicePathSymbol; +#endif static OSDictionary * gNotifications; static IORecursiveLock * gNotificationLock; @@ -130,6 +169,24 @@ static int gOutstandingJobs; static int gNumConfigThreads; static int gNumWaitingThreads; static IOLock * gIOServiceBusyLock; +static bool gCPUsRunning; + +static thread_t gIOTerminateThread; +static UInt32 gIOTerminateWork; +static OSArray * gIOTerminatePhase2List; +static OSArray * gIOStopList; +static OSArray * gIOStopProviderList; +static OSArray * gIOFinalizeList; + +static SInt32 gIOConsoleUsersSeed; +static OSData * gIOConsoleUsersSeedValue; + +extern const OSSymbol * gIODTPHandleKey; + +const OSSymbol * gIOPlatformFunctionHandlerSet; + +static IOLock * gIOConsoleUsersLock; +static thread_call_t gIOConsoleLockCallout; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ @@ -140,9 +197,38 @@ static IOLock * gIOServiceBusyLock; #define LOCKWRITE2READNOTIFY() #define UNLOCKNOTIFY() \ IORecursiveLockUnlock( gNotificationLock ) +#define SLEEPNOTIFY(event) \ + IORecursiveLockSleep( gNotificationLock, (void *)(event), THREAD_UNINT ) +#define SLEEPNOTIFYTO(event, deadline) \ + IORecursiveLockSleepDeadline( gNotificationLock, (void *)(event), deadline, THREAD_UNINT ) +#define WAKEUPNOTIFY(event) \ + IORecursiveLockWakeup( gNotificationLock, (void *)(event), /* wake one */ false ) + +#define randomDelay() \ + int del = read_processor_clock(); \ + del = (((int)IOThreadSelf()) ^ del ^ (del >> 10)) & 0x3ff; \ + IOSleep( del ); + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define queue_element(entry, element, type, field) do { \ + vm_address_t __ele = (vm_address_t) (entry); \ + __ele -= -4 + ((size_t)(&((type) 4)->field)); \ + (element) = (type) __ele; \ + } while(0) + +#define iterqueue(que, elt) \ + for (queue_entry_t elt = queue_first(que); \ + !queue_end(que, elt); \ + elt = queue_next(elt)) /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +struct IOInterruptAccountingReporter { + IOSimpleReporter * reporter; /* Reporter responsible for communicating the statistics */ + IOInterruptAccountingData * statistics; /* The live statistics values, if any */ +}; + struct ArbitrationLockQueueElement { queue_chain_t link; IOThread thread; @@ -157,6 +243,43 @@ static queue_head_t gArbitrationLockQueueWaiting; static queue_head_t gArbitrationLockQueueFree; static IOLock * gArbitrationLockQueueLock; +bool IOService::isInactive( void ) const + { return( 0 != (kIOServiceInactiveState & getState())); } + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#if defined(__i386__) || defined(__x86_64__) + +// Only used by the intel implementation of +// IOService::requireMaxBusStall(UInt32 ns) +// IOService::requireMaxInterruptDelay(uint32_t ns) +struct CpuDelayEntry +{ + IOService * fService; + UInt32 fMaxDelay; + UInt32 fDelayType; +}; + +enum { + kCpuDelayBusStall, kCpuDelayInterrupt, + kCpuNumDelayTypes +}; + +static OSData *sCpuDelayData = OSData::withCapacity(8 * sizeof(CpuDelayEntry)); +static IORecursiveLock *sCpuDelayLock = IORecursiveLockAlloc(); +static OSArray *sCpuLatencyHandlers[kCpuNumDelayTypes]; +const OSSymbol *sCPULatencyFunctionName[kCpuNumDelayTypes]; +static OSNumber * sCPULatencyHolder[kCpuNumDelayTypes]; +static char sCPULatencyHolderName[kCpuNumDelayTypes][128]; +static OSNumber * sCPULatencySet[kCpuNumDelayTypes]; + +static void +requireMaxCpuDelay(IOService * service, UInt32 ns, UInt32 delayType); +static IOReturn +setLatencyHandler(UInt32 delayType, IOService * target, bool enable); + +#endif /* defined(__i386__) || defined(__x86_64__) */ + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void IOService::initialize( void ) @@ -171,7 +294,8 @@ void IOService::initialize( void ) gIONameMatchedKey = OSSymbol::withCStringNoCopy( kIONameMatchedKey ); gIOPropertyMatchKey = OSSymbol::withCStringNoCopy( kIOPropertyMatchKey ); gIOPathMatchKey = OSSymbol::withCStringNoCopy( kIOPathMatchKey ); - gIOLocationMatchKey = OSSymbol::withCStringNoCopy( kIOLocationMatchKey ); + gIOLocationMatchKey = OSSymbol::withCStringNoCopy( kIOLocationMatchKey ); + gIOParentMatchKey = OSSymbol::withCStringNoCopy( kIOParentMatchKey ); gIOMatchCategoryKey = OSSymbol::withCStringNoCopy( kIOMatchCategoryKey ); gIODefaultMatchCategoryKey = OSSymbol::withCStringNoCopy( @@ -190,6 +314,8 @@ void IOService::initialize( void ) gIOInterruptSpecifiersKey = OSSymbol::withCStringNoCopy("IOInterruptSpecifiers"); + gIOMapperIDKey = OSSymbol::withCStringNoCopy(kIOMapperIDKey); + gIOKitDebugKey = OSSymbol::withCStringNoCopy( kIOKitDebugKey ); gIOCommandPoolSizeKey = OSSymbol::withCStringNoCopy( kIOCommandPoolSizeKey ); @@ -197,6 +323,8 @@ void IOService::initialize( void ) gIOGeneralInterest = OSSymbol::withCStringNoCopy( kIOGeneralInterest ); gIOBusyInterest = OSSymbol::withCStringNoCopy( kIOBusyInterest ); gIOAppPowerStateInterest = OSSymbol::withCStringNoCopy( kIOAppPowerStateInterest ); + gIOPriorityPowerStateInterest = OSSymbol::withCStringNoCopy( kIOPriorityPowerStateInterest ); + gIOConsoleSecurityInterest = OSSymbol::withCStringNoCopy( kIOConsoleSecurityInterest ); gNotifications = OSDictionary::withCapacity( 1 ); gIOPublishNotification = OSSymbol::withCStringNoCopy( @@ -211,24 +339,62 @@ void IOService::initialize( void ) kIOTerminatedNotification ); gIOServiceKey = OSSymbol::withCStringNoCopy( kIOServiceClass); + gIOConsoleLockedKey = OSSymbol::withCStringNoCopy( kIOConsoleLockedKey); + gIOConsoleUsersKey = OSSymbol::withCStringNoCopy( kIOConsoleUsersKey); + gIOConsoleSessionUIDKey = OSSymbol::withCStringNoCopy( kIOConsoleSessionUIDKey); + gIOConsoleSessionAuditIDKey = OSSymbol::withCStringNoCopy( kIOConsoleSessionAuditIDKey); + + gIOConsoleUsersSeedKey = OSSymbol::withCStringNoCopy(kIOConsoleUsersSeedKey); + gIOConsoleSessionOnConsoleKey = OSSymbol::withCStringNoCopy(kIOConsoleSessionOnConsoleKey); + gIOConsoleSessionLoginDoneKey = OSSymbol::withCStringNoCopy(kIOConsoleSessionLoginDoneKey); + gIOConsoleSessionSecureInputPIDKey = OSSymbol::withCStringNoCopy(kIOConsoleSessionSecureInputPIDKey); + gIOConsoleSessionScreenLockedTimeKey = OSSymbol::withCStringNoCopy(kIOConsoleSessionScreenLockedTimeKey); + + gIOConsoleUsersSeedValue = OSData::withBytesNoCopy(&gIOConsoleUsersSeed, sizeof(gIOConsoleUsersSeed)); + + gIOPlatformFunctionHandlerSet = OSSymbol::withCStringNoCopy(kIOPlatformFunctionHandlerSet); +#if defined(__i386__) || defined(__x86_64__) + sCPULatencyFunctionName[kCpuDelayBusStall] = OSSymbol::withCStringNoCopy(kIOPlatformFunctionHandlerMaxBusDelay); + sCPULatencyFunctionName[kCpuDelayInterrupt] = OSSymbol::withCStringNoCopy(kIOPlatformFunctionHandlerMaxInterruptDelay); + uint32_t idx; + for (idx = 0; idx < kCpuNumDelayTypes; idx++) + { + sCPULatencySet[idx] = OSNumber::withNumber(-1U, 32); + sCPULatencyHolder[idx] = OSNumber::withNumber(0ULL, 64); + assert(sCPULatencySet[idx] && sCPULatencyHolder[idx]); + } + gIOCreateEFIDevicePathSymbol = OSSymbol::withCString("CreateEFIDevicePath"); +#endif gNotificationLock = IORecursiveLockAlloc(); + gAKSGetKey = OSSymbol::withCStringNoCopy(AKS_PLATFORM_FUNCTION_GETKEY); + assert( gIOServicePlane && gIODeviceMemoryKey && gIOInterruptControllersKey && gIOInterruptSpecifiersKey && gIOResourcesKey && gNotifications && gNotificationLock && gIOProviderClassKey && gIONameMatchKey && gIONameMatchedKey && gIOMatchCategoryKey && gIODefaultMatchCategoryKey && gIOPublishNotification && gIOMatchedNotification - && gIOTerminatedNotification && gIOServiceKey ); + && gIOTerminatedNotification && gIOServiceKey + && gIOConsoleUsersKey && gIOConsoleSessionUIDKey + && gIOConsoleSessionOnConsoleKey && gIOConsoleSessionSecureInputPIDKey + && gIOConsoleUsersSeedKey && gIOConsoleUsersSeedValue); gJobsLock = IOLockAlloc(); gJobs = OSOrderedSet::withCapacity( 10 ); gIOServiceBusyLock = IOLockAlloc(); + gIOConsoleUsersLock = IOLockAlloc(); + err = semaphore_create(kernel_task, &gJobsSemaphore, SYNC_POLICY_FIFO, 0); - assert( gIOServiceBusyLock && gJobs && gJobsLock && (err == KERN_SUCCESS) ); + gIOConsoleLockCallout = thread_call_allocate(&IOService::consoleLockTimer, NULL); + + IORegistryEntry::getRegistryRoot()->setProperty(gIOConsoleLockedKey, kOSBooleanTrue); + + assert( gIOServiceBusyLock && gJobs && gJobsLock && gIOConsoleUsersLock + && gIOConsoleLockCallout && (err == KERN_SUCCESS) ); gIOResources = IOResources::resources(); assert( gIOResources ); @@ -240,15 +406,26 @@ void IOService::initialize( void ) assert( gArbitrationLockQueueLock ); -#ifdef LESS_THREAD_CREATE - for( int i = 0; i < kMaxConfigThreads; i++) - _IOConfigThread::configThread(); -#endif + gIOTerminatePhase2List = OSArray::withCapacity( 2 ); + gIOStopList = OSArray::withCapacity( 16 ); + gIOStopProviderList = OSArray::withCapacity( 16 ); + gIOFinalizeList = OSArray::withCapacity( 16 ); + assert( gIOTerminatePhase2List && gIOStopList && gIOStopProviderList && gIOFinalizeList ); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#if defined(__i386__) || defined(__x86_64__) +extern "C" { + +const char *getCpuDelayBusStallHolderName(void) { + return sCPULatencyHolderName[kCpuDelayBusStall]; +} + +} +#endif + #if IOMATCHDEBUG static UInt64 getDebugFlags( OSDictionary * props ) { @@ -272,8 +449,8 @@ static UInt64 getDebugFlags( OSDictionary * props ) // The default score is from the property table, & may be altered // during probe to change the start order. -IOService * IOService::probe( IOService * /* provider */, - SInt32 * /* score */) +IOService * IOService::probe( IOService * provider, + SInt32 * score ) { return( this ); } @@ -283,8 +460,98 @@ bool IOService::start( IOService * provider ) return( true ); } -void IOService::stop( IOService * /* provider */ ) +void IOService::stop( IOService * provider ) +{ +} + +bool IOService::init( OSDictionary * dictionary ) +{ + bool ret; + + ret = super::init(dictionary); + if (!ret) return (false); + if (reserved) return (true); + + reserved = IONew(ExpansionData, 1); + if (!reserved) return (false); + bzero(reserved, sizeof(*reserved)); + + /* + * TODO: Improve on this. Previous efforts to more lazily allocate this + * lock based on the presence of specifiers ran into issues as some + * platforms set up the specifiers after IOService initialization. + * + * We may be able to get away with a global lock, as this should only be + * contended by IOReporting clients and driver start/stop (unless a + * driver wants to remove/add handlers in the course of normal operation, + * which should be unlikely). + */ + reserved->interruptStatisticsLock = IOLockAlloc(); + if (!reserved->interruptStatisticsLock) return (false); + + return (true); +} + +bool IOService::init( IORegistryEntry * from, + const IORegistryPlane * inPlane ) +{ + bool ret; + + ret = super::init(from, inPlane); + if (!ret) return (false); + if (reserved) return (true); + + reserved = IONew(ExpansionData, 1); + if (!reserved) return (false); + bzero(reserved, sizeof(*reserved)); + + /* + * TODO: Improve on this. Previous efforts to more lazily allocate this + * lock based on the presence of specifiers ran into issues as some + * platforms set up the specifiers after IOService initialization. + * + * We may be able to get away with a global lock, as this should only be + * contended by IOReporting clients and driver start/stop (unless a + * driver wants to remove/add handlers in the course of normal operation, + * which should be unlikely). + */ + reserved->interruptStatisticsLock = IOLockAlloc(); + if (!reserved->interruptStatisticsLock) return (false); + + return (true); +} + +void IOService::free( void ) { + int i = 0; + requireMaxBusStall(0); + requireMaxInterruptDelay(0); + if( getPropertyTable()) + unregisterAllInterest(); + PMfree(); + + if (reserved) { + if (reserved->interruptStatisticsArray) { + for (i = 0; i < reserved->interruptStatisticsArrayCount; i++) { + if (reserved->interruptStatisticsArray[i].reporter) + reserved->interruptStatisticsArray[i].reporter->release(); + } + + IODelete(reserved->interruptStatisticsArray, IOInterruptAccountingReporter, reserved->interruptStatisticsArrayCount); + } + + if (reserved->interruptStatisticsLock) + IOLockFree(reserved->interruptStatisticsLock); + IODelete(reserved, ExpansionData, 1); + } + + if (_numInterruptSources && _interruptSources) + { + IOFree(_interruptSources, _numInterruptSources * sizeof(IOInterruptSource)); + _interruptSources = 0; + } + + super::free(); } /* @@ -308,14 +575,12 @@ bool IOService::attach( IOService * provider ) provider->unlockForArbitration(); } else { - gIOServiceRoot = this; ok = attachToParent( getRegistryRoot(), gIOServicePlane); - gIOResources->attachToParent( getRegistryRoot(), - gIOServicePlane ); - publishResource("IOKit"); } + if (ok && !__provider) (void) getProvider(); + return( ok ); } @@ -326,25 +591,69 @@ IOService * IOService::getServiceRoot( void ) void IOService::detach( IOService * provider ) { + IOService * newProvider = 0; + SInt32 busy; + bool adjParent; + if( gIOKitDebug & kIOLogAttach) LOG("%s::detach(%s)\n", getName(), provider->getName()); lockForArbitration(); - if( (__state[1] & kIOServiceBusyStateMask) - && (provider == getProvider())) - provider->adjustBusy( -1 ); + uint64_t regID1 = provider->getRegistryEntryID(); + uint64_t regID2 = getRegistryEntryID(); + IOServiceTrace( + IOSERVICE_DETACH, + (uintptr_t) regID1, + (uintptr_t) (regID1 >> 32), + (uintptr_t) regID2, + (uintptr_t) (regID2 >> 32)); + + adjParent = ((busy = (__state[1] & kIOServiceBusyStateMask)) + && (provider == getProvider())); detachFromParent( provider, gIOServicePlane ); + if( busy) { + newProvider = getProvider(); + if( busy && (__state[1] & kIOServiceTermPhase3State) && (0 == newProvider)) + _adjustBusy( -busy ); + } + + if (kIOServiceInactiveState & __state[0]) { + getMetaClass()->removeInstance(this); + IORemoveServicePlatformActions(this); + } + unlockForArbitration(); + + if( newProvider) { + newProvider->lockForArbitration(); + newProvider->_adjustBusy(1); + newProvider->unlockForArbitration(); + } + + // check for last client detach from a terminated service + if( provider->lockForArbitration( true )) + { + if (kIOServiceStartState & __state[1]) + { + provider->scheduleTerminatePhase2(); + } + if( adjParent) provider->_adjustBusy( -1 ); + if( (provider->__state[1] & kIOServiceTermPhase3State) + && (0 == provider->getClient())) { + provider->scheduleFinalize(false); + } + provider->unlockForArbitration(); + } } /* * Register instance - publish it for matching */ -void IOService::registerService( IOOptionBits options = 0 ) +void IOService::registerService( IOOptionBits options ) { char * pathBuf; const char * path; @@ -367,6 +676,8 @@ void IOService::registerService( IOOptionBits options = 0 ) if( gIOPlatform && (!gIOPlatform->platformAdjustService(this))) return; + IOInstallServicePlatformActions(this); + if( (this != gIOResources) && (kIOLogRegister & gIOKitDebug)) { @@ -397,50 +708,93 @@ void IOService::registerService( IOOptionBits options = 0 ) startMatching( options ); } -void IOService::startMatching( IOOptionBits options = 0 ) +void IOService::startMatching( IOOptionBits options ) { IOService * provider; + UInt32 prevBusy = 0; bool needConfig; + bool needWake = false; bool ok; bool sync; + bool waitAgain; lockForArbitration(); sync = (options & kIOServiceSynchronous) || ((provider = getProvider()) && (provider->__state[1] & kIOServiceSynchronousState)); - if( sync) - __state[1] |= kIOServiceSynchronousState; - else - __state[1] &= ~kIOServiceSynchronousState; - needConfig = (0 == (__state[1] & kIOServiceConfigState)) - && (0 == (__state[0] & kIOServiceInactiveState)) - && (kIOServiceRegisteredState != - (__state[0] & (kIOServiceRegisteredState - | kIOServiceMatchedState))); - __state[1] |= kIOServiceConfigState; + if ( options & kIOServiceAsynchronous ) + sync = false; + + needConfig = (0 == (__state[1] & (kIOServiceNeedConfigState | kIOServiceConfigState))) + && (0 == (__state[0] & kIOServiceInactiveState)); + + __state[1] |= kIOServiceNeedConfigState; + // __state[0] &= ~kIOServiceInactiveState; // if( sync) LOG("OSKernelStackRemaining = %08x @ %s\n", // OSKernelStackRemaining(), getName()); + if( needConfig) { + needWake = (0 != (kIOServiceSyncPubState & __state[1])); + } + + if( sync) + __state[1] |= kIOServiceSynchronousState; + else + __state[1] &= ~kIOServiceSynchronousState; + + if( needConfig) prevBusy = _adjustBusy( 1 ); + unlockForArbitration(); if( needConfig) { - adjustBusy( 1 ); - if( sync) { - doServiceMatch( options ); - waitQuiet(); - } else + + if( needWake) { + IOLockLock( gIOServiceBusyLock ); + thread_wakeup( (event_t) this/*&__state[1]*/ ); + IOLockUnlock( gIOServiceBusyLock ); + + } else if( !sync || (kIOServiceAsynchronous & options)) { + ok = (0 != _IOServiceJob::startJob( this, kMatchNubJob, options )); + + } else do { + + if( (__state[1] & kIOServiceNeedConfigState)) + doServiceMatch( options ); + + lockForArbitration(); + IOLockLock( gIOServiceBusyLock ); + + waitAgain = ((prevBusy < (__state[1] & kIOServiceBusyStateMask)) + && (0 == (__state[0] & kIOServiceInactiveState))); + + if( waitAgain) + __state[1] |= kIOServiceSyncPubState | kIOServiceBusyWaiterState; + else + __state[1] &= ~kIOServiceSyncPubState; + + unlockForArbitration(); + + if( waitAgain) + assert_wait( (event_t) this/*&__state[1]*/, THREAD_UNINT); + + IOLockUnlock( gIOServiceBusyLock ); + if( waitAgain) + thread_block(THREAD_CONTINUE_NULL); + + } while( waitAgain ); } } IOReturn IOService::catalogNewDrivers( OSOrderedSet * newTables ) { OSDictionary * table; - OSIterator * iter; + OSSet * set; + OSSet * allSet = 0; IOService * service; #if IOMATCHDEBUG SInt32 count = 0; @@ -450,24 +804,37 @@ IOReturn IOService::catalogNewDrivers( OSOrderedSet * newTables ) while( (table = (OSDictionary *) newTables->getFirstObject())) { - LOCKWRITENOTIFY(); - iter = (OSIterator *) getExistingServices( table, - kIOServiceMatchedState ); - UNLOCKNOTIFY(); - if( iter) { - while( (service = (IOService *) iter->getNextObject())) { - service->startMatching(); + LOCKWRITENOTIFY(); + set = (OSSet *) copyExistingServices( table, + kIOServiceRegisteredState, + kIOServiceExistingSet); + UNLOCKNOTIFY(); + if( set) { + #if IOMATCHDEBUG - count++; + count += set->getCount(); #endif - } - iter->release(); - } + if (allSet) { + allSet->merge((const OSSet *) set); + set->release(); + } + else + allSet = set; + } + #if IOMATCHDEBUG - if( getDebugFlags( table ) & kIOLogMatch) - LOG("Matching service count = %ld\n", 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(); } newTables->release(); @@ -476,7 +843,7 @@ IOReturn IOService::catalogNewDrivers( OSOrderedSet * newTables ) } _IOServiceJob * _IOServiceJob::startJob( IOService * nub, int type, - IOOptionBits options = 0 ) + IOOptionBits options ) { _IOServiceJob * job; @@ -532,10 +899,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()) @@ -543,7 +909,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 ); @@ -658,9 +1025,23 @@ IOReturn IOService::callPlatformFunction( const OSSymbol * functionName, void *param3, void *param4 ) { IOReturn result = kIOReturnUnsupported; - IOService *provider = getProvider(); - - if (provider != 0) { + IOService *provider; + + if (gIOPlatformFunctionHandlerSet == functionName) + { +#if defined(__i386__) || defined(__x86_64__) + const OSSymbol * functionHandlerName = (const OSSymbol *) param1; + IOService * target = (IOService *) param2; + bool enable = (param3 != 0); + + if (sCPULatencyFunctionName[kCpuDelayBusStall] == functionHandlerName) + result = setLatencyHandler(kCpuDelayBusStall, target, enable); + else if (sCPULatencyFunctionName[kCpuDelayInterrupt] == param1) + result = setLatencyHandler(kCpuDelayInterrupt, target, enable); +#endif /* defined(__i386__) || defined(__x86_64__) */ + } + + if ((kIOReturnUnsupported == result) && (provider = getProvider())) { result = provider->callPlatformFunction(functionName, waitForFunction, param1, param2, param3, param4); } @@ -687,7 +1068,7 @@ IOReturn IOService::callPlatformFunction( const char * functionName, /* - * Platform expert accessors + * Accessors for global services */ IOPlatformExpert * IOService::getPlatform( void ) @@ -695,16 +1076,52 @@ IOPlatformExpert * IOService::getPlatform( void ) return( gIOPlatform); } +class IOPMrootDomain * IOService::getPMRootDomain( void ) +{ + return( gIOPMRootDomain); +} + +IOService * IOService::getResourceService( void ) +{ + return( gIOResources ); +} + void IOService::setPlatform( IOPlatformExpert * platform) { gIOPlatform = platform; + gIOResources->attachToParent( gIOServiceRoot, gIOServicePlane ); + +#if defined(__i386__) || defined(__x86_64__) + + static const char * keys[kCpuNumDelayTypes] = { + kIOPlatformMaxBusDelay, kIOPlatformMaxInterruptDelay }; + const OSObject * objs[2]; + OSArray * array; + uint32_t idx; + + for (idx = 0; idx < kCpuNumDelayTypes; idx++) + { + objs[0] = sCPULatencySet[idx]; + objs[1] = sCPULatencyHolder[idx]; + array = OSArray::withObjects(objs, 2); + if (!array) break; + platform->setProperty(keys[idx], array); + array->release(); + } +#endif /* defined(__i386__) || defined(__x86_64__) */ +} + +void IOService::setPMRootDomain( class IOPMrootDomain * rootDomain) +{ + gIOPMRootDomain = rootDomain; + publishResource("IOKit"); } /* * Stacking change */ -bool IOService::lockForArbitration( bool isSuccessRequired = true ) +bool IOService::lockForArbitration( bool isSuccessRequired ) { bool found; bool success; @@ -825,7 +1242,9 @@ bool IOService::lockForArbitration( bool isSuccessRequired = true ) link ); // wake the victim - thread_wakeup_one(victim); + IOLockWakeup( gArbitrationLockQueueLock, + victim, + /* one thread */ true ); // allow this thread to proceed (ie. wait) success = true; // (put request on wait queue) @@ -863,15 +1282,17 @@ bool IOService::lockForArbitration( bool isSuccessRequired = true ) link ); // declare that this thread will wait for a given event -restart_sleep: assert_wait( element, - element->required ? THREAD_UNINT - : THREAD_INTERRUPTIBLE ); +restart_sleep: wait_result = assert_wait( element, + element->required ? THREAD_UNINT + : THREAD_INTERRUPTIBLE ); // unlock global access IOUnlock( gArbitrationLockQueueLock ); // put thread to sleep, waiting for our event to fire... - wait_result = thread_block((void (*)(void)) 0); + if (wait_result == THREAD_WAITING) + wait_result = thread_block(THREAD_CONTINUE_NULL); + // ...and we've been woken up; we might be in one of two states: // (a) we've been aborted and our queue element is not on @@ -1055,7 +1476,9 @@ void IOService::unlockForArbitration( void ) link ); // wake the waiting thread - thread_wakeup_one(element); + IOLockWakeup( gArbitrationLockQueueLock, + element, + /* one thread */ true ); } } @@ -1085,7 +1508,7 @@ void IOService::applyToClients( IOServiceApplierFunction applier, // send a message to a client or interested party of this service IOReturn IOService::messageClient( UInt32 type, OSObject * client, - void * argument = 0, vm_size_t argSize = 0 ) + void * argument, vm_size_t argSize ) { IOReturn ret; IOService * service; @@ -1120,7 +1543,7 @@ IOReturn IOService::messageClient( UInt32 type, OSObject * client, _IOServiceNotifierInvocation *, link ); if( kIOServiceNotifyWaiter & notify->state) { notify->state &= ~kIOServiceNotifyWaiter; - thread_wakeup( (event_t) notify); + WAKEUPNOTIFY( notify ); } UNLOCKNOTIFY(); @@ -1133,32 +1556,49 @@ IOReturn IOService::messageClient( UInt32 type, OSObject * client, return( ret ); } +static void +applyToInterestNotifiers(const IORegistryEntry *target, + const OSSymbol * typeOfInterest, + OSObjectApplierFunction applier, + void * context ) +{ + OSArray * copyArray = 0; + + LOCKREADNOTIFY(); + + IOCommand *notifyList = + OSDynamicCast( IOCommand, target->getProperty( typeOfInterest )); + + if( notifyList) { + copyArray = OSArray::withCapacity(1); + + // iterate over queue, entry is set to each element in the list + iterqueue(¬ifyList->fCommandChain, entry) { + _IOServiceInterestNotifier * notify; + + queue_element(entry, notify, _IOServiceInterestNotifier *, chain); + copyArray->setObject(notify); + } + } + UNLOCKNOTIFY(); + + if( copyArray) { + unsigned int index; + OSObject * next; + + for( index = 0; (next = copyArray->getObject( index )); index++) + (*applier)(next, context); + copyArray->release(); + } +} + void IOService::applyToInterested( const OSSymbol * typeOfInterest, OSObjectApplierFunction applier, void * context ) { - OSArray * array; - unsigned int index; - OSObject * next; - OSArray * copyArray; - - applyToClients( (IOServiceApplierFunction) applier, context ); - - LOCKREADNOTIFY(); - array = OSDynamicCast( OSArray, getProperty( typeOfInterest )); - if( array) { - copyArray = OSArray::withArray( array ); - UNLOCKNOTIFY(); - if( copyArray) { - for( index = 0; - (next = array->getObject( index )); - index++) { - (*applier)(next, context); - } - copyArray->release(); - } - } else - UNLOCKNOTIFY(); + if (gIOGeneralInterest == typeOfInterest) + applyToClients( (IOServiceApplierFunction) applier, context ); + applyToInterestNotifiers(this, typeOfInterest, applier, context); } struct MessageClientsContext { @@ -1182,7 +1622,7 @@ static void messageClientsApplier( OSObject * object, void * ctx ) // send a message to all clients IOReturn IOService::messageClients( UInt32 type, - void * argument = 0, vm_size_t argSize = 0 ) + void * argument, vm_size_t argSize ) { MessageClientsContext context; @@ -1208,72 +1648,106 @@ IONotifier * IOService::registerInterest( const OSSymbol * typeOfInterest, IOServiceInterestHandler handler, void * target, void * ref ) { _IOServiceInterestNotifier * notify = 0; - OSArray * set; + IOReturn rc = kIOReturnError; + + notify = new _IOServiceInterestNotifier; + if (!notify) return NULL; + + if(notify->init()) { + rc = registerInterestForNotifer(notify, typeOfInterest, + handler, target, ref); + } + + if (rc != kIOReturnSuccess) { + notify->release(); + notify = 0; + } + + return( notify ); +} + +IOReturn IOService::registerInterestForNotifer( IONotifier *svcNotify, const OSSymbol * typeOfInterest, + IOServiceInterestHandler handler, void * target, void * ref ) +{ + IOReturn rc = kIOReturnSuccess; + _IOServiceInterestNotifier *notify = 0; if( (typeOfInterest != gIOGeneralInterest) && (typeOfInterest != gIOBusyInterest) - && (typeOfInterest != gIOAppPowerStateInterest)) - return( 0 ); + && (typeOfInterest != gIOAppPowerStateInterest) + && (typeOfInterest != gIOConsoleSecurityInterest) + && (typeOfInterest != gIOPriorityPowerStateInterest)) + return( kIOReturnBadArgument ); + + if (!svcNotify || !(notify = OSDynamicCast(_IOServiceInterestNotifier, svcNotify))) + return( kIOReturnBadArgument ); lockForArbitration(); if( 0 == (__state[0] & kIOServiceInactiveState)) { - notify = new _IOServiceInterestNotifier; - if( notify && !notify->init()) { - notify->release(); - notify = 0; - } + notify->handler = handler; + notify->target = target; + notify->ref = ref; + notify->state = kIOServiceNotifyEnable; - if( notify) { - notify->handler = handler; - notify->target = target; - notify->ref = ref; - notify->state = kIOServiceNotifyEnable; - queue_init( ¬ify->handlerInvocations ); + ////// queue - ////// queue + LOCKWRITENOTIFY(); - LOCKWRITENOTIFY(); - if( 0 == (set = (OSArray *) getProperty( typeOfInterest ))) { - set = OSArray::withCapacity( 1 ); - if( set) { - setProperty( typeOfInterest, set ); - set->release(); - } + // Get the head of the notifier linked list + IOCommand * notifyList; + OSObject * obj = copyProperty( typeOfInterest ); + if (!(notifyList = OSDynamicCast(IOCommand, obj))) { + notifyList = OSTypeAlloc(IOCommand); + if (notifyList) { + notifyList->init(); + bool ok = setProperty( typeOfInterest, notifyList); + notifyList->release(); + if (!ok) notifyList = 0; } - notify->whence = set; - if( set) - set->setObject( notify ); - UNLOCKNOTIFY(); } + if (obj) obj->release(); + + if (notifyList) { + enqueue(¬ifyList->fCommandChain, ¬ify->chain); + notify->retain(); // ref'ed while in list + } + + UNLOCKNOTIFY(); + } + else { + rc = kIOReturnNotReady; } unlockForArbitration(); - return( notify ); + return rc; } -static void cleanInterestArray( OSObject * object ) +static void cleanInterestList( OSObject * head ) { - OSArray * array; - unsigned int index; - _IOServiceInterestNotifier * next; - - if( (array = OSDynamicCast( OSArray, object))) { - LOCKWRITENOTIFY(); - for( index = 0; - (next = (_IOServiceInterestNotifier *) - array->getObject( index )); - index++) { - next->whence = 0; - } - UNLOCKNOTIFY(); + IOCommand *notifyHead = OSDynamicCast(IOCommand, head); + if (!notifyHead) + return; + + LOCKWRITENOTIFY(); + while ( queue_entry_t entry = dequeue(¬ifyHead->fCommandChain) ) { + queue_next(entry) = queue_prev(entry) = 0; + + _IOServiceInterestNotifier * notify; + + queue_element(entry, notify, _IOServiceInterestNotifier *, chain); + notify->release(); } + UNLOCKNOTIFY(); } void IOService::unregisterAllInterest( void ) { - cleanInterestArray( getProperty( gIOGeneralInterest )); - cleanInterestArray( getProperty( gIOBusyInterest )); + cleanInterestList( getProperty( gIOGeneralInterest )); + cleanInterestList( getProperty( gIOBusyInterest )); + cleanInterestList( getProperty( gIOAppPowerStateInterest )); + cleanInterestList( getProperty( gIOPriorityPowerStateInterest )); + cleanInterestList( getProperty( gIOConsoleSecurityInterest )); } /* @@ -1299,10 +1773,7 @@ void _IOServiceInterestNotifier::wait() } if( doWait) { state |= kIOServiceNotifyWaiter; - assert_wait( this, THREAD_UNINT); - UNLOCKNOTIFY(); - thread_block((void (*)(void)) 0); - LOCKWRITENOTIFY(); + SLEEPNOTIFY(this); } } while( doWait ); @@ -1318,10 +1789,10 @@ void _IOServiceInterestNotifier::remove() { LOCKWRITENOTIFY(); - if( whence) { - whence->removeObject(whence->getNextIndexOfObject( - (OSObject *) this, 0 )); - whence = 0; + if( queue_next( &chain )) { + remqueue(&chain); + queue_next( &chain) = queue_prev( &chain) = 0; + release(); } state &= ~kIOServiceNotifyEnable; @@ -1359,239 +1830,920 @@ void _IOServiceInterestNotifier::enable( bool was ) UNLOCKNOTIFY(); } +bool _IOServiceInterestNotifier::init() +{ + queue_init( &handlerInvocations ); + return (OSObject::init()); +} +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* - * Terminate + * Termination */ -// a method in case someone needs to override it -bool IOService::terminateClient( IOService * client, IOOptionBits options ) +#define tailQ(o) setObject(o) +#define headQ(o) setObject(0, o) +#define TLOG(fmt, args...) { if(kIOLogYield & gIOKitDebug) { IOLog("[%llx] ", thread_tid(current_thread())); IOLog(fmt, ## args); }} + +static void _workLoopAction( IOWorkLoop::Action action, + IOService * service, + void * p0 = 0, void * p1 = 0, + void * p2 = 0, void * p3 = 0 ) { - bool ok; + IOWorkLoop * wl; - if( client->isParent( this, gIOServicePlane, true)) - // we are the clients only provider - ok = client->terminate( options ); - else - ok = true; - - return( ok ); + if( (wl = service->getWorkLoop())) { + wl->retain(); + wl->runAction( action, service, p0, p1, p2, p3 ); + wl->release(); + } else + (*action)( service, p0, p1, p2, p3 ); } -struct TerminateClientsContext { - IOService * provider; - IOOptionBits options; -}; - -static void terminateInterestApplier( OSObject * object, void * ctx ) +bool IOService::requestTerminate( IOService * provider, IOOptionBits options ) { - TerminateClientsContext * context = (TerminateClientsContext *) ctx; + bool ok; + + // if its our only provider + ok = isParent( provider, gIOServicePlane, true); + + // -- compat + if( ok) { + provider->terminateClient( this, options | kIOServiceRecursing ); + ok = (0 != (kIOServiceInactiveState & __state[0])); + } + // -- - context->provider->messageClient( kIOMessageServiceIsTerminated, - object, (void *) context->options ); + return( ok ); } -static void terminateClientsApplier( IOService * client, void * ctx ) +bool IOService::terminatePhase1( IOOptionBits options ) { - TerminateClientsContext * context = (TerminateClientsContext *) ctx; + IOService * victim; + IOService * client; + OSIterator * iter; + OSArray * makeInactive; + OSArray * waitingInactive; + int waitResult = THREAD_AWAKENED; + bool wait; + bool ok; + bool didInactive; + bool startPhase2 = false; + + TLOG("%s[0x%qx]::terminatePhase1(%08llx)\n", getName(), getRegistryEntryID(), (long long)options); + + uint64_t regID = getRegistryEntryID(); + IOServiceTrace( + IOSERVICE_TERMINATE_PHASE1, + (uintptr_t) regID, + (uintptr_t) (regID >> 32), + (uintptr_t) this, + (uintptr_t) options); + + // -- compat + if( options & kIOServiceRecursing) { + lockForArbitration(); + if (0 == (kIOServiceInactiveState & __state[0])) + { + __state[0] |= kIOServiceInactiveState; + __state[1] |= kIOServiceRecursing | kIOServiceTermPhase1State; + } + unlockForArbitration(); + + return( true ); + } + // -- - if( gIOKitDebug & kIOLogYield) - LOG("%s::terminateClient(%s,%08lx)\n", - context->provider->getName(), - client->getName(), context->options); + makeInactive = OSArray::withCapacity( 16 ); + waitingInactive = OSArray::withCapacity( 16 ); + if(!makeInactive || !waitingInactive) return( false ); - context->provider->terminateClient( client, - ((context->options) | kIOServiceRecursing) & ~kIOServiceSynchronous ); + victim = this; + victim->retain(); + + while( victim ) + { + didInactive = victim->lockForArbitration( true ); + if( didInactive) + { + uint64_t regID1 = victim->getRegistryEntryID(); + IOServiceTrace(IOSERVICE_TERM_SET_INACTIVE, + (uintptr_t) regID1, + (uintptr_t) (regID1 >> 32), + (uintptr_t) victim->__state[1], + (uintptr_t) 0); + + enum { kRP1 = kIOServiceRecursing | kIOServiceTermPhase1State }; + didInactive = (kRP1 == (victim->__state[1] & kRP1)) + || (0 == (victim->__state[0] & kIOServiceInactiveState)); + + if (!didInactive) + { + // a multiply attached IOService can be visited twice + if (-1U == waitingInactive->getNextIndexOfObject(victim, 0)) do + { + IOLockLock(gIOServiceBusyLock); + wait = (victim->__state[1] & kIOServiceTermPhase1State); + if( wait) { + TLOG("%s[0x%qx]::waitPhase1(%s[0x%qx])\n", + getName(), getRegistryEntryID(), victim->getName(), victim->getRegistryEntryID()); + victim->__state[1] |= kIOServiceTerm1WaiterState; + victim->unlockForArbitration(); + assert_wait((event_t)&victim->__state[1], THREAD_UNINT); + } + IOLockUnlock(gIOServiceBusyLock); + if( wait) { + waitResult = thread_block(THREAD_CONTINUE_NULL); + TLOG("%s[0x%qx]::did waitPhase1(%s[0x%qx])\n", + getName(), getRegistryEntryID(), victim->getName(), victim->getRegistryEntryID()); + victim->lockForArbitration(); + } + } + while (wait && (waitResult != THREAD_TIMED_OUT)); + } + else + { + victim->__state[0] |= kIOServiceInactiveState; + victim->__state[0] &= ~(kIOServiceRegisteredState | kIOServiceMatchedState + | kIOServiceFirstPublishState | kIOServiceFirstMatchState); + victim->__state[1] &= ~kIOServiceRecursing; + victim->__state[1] |= kIOServiceTermPhase1State; + waitingInactive->headQ(victim); + if (victim == this) + { + if (kIOServiceTerminateNeedWillTerminate & options) + { + victim->__state[1] |= kIOServiceNeedWillTerminate; + } + } + victim->_adjustBusy( 1 ); + } + victim->unlockForArbitration(); + } + if( victim == this) startPhase2 = didInactive; + if (didInactive) + { + victim->deliverNotification( gIOTerminatedNotification, 0, 0xffffffff ); + IOUserClient::destroyUserReferences( victim ); + + iter = victim->getClientIterator(); + if( iter) { + while( (client = (IOService *) iter->getNextObject())) { + TLOG("%s[0x%qx]::requestTerminate(%s[0x%qx], %08llx)\n", + client->getName(), client->getRegistryEntryID(), + victim->getName(), victim->getRegistryEntryID(), (long long)options); + ok = client->requestTerminate( victim, options ); + TLOG("%s[0x%qx]::requestTerminate(%s[0x%qx], ok = %d)\n", + client->getName(), client->getRegistryEntryID(), + victim->getName(), victim->getRegistryEntryID(), ok); + + uint64_t regID1 = client->getRegistryEntryID(); + uint64_t regID2 = victim->getRegistryEntryID(); + IOServiceTrace( + (ok ? IOSERVICE_TERMINATE_REQUEST_OK + : IOSERVICE_TERMINATE_REQUEST_FAIL), + (uintptr_t) regID1, + (uintptr_t) (regID1 >> 32), + (uintptr_t) regID2, + (uintptr_t) (regID2 >> 32)); + + if( ok) + makeInactive->setObject( client ); + } + iter->release(); + } + } + victim->release(); + victim = (IOService *) makeInactive->getObject(0); + if( victim) { + victim->retain(); + makeInactive->removeObject(0); + } + } + makeInactive->release(); + + while ((victim = (IOService *) waitingInactive->getObject(0))) + { + victim->retain(); + waitingInactive->removeObject(0); + + victim->lockForArbitration(); + victim->__state[1] &= ~kIOServiceTermPhase1State; + if (kIOServiceTerm1WaiterState & victim->__state[1]) + { + victim->__state[1] &= ~kIOServiceTerm1WaiterState; + TLOG("%s[0x%qx]::wakePhase1\n", victim->getName(), victim->getRegistryEntryID()); + IOLockLock( gIOServiceBusyLock ); + thread_wakeup( (event_t) &victim->__state[1]); + IOLockUnlock( gIOServiceBusyLock ); + } + victim->unlockForArbitration(); + victim->release(); + } + waitingInactive->release(); + + if( startPhase2) + { + retain(); + lockForArbitration(); + scheduleTerminatePhase2(options); + unlockForArbitration(); + release(); + } + + return( true ); } -static void terminateRequestClose( IOService * client, void * ctx ) +void IOService::setTerminateDefer(IOService * provider, bool defer) { - TerminateClientsContext * context = (TerminateClientsContext *) ctx; - IOService * provider = context->provider; + lockForArbitration(); + if (defer) __state[1] |= kIOServiceStartState; + else __state[1] &= ~kIOServiceStartState; + unlockForArbitration(); - if( provider->isOpen( client)) { - if( gIOKitDebug & kIOLogYield) - LOG("%s::terminate request close(%s,%08lx)\n", - provider->getName(), - client->getName(), context->options); - provider->messageClient( kIOMessageServiceIsRequestingClose, - client, (void *) context->options ); + if (provider && !defer) + { + provider->lockForArbitration(); + provider->scheduleTerminatePhase2(); + provider->unlockForArbitration(); } } -bool IOService::terminate( IOOptionBits options = 0 ) +// call with lockForArbitration +void IOService::scheduleTerminatePhase2( IOOptionBits options ) { - bool ok; - bool wasClosed; - bool madeInactive; - TerminateClientsContext context; + AbsoluteTime deadline; + uint64_t regID1; + int waitResult = THREAD_AWAKENED; + bool wait, haveDeadline = false; - if( false == lockForArbitration( (options & kIOServiceRequired) )) - return false; + if (!(__state[0] & kIOServiceInactiveState)) return; + + regID1 = getRegistryEntryID(); + IOServiceTrace( + IOSERVICE_TERM_SCHED_PHASE2, + (uintptr_t) regID1, + (uintptr_t) (regID1 >> 32), + (uintptr_t) __state[1], + (uintptr_t) options); + + if (__state[1] & kIOServiceTermPhase1State) return; retain(); + unlockForArbitration(); + options |= kIOServiceRequired; + IOLockLock( gJobsLock ); - context.provider = this; - context.options = options; + if( (options & kIOServiceSynchronous) + && (current_thread() != gIOTerminateThread)) { - ok = (options & kIOServiceRequired); - wasClosed = (false == handleIsOpen( 0 )); - if( !ok) - ok = wasClosed; + do { + wait = (gIOTerminateThread != 0); + if( wait) { + // wait to become the terminate thread + IOLockSleep( gJobsLock, &gIOTerminateThread, THREAD_UNINT); + } + } while( wait ); - if( !ok) { - applyToClients( &terminateRequestClose, (void *) &context ); - wasClosed = (false == handleIsOpen( 0 )); - ok = wasClosed; - } + gIOTerminateThread = current_thread(); + gIOTerminatePhase2List->setObject( this ); + gIOTerminateWork++; - if( ok) { - madeInactive = (0 == (__state[0] & kIOServiceInactiveState)); - if( madeInactive) { - __state[0] |= kIOServiceInactiveState; - __state[0] &= ~(kIOServiceRegisteredState | kIOServiceMatchedState); - if( 0 == (options & kIOServiceRecursing)) - __state[1] |= kIOServiceTerminatedState; + do { + while( gIOTerminateWork ) + terminateWorker( options ); + wait = (0 != (__state[1] & kIOServiceBusyStateMask)); + if( wait) { + // wait for the victim to go non-busy + if( !haveDeadline) { + clock_interval_to_deadline( 15, kSecondScale, &deadline ); + haveDeadline = true; + } + waitResult = IOLockSleepDeadline( gJobsLock, &gIOTerminateWork, + deadline, THREAD_UNINT ); + if( waitResult == THREAD_TIMED_OUT) { + IOLog("%s[0x%qx]::terminate(kIOServiceSynchronous) timeout\n", getName(), getRegistryEntryID()); + } + } + } while(gIOTerminateWork || (wait && (waitResult != THREAD_TIMED_OUT))); + + gIOTerminateThread = 0; + IOLockWakeup( gJobsLock, (event_t) &gIOTerminateThread, /* one-thread */ false); + + } else { + // ! kIOServiceSynchronous + + gIOTerminatePhase2List->setObject( this ); + if( 0 == gIOTerminateWork++) { + if( !gIOTerminateThread) + kernel_thread_start(&terminateThread, (void *)(uintptr_t) options, &gIOTerminateThread); + else + IOLockWakeup(gJobsLock, (event_t) &gIOTerminateWork, /* one-thread */ false ); } - } else - madeInactive = false; + } - unlockForArbitration(); + IOLockUnlock( gJobsLock ); + lockForArbitration(); + release(); +} - if( madeInactive) { +void IOService::terminateThread( void * arg, wait_result_t waitResult ) +{ + IOLockLock( gJobsLock ); - adjustBusy( 1 ); - applyToInterested( gIOGeneralInterest, - &terminateInterestApplier, (void *) &context ); + while (gIOTerminateWork) + terminateWorker( (uintptr_t) arg ); + + thread_deallocate(gIOTerminateThread); + gIOTerminateThread = 0; + IOLockWakeup( gJobsLock, (event_t) &gIOTerminateThread, /* one-thread */ false); + + IOLockUnlock( gJobsLock ); +} - deliverNotification( gIOTerminatedNotification, 0, 0xffffffff ); +void IOService::scheduleStop( IOService * provider ) +{ + uint64_t regID1 = getRegistryEntryID(); + uint64_t regID2 = provider->getRegistryEntryID(); + + TLOG("%s[0x%qx]::scheduleStop(%s[0x%qx])\n", getName(), regID1, provider->getName(), regID2); + IOServiceTrace( + IOSERVICE_TERMINATE_SCHEDULE_STOP, + (uintptr_t) regID1, + (uintptr_t) (regID1 >> 32), + (uintptr_t) regID2, + (uintptr_t) (regID2 >> 32)); + + IOLockLock( gJobsLock ); + gIOStopList->tailQ( this ); + gIOStopProviderList->tailQ( provider ); + + if( 0 == gIOTerminateWork++) { + if( !gIOTerminateThread) + kernel_thread_start(&terminateThread, (void *) 0, &gIOTerminateThread); + else + IOLockWakeup(gJobsLock, (event_t) &gIOTerminateWork, /* one-thread */ false ); + } + + IOLockUnlock( gJobsLock ); +} + +void IOService::scheduleFinalize(bool now) +{ + uint64_t regID1 = getRegistryEntryID(); - applyToClients( &terminateClientsApplier, (void *) &context ); + TLOG("%s[0x%qx]::scheduleFinalize\n", getName(), regID1); + IOServiceTrace( + IOSERVICE_TERMINATE_SCHEDULE_FINALIZE, + (uintptr_t) regID1, + (uintptr_t) (regID1 >> 32), + 0, 0); - if( wasClosed && (0 == (options & kIOServiceRecursing))) { - if( options & kIOServiceSynchronous) - doServiceTerminate( 0 ); + if (now || IOUserClient::finalizeUserReferences(this)) + { + IOLockLock( gJobsLock ); + gIOFinalizeList->tailQ(this); + if( 0 == gIOTerminateWork++) + { + if( !gIOTerminateThread) + kernel_thread_start(&terminateThread, (void *) 0, &gIOTerminateThread); else - ok = (0 != _IOServiceJob::startJob( this, kTerminateNubJob, 0 )); + IOLockWakeup(gJobsLock, (event_t) &gIOTerminateWork, /* one-thread */ false ); } - if( options & kIOServiceSynchronous) - waitQuiet(); + IOLockUnlock( gJobsLock ); } +} - release(); +bool IOService::willTerminate( IOService * provider, IOOptionBits options ) +{ + return( true ); +} - return( ok ); +bool IOService::didTerminate( IOService * provider, IOOptionBits options, bool * defer ) +{ + if( false == *defer) { + + if( lockForArbitration( true )) { + if( false == provider->handleIsOpen( this )) + scheduleStop( provider ); + // -- compat + else { + message( kIOMessageServiceIsRequestingClose, provider, (void *)(uintptr_t) options ); + if( false == provider->handleIsOpen( this )) + scheduleStop( provider ); + } + // -- + unlockForArbitration(); + } + } + + return( true ); } -void IOService::doServiceTerminate( IOOptionBits options ) +void IOService::actionWillTerminate( IOService * victim, IOOptionBits options, + OSArray * doPhase2List, + void *unused2 __unused, + void *unused3 __unused ) { - IOService * next; - OSIterator * iter; - IOService * client; - OSArray * deathList = 0; - unsigned int index; - bool finalize; - bool ok = true; + OSIterator * iter; + IOService * client; + bool ok; + uint64_t regID1, regID2 = victim->getRegistryEntryID(); - next = this; - deathList = OSArray::withObjects( & (const OSObject *) next, 1, 1 ); - assert( deathList ); - if( !deathList) - return; + iter = victim->getClientIterator(); + if( iter) { + while( (client = (IOService *) iter->getNextObject())) { + + regID1 = client->getRegistryEntryID(); + TLOG("%s[0x%qx]::willTerminate(%s[0x%qx], %08llx)\n", + client->getName(), regID1, + victim->getName(), regID2, (long long)options); + IOServiceTrace( + IOSERVICE_TERMINATE_WILL, + (uintptr_t) regID1, + (uintptr_t) (regID1 >> 32), + (uintptr_t) regID2, + (uintptr_t) (regID2 >> 32)); + + ok = client->willTerminate( victim, options ); + doPhase2List->tailQ( client ); + } + iter->release(); + } +} - index = 0; - do { - iter = next->getClientIterator(); - assert( iter ); - if( iter) { +void IOService::actionDidTerminate( IOService * victim, IOOptionBits options, + void *unused1 __unused, void *unused2 __unused, + void *unused3 __unused ) +{ + OSIterator * iter; + IOService * client; + bool defer; + uint64_t regID1, regID2 = victim->getRegistryEntryID(); + + victim->messageClients( kIOMessageServiceIsTerminated, (void *)(uintptr_t) options ); + + iter = victim->getClientIterator(); + if( iter) { + while( (client = (IOService *) iter->getNextObject())) { + + regID1 = client->getRegistryEntryID(); + TLOG("%s[0x%qx]::didTerminate(%s[0x%qx], %08llx)\n", + client->getName(), regID1, + victim->getName(), regID2, (long long)options); + defer = false; + client->didTerminate( victim, options, &defer ); + + IOServiceTrace( + (defer ? IOSERVICE_TERMINATE_DID_DEFER + : IOSERVICE_TERMINATE_DID), + (uintptr_t) regID1, + (uintptr_t) (regID1 >> 32), + (uintptr_t) regID2, + (uintptr_t) (regID2 >> 32)); + + TLOG("%s[0x%qx]::didTerminate(%s[0x%qx], defer %d)\n", + client->getName(), regID1, + victim->getName(), regID2, defer); + } + iter->release(); + } +} - while( (client = (IOService *) iter->getNextObject())) { - if( gIOKitDebug & kIOLogYield) - LOG("%s::actionClients(%s)\n", - next->getName(), client->getName()); +void IOService::actionWillStop( IOService * victim, IOOptionBits options, + void *unused1 __unused, void *unused2 __unused, + void *unused3 __unused ) +{ + OSIterator * iter; + IOService * provider; + bool ok; + uint64_t regID1, regID2 = victim->getRegistryEntryID(); + + iter = victim->getProviderIterator(); + if( iter) { + while( (provider = (IOService *) iter->getNextObject())) { + + regID1 = provider->getRegistryEntryID(); + TLOG("%s[0x%qx]::willTerminate(%s[0x%qx], %08llx)\n", + victim->getName(), regID2, + provider->getName(), regID1, (long long)options); + IOServiceTrace( + IOSERVICE_TERMINATE_WILL, + (uintptr_t) regID2, + (uintptr_t) (regID2 >> 32), + (uintptr_t) regID1, + (uintptr_t) (regID1 >> 32)); + + ok = victim->willTerminate( provider, options ); + } + iter->release(); + } +} - client->stop( next ); +void IOService::actionDidStop( IOService * victim, IOOptionBits options, + void *unused1 __unused, void *unused2 __unused, + void *unused3 __unused ) +{ + OSIterator * iter; + IOService * provider; + bool defer = false; + uint64_t regID1, regID2 = victim->getRegistryEntryID(); - if( next->isOpen( client )) - next->close( client ); + iter = victim->getProviderIterator(); + if( iter) { + while( (provider = (IOService *) iter->getNextObject())) { + + regID1 = provider->getRegistryEntryID(); + TLOG("%s[0x%qx]::didTerminate(%s[0x%qx], %08llx)\n", + victim->getName(), regID2, + provider->getName(), regID1, (long long)options); + victim->didTerminate( provider, options, &defer ); + + IOServiceTrace( + (defer ? IOSERVICE_TERMINATE_DID_DEFER + : IOSERVICE_TERMINATE_DID), + (uintptr_t) regID2, + (uintptr_t) (regID2 >> 32), + (uintptr_t) regID1, + (uintptr_t) (regID1 >> 32)); + + TLOG("%s[0x%qx]::didTerminate(%s[0x%qx], defer %d)\n", + victim->getName(), regID2, + provider->getName(), regID1, defer); + } + iter->release(); + } +} + + +void IOService::actionFinalize( IOService * victim, IOOptionBits options, + void *unused1 __unused, void *unused2 __unused, + void *unused3 __unused ) +{ + uint64_t regID1 = victim->getRegistryEntryID(); + TLOG("%s[0x%qx]::finalize(%08llx)\n", victim->getName(), regID1, (long long)options); + IOServiceTrace( + IOSERVICE_TERMINATE_FINALIZE, + (uintptr_t) regID1, + (uintptr_t) (regID1 >> 32), + 0, 0); + + victim->finalize( options ); +} + +void IOService::actionStop( IOService * provider, IOService * client, + void *unused1 __unused, void *unused2 __unused, + void *unused3 __unused ) +{ + uint64_t regID1 = provider->getRegistryEntryID(); + uint64_t regID2 = client->getRegistryEntryID(); + + TLOG("%s[0x%qx]::stop(%s[0x%qx])\n", client->getName(), regID2, provider->getName(), regID1); + IOServiceTrace( + IOSERVICE_TERMINATE_STOP, + (uintptr_t) regID1, + (uintptr_t) (regID1 >> 32), + (uintptr_t) regID2, + (uintptr_t) (regID2 >> 32)); + + client->stop( provider ); + if( provider->isOpen( client )) + provider->close( client ); + + TLOG("%s[0x%qx]::detach(%s[0x%qx])\n", client->getName(), regID2, provider->getName(), regID1); + client->detach( provider ); +} + +void IOService::terminateWorker( IOOptionBits options ) +{ + OSArray * doPhase2List; + OSArray * didPhase2List; + OSSet * freeList; + OSIterator * iter; + UInt32 workDone; + IOService * victim; + IOService * client; + IOService * provider; + unsigned int idx; + bool moreToDo; + bool doPhase2; + bool doPhase3; + + options |= kIOServiceRequired; + + doPhase2List = OSArray::withCapacity( 16 ); + didPhase2List = OSArray::withCapacity( 16 ); + freeList = OSSet::withCapacity( 16 ); + if( (0 == doPhase2List) || (0 == didPhase2List) || (0 == freeList)) + return; + + do { + workDone = gIOTerminateWork; + + while( (victim = (IOService *) gIOTerminatePhase2List->getObject(0) )) { + + victim->retain(); + gIOTerminatePhase2List->removeObject(0); + IOLockUnlock( gJobsLock ); + + uint64_t regID1 = victim->getRegistryEntryID(); + IOServiceTrace( + IOSERVICE_TERM_START_PHASE2, + (uintptr_t) regID1, + (uintptr_t) (regID1 >> 32), + (uintptr_t) 0, + (uintptr_t) 0); + + while( victim ) { + + doPhase2 = victim->lockForArbitration( true ); + if( doPhase2) { + doPhase2 = (0 != (kIOServiceInactiveState & victim->__state[0])); + if( doPhase2) { + + uint64_t regID1 = victim->getRegistryEntryID(); + IOServiceTrace( + IOSERVICE_TERM_TRY_PHASE2, + (uintptr_t) regID1, + (uintptr_t) (regID1 >> 32), + (uintptr_t) victim->__state[1], + (uintptr_t) 0); + + doPhase2 = (0 == (victim->__state[1] & kIOServiceTermPhase2State)) + && (0 == (victim->__state[1] & 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) { - client->detach( next ); + if (kIOServiceNeedWillTerminate & victim->__state[1]) { + _workLoopAction( (IOWorkLoop::Action) &actionWillStop, + victim, (void *)(uintptr_t) options, NULL ); + } - client->lockForArbitration(); - if( (client->__state[0] & kIOServiceInactiveState) - && (0 == (client->__state[1] & kIOServiceTerminatedState)) - && (0 == client->getProvider()) ) { - client->__state[1] |= kIOServiceTerminatedState; - finalize = (false == client->handleIsOpen( 0 )); - } else - finalize = false; - client->unlockForArbitration(); + if( 0 == victim->getClient()) { - if( finalize) - deathList->setObject( client ); + // no clients - will go to finalize + victim->scheduleFinalize(false); + + } else { + _workLoopAction( (IOWorkLoop::Action) &actionWillTerminate, + victim, (void *)(uintptr_t) options, (void *)(uintptr_t) doPhase2List ); + } + didPhase2List->headQ( victim ); + } + victim->release(); + victim = (IOService *) doPhase2List->getObject(0); + if( victim) { + victim->retain(); + doPhase2List->removeObject(0); + } } - iter->release(); + + while( (victim = (IOService *) didPhase2List->getObject(0)) ) { + + if( victim->lockForArbitration( true )) { + victim->__state[1] |= kIOServiceTermPhase3State; + 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 ); + } + didPhase2List->removeObject(0); + } + IOLockLock( gJobsLock ); } - } while( (next = (IOService *) deathList->getObject( ++index )) ); + // phase 3 + do { + doPhase3 = false; + // finalize leaves + while( (victim = (IOService *) gIOFinalizeList->getObject(0))) { + + IOLockUnlock( gJobsLock ); + _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 ); - while( index--) { + 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 + if( (kIOServiceInactiveState & client->__state[0]) && client->getClient()) { + TLOG("%s[0x%qx]::defer stop(%s[0x%qx])\n", + client->getName(), regID2, + client->getClient()->getName(), client->getClient()->getRegistryEntryID()); + IOServiceTrace( + IOSERVICE_TERMINATE_STOP_DEFER, + (uintptr_t) regID1, + (uintptr_t) (regID1 >> 32), + (uintptr_t) regID2, + (uintptr_t) (regID2 >> 32)); + + idx++; + continue; + } + + IOLockUnlock( gJobsLock ); + _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; + } - next = (IOService *) deathList->getObject( index ); - assert( next ); - next->retain(); - deathList->removeObject( index ); + } while( doPhase3 ); - IOUserClient::destroyUserReferences( next ); + gIOTerminateWork -= workDone; + moreToDo = (gIOTerminateWork != 0); - next->unregisterAllInterest(); + if( !moreToDo) { + TLOG("iokit terminate done, %d stops remain\n", gIOStopList->getCount()); + IOServiceTrace( + IOSERVICE_TERMINATE_DONE, + (uintptr_t) gIOStopList->getCount(), 0, 0, 0); + } - ok = next->finalize( options ); - assert( ok ); + } while( moreToDo ); - if( gIOKitDebug & kIOLogYield) - LOG("%s __state = %08lx:%08lx\n", next->getName(), - next->__state[0], next->__state[1]); - next->adjustBusy( -1 ); + IOLockUnlock( gJobsLock ); - next->release(); - } + freeList->release(); + doPhase2List->release(); + didPhase2List->release(); - deathList->release(); + IOLockLock( gJobsLock ); } bool IOService::finalize( IOOptionBits options ) { - OSIterator * iter; - IOService * provider; - + OSIterator * iter; + IOService * provider; + uint64_t regID1, regID2 = getRegistryEntryID(); + iter = getProviderIterator(); assert( iter ); if( iter) { - while( (provider = (IOService *) iter->getNextObject())) { - /* we come down here on programmatic terminate */ - stop( provider ); - if( provider->isOpen( this )) - provider->close( this ); - detach( provider ); - } - iter->release(); + 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 = 0, - void * arg = 0 ) + IOOptionBits options, + void * arg ) { - bool ok; + 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; @@ -1606,46 +2758,39 @@ bool IOService::open( IOService * forClient, } void IOService::close( IOService * forClient, - IOOptionBits options = 0 ) + IOOptionBits options ) { - bool ok; bool wasClosed; bool last = false; - OSIterator * iter; - IOService * client; lockForArbitration(); wasClosed = handleIsOpen( forClient ); if( wasClosed) { handleClose( forClient, options ); - - last = ( (__state[1] & kIOServiceTerminatedState) - && (false == handleIsOpen( 0 )) ); + last = (__state[1] & kIOServiceTermPhase3State); } unlockForArbitration(); - if( last) { - ok = (0 != _IOServiceJob::startJob( this, kTerminateNubJob, 0 )); - assert( ok ); + if( last) + forClient->scheduleStop( this ); - } else if( wasClosed) { + else if( wasClosed) { - iter = getClientIterator(); - assert( iter ); + ServiceOpenMessageContext context; + + context.service = this; + context.type = kIOMessageServiceWasClosed; + context.excludeClient = forClient; + context.options = options; - if( iter) { - while( (client = (IOService *) iter->getNextObject())) { - if( client != forClient) - messageClient( kIOMessageServiceWasClosed, client, 0 ); - } - iter->release(); - } + applyToInterested( gIOGeneralInterest, + &serviceOpenMessageApplier, &context ); } } -bool IOService::isOpen( const IOService * forClient = 0 ) const +bool IOService::isOpen( const IOService * forClient ) const { IOService * self = (IOService *) this; bool ok; @@ -1671,11 +2816,12 @@ bool IOService::handleOpen( IOService * forClient, else if( options & kIOServiceSeize ) { ok = (kIOReturnSuccess == messageClient( kIOMessageServiceIsRequestingClose, - __owner, (void *) options )); - if( ok && (0 == __owner )) + __owner, (void *)(uintptr_t) options )); + if( ok && (0 == __owner )) __owner = forClient; + else + ok = false; } - return( ok ); } @@ -1724,13 +2870,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; @@ -1761,7 +2907,7 @@ SInt32 IOServiceOrdering( const OSMetaClassBase * inObj1, const OSMetaClassBase return ( val1 - val2 ); } -IOService * IOService::getClientWithCategory( const OSSymbol * category ) +IOService * IOService::copyClientWithCategory( const OSSymbol * category ) { IOService * service = 0; OSIterator * iter; @@ -1775,13 +2921,25 @@ IOService * IOService::getClientWithCategory( const OSSymbol * category ) 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::invokeNotifer( _IOServiceNotifier * notify ) { _IOServiceNotifierInvocation invocation; @@ -1801,14 +2959,14 @@ bool IOService::invokeNotifer( _IOServiceNotifier * notify ) if( willNotify) { - ret = (*notify->handler)( notify->target, notify->ref, this ); + ret = (*notify->handler)(notify->target, notify->ref, this, notify); LOCKWRITENOTIFY(); queue_remove( ¬ify->handlerInvocations, &invocation, _IOServiceNotifierInvocation *, link ); if( kIOServiceNotifyWaiter & notify->state) { notify->state &= ~kIOServiceNotifyWaiter; - thread_wakeup( (event_t) notify); + WAKEUPNOTIFY( notify ); } UNLOCKNOTIFY(); } @@ -1842,6 +3000,8 @@ void IOService::probeCandidates( OSOrderedSet * matches ) #if IOMATCHDEBUG SInt64 debugFlags; #endif + IOService * client = NULL; + assert( matches ); while( !needReloc && (nextMatch = matches->getFirstObject())) { @@ -1875,15 +3035,19 @@ void IOService::probeCandidates( OSOrderedSet * matches ) match->getObject( gIOMatchCategoryKey )); if( 0 == category) category = gIODefaultMatchCategoryKey; - - if( getClientWithCategory( category )) { + + if( (client = copyClientWithCategory(category)) ) { #if IOMATCHDEBUG - if( debugFlags & kIOLogMatch) + if( (debugFlags & kIOLogMatch) && (this != gIOResources)) LOG("%s: match category %s exists\n", getName(), category->getCStringNoCopy()); #endif nextMatch->release(); nextMatch = 0; + + client->release(); + client = NULL; + continue; } @@ -1894,7 +3058,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. @@ -1947,6 +3111,8 @@ void IOService::probeCandidates( OSOrderedSet * matches ) if( !symbol) continue; + //IOLog("%s alloc (symbol %p props %p)\n", symbol->getCStringNoCopy(), OBFUSCATE(symbol), OBFUSCATE(props)); + // alloc the driver instance inst = (IOService *) OSMetaClass::allocClassWithName( symbol); @@ -1973,7 +3139,6 @@ void IOService::probeCandidates( OSOrderedSet * matches ) if( 0 == category) category = gIODefaultMatchCategoryKey; inst->setProperty( gIOMatchCategoryKey, (OSObject *) category ); - // attach driver instance if( !(inst->attach( this ))) continue; @@ -2074,6 +3239,43 @@ void IOService::probeCandidates( OSOrderedSet * matches ) iter->release(); } + + // 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(); } @@ -2089,25 +3291,44 @@ bool IOService::startCandidate( IOService * service ) ok = service->attach( this ); - if( ok) { - // stall for any nub resources - checkResources(); - // stall for any driver resources - service->checkResources(); + if( ok) + { + if (this != gIOResources) + { + // stall for any nub resources + checkResources(); + // stall for any driver resources + service->checkResources(); + } + + AbsoluteTime startTime; + AbsoluteTime endTime; + UInt64 nano; + + if (kIOLogStart & gIOKitDebug) + clock_get_uptime(&startTime); - ok = service->start( this ); + 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( !ok) service->detach( this ); } return( ok ); } -IOService * IOService::resources( void ) -{ - return( gIOResources ); -} - -void IOService::publishResource( const char * key, OSObject * value = 0 ) +void IOService::publishResource( const char * key, OSObject * value ) { const OSSymbol * sym; @@ -2117,35 +3338,38 @@ void IOService::publishResource( const char * key, OSObject * value = 0 ) } } -void IOService::publishResource( const OSSymbol * key, OSObject * value = 0 ) +void IOService::publishResource( const OSSymbol * key, OSObject * value ) { if( 0 == value) value = (OSObject *) gIOServiceKey; gIOResources->setProperty( key, value); + if( IORecursiveLockHaveLock( gNotificationLock)) + return; + gIOResourceGenerationCount++; gIOResources->registerService(); } bool IOService::addNeededResource( const char * key ) { - OSObject * resources; + OSObject * resourcesProp; OSSet * set; OSString * newKey; bool ret; - resources = getProperty( gIOResourceMatchKey ); + resourcesProp = getProperty( gIOResourceMatchKey ); newKey = OSString::withCString( key ); - if( (0 == resources) || (0 == newKey)) + if( (0 == resourcesProp) || (0 == newKey)) return( false); - set = OSDynamicCast( OSSet, resources ); + set = OSDynamicCast( OSSet, resourcesProp ); if( !set) { set = OSSet::withCapacity( 1 ); if( set) - set->setObject( resources ); + set->setObject( resourcesProp ); } else set->retain(); @@ -2180,44 +3404,44 @@ bool IOService::checkResource( OSObject * matching ) } if( gIOKitDebug & kIOLogConfig) - LOG("config(%x): stalling %s\n", (int) IOThreadSelf(), getName()); + LOG("config(%p): stalling %s\n", OBFUSCATE(IOThreadSelf()), getName()); waitForService( table ); if( gIOKitDebug & kIOLogConfig) - LOG("config(%x): waking\n", (int) IOThreadSelf() ); + LOG("config(%p): waking\n", OBFUSCATE(IOThreadSelf()) ); return( true ); } bool IOService::checkResources( void ) { - OSObject * resources; + OSObject * resourcesProp; OSSet * set; OSIterator * iter; bool ok; - resources = getProperty( gIOResourceMatchKey ); - if( 0 == resources) + resourcesProp = getProperty( gIOResourceMatchKey ); + if( 0 == resourcesProp) return( true ); - if( (set = OSDynamicCast( OSSet, resources ))) { + if( (set = OSDynamicCast( OSSet, resourcesProp ))) { iter = OSCollectionIterator::withCollection( set ); ok = (0 != iter); - while( ok && (resources = iter->getNextObject()) ) - ok = checkResource( resources ); + while( ok && (resourcesProp = iter->getNextObject()) ) + ok = checkResource( resourcesProp ); if( iter) iter->release(); } else - ok = checkResource( resources ); + ok = checkResource( resourcesProp ); return( ok ); } -_IOConfigThread * _IOConfigThread::configThread( void ) +void _IOConfigThread::configThread( void ) { _IOConfigThread * inst; @@ -2226,22 +3450,23 @@ _IOConfigThread * _IOConfigThread::configThread( void ) continue; if( !inst->init()) continue; - if( !(inst->thread = IOCreateThread - ( (IOThreadFunc) &_IOConfigThread::main, inst ))) + thread_t unused; + if (KERN_SUCCESS != kernel_thread_start(&_IOConfigThread::main, inst, &unused)) continue; - return( inst ); + return; } while( false); if( inst) inst->release(); - return( 0 ); + return; } void _IOConfigThread::free( void ) { + thread_deallocate(current_thread()); OSObject::free(); } @@ -2253,6 +3478,7 @@ void IOService::doServiceMatch( IOOptionBits options ) SInt32 catalogGeneration; bool keepGuessing = true; bool reRegistered = true; + bool didRegister; // job->nub->deliverNotification( gIOPublishNotification, // kIOServiceRegisteredState, 0xffffffff ); @@ -2264,22 +3490,26 @@ void IOService::doServiceMatch( IOOptionBits options ) if( matches) { lockForArbitration(); - if( 0 == (__state[0] & kIOServiceFirstPublishState)) + if( 0 == (__state[0] & kIOServiceFirstPublishState)) { + getMetaClass()->addInstance(this); deliverNotification( gIOFirstPublishNotification, kIOServiceFirstPublishState, 0xffffffff ); + } LOCKREADNOTIFY(); - __state[1] &= ~kIOServiceConfigState; + __state[1] &= ~kIOServiceNeedConfigState; + __state[1] |= kIOServiceConfigState; + didRegister = (0 == (kIOServiceRegisteredState & __state[0])); __state[0] |= kIOServiceRegisteredState; - if( reRegistered && (0 == (__state[0] & kIOServiceInactiveState))) { - + keepGuessing &= (0 == (__state[0] & kIOServiceInactiveState)); + if (reRegistered && keepGuessing) { iter = OSCollectionIterator::withCollection( (OSOrderedSet *) gNotifications->getObject( gIOPublishNotification ) ); if( iter) { while((notify = (_IOServiceNotifier *) iter->getNextObject())) { - if( passiveMatch( notify->matching ) + if( matchPassive(notify->matching, 0) && (kIOServiceNotifyEnable & notify->state)) matches->setObject( notify ); } @@ -2290,14 +3520,14 @@ void IOService::doServiceMatch( IOOptionBits options ) UNLOCKNOTIFY(); unlockForArbitration(); - if( matches->getCount() && (kIOReturnSuccess == getResources())) + if (keepGuessing && matches->getCount() && (kIOReturnSuccess == getResources())) probeCandidates( matches ); else matches->release(); } lockForArbitration(); - reRegistered = (0 != (__state[1] & kIOServiceConfigState)); + reRegistered = (0 != (__state[1] & kIOServiceNeedConfigState)); keepGuessing = (reRegistered || (catalogGeneration != gIOCatalogue->getGenerationCount())) @@ -2307,7 +3537,8 @@ void IOService::doServiceMatch( IOOptionBits options ) unlockForArbitration(); } - if( 0 == (__state[0] & kIOServiceInactiveState)) { + if( (0 == (__state[0] & kIOServiceInactiveState)) + && (0 == (__state[1] & kIOServiceModuleStallState)) ) { deliverNotification( gIOMatchedNotification, kIOServiceMatchedState, 0xffffffff ); if( 0 == (__state[0] & kIOServiceFirstMatchState)) @@ -2315,60 +3546,115 @@ void IOService::doServiceMatch( IOOptionBits options ) kIOServiceFirstMatchState, 0xffffffff ); } - unlockForArbitration(); + __state[1] &= ~kIOServiceConfigState; + scheduleTerminatePhase2(); - adjustBusy( -1 ); + _adjustBusy( -1 ); + unlockForArbitration(); } -void IOService::adjustBusy( SInt32 delta ) +UInt32 IOService::_adjustBusy( SInt32 delta ) { IOService * next; UInt32 count; - bool wasQuiet, nowQuiet; + UInt32 result; + bool wasQuiet, nowQuiet, needWake; - if( 0 == delta) - return; - - IOTakeLock( gIOServiceBusyLock ); next = this; + result = __state[1] & kIOServiceBusyStateMask; - do { + if( delta) do { + if( next != this) + next->lockForArbitration(); count = next->__state[1] & kIOServiceBusyStateMask; - assert( count < kIOServiceBusyMax); wasQuiet = (0 == count); - assert( (!wasQuiet) || (delta > 0)); - next->__state[1] += delta; - nowQuiet = (0 == (next->__state[1] & kIOServiceBusyStateMask)); - - if( nowQuiet) + 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) ) { - OSArray * array; - unsigned int index; - OSObject * interested; - - array = OSDynamicCast( OSArray, next->getProperty( gIOBusyInterest )); - if( array) { - LOCKREADNOTIFY(); - for( index = 0; - (interested = array->getObject( index )); - index++) { - next->messageClient(kIOMessageServiceBusyStateChange, - interested, (void *) wasQuiet /* busy now */); - } - UNLOCKNOTIFY(); - } - if( nowQuiet && (next == gIOServiceRoot)) - OSMetaClass::considerUnloads(); + 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())); - IOUnlock( gIOServiceBusyLock ); + 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 ) @@ -2377,7 +3663,14 @@ UInt32 IOService::getBusyState( void ) } IOReturn IOService::waitForState( UInt32 mask, UInt32 value, - mach_timespec_t * timeout = 0 ) + mach_timespec_t * timeout ) +{ + panic("waitForState"); + return (kIOReturnUnsupported); +} + +IOReturn IOService::waitForState( UInt32 mask, UInt32 value, + uint64_t timeout ) { bool wait; int waitResult = THREAD_AWAKENED; @@ -2385,47 +3678,103 @@ IOReturn IOService::waitForState( UInt32 mask, UInt32 value, AbsoluteTime abstime; do { - IOTakeLock( gIOServiceBusyLock ); + lockForArbitration(); + IOLockLock( gIOServiceBusyLock ); wait = (value != (__state[1] & mask)); - if( wait) - { - assert_wait( (event_t) this, THREAD_UNINT ); - if ( timeout ) - { - if ( computeDeadline ) - { + if( wait) { + __state[1] |= kIOServiceBusyWaiterState; + unlockForArbitration(); + if( timeout != UINT64_MAX ) { + if( computeDeadline ) { AbsoluteTime nsinterval; - clock_interval_to_absolutetime_interval( - timeout->tv_sec, kSecondScale, &abstime ); - clock_interval_to_absolutetime_interval( - timeout->tv_nsec, kNanosecondScale, &nsinterval ); - ADD_ABSOLUTETIME( &abstime, &nsinterval ); - clock_absolutetime_interval_to_deadline( - abstime, &abstime ); + nanoseconds_to_absolutetime(timeout, &nsinterval ); + clock_absolutetime_interval_to_deadline(nsinterval, &abstime); computeDeadline = false; } - thread_set_timer_deadline( abstime ); + assert_wait_deadline((event_t)this, THREAD_UNINT, __OSAbsoluteTime(abstime)); } - } - IOUnlock( gIOServiceBusyLock ); + else + assert_wait((event_t)this, THREAD_UNINT ); + } else + unlockForArbitration(); + IOLockUnlock( gIOServiceBusyLock ); if( wait) - { - waitResult = thread_block((void (*)(void)) 0); - if ( timeout && (waitResult != THREAD_TIMED_OUT)) - thread_cancel_timer(); - } + waitResult = thread_block(THREAD_CONTINUE_NULL); - } while( wait && ( waitResult != THREAD_TIMED_OUT ) ); + } while( wait && (waitResult != THREAD_TIMED_OUT)); - if ( waitResult == THREAD_TIMED_OUT ) + if( waitResult == THREAD_TIMED_OUT) return( kIOReturnTimeout ); else return( kIOReturnSuccess ); } -IOReturn IOService::waitQuiet( mach_timespec_t * timeout = 0 ) +IOReturn IOService::waitQuiet( uint64_t timeout ) { - return( waitForState( kIOServiceBusyStateMask, 0, timeout )); + IOReturn ret; + ret = waitForState( kIOServiceBusyStateMask, 0, timeout ); + if ((kIOReturnTimeout == ret) && (timeout >= 41000000000) && (kIOWaitQuietPanics & gIOKitDebug)) + { + IORegistryIterator * iter; + OSOrderedSet * set; + OSOrderedSet * leaves; + IOService * next; + IOService * nextParent; + char * string; + char * s; + size_t len, l; + + len = 256; + string = IONew(char, len); + set = NULL; + iter = IORegistryIterator::iterateOver(this, gIOServicePlane, kIORegistryIterateRecursively); + leaves = OSOrderedSet::withCapacity(4); + if (iter) set = iter->iterateAll(); + if (string && leaves && set) + { + while ((next = (IOService *) set->getLastObject())) + { + if (next->getBusyState()) + { + 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); + } + } + panic("busy timeout(%llds): %s", timeout / 1000000000ULL, string ? string : ""); + } + 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 @@ -2438,21 +3787,25 @@ bool IOService::serializeProperties( OSSerialize * s ) const } -void _IOConfigThread::main( _IOConfigThread * self ) +void _IOConfigThread::main(void * arg, wait_result_t result) { - _IOServiceJob * job; - IOService * nub; - bool alive = true; + _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 { -#if 0 -#define randomDelay() \ - int del = read_processor_clock(); \ - del = (((int)IOThreadSelf()) ^ del ^ (del >> 10)) & 0x3ff; \ - IOSleep( del ); - randomDelay(); -#endif +// randomDelay(); semaphore_wait( gJobsSemaphore ); @@ -2462,10 +3815,8 @@ void _IOConfigThread::main( _IOConfigThread * self ) gJobs->removeObject(job); if( job) { gOutstandingJobs--; -#ifndef LESS_THREAD_CREATE // gNumConfigThreads--; // we're out of service gNumWaitingThreads--; // we're out of service -#endif } IOUnlock( gJobsLock ); @@ -2474,8 +3825,8 @@ void _IOConfigThread::main( _IOConfigThread * self ) nub = job->nub; if( gIOKitDebug & kIOLogConfig) - LOG("config(%x): starting on %s, %d\n", - (int) IOThreadSelf(), job->nub->getName(), job->type); + LOG("config(%p): starting on %s, %d\n", + OBFUSCATE(IOThreadSelf()), job->nub->getName(), job->type); switch( job->type) { @@ -2483,13 +3834,9 @@ void _IOConfigThread::main( _IOConfigThread * self ) nub->doServiceMatch( job->options ); break; - case kTerminateNubJob: - nub->doServiceTerminate( job->options ); - break; - default: - LOG("config(%x): strange type (%d)\n", - (int) IOThreadSelf(), job->type ); + LOG("config(%p): strange type (%d)\n", + OBFUSCATE(IOThreadSelf()), job->type ); break; } @@ -2497,25 +3844,65 @@ void _IOConfigThread::main( _IOConfigThread * self ) job->release(); IOTakeLock( gJobsLock ); -#ifndef LESS_THREAD_CREATE alive = (gOutstandingJobs > gNumWaitingThreads); if( alive) gNumWaitingThreads++; // back in service // gNumConfigThreads++; - else - gNumConfigThreads--; -#endif + 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(%x): terminating\n", (int) IOThreadSelf() ); + LOG("config(%p): terminating\n", 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; @@ -2532,7 +3919,9 @@ void _IOServiceJob::pingConfig( _IOServiceJob * job ) // if( gNumConfigThreads) count++;// assume we're called from a config thread create = ( (gOutstandingJobs > count) - && (gNumConfigThreads < kMaxConfigThreads) ); + && ((gNumConfigThreads < kMaxConfigThreads) + || (job->nub == gIOResources) + || !gCPUsRunning)); if( create) { gNumConfigThreads++; gNumWaitingThreads++; @@ -2540,55 +3929,211 @@ void _IOServiceJob::pingConfig( _IOServiceJob * job ) IOUnlock( gJobsLock ); - job->release(); + job->release(); + + if( create) { + if( gIOKitDebug & kIOLogConfig) + LOG("config(%d): creating\n", gNumConfigThreads - 1); + _IOConfigThread::configThread(); + } + + 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; + OSString * str; + + if( !matching) + return( 0 ); + +#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; + 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); + } + - if( create) { - if( gIOKitDebug & kIOLogConfig) - LOG("config(%d): creating\n", gNumConfigThreads - 1); - _IOConfigThread::configThread(); + 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(); + } } - semaphore_signal( gJobsSemaphore ); -} +#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(); + } -// internal - call with gNotificationLock -OSObject * IOService::getExistingServices( OSDictionary * matching, - IOOptionBits inState, IOOptionBits options = 0 ) -{ - OSObject * current = 0; - OSIterator * iter; - IOService * service; + 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", OBFUSCATE(current), + OBFUSCATE(_current), s->text(), s1->text(), s2->text()); + s1->release(); + s2->release(); + } - if( !matching) - return( 0 ); + if (_current) _current->release(); + } - 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->passiveMatch( matching )) { - - if( options & kIONotifyOnce) { - current = service; - break; - } - if( current) - ((OSSet *)current)->setObject( service ); - else - current = OSSet::withObjects( - & (const OSObject *) service, 1, 1 ); - } - } - } while( !service && !iter->isValid()); - iter->release(); - } + s->release(); +#endif - if( current && (0 == (options & kIONotifyOnce))) { + if( current && (0 == (options & (kIONotifyOnce | kIOServiceExistingSet)))) { iter = OSCollectionIterator::withCollection( (OSSet *)current ); current->release(); current = iter; @@ -2605,20 +4150,47 @@ OSIterator * IOService::getMatchingServices( OSDictionary * matching ) // is a lock even needed? LOCKWRITENOTIFY(); - iter = (OSIterator *) getExistingServices( matching, - kIOServiceRegisteredState ); + 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, - IOServiceNotificationHandler handler, void * target, void * ref, - SInt32 priority = 0 ) + IOServiceMatchingNotificationHandler handler, void * target, void * ref, + SInt32 priority ) { _IOServiceNotifier * notify = 0; OSOrderedSet * set; @@ -2633,10 +4205,17 @@ IONotifier * IOService::setNotification( } if( notify) { - notify->matching = matching; - notify->handler = handler; + notify->handler = handler; notify->target = target; - notify->ref = ref; + 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 ); @@ -2662,7 +4241,7 @@ IONotifier * IOService::setNotification( // internal - call with gNotificationLock IONotifier * IOService::doInstallNotification( const OSSymbol * type, OSDictionary * matching, - IOServiceNotificationHandler handler, + IOServiceMatchingNotificationHandler handler, void * target, void * ref, SInt32 priority, OSIterator ** existing ) { @@ -2673,9 +4252,12 @@ IONotifier * IOService::doInstallNotification( if( !matching) return( 0 ); - if( (type == gIOPublishNotification) - || (type == gIOFirstPublishNotification)) + if( type == gIOPublishNotification) inState = kIOServiceRegisteredState; + + else if( type == gIOFirstPublishNotification) + inState = kIOServiceFirstPublishState; + else if( (type == gIOMatchedNotification) || (type == gIOFirstMatchNotification)) inState = kIOServiceMatchedState; @@ -2688,7 +4270,7 @@ IONotifier * IOService::doInstallNotification( if( inState) // get the current set - exist = (OSIterator *) getExistingServices( matching, inState ); + exist = (OSIterator *) copyExistingServices( matching, inState ); else exist = 0; @@ -2697,14 +4279,35 @@ IONotifier * IOService::doInstallNotification( 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, - IOServiceNotificationHandler handler, + IOServiceMatchingNotificationHandler handler, void * target, void * ref, SInt32 priority, OSIterator ** existing ) { - IONotifier * notify; + IONotifier * notify; LOCKWRITENOTIFY(); @@ -2719,10 +4322,31 @@ IONotifier * IOService::installNotification( IONotifier * IOService::addNotification( const OSSymbol * type, OSDictionary * matching, IOServiceNotificationHandler handler, - void * target, void * ref = 0, - SInt32 priority = 0 ) + void * target, void * refCon, + SInt32 priority ) { - OSIterator * existing; + 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; _IOServiceNotifier * notify; IOService * next; @@ -2747,76 +4371,93 @@ IONotifier * IOService::addNotification( return( notify ); } -struct SyncNotifyVars { - semaphore_port_t waitHere; - IOService * result; -}; - bool IOService::syncNotificationHandler( void * /* target */, void * ref, - IOService * newService ) + IOService * newService, + IONotifier * notifier ) { - // result may get written more than once before the - // notification is removed! - ((SyncNotifyVars *) ref)->result = newService; - semaphore_signal( ((SyncNotifyVars *) ref)->waitHere ); + LOCKWRITENOTIFY(); + if (!*((IOService **) ref)) + { + newService->retain(); + (*(IOService **) ref) = newService; + WAKEUPNOTIFY(ref); + } + UNLOCKNOTIFY(); return( false ); } -IOService * IOService::waitForService( OSDictionary * matching, - mach_timespec_t * timeout = 0 ) +IOService * IOService::waitForMatchingService( OSDictionary * matching, + uint64_t timeout) { IONotifier * notify = 0; // priority doesn't help us much since we need a thread wakeup SInt32 priority = 0; - SyncNotifyVars state; - kern_return_t err = kIOReturnBadArgument; + IOService * result; - if( !matching) + if (!matching) return( 0 ); - state.waitHere = 0; - state.result = 0; + result = NULL; LOCKWRITENOTIFY(); - - do { - - state.result = (IOService *) getExistingServices( matching, + do + { + result = (IOService *) copyExistingServices( matching, kIOServiceMatchedState, kIONotifyOnce ); - if( state.result) - continue; - - err = semaphore_create( kernel_task, &state.waitHere, - SYNC_POLICY_FIFO, 0 ); - if( KERN_SUCCESS != err) - continue; - + if (result) + break; notify = IOService::setNotification( gIOMatchedNotification, matching, &IOService::syncNotificationHandler, (void *) 0, - (void *) &state, priority ); - - } while( false ); - - UNLOCKNOTIFY(); - - if( notify) { - if( timeout) - err = semaphore_timedwait( state.waitHere, *timeout ); + &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 - err = semaphore_wait( state.waitHere ); + { + SLEEPNOTIFY(&result); + } } + while( false ); + + UNLOCKNOTIFY(); - if( notify) + if (notify) notify->remove(); // dequeues + + return( result ); +} + +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 - matching->release(); - if( state.waitHere) - semaphore_destroy( kernel_task, state.waitHere ); + timeoutNS = UINT64_MAX; - return( state.result ); + result = waitForMatchingService(matching, timeoutNS); + + matching->release(); + if (result) + result->release(); + + return (result); } void IOService::deliverNotification( const OSSymbol * type, @@ -2839,7 +4480,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); @@ -2876,18 +4517,26 @@ IOOptionBits IOService::getState( void ) const */ OSDictionary * IOService::serviceMatching( const OSString * name, - OSDictionary * table = 0 ) + 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 ); } OSDictionary * IOService::serviceMatching( const char * name, - OSDictionary * table = 0 ) + OSDictionary * table ) { const OSString * str; @@ -2901,7 +4550,7 @@ OSDictionary * IOService::serviceMatching( const char * name, } OSDictionary * IOService::nameMatching( const OSString * name, - OSDictionary * table = 0 ) + OSDictionary * table ) { if( !table) table = OSDictionary::withCapacity( 2 ); @@ -2912,7 +4561,7 @@ OSDictionary * IOService::nameMatching( const OSString * name, } OSDictionary * IOService::nameMatching( const char * name, - OSDictionary * table = 0 ) + OSDictionary * table ) { const OSString * str; @@ -2926,7 +4575,7 @@ OSDictionary * IOService::nameMatching( const char * name, } OSDictionary * IOService::resourceMatching( const OSString * str, - OSDictionary * table = 0 ) + OSDictionary * table ) { table = serviceMatching( gIOResourcesKey, table ); if( table) @@ -2936,7 +4585,7 @@ OSDictionary * IOService::resourceMatching( const OSString * str, } OSDictionary * IOService::resourceMatching( const char * name, - OSDictionary * table = 0 ) + OSDictionary * table ) { const OSSymbol * str; @@ -2950,6 +4599,47 @@ OSDictionary * IOService::resourceMatching( const char * name, return( table ); } +OSDictionary * IOService::propertyMatching( const OSSymbol * key, const OSObject * value, + OSDictionary * table ) +{ + OSDictionary * properties; + + properties = OSDictionary::withCapacity( 2 ); + if( !properties) + return( 0 ); + 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( 0 ); + + if( !table) + table = OSDictionary::withCapacity( 2 ); + if( table) + table->setObject( gIORegistryEntryIDKey, num ); + + if (num) + num->release(); + + return( table ); +} + + /* * _IOServiceNotifier */ @@ -2973,10 +4663,7 @@ void _IOServiceNotifier::wait() } if( doWait) { state |= kIOServiceNotifyWaiter; - assert_wait( this, THREAD_UNINT); - UNLOCKNOTIFY(); - thread_block((void (*)(void)) 0); - LOCKWRITENOTIFY(); + SLEEPNOTIFY(this); } } while( doWait ); @@ -3053,10 +4740,45 @@ IOService * IOResources::resources( void ) return( inst ); } +bool IOResources::init( OSDictionary * dictionary ) +{ + // Do super init first + if ( !super::init() ) + return false; + + // Allow PAL layer to publish a value + const char *property_name; + int property_value; + + pal_get_resource_property( &property_name, &property_value ); + + if( property_name ) { + OSNumber *num; + const OSSymbol * sym; + + if( (num = OSNumber::withNumber(property_value, 32)) != 0 ) { + if( (sym = OSSymbol::withCString( property_name)) != 0 ) { + this->setProperty( sym, num ); + sym->release(); + } + num->release(); + } + } + + return true; +} + +IOReturn IOResources::newUserClient(task_t owningTask, void * securityID, + UInt32 type, OSDictionary * properties, + IOUserClient ** handler) +{ + return( kIOReturnUnsupported ); +} + IOWorkLoop * IOResources::getWorkLoop() const { - // If we are the resource root then bringe over to the - // platform to get its workloop + // If we are the resource root + // then use the platform's workloop if (this == (IOResources *) gIOResources) return getPlatform()->getWorkLoop(); else @@ -3069,7 +4791,7 @@ bool IOResources::matchPropertyTable( OSDictionary * table ) OSString * str; OSSet * set; OSIterator * iter; - bool ok = false; + bool ok = true; prop = table->getObject( gIOResourceMatchKey ); str = OSDynamicCast( OSString, prop ); @@ -3090,6 +4812,158 @@ bool IOResources::matchPropertyTable( OSDictionary * table ) return( ok ); } +void IOService::consoleLockTimer(thread_call_param_t p0, thread_call_param_t p1) +{ + IOService::updateConsoleUsers(NULL, 0); +} + +void IOService::updateConsoleUsers(OSArray * consoleUsers, IOMessage systemMessage) +{ + IORegistryEntry * regEntry; + OSObject * locked = kOSBooleanFalse; + uint32_t idx; + bool publish; + OSDictionary * user; + static IOMessage sSystemPower; + + 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; + (user = OSDynamicCast(OSDictionary, consoleUsers->getObject(idx))); + idx++) + { + gIOConsoleLoggedIn |= ((kOSBooleanTrue == user->getObject(gIOConsoleSessionOnConsoleKey)) + && (kOSBooleanTrue == user->getObject(gIOConsoleSessionLoginDoneKey))); + if (!num) + { + num = OSDynamicCast(OSNumber, user->getObject(gIOConsoleSessionScreenLockedTimeKey)); + } + } + gIOConsoleLockTime = num ? num->unsigned32BitValue() : 0; + } + + if (!gIOConsoleLoggedIn + || (kIOMessageSystemWillSleep == sSystemPower) + || (kIOMessageSystemPagingOff == sSystemPower)) + { + locked = kOSBooleanTrue; + } + else if (gIOConsoleLockTime) + { + clock_sec_t now; + clock_usec_t microsecs; + + clock_get_calendar_microtime(&now, µsecs); + if (gIOConsoleLockTime > now) + { + AbsoluteTime deadline; + clock_interval_to_deadline(gIOConsoleLockTime - now, kSecondScale, &deadline); + thread_call_enter_delayed(gIOConsoleLockCallout, deadline); + } + else + { + locked = kOSBooleanTrue; + } + } + + publish = (consoleUsers || (locked != regEntry->getProperty(gIOConsoleLockedKey))); + if (publish) + { + regEntry->setProperty(gIOConsoleLockedKey, locked); + if (consoleUsers) + { + regEntry->setProperty(gIOConsoleUsersKey, consoleUsers); + } + OSIncrementAtomic( &gIOConsoleUsersSeed ); + } + +#if HIBERNATION + if (gIOChosenEntry) + { + if (locked == kOSBooleanTrue) gIOScreenLockState = kIOScreenLockLocked; + else if (gIOConsoleLockTime) gIOScreenLockState = kIOScreenLockUnlocked; + else gIOScreenLockState = kIOScreenLockNoLock; + gIOChosenEntry->setProperty(kIOScreenLockStateKey, &gIOScreenLockState, sizeof(gIOScreenLockState)); + } +#endif /* HIBERNATION */ + + IOLockUnlock(gIOConsoleUsersLock); + + if (publish) + { + publishResource( gIOConsoleUsersSeedKey, gIOConsoleUsersSeedValue ); + + MessageClientsContext context; + + context.service = getServiceRoot(); + context.type = kIOMessageConsoleSecurityChange; + context.argument = (void *) regEntry; + context.argSize = 0; + + applyToInterestNotifiers(getServiceRoot(), gIOConsoleSecurityInterest, + &messageClientsApplier, &context ); + } +} + +IOReturn IOResources::setProperties( OSObject * properties ) +{ + IOReturn err; + const OSSymbol * key; + OSDictionary * dict; + OSCollectionIterator * iter; + + err = IOUserClient::clientHasPrivilege(current_task(), kIOClientPrivilegeAdministrator); + if ( kIOReturnSuccess != err) + return( err ); + + dict = OSDynamicCast(OSDictionary, properties); + if( 0 == dict) + return( kIOReturnBadArgument); + + iter = OSCollectionIterator::withCollection( dict); + if( 0 == iter) + return( kIOReturnBadArgument); + + while( (key = OSDynamicCast(OSSymbol, iter->getNextObject()))) + { + if (gIOConsoleUsersKey == key) do + { + OSArray * consoleUsers; + consoleUsers = OSDynamicCast(OSArray, dict->getObject(key)); + if (!consoleUsers) + continue; + IOService::updateConsoleUsers(consoleUsers, 0); + } + while (false); + + publishResource( key, dict->getObject(key) ); + } + + iter->release(); + + return( kIOReturnSuccess ); +} + /* * Helpers for matching dictionaries. * Keys existing in matching are checked in properties. @@ -3168,175 +5042,315 @@ OSDictionary * IOService::addLocation( OSDictionary * table ) return( dict ); } -/* - * Go looking for a provider to match a location dict. - */ +/* + * Go looking for a provider to match a location dict. + */ + +IOService * IOService::matchLocation( IOService * /* client */ ) +{ + IOService * parent; + + parent = getProvider(); + + if( parent) + parent = parent->matchLocation( this ); + + return( parent ); +} + +bool IOService::matchInternal(OSDictionary * table, uint32_t options, uint32_t * did) +{ + OSString * matched; + OSObject * obj; + OSString * str; + IORegistryEntry * entry; + OSNumber * num; + bool match = true; + bool changesOK = (0 != (kIOServiceChangesOK & options)); + uint32_t count; + uint32_t done; + + 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; + } + + 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; + } + + 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; + } + + 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; + } + + 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; + } -IOService * IOService::matchLocation( IOService * /* client */ ) -{ - IOService * parent; + num = OSDynamicCast( OSNumber, table->getObject( gIORegistryEntryIDKey )); + if (num) { + done++; + match = (getRegistryEntryID() == num->unsigned64BitValue()); + if ((!match) || (done == count)) break; + } - parent = getProvider(); + num = OSDynamicCast( OSNumber, table->getObject( gIOMatchedServiceCountKey )); + if( num) + { + OSIterator * iter; + IOService * service = 0; + UInt32 serviceCount = 0; + + 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; + } - if( parent) - parent = parent->matchLocation( this ); +#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); - return( parent ); + if (did) *did = done; + return (match); } bool IOService::passiveMatch( OSDictionary * table, bool changesOK ) +{ + return (matchPassive(table, changesOK ? kIOServiceChangesOK : 0)); +} + +bool IOService::matchPassive(OSDictionary * table, uint32_t options) { IOService * where; - OSString * matched; - OSObject * obj; - OSString * str; - IORegistryEntry * entry; - OSNumber * num; + OSDictionary * nextTable; SInt32 score; OSNumber * newPri; bool match = true; - UInt32 done; + bool matchParent = false; + uint32_t count; + uint32_t done; assert( table ); + OSArray* aliasServiceRegIds = NULL; + IOService* foundAlternateService = NULL; + +#if MATCH_DEBUG + OSDictionary * root = table; +#endif + 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; + } - do { - done = 0; + // pass in score from property table + score = IOServiceObjectOrder( table, (void *) gIOProbeScoreKey); - str = OSDynamicCast( OSString, table->getObject( gIOProviderClassKey)); - if( str) { - done++; - match = (0 != where->metaCast( str )); - if( !match) - break; - } + // do family specific matching + match = where->matchPropertyTable( table, &score ); - obj = table->getObject( gIONameMatchKey ); - if( obj) { - done++; - match = compareNames( obj, changesOK ? &matched : 0 ); - if( !match) + if( !match) { +#if IOMATCHDEBUG + if( kIOLogMatch & getDebugFlags( table )) + LOG("%s: family specific matching fails\n", where->getName()); +#endif break; - if( changesOK && matched) { - // leave a hint as to which name matched - table->setObject( gIONameMatchedKey, matched ); - matched->release(); } - } - obj = table->getObject( gIOPropertyMatchKey ); - if( obj) { - - OSDictionary * dict; - OSDictionary * nextDict; - OSIterator * iter; - - done++; - 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; + + if (kIOServiceChangesOK & options) { + // save the score + newPri = OSNumber::withNumber( score, 32 ); + if( newPri) { + table->setObject( gIOProbeScoreKey, newPri ); + newPri->release(); } - dict->release(); - if( iter) - iter->release(); - if( !match) - break; } - } - str = OSDynamicCast( OSString, table->getObject( gIOPathMatchKey )); - if( str) { - done++; - entry = IORegistryEntry::fromPath( str->getCStringNoCopy() ); - match = (where == entry); - if( entry) - entry->release(); - if( !match) + options = 0; + matchParent = false; + + nextTable = OSDynamicCast(OSDictionary, + table->getObject( gIOParentMatchKey )); + if(nextTable) { + // look for a matching entry anywhere up to root + match = false; + matchParent = true; + table = nextTable; break; - } + } - num = OSDynamicCast( OSNumber, table->getObject( gIOMatchedServiceCountKey )); - if( num) { + table = OSDynamicCast(OSDictionary, + table->getObject( gIOLocationMatchKey )); + if (table) { + // look for a matching entry at matchLocation() + match = false; + where = where->getProvider(); + if (where && (where = where->matchLocation(where))) continue; + } + break; + } + while (true); - OSIterator * iter; - IOService * service = 0; - UInt32 serviceCount = 0; + if(match == true) { + break; + } - done++; - iter = getClientIterator(); - if( iter) { - while( (service = (IOService *) iter->getNextObject())) { - if( kIOServiceInactiveState & service->__state[0]) - continue; - if( 0 == service->getProperty( gIOMatchCategoryKey )) - continue; - ++serviceCount; + if(matchParent == true) { + // check if service has an alias to search its other "parents" if a parent match isn't found + OSNumber* alternateRegistryID = OSDynamicCast(OSNumber, where->getProperty(kIOServiceLegacyMatchingRegistryIDKey)); + if(alternateRegistryID != NULL) { + if(aliasServiceRegIds == NULL) + { + aliasServiceRegIds = OSArray::withCapacity(sizeof(alternateRegistryID)); } - iter->release(); + aliasServiceRegIds->setObject(alternateRegistryID); } - match = (serviceCount == num->unsigned32BitValue()); - if( !match) - break; - } - - if( done == table->getCount()) - // don't call family if we've done all the entries in the table - break; - - // pass in score from property table - score = IOServiceObjectOrder( table, (void *) gIOProbeScoreKey); - - // do family specific matching - match = where->matchPropertyTable( table, &score ); - - if( !match) { -#if IOMATCHDEBUG - if( kIOLogMatch & getDebugFlags( table )) - LOG("%s: family specific matching fails\n", where->getName()); -#endif - break; - } + } + else { + break; + } - if( changesOK) { - // save the score - newPri = OSNumber::withNumber( score, 32 ); - if( newPri) { - table->setObject( gIOProbeScoreKey, newPri ); - newPri->release(); + where = where->getProvider(); + if(where == NULL) { + // there were no matching parent services, check to see if there are aliased services that have a matching parent + if(aliasServiceRegIds != NULL) { + unsigned int numAliasedServices = aliasServiceRegIds->getCount(); + if(numAliasedServices != 0) { + OSNumber* alternateRegistryID = OSDynamicCast(OSNumber, aliasServiceRegIds->getObject(numAliasedServices - 1)); + if(alternateRegistryID != NULL) { + OSDictionary* alternateMatchingDict = IOService::registryEntryIDMatching(alternateRegistryID->unsigned64BitValue()); + aliasServiceRegIds->removeObject(numAliasedServices - 1); + if(alternateMatchingDict != NULL) { + OSSafeReleaseNULL(foundAlternateService); + foundAlternateService = IOService::copyMatchingService(alternateMatchingDict); + alternateMatchingDict->release(); + if(foundAlternateService != NULL) { + where = foundAlternateService; + } + } + } + } } } + } + while( where != NULL ); - if( !(match = where->compareProperty( table, kIOBSDNameKey ))) - break; - - table = OSDynamicCast( OSDictionary, - table->getObject( gIOLocationMatchKey )); - if( table) { - match = false; - where = where->getProvider(); - if( where) - where = where->matchLocation( where ); - } - - } while( table && where ); + OSSafeRelease(foundAlternateService); + OSSafeRelease(aliasServiceRegIds); - if( kIOLogMatch & gIOKitDebug) - if( where != this) - LOG("match location @ %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 ); } @@ -3350,6 +5364,9 @@ IOReturn IOService::newUserClient( task_t owningTask, void * securityID, IOUserClient *client; OSObject *temp; + if (kIOReturnSuccess == newUserClient( owningTask, securityID, type, handler )) + return kIOReturnSuccess; + // First try my own properties for a user client class name temp = getProperty(gIOUserClientClassKey); if (temp) { @@ -3367,6 +5384,7 @@ IOReturn IOService::newUserClient( task_t owningTask, void * securityID, if (!userClientClass) return kIOReturnUnsupported; + // This reference is consumed by the IOServiceOpen call temp = OSMetaClass::allocClassWithName(userClientClass); if (!temp) return kIOReturnNoMemory; @@ -3401,7 +5419,7 @@ IOReturn IOService::newUserClient( task_t owningTask, void * securityID, IOReturn IOService::newUserClient( task_t owningTask, void * securityID, UInt32 type, IOUserClient ** handler ) { - return( newUserClient( owningTask, securityID, type, 0, handler )); + return( kIOReturnUnsupported ); } IOReturn IOService::requestProbe( IOOptionBits options ) @@ -3479,6 +5497,9 @@ const char * IOService::stringFromReturn( IOReturn rtn ) */ int IOService::errnoFromReturn( IOReturn rtn ) { + if (unix_err(err_get_code(rtn)) == rtn) + return err_get_code(rtn); + switch(rtn) { // (obvious match) case kIOReturnSuccess: @@ -3500,7 +5521,7 @@ int IOService::errnoFromReturn( IOReturn rtn ) case kIOReturnBadArgument: return(EINVAL); case kIOReturnUnsupported: - return(EOPNOTSUPP); + return(ENOTSUP); case kIOReturnBusy: return(EBUSY); case kIOReturnNoPower: @@ -3531,7 +5552,6 @@ int IOService::errnoFromReturn( IOReturn rtn ) case kIOReturnExclusiveAccess: case kIOReturnLockedRead: case kIOReturnLockedWrite: - case kIOReturnNotAttached: case kIOReturnNotOpen: case kIOReturnNotReadable: return(EACCES); @@ -3544,8 +5564,9 @@ int IOService::errnoFromReturn( IOReturn rtn ) return(EBUSY); case kIOReturnBadMedia: case kIOReturnNoMedia: + case kIOReturnNotAttached: case kIOReturnUnformattedMedia: - return(EIO); // (media error) + return(ENXIO); // (media error) case kIOReturnDMAError: case kIOReturnOverrun: case kIOReturnUnderrun: @@ -3611,7 +5632,7 @@ IODeviceMemory * IOService::getDeviceMemoryWithIndex( unsigned int index ) } IOMemoryMap * IOService::mapDeviceMemoryWithIndex( unsigned int index, - IOOptionBits options = 0 ) + IOOptionBits options ) { IODeviceMemory * range; IOMemoryMap * map; @@ -3636,6 +5657,206 @@ void IOService::setDeviceMemory( OSArray * array ) setProperty( gIODeviceMemoryKey, array); } +/* + * For machines where the transfers on an I/O bus can stall because + * the CPU is in an idle mode, These APIs allow a driver to specify + * the maximum bus stall that they can handle. 0 indicates no limit. + */ +void IOService:: +setCPUSnoopDelay(UInt32 __unused ns) +{ +#if defined(__i386__) || defined(__x86_64__) + ml_set_maxsnoop(ns); +#endif /* defined(__i386__) || defined(__x86_64__) */ +} + +UInt32 IOService:: +getCPUSnoopDelay() +{ +#if defined(__i386__) || defined(__x86_64__) + return ml_get_maxsnoop(); +#else + return 0; +#endif /* defined(__i386__) || defined(__x86_64__) */ +} + +#if defined(__i386__) || defined(__x86_64__) +static void +requireMaxCpuDelay(IOService * service, UInt32 ns, UInt32 delayType) +{ + static const UInt kNoReplace = -1U; // Must be an illegal index + UInt replace = kNoReplace; + bool setCpuDelay = false; + + IORecursiveLockLock(sCpuDelayLock); + + UInt count = sCpuDelayData->getLength() / sizeof(CpuDelayEntry); + CpuDelayEntry *entries = (CpuDelayEntry *) sCpuDelayData->getBytesNoCopy(); + IOService * holder = NULL; + + if (ns) { + const CpuDelayEntry ne = {service, ns, delayType}; + holder = service; + // Set maximum delay. + for (UInt i = 0; i < count; i++) { + IOService *thisService = entries[i].fService; + bool sameType = (delayType == entries[i].fDelayType); + if ((service == thisService) && sameType) + replace = i; + else if (!thisService) { + if (kNoReplace == replace) + replace = i; + } + else if (sameType) { + const UInt32 thisMax = entries[i].fMaxDelay; + if (thisMax < ns) + { + ns = thisMax; + holder = thisService; + } + } + } + + setCpuDelay = true; + if (kNoReplace == replace) + sCpuDelayData->appendBytes(&ne, sizeof(ne)); + else + entries[replace] = ne; + } + else { + ns = -1U; // Set to max unsigned, i.e. no restriction + + for (UInt i = 0; i < count; i++) { + // Clear a maximum delay. + IOService *thisService = entries[i].fService; + if (thisService && (delayType == entries[i].fDelayType)) { + UInt32 thisMax = entries[i].fMaxDelay; + if (service == thisService) + replace = i; + else if (thisMax < ns) { + ns = thisMax; + holder = thisService; + } + } + } + + // Check if entry found + if (kNoReplace != replace) { + entries[replace].fService = 0; // Null the entry + setCpuDelay = true; + } + } + + if (setCpuDelay) + { + if (holder && debug_boot_arg) { + strlcpy(sCPULatencyHolderName[delayType], holder->getName(), sizeof(sCPULatencyHolderName[delayType])); + } + + // Must be safe to call from locked context + if (delayType == kCpuDelayBusStall) + { + ml_set_maxbusdelay(ns); + } + else if (delayType == kCpuDelayInterrupt) + { + ml_set_maxintdelay(ns); + } + sCPULatencyHolder[delayType]->setValue(holder ? holder->getRegistryEntryID() : 0); + sCPULatencySet [delayType]->setValue(ns); + + OSArray * handlers = sCpuLatencyHandlers[delayType]; + IOService * target; + if (handlers) for (unsigned int idx = 0; + (target = (IOService *) handlers->getObject(idx)); + idx++) + { + target->callPlatformFunction(sCPULatencyFunctionName[delayType], false, + (void *) (uintptr_t) ns, holder, + NULL, NULL); + } + } + + IORecursiveLockUnlock(sCpuDelayLock); +} + +static IOReturn +setLatencyHandler(UInt32 delayType, IOService * target, bool enable) +{ + IOReturn result = kIOReturnNotFound; + OSArray * array; + unsigned int idx; + + IORecursiveLockLock(sCpuDelayLock); + + do + { + if (enable && !sCpuLatencyHandlers[delayType]) + sCpuLatencyHandlers[delayType] = OSArray::withCapacity(4); + array = sCpuLatencyHandlers[delayType]; + if (!array) + break; + idx = array->getNextIndexOfObject(target, 0); + if (!enable) + { + if (-1U != idx) + { + array->removeObject(idx); + result = kIOReturnSuccess; + } + } + else + { + if (-1U != idx) { + result = kIOReturnExclusiveAccess; + break; + } + array->setObject(target); + + UInt count = sCpuDelayData->getLength() / sizeof(CpuDelayEntry); + CpuDelayEntry *entries = (CpuDelayEntry *) sCpuDelayData->getBytesNoCopy(); + UInt32 ns = -1U; // Set to max unsigned, i.e. no restriction + IOService * holder = NULL; + + for (UInt i = 0; i < count; i++) { + if (entries[i].fService + && (delayType == entries[i].fDelayType) + && (entries[i].fMaxDelay < ns)) { + ns = entries[i].fMaxDelay; + holder = entries[i].fService; + } + } + target->callPlatformFunction(sCPULatencyFunctionName[delayType], false, + (void *) (uintptr_t) ns, holder, + NULL, NULL); + result = kIOReturnSuccess; + } + } + while (false); + + IORecursiveLockUnlock(sCpuDelayLock); + + return (result); +} + +#endif /* defined(__i386__) || defined(__x86_64__) */ + +void IOService:: +requireMaxBusStall(UInt32 __unused ns) +{ +#if defined(__i386__) || defined(__x86_64__) + requireMaxCpuDelay(this, ns, kCpuDelayBusStall); +#endif +} + +void IOService:: +requireMaxInterruptDelay(uint32_t __unused ns) +{ +#if defined(__i386__) || defined(__x86_64__) + requireMaxCpuDelay(this, ns, kCpuDelayInterrupt); +#endif +} + /* * Device interrupts */ @@ -3643,20 +5864,14 @@ void IOService::setDeviceMemory( OSArray * array ) IOReturn IOService::resolveInterrupt(IOService *nub, int source) { IOInterruptController *interruptController; - OSDictionary *propTable; OSArray *array; OSData *data; OSSymbol *interruptControllerName; long numSources; IOInterruptSource *interruptSources; - // Get the property table from the nub. - propTable = nub->getPropertyTable(); - if (propTable == 0) return kIOReturnNoResources; - - // Get the parents list from the property table. - array = OSDynamicCast(OSArray, - propTable->getObject(gIOInterruptControllersKey)); + // Get the parents list from the nub. + array = OSDynamicCast(OSArray, nub->getProperty(gIOInterruptControllersKey)); if (array == 0) return kIOReturnNoResources; // Allocate space for the IOInterruptSources if needed... then return early. @@ -3678,9 +5893,8 @@ IOReturn IOService::resolveInterrupt(IOService *nub, int source) interruptController = getPlatform()->lookUpInterruptController(interruptControllerName); if (interruptController == 0) return kIOReturnNoResources; - // Get the interrupt numbers from the property table. - array = OSDynamicCast(OSArray, - propTable->getObject(gIOInterruptSpecifiersKey)); + // Get the interrupt numbers from the nub. + array = OSDynamicCast(OSArray, nub->getProperty(gIOInterruptSpecifiersKey)); if (array == 0) return kIOReturnNoResources; data = OSDynamicCast(OSData, array->getObject(source)); if (data == 0) return kIOReturnNoResources; @@ -3751,6 +5965,204 @@ IOReturn IOService::unregisterInterrupt(int source) return interruptController->unregisterInterrupt(this, source); } +IOReturn IOService::addInterruptStatistics(IOInterruptAccountingData * statistics, int source) +{ + IOReportLegend * legend = NULL; + IOInterruptAccountingData * oldValue = NULL; + IOInterruptAccountingReporter * newArray = NULL; + char subgroupName[64]; + int newArraySize = 0; + int i = 0; + + if (source < 0) { + return kIOReturnBadArgument; + } + + /* + * We support statistics on a maximum of 256 interrupts per nub; if a nub + * has more than 256 interrupt specifiers associated with it, and tries + * to register a high interrupt index with interrupt accounting, panic. + * Having more than 256 interrupts associated with a single nub is + * probably a sign that something fishy is going on. + */ + if (source > IA_INDEX_MAX) { + panic("addInterruptStatistics called for an excessively large index (%d)", source); + } + + /* + * TODO: This is ugly (wrapping a lock around an allocation). I'm only + * leaving it as is because the likelihood of contention where we are + * actually growing the array is minimal (we would realistically need + * to be starting a driver for the first time, with an IOReporting + * client already in place). Nonetheless, cleanup that can be done + * to adhere to best practices; it'll make the code more complicated, + * unfortunately. + */ + IOLockLock(reserved->interruptStatisticsLock); + + /* + * Lazily allocate the statistics array. + */ + if (!reserved->interruptStatisticsArray) { + reserved->interruptStatisticsArray = IONew(IOInterruptAccountingReporter, 1); + assert(reserved->interruptStatisticsArray); + reserved->interruptStatisticsArrayCount = 1; + bzero(reserved->interruptStatisticsArray, sizeof(*reserved->interruptStatisticsArray)); + } + + if (source >= reserved->interruptStatisticsArrayCount) { + /* + * We're still within the range of supported indices, but we are out + * of space in the current array. Do a nasty realloc (because + * IORealloc isn't a thing) here. We'll double the size with each + * reallocation. + * + * Yes, the "next power of 2" could be more efficient; but this will + * be invoked incredibly rarely. Who cares. + */ + newArraySize = (reserved->interruptStatisticsArrayCount << 1); + + while (newArraySize <= source) + newArraySize = (newArraySize << 1); + newArray = IONew(IOInterruptAccountingReporter, newArraySize); + + assert(newArray); + + /* + * TODO: This even zeroes the memory it is about to overwrite. + * Shameful; fix it. Not particularly high impact, however. + */ + bzero(newArray, newArraySize * sizeof(*newArray)); + memcpy(newArray, reserved->interruptStatisticsArray, reserved->interruptStatisticsArrayCount * sizeof(*newArray)); + IODelete(reserved->interruptStatisticsArray, IOInterruptAccountingReporter, reserved->interruptStatisticsArrayCount); + reserved->interruptStatisticsArray = newArray; + reserved->interruptStatisticsArrayCount = newArraySize; + } + + if (!reserved->interruptStatisticsArray[source].reporter) { + /* + * We don't have a reporter associated with this index yet, so we + * need to create one. + */ + /* + * TODO: Some statistics do in fact have common units (time); should this be + * split into separate reporters to communicate this? + */ + reserved->interruptStatisticsArray[source].reporter = IOSimpleReporter::with(this, kIOReportCategoryPower, kIOReportUnitNone); + + /* + * Each statistic is given an identifier based on the interrupt index (which + * should be unique relative to any single nub) and the statistic involved. + * We should now have a sane (small and positive) index, so start + * constructing the channels for statistics. + */ + for (i = 0; i < IA_NUM_INTERRUPT_ACCOUNTING_STATISTICS; i++) { + /* + * TODO: Currently, this does not add channels for disabled statistics. + * Will this be confusing for clients? If so, we should just add the + * channels; we can avoid updating the channels even if they exist. + */ + if (IA_GET_STATISTIC_ENABLED(i)) + reserved->interruptStatisticsArray[source].reporter->addChannel(IA_GET_CHANNEL_ID(source, i), kInterruptAccountingStatisticNameArray[i]); + } + + /* + * We now need to add the legend for this reporter to the registry. + */ + legend = IOReportLegend::with(OSDynamicCast(OSArray, getProperty(kIOReportLegendKey))); + + /* + * Note that while we compose the subgroup name, we do not need to + * manage its lifecycle (the reporter will handle this). + */ + snprintf(subgroupName, sizeof(subgroupName), "%s %d", getName(), source); + subgroupName[sizeof(subgroupName) - 1] = 0; + legend->addReporterLegend(reserved->interruptStatisticsArray[source].reporter, kInterruptAccountingGroupName, subgroupName); + setProperty(kIOReportLegendKey, legend->getLegend()); + legend->release(); + + /* + * TODO: Is this a good idea? Probably not; my assumption is it opts + * all entities who register interrupts into public disclosure of all + * IOReporting channels. Unfortunately, this appears to be as fine + * grain as it gets. + */ + setProperty(kIOReportLegendPublicKey, true); + } + + /* + * Don't stomp existing entries. If we are about to, panic; this + * probably means we failed to tear down our old interrupt source + * correctly. + */ + oldValue = reserved->interruptStatisticsArray[source].statistics; + + if (oldValue) { + panic("addInterruptStatistics call for index %d would have clobbered existing statistics", source); + } + + reserved->interruptStatisticsArray[source].statistics = statistics; + + /* + * Inherit the reporter values for each statistic. The target may + * be torn down as part of the runtime of the service (especially + * for sleep/wake), so we inherit in order to avoid having values + * reset for no apparent reason. Our statistics are ultimately + * tied to the index and the sevice, not to an individual target, + * so we should maintain them accordingly. + */ + interruptAccountingDataInheritChannels(reserved->interruptStatisticsArray[source].statistics, reserved->interruptStatisticsArray[source].reporter); + + IOLockUnlock(reserved->interruptStatisticsLock); + + return kIOReturnSuccess; +} + +IOReturn IOService::removeInterruptStatistics(int source) +{ + IOInterruptAccountingData * value = NULL; + + if (source < 0) { + return kIOReturnBadArgument; + } + + IOLockLock(reserved->interruptStatisticsLock); + + /* + * We dynamically grow the statistics array, so an excessively + * large index value has NEVER been registered. This either + * means our cap on the array size is too small (unlikely), or + * that we have been passed a corrupt index (this must be passed + * the plain index into the interrupt specifier list). + */ + if (source >= reserved->interruptStatisticsArrayCount) { + panic("removeInterruptStatistics called for index %d, which was never registered", source); + } + + assert(reserved->interruptStatisticsArray); + + /* + * If there is no existing entry, we are most likely trying to + * free an interrupt owner twice, or we have corrupted the + * index value. + */ + value = reserved->interruptStatisticsArray[source].statistics; + + if (!value) { + panic("removeInterruptStatistics called for empty index %d", source); + } + + /* + * We update the statistics, so that any delta with the reporter + * state is not lost. + */ + interruptAccountingDataUpdateChannels(reserved->interruptStatisticsArray[source].statistics, reserved->interruptStatisticsArray[source].reporter); + reserved->interruptStatisticsArray[source].statistics = NULL; + IOLockUnlock(reserved->interruptStatisticsLock); + + return kIOReturnSuccess; +} + IOReturn IOService::getInterruptType(int source, int *interruptType) { IOInterruptController *interruptController; @@ -3799,14 +6211,138 @@ IOReturn IOService::causeInterrupt(int source) return interruptController->causeInterrupt(this, source); } -OSMetaClassDefineReservedUnused(IOService, 0); -OSMetaClassDefineReservedUnused(IOService, 1); +IOReturn IOService::configureReport(IOReportChannelList *channelList, + IOReportConfigureAction action, + void *result, + void *destination) +{ + unsigned cnt; + + for (cnt = 0; cnt < channelList->nchannels; cnt++) { + if ( channelList->channels[cnt].channel_id == kPMPowerStatesChID ) { + if (pwrMgt) configurePowerStatesReport(action, result); + else return kIOReturnUnsupported; + } + else if ( channelList->channels[cnt].channel_id == kPMCurrStateChID ) { + if (pwrMgt) configureSimplePowerReport(action, result); + else return kIOReturnUnsupported; + } + } + + IOLockLock(reserved->interruptStatisticsLock); + + /* The array count is signed (because the interrupt indices are signed), hence the cast */ + for (cnt = 0; cnt < (unsigned) reserved->interruptStatisticsArrayCount; cnt++) { + if (reserved->interruptStatisticsArray[cnt].reporter) { + /* + * If the reporter is currently associated with the statistics + * for an event source, we may need to update the reporter. + */ + if (reserved->interruptStatisticsArray[cnt].statistics) + interruptAccountingDataUpdateChannels(reserved->interruptStatisticsArray[cnt].statistics, reserved->interruptStatisticsArray[cnt].reporter); + + reserved->interruptStatisticsArray[cnt].reporter->configureReport(channelList, action, result, destination); + } + } + + IOLockUnlock(reserved->interruptStatisticsLock); + + return kIOReturnSuccess; +} + +IOReturn IOService::updateReport(IOReportChannelList *channelList, + IOReportUpdateAction action, + void *result, + void *destination) +{ + unsigned cnt; + + for (cnt = 0; cnt < channelList->nchannels; cnt++) { + if ( channelList->channels[cnt].channel_id == kPMPowerStatesChID ) { + if (pwrMgt) updatePowerStatesReport(action, result, destination); + else return kIOReturnUnsupported; + } + else if ( channelList->channels[cnt].channel_id == kPMCurrStateChID ) { + if (pwrMgt) updateSimplePowerReport(action, result, destination); + else return kIOReturnUnsupported; + } + } + + IOLockLock(reserved->interruptStatisticsLock); + + /* The array count is signed (because the interrupt indices are signed), hence the cast */ + for (cnt = 0; cnt < (unsigned) reserved->interruptStatisticsArrayCount; cnt++) { + if (reserved->interruptStatisticsArray[cnt].reporter) { + /* + * If the reporter is currently associated with the statistics + * for an event source, we need to update the reporter. + */ + if (reserved->interruptStatisticsArray[cnt].statistics) + interruptAccountingDataUpdateChannels(reserved->interruptStatisticsArray[cnt].statistics, reserved->interruptStatisticsArray[cnt].reporter); + + reserved->interruptStatisticsArray[cnt].reporter->updateReport(channelList, action, result, destination); + } + } + + IOLockUnlock(reserved->interruptStatisticsLock); + + return kIOReturnSuccess; +} + +uint64_t IOService::getAuthorizationID( void ) +{ + return reserved->authorizationID; +} + +IOReturn IOService::setAuthorizationID( uint64_t authorizationID ) +{ + OSObject * entitlement; + IOReturn status; + + entitlement = IOUserClient::copyClientEntitlement( current_task( ), "com.apple.private.iokit.IOServiceSetAuthorizationID" ); + + if ( entitlement ) + { + if ( entitlement == kOSBooleanTrue ) + { + reserved->authorizationID = authorizationID; + + status = kIOReturnSuccess; + } + else + { + status = kIOReturnNotPrivileged; + } + + entitlement->release( ); + } + else + { + status = kIOReturnNotPrivileged; + } + + return status; +} + +#if __LP64__ +OSMetaClassDefineReservedUsed(IOService, 0); +OSMetaClassDefineReservedUsed(IOService, 1); OSMetaClassDefineReservedUnused(IOService, 2); OSMetaClassDefineReservedUnused(IOService, 3); OSMetaClassDefineReservedUnused(IOService, 4); OSMetaClassDefineReservedUnused(IOService, 5); OSMetaClassDefineReservedUnused(IOService, 6); OSMetaClassDefineReservedUnused(IOService, 7); +#else +OSMetaClassDefineReservedUsed(IOService, 0); +OSMetaClassDefineReservedUsed(IOService, 1); +OSMetaClassDefineReservedUsed(IOService, 2); +OSMetaClassDefineReservedUsed(IOService, 3); +OSMetaClassDefineReservedUsed(IOService, 4); +OSMetaClassDefineReservedUsed(IOService, 5); +OSMetaClassDefineReservedUsed(IOService, 6); +OSMetaClassDefineReservedUsed(IOService, 7); +#endif OSMetaClassDefineReservedUnused(IOService, 8); OSMetaClassDefineReservedUnused(IOService, 9); OSMetaClassDefineReservedUnused(IOService, 10); @@ -3847,19 +6383,3 @@ OSMetaClassDefineReservedUnused(IOService, 44); OSMetaClassDefineReservedUnused(IOService, 45); OSMetaClassDefineReservedUnused(IOService, 46); OSMetaClassDefineReservedUnused(IOService, 47); -OSMetaClassDefineReservedUnused(IOService, 48); -OSMetaClassDefineReservedUnused(IOService, 49); -OSMetaClassDefineReservedUnused(IOService, 50); -OSMetaClassDefineReservedUnused(IOService, 51); -OSMetaClassDefineReservedUnused(IOService, 52); -OSMetaClassDefineReservedUnused(IOService, 53); -OSMetaClassDefineReservedUnused(IOService, 54); -OSMetaClassDefineReservedUnused(IOService, 55); -OSMetaClassDefineReservedUnused(IOService, 56); -OSMetaClassDefineReservedUnused(IOService, 57); -OSMetaClassDefineReservedUnused(IOService, 58); -OSMetaClassDefineReservedUnused(IOService, 59); -OSMetaClassDefineReservedUnused(IOService, 60); -OSMetaClassDefineReservedUnused(IOService, 61); -OSMetaClassDefineReservedUnused(IOService, 62); -OSMetaClassDefineReservedUnused(IOService, 63);