extern "C" {
#include <machine/machine_routines.h>
#include <libkern/kernel_mach_header.h>
#include <kern/host.h>
#include <security/mac_data.h>
};
#include <libkern/c++/OSContainers.h>
#include <libkern/c++/OSUnserialize.h>
#include <libkern/c++/OSKext.h>
#include <libkern/OSKextLibPrivate.h>
#include <libkern/OSDebug.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IOService.h>
#include <IOKit/IOCatalogue.h>
#include <IOKit/IOLib.h>
#include <IOKit/assert.h>
#if PRAGMA_MARK
#pragma mark Internal Declarations
#endif
IOCatalogue * gIOCatalogue;
const OSSymbol * gIOClassKey;
const OSSymbol * gIOProbeScoreKey;
const OSSymbol * gIOModuleIdentifierKey;
IORWLock * gIOCatalogLock;
#if PRAGMA_MARK
#pragma mark Utility functions
#endif
#if PRAGMA_MARK
#pragma mark IOCatalogue class implementation
#endif
#define super OSObject
OSDefineMetaClassAndStructors(IOCatalogue, OSObject)
void IOCatalogue::initialize(void)
{
OSArray * array;
OSString * errorString;
bool rc;
extern const char * gIOKernelConfigTables;
array = OSDynamicCast(OSArray, OSUnserialize(gIOKernelConfigTables, &errorString));
if (!array && errorString) {
IOLog("KernelConfigTables syntax error: %s\n",
errorString->getCStringNoCopy());
errorString->release();
}
gIOClassKey = OSSymbol::withCStringNoCopy( kIOClassKey );
gIOProbeScoreKey = OSSymbol::withCStringNoCopy( kIOProbeScoreKey );
gIOModuleIdentifierKey = OSSymbol::withCStringNoCopy( kCFBundleIdentifierKey );
assert( array && gIOClassKey && gIOProbeScoreKey
&& gIOModuleIdentifierKey);
gIOCatalogue = new IOCatalogue;
assert(gIOCatalogue);
rc = gIOCatalogue->init(array);
assert(rc);
array->release();
}
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();
}
}
bool IOCatalogue::init(OSArray * initArray)
{
OSDictionary * dict;
OSObject * obj;
if ( !super::init() )
return false;
generation = 1;
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);
}
gIOCatalogLock = IORWLockAlloc();
lock = gIOCatalogLock;
return true;
}
void IOCatalogue::free( void )
{
panic("");
}
OSOrderedSet *
IOCatalogue::findDrivers(
IOService * service,
SInt32 * generationCount)
{
OSDictionary * nextTable;
OSOrderedSet * set;
OSArray * array;
const OSMetaClass * meta;
unsigned int idx;
set = OSOrderedSet::withCapacity( 1, IOServiceOrdering,
(void *)gIOProbeScoreKey );
if( !set )
return( 0 );
IORWLockRead(lock);
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();
IORWLockUnlock(lock);
return( set );
}
OSOrderedSet *
IOCatalogue::findDrivers(
OSDictionary * matching,
SInt32 * generationCount)
{
OSCollectionIterator * iter;
OSDictionary * dict;
OSOrderedSet * set;
OSArray * array;
const OSSymbol * key;
unsigned int idx;
OSKext::uniquePersonalityProperties(matching);
set = OSOrderedSet::withCapacity( 1, IOServiceOrdering,
(void *)gIOProbeScoreKey );
if (!set) return (0);
iter = OSCollectionIterator::withCollection(personalities);
if (!iter)
{
set->release();
return (0);
}
IORWLockRead(lock);
while ((key = (const OSSymbol *) iter->getNextObject()))
{
array = (OSArray *) personalities->getObject(key);
if (array) for (idx = 0; (dict = (OSDictionary *) array->getObject(idx)); idx++)
{
if ( dict->isEqualTo(matching, matching) )
set->setObject(dict);
}
}
*generationCount = getGenerationCount();
IORWLockUnlock(lock);
iter->release();
return set;
}
bool IOCatalogue::addDrivers(
OSArray * drivers,
bool doNubMatching)
{
bool result = false;
OSCollectionIterator * iter = NULL; OSOrderedSet * set = NULL; OSObject * object = NULL; OSArray * persons = NULL;
persons = OSDynamicCast(OSArray, drivers);
if (!persons) {
goto finish;
}
set = OSOrderedSet::withCapacity( 10, IOServiceOrdering,
(void *)gIOProbeScoreKey );
if (!set) {
goto finish;
}
iter = OSCollectionIterator::withCollection(persons);
if (!iter) {
goto finish;
}
result = true;
IORWLockWrite(lock);
while ( (object = iter->getNextObject()) ) {
OSDictionary * personality = OSDynamicCast(OSDictionary, object);
SInt count;
if (!personality) {
IOLog("IOCatalogue::addDrivers() encountered non-dictionary; bailing.\n");
result = false;
break;
}
OSKext::uniquePersonalityProperties(personality);
OSArray * array = arrayForPersonality(personality);
if (!array) addPersonality(personality);
else
{
count = array->getCount();
while (count--) {
OSDictionary * driver;
driver = (OSDictionary *)array->getObject(count);
if (personality->isEqualTo(driver)) {
break;
}
}
if (count >= 0) {
continue;
}
result = array->setObject(personality);
if (!result) {
break;
}
}
set->setObject(personality);
}
if (result && doNubMatching && (set->getCount() > 0)) {
IOService::catalogNewDrivers(set);
generation++;
}
IORWLockUnlock(lock);
finish:
if (set) set->release();
if (iter) iter->release();
return result;
}
bool
IOCatalogue::removeDrivers(
OSDictionary * matching,
bool doNubMatching)
{
OSOrderedSet * set;
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;
iter = OSCollectionIterator::withCollection(personalities);
if (!iter)
{
set->release();
return (false);
}
IORWLockWrite(lock);
while ((key = (const OSSymbol *) iter->getNextObject()))
{
array = (OSArray *) personalities->getObject(key);
if (array) for (idx = 0; (dict = (OSDictionary *) array->getObject(idx)); idx++)
{
if ( dict->isEqualTo(matching, matching) ) {
set->setObject(dict);
array->removeObject(idx);
idx--;
}
}
if ( doNubMatching && (set->getCount() > 0) ) {
IOService::catalogNewDrivers(set);
generation++;
}
}
IORWLockUnlock(lock);
set->release();
iter->release();
return true;
}
SInt32 IOCatalogue::getGenerationCount(void) const
{
return( generation );
}
bool IOCatalogue::isModuleLoaded(OSString * moduleName) const
{
return isModuleLoaded(moduleName->getCStringNoCopy());
}
bool IOCatalogue::isModuleLoaded(const char * moduleName) const
{
OSReturn ret;
ret = OSKext::loadKextWithIdentifier(moduleName);
if (kOSKextReturnDeferred == ret) {
return false;
}
return true;
}
bool IOCatalogue::isModuleLoaded(OSDictionary * driver) const
{
OSString * moduleName = NULL;
OSString * publisherName = NULL;
if ( !driver )
return false;
publisherName = OSDynamicCast(OSString,
driver->getObject(kIOPersonalityPublisherKey));
OSKext::recordIdentifierRequest(publisherName);
moduleName = OSDynamicCast(OSString, driver->getObject(gIOModuleIdentifierKey));
if ( moduleName )
return isModuleLoaded(moduleName);
return true;
}
void IOCatalogue::moduleHasLoaded(OSString * moduleName)
{
OSDictionary * dict;
dict = OSDictionary::withCapacity(2);
dict->setObject(gIOModuleIdentifierKey, moduleName);
startMatching(dict);
dict->release();
(void) OSKext::setDeferredLoadSucceeded();
(void) OSKext::considerRebuildOfPrelinkedKernel();
}
void IOCatalogue::moduleHasLoaded(const char * moduleName)
{
OSString * name;
name = OSString::withCString(moduleName);
moduleHasLoaded(name);
name->release();
}
IOReturn IOCatalogue::unloadModule(OSString * moduleName) const
{
return OSKext::removeKextWithIdentifier(moduleName->getCStringNoCopy());
}
IOReturn IOCatalogue::_terminateDrivers(OSDictionary * matching)
{
OSDictionary * dict;
OSIterator * iter;
IOService * service;
IOReturn ret;
if ( !matching )
return kIOReturnBadArgument;
ret = kIOReturnSuccess;
dict = 0;
iter = IORegistryIterator::iterateOver(gIOServicePlane,
kIORegistryIterateRecursively);
if ( !iter )
return kIOReturnNoMemory;
OSKext::uniquePersonalityProperties( matching );
do {
iter->reset();
while( (service = (IOService *)iter->getNextObject()) ) {
dict = service->getPropertyTable();
if ( !dict )
continue;
if ( !dict->isEqualTo(matching, matching) )
continue;
if ( !service->terminate(kIOServiceRequired|kIOServiceSynchronous) ) {
ret = kIOReturnUnsupported;
break;
}
}
} while( !service && !iter->isValid());
iter->release();
return ret;
}
IOReturn IOCatalogue::_removeDrivers(OSDictionary * matching)
{
IOReturn ret = kIOReturnSuccess;
OSCollectionIterator * iter;
OSDictionary * dict;
OSArray * array;
const OSSymbol * key;
unsigned int idx;
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++)
{
if (dict->isEqualTo(matching, matching))
{
array->removeObject(idx);
idx--;
}
}
}
iter->release();
return ret;
}
IOReturn IOCatalogue::terminateDrivers(OSDictionary * matching)
{
IOReturn ret;
ret = _terminateDrivers(matching);
IORWLockWrite(lock);
if (kIOReturnSuccess == ret)
ret = _removeDrivers(matching);
IORWLockUnlock(lock);
return ret;
}
IOReturn IOCatalogue::terminateDriversForModule(
OSString * moduleName,
bool unload)
{
IOReturn ret;
OSDictionary * dict;
bool isLoaded = false;
if (OSKext::isKextWithIdentifierLoaded(moduleName->getCStringNoCopy())) {
isLoaded = true;
if (!OSKext::canUnloadKextWithIdentifier(moduleName,
false)) {
ret = kOSKextReturnInUse;
goto finish;
}
}
dict = OSDictionary::withCapacity(1);
if (!dict) {
ret = kIOReturnNoMemory;
goto finish;
}
dict->setObject(gIOModuleIdentifierKey, moduleName);
ret = _terminateDrivers(dict);
IORWLockWrite(lock);
if (kIOReturnSuccess == ret) {
ret = _removeDrivers(dict);
}
if (unload && isLoaded && ret == kIOReturnSuccess) {
ret = unloadModule(moduleName);
}
IORWLockUnlock(lock);
dict->release();
finish:
return ret;
}
IOReturn IOCatalogue::terminateDriversForModule(
const char * moduleName,
bool unload)
{
OSString * name;
IOReturn ret;
name = OSString::withCString(moduleName);
if ( !name )
return kIOReturnNoMemory;
ret = terminateDriversForModule(name, unload);
name->release();
return ret;
}
bool IOCatalogue::startMatching( OSDictionary * matching )
{
OSCollectionIterator * iter;
OSDictionary * dict;
OSOrderedSet * set;
OSArray * array;
const OSSymbol * key;
unsigned int idx;
if ( !matching )
return false;
set = OSOrderedSet::withCapacity(10, IOServiceOrdering,
(void *)gIOProbeScoreKey);
if ( !set )
return false;
iter = OSCollectionIterator::withCollection(personalities);
if (!iter)
{
set->release();
return false;
}
IORWLockRead(lock);
while ((key = (const OSSymbol *) iter->getNextObject()))
{
array = (OSArray *) personalities->getObject(key);
if (array) for (idx = 0; (dict = (OSDictionary *) array->getObject(idx)); idx++)
{
if (dict->isEqualTo(matching, matching)) {
set->setObject(dict);
}
}
}
if ( set->getCount() > 0 ) {
IOService::catalogNewDrivers(set);
generation++;
}
IORWLockUnlock(lock);
set->release();
iter->release();
return true;
}
void IOCatalogue::reset(void)
{
IOCatalogue::resetAndAddDrivers( NULL,
false);
return;
}
bool IOCatalogue::resetAndAddDrivers(OSArray * drivers, bool doNubMatching)
{
bool result = false;
OSArray * newPersonalities = NULL; OSCollectionIterator * iter = NULL; OSOrderedSet * matchSet = NULL; const OSSymbol * key;
OSArray * array;
OSDictionary * thisNewPersonality = NULL; OSDictionary * thisOldPersonality = NULL; 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;
}
}
result = true;
IOLog("Resetting IOCatalogue.\n");
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;
if (newPersonalities)
for (newIdx = 0;
(thisNewPersonality = (OSDictionary *) newPersonalities->getObject(newIdx));
newIdx++)
{
if (OSDynamicCast(OSDictionary, thisNewPersonality) == NULL) {
continue;
}
if (thisNewPersonality->isEqualTo(thisOldPersonality))
break;
}
if (thisNewPersonality)
{
newPersonalities->removeObject(newIdx);
}
else
{
if ( isModuleLoaded(thisOldPersonality) == false )
{
if (matchSet) matchSet->setObject(thisOldPersonality);
array->removeObject(idx);
idx--;
}
}
}
}
for (newIdx = 0;
(thisNewPersonality = (OSDictionary *) newPersonalities->getObject(newIdx));
newIdx++)
{
if (OSDynamicCast(OSDictionary, thisNewPersonality) == NULL) {
continue;
}
OSKext::uniquePersonalityProperties(thisNewPersonality);
addPersonality(thisNewPersonality);
matchSet->setObject(thisNewPersonality);
}
if (result && doNubMatching && (matchSet->getCount() > 0)) {
IOService::catalogNewDrivers(matchSet);
generation++;
}
IORWLockUnlock(lock);
finish:
if (matchSet) matchSet->release();
if (iter) iter->release();
return result;
}
bool IOCatalogue::serialize(OSSerialize * s) const
{
if ( !s )
return false;
return super::serialize(s);
}
bool IOCatalogue::serializeData(IOOptionBits kind, OSSerialize * s) const
{
kern_return_t kr = kIOReturnSuccess;
switch ( kind )
{
case kIOCatalogGetContents:
kr = KERN_NOT_SUPPORTED;
break;
case kIOCatalogGetModuleDemandList:
kr = KERN_NOT_SUPPORTED;
break;
case kIOCatalogGetCacheMissList:
kr = KERN_NOT_SUPPORTED;
break;
case kIOCatalogGetROMMkextList:
kr = KERN_NOT_SUPPORTED;
break;
default:
kr = kIOReturnBadArgument;
break;
}
return kr;
}
#if PRAGMA_MARK
#pragma mark Obsolete Kext Loading Stuff
#endif