X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/9bccf70c0258c7cac2dcb80011b2a964d884c552..fe8ab488e9161c46dd9885d58fc52996dc0249ff:/iokit/Kernel/IOCatalogue.cpp diff --git a/iokit/Kernel/IOCatalogue.cpp b/iokit/Kernel/IOCatalogue.cpp index 14dabbd65..b6c335fbf 100644 --- a/iokit/Kernel/IOCatalogue.cpp +++ b/iokit/Kernel/IOCatalogue.cpp @@ -1,114 +1,95 @@ /* - * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 1998-2012 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@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* - * Copyright (c) 1998 Apple Computer, Inc. All rights reserved. + * Copyright (c) 1998 Apple Inc. All rights reserved. * * HISTORY * */ +/* + * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce + * support for mandatory and extensible security protections. This notice + * is included in support of clause 2.2 (b) of the Apple Public License, + * Version 2.0. + */ -#include -#include -#include -#include -#include extern "C" { #include -#include -#include +#include #include +#include }; -#include - -#include - +#include +#include +#include +#include +#include -extern "C" { -int IODTGetLoaderInfo( char *key, void **infoAddr, int *infoSize ); -extern void IODTFreeLoaderInfo( char *key, void *infoAddr, int infoSize ); -extern void OSRuntimeUnloadCPPForSegment( - struct segment_command * segment); -}; +#include +#include +#include +#include +#include -/***** - * At startup these function pointers are set to use the libsa in-kernel - * linker for recording and loading kmods. Once the root filesystem - * is available, the kmod_load_function pointer gets switched to point - * at the kmod_load_extension() function built into the kernel, and the - * others are set to zero. Those two functions must *always* be checked - * before being invoked. - */ -extern "C" { -kern_return_t (*kmod_load_function)(char *extension_name) = - &kmod_load_extension; -bool (*record_startup_extensions_function)(void) = 0; -bool (*add_from_mkext_function)(OSData * mkext) = 0; -void (*remove_startup_extension_function)(const char * name) = 0; -}; +#if PRAGMA_MARK +#pragma mark Internal Declarations +#endif +/********************************************************************* +*********************************************************************/ +IOCatalogue * gIOCatalogue; +const OSSymbol * gIOClassKey; +const OSSymbol * gIOProbeScoreKey; +const OSSymbol * gIOModuleIdentifierKey; +IORWLock * gIOCatalogLock; -/***** - * A few parts of IOCatalogue require knowledge of - * whether the in-kernel linker is present. This - * variable is set by libsa's bootstrap code. - */ -int kernelLinkerPresent = 0; +#if PRAGMA_MARK +#pragma mark Utility functions +#endif +#if PRAGMA_MARK +#pragma mark IOCatalogue class implementation +#endif +/********************************************************************* +*********************************************************************/ #define super OSObject -#define kModuleKey "CFBundleIdentifier" - OSDefineMetaClassAndStructors(IOCatalogue, OSObject) -#define CATALOGTEST 0 - -IOCatalogue * gIOCatalogue; -const OSSymbol * gIOClassKey; -const OSSymbol * gIOProbeScoreKey; - -static void UniqueProperties( OSDictionary * dict ) -{ - OSString * data; - - data = OSDynamicCast( OSString, dict->getObject( gIOClassKey )); - if( data) { - const OSSymbol *classSymbol = OSSymbol::withString(data); - - dict->setObject( gIOClassKey, (OSSymbol *) classSymbol); - classSymbol->release(); - } - - data = OSDynamicCast( OSString, dict->getObject( gIOMatchCategoryKey )); - if( data) { - const OSSymbol *classSymbol = OSSymbol::withString(data); +static bool isModuleLoadedNoOSKextLock(OSDictionary *theKexts, + OSDictionary *theModuleDict); - dict->setObject( gIOMatchCategoryKey, (OSSymbol *) classSymbol); - classSymbol->release(); - } -} -void IOCatalogue::initialize( void ) +/********************************************************************* +*********************************************************************/ +void IOCatalogue::initialize(void) { OSArray * array; OSString * errorString; @@ -118,14 +99,17 @@ void IOCatalogue::initialize( void ) array = OSDynamicCast(OSArray, OSUnserialize(gIOKernelConfigTables, &errorString)); if (!array && errorString) { - IOLog("KernelConfigTables syntax error: %s\n", - errorString->getCStringNoCopy()); - errorString->release(); + IOLog("KernelConfigTables syntax error: %s\n", + errorString->getCStringNoCopy()); + errorString->release(); } - gIOClassKey = OSSymbol::withCStringNoCopy( kIOClassKey ); - gIOProbeScoreKey = OSSymbol::withCStringNoCopy( kIOProbeScoreKey ); - assert( array && gIOClassKey && gIOProbeScoreKey); + gIOClassKey = OSSymbol::withCStringNoCopy( kIOClassKey ); + gIOProbeScoreKey = OSSymbol::withCStringNoCopy( kIOProbeScoreKey ); + gIOModuleIdentifierKey = OSSymbol::withCStringNoCopy( kCFBundleIdentifierKey ); + + assert( array && gIOClassKey && gIOProbeScoreKey + && gIOModuleIdentifierKey); gIOCatalogue = new IOCatalogue; assert(gIOCatalogue); @@ -134,398 +118,379 @@ void IOCatalogue::initialize( void ) array->release(); } -// Initialize the IOCatalog object. +/********************************************************************* +* Initialize the IOCatalog object. +*********************************************************************/ +OSArray * IOCatalogue::arrayForPersonality(OSDictionary * dict) +{ + const OSSymbol * sym; + + sym = OSDynamicCast(OSSymbol, dict->getObject(gIOProviderClassKey)); + if (!sym) return (0); + + return ((OSArray *) personalities->getObject(sym)); +} + +void IOCatalogue::addPersonality(OSDictionary * dict) +{ + const OSSymbol * sym; + OSArray * arr; + + sym = OSDynamicCast(OSSymbol, dict->getObject(gIOProviderClassKey)); + if (!sym) return; + arr = (OSArray *) personalities->getObject(sym); + if (arr) arr->setObject(dict); + else + { + arr = OSArray::withObjects((const OSObject **)&dict, 1, 2); + personalities->setObject(sym, arr); + arr->release(); + } +} + +/********************************************************************* +* Initialize the IOCatalog object. +*********************************************************************/ bool IOCatalogue::init(OSArray * initArray) { - IORegistryEntry * entry; OSDictionary * dict; - + OSObject * obj; + if ( !super::init() ) return false; generation = 1; - array = initArray; - array->retain(); - kernelTables = OSCollectionIterator::withCollection( array ); - - lock = IOLockAlloc(); - kld_lock = IOLockAlloc(); - - kernelTables->reset(); - while( (dict = (OSDictionary *) kernelTables->getNextObject())) { - UniqueProperties(dict); + personalities = OSDictionary::withCapacity(32); + personalities->setOptions(OSCollection::kSort, OSCollection::kSort); + for (unsigned int idx = 0; (obj = initArray->getObject(idx)); idx++) + { + dict = OSDynamicCast(OSDictionary, obj); + if (!dict) continue; + OSKext::uniquePersonalityProperties(dict); if( 0 == dict->getObject( gIOClassKey )) + { IOLog("Missing or bad \"%s\" key\n", gIOClassKey->getCStringNoCopy()); + continue; + } + dict->setObject("KernelConfigTable", kOSBooleanTrue); + addPersonality(dict); } -#if CATALOGTEST - AbsoluteTime deadline; - clock_interval_to_deadline( 1000, kMillisecondScale ); - thread_call_func_delayed( ping, this, deadline ); -#endif - - entry = IORegistryEntry::getRegistryRoot(); - if ( entry ) - entry->setProperty(kIOCatalogueKey, this); + gIOCatalogLock = IORWLockAlloc(); + lock = gIOCatalogLock; return true; } -// Release all resources used by IOCatalogue and deallocate. -// This will probably never be called. +/********************************************************************* +* Release all resources used by IOCatalogue and deallocate. +* This will probably never be called. +*********************************************************************/ void IOCatalogue::free( void ) { - if ( array ) - array->release(); - - if ( kernelTables ) - kernelTables->release(); - - super::free(); + panic(""); } -#if CATALOGTEST - -static int hackLimit; - -enum { kDriversPerIter = 4 }; - -void IOCatalogue::ping( thread_call_param_t arg, thread_call_param_t) -{ - IOCatalogue * self = (IOCatalogue *) arg; - OSOrderedSet * set; - OSDictionary * table; - int newLimit; - - set = OSOrderedSet::withCapacity( 1 ); - - IOTakeLock( &self->lock ); - - for( newLimit = 0; newLimit < kDriversPerIter; newLimit++) { - table = (OSDictionary *) self->array->getObject( - hackLimit + newLimit ); - if( table) { - set->setLastObject( table ); - - OSSymbol * sym = (OSSymbol *) table->getObject( gIOClassKey ); - kprintf("enabling %s\n", sym->getCStringNoCopy()); - - } else { - newLimit--; - break; - } - } - - IOService::catalogNewDrivers( set ); - - hackLimit += newLimit; - self->generation++; - - IOUnlock( &self->lock ); - - if( kDriversPerIter == newLimit) { - AbsoluteTime deadline; - clock_interval_to_deadline( 500, kMillisecondScale ); - thread_call_func_delayed( ping, this, deadline ); - } -} -#endif - -OSOrderedSet * IOCatalogue::findDrivers( IOService * service, - SInt32 * generationCount ) +/********************************************************************* +*********************************************************************/ +OSOrderedSet * +IOCatalogue::findDrivers( + IOService * service, + SInt32 * generationCount) { OSDictionary * nextTable; OSOrderedSet * set; - OSString * imports; + OSArray * array; + const OSMetaClass * meta; + unsigned int idx; set = OSOrderedSet::withCapacity( 1, IOServiceOrdering, (void *)gIOProbeScoreKey ); if( !set ) return( 0 ); - IOTakeLock( lock ); - kernelTables->reset(); + IORWLockRead(lock); -#if CATALOGTEST - int hackIndex = 0; -#endif - while( (nextTable = (OSDictionary *) kernelTables->getNextObject())) { -#if CATALOGTEST - if( hackIndex++ > hackLimit) - break; -#endif - imports = OSDynamicCast( OSString, - nextTable->getObject( gIOProviderClassKey )); - if( imports && service->metaCast( imports )) - set->setObject( nextTable ); + meta = service->getMetaClass(); + while (meta) + { + array = (OSArray *) personalities->getObject(meta->getClassNameSymbol()); + if (array) for (idx = 0; (nextTable = (OSDictionary *) array->getObject(idx)); idx++) + { + set->setObject(nextTable); + } + if (meta == &IOService::gMetaClass) break; + meta = meta->getSuperClass(); } *generationCount = getGenerationCount(); - IOUnlock( lock ); + IORWLockUnlock(lock); return( set ); } -// Is personality already in the catalog? -OSOrderedSet * IOCatalogue::findDrivers( OSDictionary * matching, - SInt32 * generationCount) +/********************************************************************* +* Is personality already in the catalog? +*********************************************************************/ +OSOrderedSet * +IOCatalogue::findDrivers( + OSDictionary * matching, + SInt32 * generationCount) { + OSCollectionIterator * iter; OSDictionary * dict; OSOrderedSet * set; + OSArray * array; + const OSSymbol * key; + unsigned int idx; - UniqueProperties(matching); + OSKext::uniquePersonalityProperties(matching); set = OSOrderedSet::withCapacity( 1, IOServiceOrdering, (void *)gIOProbeScoreKey ); + if (!set) return (0); + iter = OSCollectionIterator::withCollection(personalities); + if (!iter) + { + set->release(); + return (0); + } - IOTakeLock( lock ); - kernelTables->reset(); - while ( (dict = (OSDictionary *) kernelTables->getNextObject()) ) { - - /* This comparison must be done with only the keys in the - * "matching" dict to enable general searches. - */ - if ( dict->isEqualTo(matching, matching) ) - set->setObject(dict); + IORWLockRead(lock); + while ((key = (const OSSymbol *) iter->getNextObject())) + { + array = (OSArray *) personalities->getObject(key); + if (array) for (idx = 0; (dict = (OSDictionary *) array->getObject(idx)); idx++) + { + /* This comparison must be done with only the keys in the + * "matching" dict to enable general searches. + */ + if ( dict->isEqualTo(matching, matching) ) + set->setObject(dict); + } } *generationCount = getGenerationCount(); - IOUnlock( lock ); + IORWLockUnlock(lock); + iter->release(); return set; } -// Add a new personality to the set if it has a unique IOResourceMatchKey value. -// XXX -- svail: This should be optimized. -// esb - There doesn't seem like any reason to do this - it causes problems -// esb - when there are more than one loadable driver matching on the same provider class -static void AddNewImports( OSOrderedSet * set, OSDictionary * dict ) -{ - set->setObject(dict); -} +/********************************************************************* +* Add driver config tables to catalog and start matching process. +* +* Important that existing personalities are kept (not replaced) +* if duplicates found. Personalities can come from OSKext objects +* or from userland kext library. We want to minimize distinct +* copies between OSKext & IOCatalogue. +* +* xxx - userlib used to refuse to send personalities with IOKitDebug +* xxx - during safe boot. That would be better implemented here. +*********************************************************************/ -// Add driver config tables to catalog and start matching process. -bool IOCatalogue::addDrivers(OSArray * drivers, - bool doNubMatching = true ) +bool IOCatalogue::addDrivers( + OSArray * drivers, + bool doNubMatching) { - OSCollectionIterator * iter; - OSDictionary * dict; - OSOrderedSet * set; - OSArray * persons; - bool ret; - - ret = true; + bool result = false; + OSCollectionIterator * iter = NULL; // must release + OSOrderedSet * set = NULL; // must release + OSObject * object = NULL; // do not release + OSArray * persons = NULL; // do not release + persons = OSDynamicCast(OSArray, drivers); - if ( !persons ) - return false; - - iter = OSCollectionIterator::withCollection( persons ); - if (!iter ) - return false; + if (!persons) { + goto finish; + } set = OSOrderedSet::withCapacity( 10, IOServiceOrdering, - (void *)gIOProbeScoreKey ); - if ( !set ) { - iter->release(); - return false; + (void *)gIOProbeScoreKey ); + if (!set) { + goto finish; } - IOTakeLock( lock ); - while ( (dict = (OSDictionary *) iter->getNextObject()) ) { - UInt count; - - UniqueProperties( dict ); + iter = OSCollectionIterator::withCollection(persons); + if (!iter) { + goto finish; + } + + /* Start with success; clear it on an error. + */ + result = true; + + IORWLockWrite(lock); + while ( (object = iter->getNextObject()) ) { + + // xxx Deleted OSBundleModuleDemand check; will handle in other ways for SL + + OSDictionary * personality = OSDynamicCast(OSDictionary, object); + + SInt count; + + if (!personality) { + IOLog("IOCatalogue::addDrivers() encountered non-dictionary; bailing.\n"); + result = false; + break; + } + + OSKext::uniquePersonalityProperties(personality); // Add driver personality to catalogue. - count = array->getCount(); - while ( count-- ) { - OSDictionary * driver; - - // Be sure not to double up on personalities. - driver = (OSDictionary *)array->getObject(count); - - /* Unlike in other functions, this comparison must be exact! - * The catalogue must be able to contain personalities that - * are proper supersets of others. - * Do not compare just the properties present in one driver - * pesonality or the other. - */ - if ( dict->isEqualTo(driver) ) { - array->removeObject(count); - break; - } + + OSArray * array = arrayForPersonality(personality); + if (!array) addPersonality(personality); + else + { + count = array->getCount(); + while (count--) { + OSDictionary * driver; + + // Be sure not to double up on personalities. + driver = (OSDictionary *)array->getObject(count); + + /* Unlike in other functions, this comparison must be exact! + * The catalogue must be able to contain personalities that + * are proper supersets of others. + * Do not compare just the properties present in one driver + * personality or the other. + */ + if (personality->isEqualTo(driver)) { + break; + } + } + if (count >= 0) { + // its a dup + continue; + } + result = array->setObject(personality); + if (!result) { + break; + } } - - ret = array->setObject( dict ); - if ( !ret ) - break; - AddNewImports( set, dict ); + set->setObject(personality); } // Start device matching. - if ( doNubMatching && (set->getCount() > 0) ) { - IOService::catalogNewDrivers( set ); + if (result && doNubMatching && (set->getCount() > 0)) { + IOService::catalogNewDrivers(set); generation++; } - IOUnlock( lock ); + IORWLockUnlock(lock); - set->release(); - iter->release(); - - return ret; +finish: + if (set) set->release(); + if (iter) iter->release(); + + return result; } -// Remove drivers from the catalog which match the -// properties in the matching dictionary. -bool IOCatalogue::removeDrivers( OSDictionary * matching, - bool doNubMatching = true) +/********************************************************************* +* Remove drivers from the catalog which match the +* properties in the matching dictionary. +*********************************************************************/ +bool +IOCatalogue::removeDrivers( + OSDictionary * matching, + bool doNubMatching) { - OSCollectionIterator * tables; - OSDictionary * dict; OSOrderedSet * set; - OSArray * arrayCopy; + OSCollectionIterator * iter; + OSDictionary * dict; + OSArray * array; + const OSSymbol * key; + unsigned int idx; if ( !matching ) return false; - + set = OSOrderedSet::withCapacity(10, IOServiceOrdering, (void *)gIOProbeScoreKey); if ( !set ) return false; - - arrayCopy = OSArray::withCapacity(100); - if ( !arrayCopy ) { - set->release(); - return false; - } - - tables = OSCollectionIterator::withCollection(arrayCopy); - arrayCopy->release(); - if ( !tables ) { - set->release(); - return false; + iter = OSCollectionIterator::withCollection(personalities); + if (!iter) + { + set->release(); + return (false); } - UniqueProperties( matching ); - - IOTakeLock( lock ); - kernelTables->reset(); - arrayCopy->merge(array); - array->flushCollection(); - tables->reset(); - while ( (dict = (OSDictionary *)tables->getNextObject()) ) { - - /* This comparison must be done with only the keys in the - * "matching" dict to enable general searches. - */ - if ( dict->isEqualTo(matching, matching) ) { - AddNewImports( set, dict ); - continue; + IORWLockWrite(lock); + while ((key = (const OSSymbol *) iter->getNextObject())) + { + array = (OSArray *) personalities->getObject(key); + if (array) for (idx = 0; (dict = (OSDictionary *) array->getObject(idx)); idx++) + { + /* This comparison must be done with only the keys in the + * "matching" dict to enable general searches. + */ + if ( dict->isEqualTo(matching, matching) ) { + set->setObject(dict); + array->removeObject(idx); + idx--; + } + } + // Start device matching. + if ( doNubMatching && (set->getCount() > 0) ) { + IOService::catalogNewDrivers(set); + generation++; } - - array->setObject(dict); - } - // Start device matching. - if ( doNubMatching && (set->getCount() > 0) ) { - IOService::catalogNewDrivers(set); - generation++; } - IOUnlock( lock ); - + IORWLockUnlock(lock); + set->release(); - tables->release(); + iter->release(); return true; } // Return the generation count. -SInt32 IOCatalogue::getGenerationCount( void ) const +SInt32 IOCatalogue::getGenerationCount(void) const { return( generation ); } -bool IOCatalogue::isModuleLoaded( OSString * moduleName ) const +bool IOCatalogue::isModuleLoaded(OSString * moduleName) const { return isModuleLoaded(moduleName->getCStringNoCopy()); } -bool IOCatalogue::isModuleLoaded( const char * moduleName ) const +bool IOCatalogue::isModuleLoaded(const char * moduleName) const { - kmod_info_t * k_info; - - if ( !moduleName ) - return false; - - // Is the module already loaded? - k_info = kmod_lookupbyname_locked((char *)moduleName); - if ( !k_info ) { - kern_return_t ret; - - /* To make sure this operation completes even if a bad extension needs - * to be removed, take the kld lock for this whole block, spanning the - * kmod_load_function() and remove_startup_extension_function() calls. - */ - IOLockLock(kld_lock); - - // If the module hasn't been loaded, then load it. - if (kmod_load_function != 0) { - - ret = kmod_load_function((char *)moduleName); - - if ( ret != kIOReturnSuccess ) { - IOLog("IOCatalogue: %s cannot be loaded.\n", moduleName); - - /* If the extension couldn't be loaded this time, - * make it unavailable so that no more requests are - * made in vain. This also enables other matching - * extensions to have a chance. - */ - if (kernelLinkerPresent && remove_startup_extension_function) { - (*remove_startup_extension_function)(moduleName); - } - IOLockUnlock(kld_lock); - return false; - } else if (kernelLinkerPresent) { - // If kern linker is here, the driver is actually loaded, - // so return true. - IOLockUnlock(kld_lock); - return true; - } else { - // kern linker isn't here, a request has been queued - // but the module isn't necessarily loaded yet, so stall. - IOLockUnlock(kld_lock); - return false; - } - } else { - IOLog("IOCatalogue: %s cannot be loaded " - "(kmod load function not set).\n", - moduleName); - } - - IOLockUnlock(kld_lock); + OSReturn ret; + ret = OSKext::loadKextWithIdentifier(moduleName); + if (kOSKextReturnDeferred == ret) { + // a request has been queued but the module isn't necessarily + // loaded yet, so stall. return false; } - - if (k_info) { - kfree(k_info, sizeof(kmod_info_t)); - } - - /* Lock wasn't taken if we get here. */ + // module is present or never will be return true; } // Check to see if module has been loaded already. -bool IOCatalogue::isModuleLoaded( OSDictionary * driver ) const +bool IOCatalogue::isModuleLoaded(OSDictionary * driver) const { OSString * moduleName = NULL; + OSString * publisherName = NULL; if ( !driver ) return false; - moduleName = OSDynamicCast(OSString, driver->getObject(kModuleKey)); + /* The personalities of codeless kexts often contain the bundle ID of the + * kext they reference, and not the bundle ID of the codeless kext itself. + * The prelinked kernel needs to know the bundle ID of the codeless kext + * so it can include these personalities, so OSKext stores that bundle ID + * in the IOPersonalityPublisher key, and we record it as requested here. + */ + publisherName = OSDynamicCast(OSString, + driver->getObject(kIOPersonalityPublisherKey)); + OSKext::recordIdentifierRequest(publisherName); + + moduleName = OSDynamicCast(OSString, driver->getObject(gIOModuleIdentifierKey)); if ( moduleName ) return isModuleLoaded(moduleName); @@ -535,61 +500,42 @@ bool IOCatalogue::isModuleLoaded( OSDictionary * driver ) const return true; } -// This function is called after a module has been loaded. -void IOCatalogue::moduleHasLoaded( OSString * moduleName ) +/* This function is called after a module has been loaded. + * Is invoked from user client call, ultimately from IOKitLib's + * IOCatalogueModuleLoaded(). Sent from kextd. + */ +void IOCatalogue::moduleHasLoaded(OSString * moduleName) { - OSDictionary * dict; + OSDictionary * dict; dict = OSDictionary::withCapacity(2); - dict->setObject(kModuleKey, moduleName); + dict->setObject(gIOModuleIdentifierKey, moduleName); startMatching(dict); dict->release(); + + (void) OSKext::setDeferredLoadSucceeded(); + (void) OSKext::considerRebuildOfPrelinkedKernel(); } -void IOCatalogue::moduleHasLoaded( const char * moduleName ) +void IOCatalogue::moduleHasLoaded(const char * moduleName) { - OSString * name; + OSString * name; name = OSString::withCString(moduleName); moduleHasLoaded(name); name->release(); } -IOReturn IOCatalogue::unloadModule( OSString * moduleName ) const +// xxx - return is really OSReturn/kern_return_t +IOReturn IOCatalogue::unloadModule(OSString * moduleName) const { - kmod_info_t * k_info = 0; - kern_return_t ret; - const char * name; - - ret = kIOReturnBadArgument; - if ( moduleName ) { - name = moduleName->getCStringNoCopy(); - k_info = kmod_lookupbyname_locked((char *)name); - if ( k_info && (k_info->reference_count < 1) ) { - if ( k_info->stop && - !((ret = k_info->stop(k_info, 0)) == kIOReturnSuccess) ) { - - kfree(k_info, sizeof(kmod_info_t)); - return ret; - } - - ret = kmod_destroy(host_priv_self(), k_info->id); - } - } - - if (k_info) { - kfree(k_info, sizeof(kmod_info_t)); - } - - return ret; + return OSKext::removeKextWithIdentifier(moduleName->getCStringNoCopy()); } -static IOReturn _terminateDrivers( OSArray * array, OSDictionary * matching ) +IOReturn IOCatalogue::_terminateDrivers(OSDictionary * matching) { - OSCollectionIterator * tables; OSDictionary * dict; OSIterator * iter; - OSArray * arrayCopy; IOService * service; IOReturn ret; @@ -603,7 +549,7 @@ static IOReturn _terminateDrivers( OSArray * array, OSDictionary * matching ) if ( !iter ) return kIOReturnNoMemory; - UniqueProperties( matching ); + OSKext::uniquePersonalityProperties( matching ); // terminate instances. do { @@ -628,87 +574,120 @@ static IOReturn _terminateDrivers( OSArray * array, OSDictionary * matching ) } while( !service && !iter->isValid()); iter->release(); - // remove configs from catalog. - if ( ret != kIOReturnSuccess ) - return ret; - - arrayCopy = OSArray::withCapacity(100); - if ( !arrayCopy ) - return kIOReturnNoMemory; - - tables = OSCollectionIterator::withCollection(arrayCopy); - arrayCopy->release(); - if ( !tables ) - return kIOReturnNoMemory; + return ret; +} - arrayCopy->merge(array); - array->flushCollection(); - tables->reset(); - while ( (dict = (OSDictionary *)tables->getNextObject()) ) { +IOReturn IOCatalogue::_removeDrivers(OSDictionary * matching) +{ + IOReturn ret = kIOReturnSuccess; + OSCollectionIterator * iter; + OSDictionary * dict; + OSArray * array; + const OSSymbol * key; + unsigned int idx; - /* Remove from the catalogue's array any personalities - * that match the matching dictionary. - * This comparison must be done with only the keys in the - * "matching" dict to enable general matching. - */ - if ( dict->isEqualTo(matching, matching) ) - continue; + // remove configs from catalog. - array->setObject(dict); + iter = OSCollectionIterator::withCollection(personalities); + if (!iter) return (kIOReturnNoMemory); + + while ((key = (const OSSymbol *) iter->getNextObject())) + { + array = (OSArray *) personalities->getObject(key); + if (array) for (idx = 0; (dict = (OSDictionary *) array->getObject(idx)); idx++) + { + + /* Remove from the catalogue's array any personalities + * that match the matching dictionary. + * This comparison must be done with only the keys in the + * "matching" dict to enable general matching. + */ + if (dict->isEqualTo(matching, matching)) + { + array->removeObject(idx); + idx--; + } + } } - - tables->release(); + iter->release(); return ret; } -IOReturn IOCatalogue::terminateDrivers( OSDictionary * matching ) +IOReturn IOCatalogue::terminateDrivers(OSDictionary * matching) { IOReturn ret; - ret = kIOReturnSuccess; - IOTakeLock( lock ); - ret = _terminateDrivers(array, matching); - kernelTables->reset(); - IOUnlock( lock ); + ret = _terminateDrivers(matching); + IORWLockWrite(lock); + if (kIOReturnSuccess == ret) + ret = _removeDrivers(matching); + IORWLockUnlock(lock); return ret; } IOReturn IOCatalogue::terminateDriversForModule( - OSString * moduleName, - bool unload ) + OSString * moduleName, + bool unload) { IOReturn ret; OSDictionary * dict; + bool isLoaded = false; + + /* Check first if the kext currently has any linkage dependents; + * in such a case the unload would fail so let's not terminate any + * IOServices (since doing so typically results in a panic when there + * are loaded dependencies). Note that we aren't locking the kext here + * so it might lose or gain dependents by the time we call unloadModule(); + * I think that's ok, our unload can fail if a kext comes in on top of + * this one even after we've torn down IOService objects. Conversely, + * if we fail the unload here and then lose a library, the autounload + * thread will get us in short order. + */ + if (OSKext::isKextWithIdentifierLoaded(moduleName->getCStringNoCopy())) { + + isLoaded = true; + if (!OSKext::canUnloadKextWithIdentifier(moduleName, + /* checkClasses */ false)) { + ret = kOSKextReturnInUse; + goto finish; + } + } dict = OSDictionary::withCapacity(1); - if ( !dict ) - return kIOReturnNoMemory; - - dict->setObject(kModuleKey, moduleName); + if (!dict) { + ret = kIOReturnNoMemory; + goto finish; + } - IOTakeLock( lock ); + dict->setObject(gIOModuleIdentifierKey, moduleName); - ret = _terminateDrivers(array, dict); - kernelTables->reset(); + ret = _terminateDrivers(dict); + + /* No goto between IOLock calls! + */ + IORWLockWrite(lock); + if (kIOReturnSuccess == ret) { + ret = _removeDrivers(dict); + } // Unload the module itself. - if ( unload && ret == kIOReturnSuccess ) { - // Do kmod stop first. + if (unload && isLoaded && ret == kIOReturnSuccess) { ret = unloadModule(moduleName); } - IOUnlock( lock ); + IORWLockUnlock(lock); dict->release(); +finish: return ret; } IOReturn IOCatalogue::terminateDriversForModule( - const char * moduleName, - bool unload ) + const char * moduleName, + bool unload) { OSString * name; IOReturn ret; @@ -725,8 +704,12 @@ IOReturn IOCatalogue::terminateDriversForModule( bool IOCatalogue::startMatching( OSDictionary * matching ) { + OSCollectionIterator * iter; OSDictionary * dict; OSOrderedSet * set; + OSArray * array; + const OSSymbol * key; + unsigned int idx; if ( !matching ) return false; @@ -736,191 +719,258 @@ bool IOCatalogue::startMatching( OSDictionary * matching ) if ( !set ) return false; - IOTakeLock( lock ); - kernelTables->reset(); - - while ( (dict = (OSDictionary *)kernelTables->getNextObject()) ) { + iter = OSCollectionIterator::withCollection(personalities); + if (!iter) + { + set->release(); + return false; + } - /* This comparison must be done with only the keys in the - * "matching" dict to enable general matching. - */ - if ( dict->isEqualTo(matching, matching) ) - AddNewImports(set, dict); + IORWLockRead(lock); + + while ((key = (const OSSymbol *) iter->getNextObject())) + { + array = (OSArray *) personalities->getObject(key); + if (array) for (idx = 0; (dict = (OSDictionary *) array->getObject(idx)); idx++) + { + /* This comparison must be done with only the keys in the + * "matching" dict to enable general matching. + */ + if (dict->isEqualTo(matching, matching)) { + set->setObject(dict); + } + } } + // Start device matching. if ( set->getCount() > 0 ) { IOService::catalogNewDrivers(set); generation++; } - IOUnlock( lock ); + IORWLockUnlock(lock); set->release(); + iter->release(); return true; } void IOCatalogue::reset(void) { - OSArray * tables; - OSDictionary * entry; - unsigned int count; + IOCatalogue::resetAndAddDrivers(/* no drivers; true reset */ NULL, + /* doMatching */ false); + return; +} - IOLog("Resetting IOCatalogue.\n"); - - IOTakeLock( lock ); - tables = OSArray::withArray(array); - array->flushCollection(); - - count = tables->getCount(); - while ( count-- ) { - entry = (OSDictionary *)tables->getObject(count); - if ( entry && !entry->getObject(kModuleKey) ) { - array->setObject(entry); +bool IOCatalogue::resetAndAddDrivers(OSArray * drivers, bool doNubMatching) +{ + bool result = false; + OSArray * newPersonalities = NULL; // do not release + OSCollectionIterator * iter = NULL; // must release + OSOrderedSet * matchSet = NULL; // must release + const OSSymbol * key; + OSArray * array; + OSDictionary * thisNewPersonality = NULL; // do not release + OSDictionary * thisOldPersonality = NULL; // do not release + OSDictionary * myKexts = NULL; // must release + signed int idx, newIdx; + + if (drivers) { + newPersonalities = OSDynamicCast(OSArray, drivers); + if (!newPersonalities) { + goto finish; } } + matchSet = OSOrderedSet::withCapacity(10, IOServiceOrdering, + (void *)gIOProbeScoreKey); + if (!matchSet) { + goto finish; + } + iter = OSCollectionIterator::withCollection(personalities); + if (!iter) { + goto finish; + } - kernelTables->reset(); - IOUnlock( lock ); - - tables->release(); -} - -bool IOCatalogue::serialize(OSSerialize * s) const -{ - bool ret; + /* need copy of loaded kexts so we can check if for loaded modules without + * taking the OSKext lock. There is a potential of deadlocking if we get + * an OSKext via the normal path. See 14672140. + */ + myKexts = OSKext::copyKexts(); - if ( !s ) - return false; - - IOTakeLock( lock ); - ret = array->serialize(s); - IOUnlock( lock ); - - return ret; -} + result = true; + IOLog("Resetting IOCatalogue.\n"); -bool IOCatalogue::recordStartupExtensions(void) { - bool result = false; - - IOLockLock(kld_lock); - if (kernelLinkerPresent && record_startup_extensions_function) { - result = (*record_startup_extensions_function)(); - } else { - IOLog("Can't record startup extensions; " - "kernel linker is not present.\n"); - result = false; + /* No goto finish from here to unlock. + */ + IORWLockWrite(lock); + + while ((key = (const OSSymbol *) iter->getNextObject())) + { + array = (OSArray *) personalities->getObject(key); + if (!array) continue; + + for (idx = 0; + (thisOldPersonality = (OSDictionary *) array->getObject(idx)); + idx++) + { + if (thisOldPersonality->getObject("KernelConfigTable")) continue; + thisNewPersonality = NULL; + + if (newPersonalities) { + for (newIdx = 0; + (thisNewPersonality = (OSDictionary *) newPersonalities->getObject(newIdx)); + newIdx++) + { + /* Unlike in other functions, this comparison must be exact! + * The catalogue must be able to contain personalities that + * are proper supersets of others. + * Do not compare just the properties present in one driver + * personality or the other. + */ + if (OSDynamicCast(OSDictionary, thisNewPersonality) == NULL) { + /* skip thisNewPersonality if it is not an OSDictionary */ + continue; + } + if (thisNewPersonality->isEqualTo(thisOldPersonality)) + break; + } + } + if (thisNewPersonality) { + // dup, ignore + newPersonalities->removeObject(newIdx); + } + else { + // not in new set - remove + // only remove dictionary if this module in not loaded - 9953845 + if ( isModuleLoadedNoOSKextLock(myKexts, thisOldPersonality) == false ) { + if (matchSet) { + matchSet->setObject(thisOldPersonality); + } + array->removeObject(idx); + idx--; + } + } + } // for... + } // while... + + // add new + if (newPersonalities) { + for (newIdx = 0; + (thisNewPersonality = (OSDictionary *) newPersonalities->getObject(newIdx)); + newIdx++) + { + if (OSDynamicCast(OSDictionary, thisNewPersonality) == NULL) { + /* skip thisNewPersonality if it is not an OSDictionary */ + continue; + } + + OSKext::uniquePersonalityProperties(thisNewPersonality); + addPersonality(thisNewPersonality); + matchSet->setObject(thisNewPersonality); + } } - IOLockUnlock(kld_lock); - return result; -} + /* Finally, start device matching on all new & removed personalities. + */ + if (result && doNubMatching && (matchSet->getCount() > 0)) { + IOService::catalogNewDrivers(matchSet); + generation++; + } + IORWLockUnlock(lock); -/********************************************************************* -*********************************************************************/ -bool IOCatalogue::addExtensionsFromArchive(OSData * mkext) { - bool result = false; - - IOLockLock(kld_lock); - if (kernelLinkerPresent && add_from_mkext_function) { - result = (*add_from_mkext_function)(mkext); - } else { - IOLog("Can't add startup extensions from archive; " - "kernel linker is not present.\n"); - result = false; - } - IOLockUnlock(kld_lock); +finish: + if (matchSet) matchSet->release(); + if (iter) iter->release(); + if (myKexts) myKexts->release(); return result; } +bool IOCatalogue::serialize(OSSerialize * s) const +{ + if ( !s ) + return false; -/********************************************************************* -* This function clears out all references to the in-kernel linker, -* frees the list of startup extensions in extensionDict, and -* deallocates the kernel's __KLD segment to reclaim that memory. -*********************************************************************/ -kern_return_t IOCatalogue::removeKernelLinker(void) { - kern_return_t result = KERN_SUCCESS; - extern struct mach_header _mh_execute_header; - struct segment_command * segment; - char * dt_segment_name; - void * segment_paddress; - int segment_size; - - /* This must be the very first thing done by this function. - */ - IOLockLock(kld_lock); - - - /* If the kernel linker isn't here, that's automatically - * a success. - */ - if (!kernelLinkerPresent) { - result = KERN_SUCCESS; - goto finish; - } + return super::serialize(s); +} - IOLog("Jettisoning kernel linker.\n"); +bool IOCatalogue::serializeData(IOOptionBits kind, OSSerialize * s) const +{ + kern_return_t kr = kIOReturnSuccess; - kernelLinkerPresent = 0; + switch ( kind ) + { + case kIOCatalogGetContents: + kr = KERN_NOT_SUPPORTED; + break; - /* Set the kmod_load_extension function as the means for loading - * a kernel extension. - */ - kmod_load_function = &kmod_load_extension; + case kIOCatalogGetModuleDemandList: + kr = KERN_NOT_SUPPORTED; + break; - record_startup_extensions_function = 0; - add_from_mkext_function = 0; - remove_startup_extension_function = 0; + case kIOCatalogGetCacheMissList: + kr = KERN_NOT_SUPPORTED; + break; + case kIOCatalogGetROMMkextList: + kr = KERN_NOT_SUPPORTED; + break; - /* Invoke destructors for the __KLD and __LINKEDIT segments. - * Do this for all segments before actually freeing their - * memory so that any cross-dependencies (not that there - * should be any) are handled. - */ - segment = getsegbynamefromheader( - &_mh_execute_header, "__KLD"); - if (!segment) { - IOLog("error removing kernel linker: can't find __KLD segment\n"); - result = KERN_FAILURE; - goto finish; + default: + kr = kIOReturnBadArgument; + break; } - OSRuntimeUnloadCPPForSegment(segment); - segment = getsegbynamefromheader( - &_mh_execute_header, "__LINKEDIT"); - if (!segment) { - IOLog("error removing kernel linker: can't find __LINKEDIT segment\n"); - result = KERN_FAILURE; - goto finish; - } - OSRuntimeUnloadCPPForSegment(segment); + return kr; +} +/* isModuleLoadedNoOSKextLock - used to check to see if a kext is loaded + * without taking the OSKext lock. We use this to avoid the problem + * where taking the IOCatalog lock then the OSKext lock will dealock when + * a kext load or unload is happening at the same time as IOCatalog changing. + * + * theKexts - is a dictionary of current kexts (from OSKext::copyKexts) with + * key set to the kext bundle ID and value set to an OSKext object + * theModuleDict - is an IOKit personality dictionary for a given module (kext) + */ +static bool isModuleLoadedNoOSKextLock(OSDictionary *theKexts, + OSDictionary *theModuleDict) +{ + bool myResult = false; + const OSString * myBundleID = NULL; // do not release + OSKext * myKext = NULL; // do not release - /* Free the memory that was set up by bootx. - */ - dt_segment_name = "Kernel-__KLD"; - if (0 == IODTGetLoaderInfo(dt_segment_name, &segment_paddress, &segment_size)) { - IODTFreeLoaderInfo(dt_segment_name, (void *)segment_paddress, - (int)segment_size); + if (theKexts == NULL || theModuleDict == NULL) { + return( myResult ); } - - dt_segment_name = "Kernel-__LINKEDIT"; - if (0 == IODTGetLoaderInfo(dt_segment_name, &segment_paddress, &segment_size)) { - IODTFreeLoaderInfo(dt_segment_name, (void *)segment_paddress, - (int)segment_size); + + // gIOModuleIdentifierKey is "CFBundleIdentifier" + myBundleID = OSDynamicCast(OSString, + theModuleDict->getObject(gIOModuleIdentifierKey)); + if (myBundleID == NULL) { + return( myResult ); } + myKext = OSDynamicCast(OSKext, theKexts->getObject(myBundleID->getCStringNoCopy())); + if (myKext) { + myResult = myKext->isLoaded(); + } + + return( myResult ); +} -finish: - - /* This must be the very last thing done before returning. - */ - IOLockUnlock(kld_lock); - return result; -} +#if PRAGMA_MARK +#pragma mark Obsolete Kext Loading Stuff +#endif +/********************************************************************* +********************************************************************** +*** BINARY COMPATIBILITY SECTION *** +********************************************************************** +********************************************************************** +* These functions are no longer used are necessary for C++ binary +* compatibility on i386. +**********************************************************************/