#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IOService.h>
#include <libkern/c++/OSContainers.h>
#include <IOKit/IOCatalogue.h>
#include <libkern/c++/OSUnserialize.h>
extern "C" {
#include <machine/machine_routines.h>
#include <mach/kmod.h>
#include <mach-o/mach_header.h>
#include <kern/host.h>
};
#include <IOKit/IOLib.h>
#include <IOKit/assert.h>
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);
};
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;
};
int kernelLinkerPresent = 0;
#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);
dict->setObject( gIOMatchCategoryKey, (OSSymbol *) classSymbol);
classSymbol->release();
}
}
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 );
assert( array && gIOClassKey && gIOProbeScoreKey);
gIOCatalogue = new IOCatalogue;
assert(gIOCatalogue);
rc = gIOCatalogue->init(array);
assert(rc);
array->release();
}
bool IOCatalogue::init(OSArray * initArray)
{
IORegistryEntry * entry;
OSDictionary * dict;
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);
if( 0 == dict->getObject( gIOClassKey ))
IOLog("Missing or bad \"%s\" key\n",
gIOClassKey->getCStringNoCopy());
}
#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);
return true;
}
void IOCatalogue::free( void )
{
if ( array )
array->release();
if ( kernelTables )
kernelTables->release();
super::free();
}
#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 )
{
OSDictionary * nextTable;
OSOrderedSet * set;
OSString * imports;
set = OSOrderedSet::withCapacity( 1, IOServiceOrdering,
(void *)gIOProbeScoreKey );
if( !set )
return( 0 );
IOTakeLock( lock );
kernelTables->reset();
#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 );
}
*generationCount = getGenerationCount();
IOUnlock( lock );
return( set );
}
OSOrderedSet * IOCatalogue::findDrivers( OSDictionary * matching,
SInt32 * generationCount)
{
OSDictionary * dict;
OSOrderedSet * set;
UniqueProperties(matching);
set = OSOrderedSet::withCapacity( 1, IOServiceOrdering,
(void *)gIOProbeScoreKey );
IOTakeLock( lock );
kernelTables->reset();
while ( (dict = (OSDictionary *) kernelTables->getNextObject()) ) {
if ( dict->isEqualTo(matching, matching) )
set->setObject(dict);
}
*generationCount = getGenerationCount();
IOUnlock( lock );
return set;
}
static void AddNewImports( OSOrderedSet * set, OSDictionary * dict )
{
set->setObject(dict);
}
bool IOCatalogue::addDrivers(OSArray * drivers,
bool doNubMatching = true )
{
OSCollectionIterator * iter;
OSDictionary * dict;
OSOrderedSet * set;
OSArray * persons;
bool ret;
ret = true;
persons = OSDynamicCast(OSArray, drivers);
if ( !persons )
return false;
iter = OSCollectionIterator::withCollection( persons );
if (!iter )
return false;
set = OSOrderedSet::withCapacity( 10, IOServiceOrdering,
(void *)gIOProbeScoreKey );
if ( !set ) {
iter->release();
return false;
}
IOTakeLock( lock );
while ( (dict = (OSDictionary *) iter->getNextObject()) ) {
UInt count;
UniqueProperties( dict );
count = array->getCount();
while ( count-- ) {
OSDictionary * driver;
driver = (OSDictionary *)array->getObject(count);
if ( dict->isEqualTo(driver) ) {
array->removeObject(count);
break;
}
}
ret = array->setObject( dict );
if ( !ret )
break;
AddNewImports( set, dict );
}
if ( doNubMatching && (set->getCount() > 0) ) {
IOService::catalogNewDrivers( set );
generation++;
}
IOUnlock( lock );
set->release();
iter->release();
return ret;
}
bool IOCatalogue::removeDrivers( OSDictionary * matching,
bool doNubMatching = true)
{
OSCollectionIterator * tables;
OSDictionary * dict;
OSOrderedSet * set;
OSArray * arrayCopy;
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;
}
UniqueProperties( matching );
IOTakeLock( lock );
kernelTables->reset();
arrayCopy->merge(array);
array->flushCollection();
tables->reset();
while ( (dict = (OSDictionary *)tables->getNextObject()) ) {
if ( dict->isEqualTo(matching, matching) ) {
AddNewImports( set, dict );
continue;
}
array->setObject(dict);
}
if ( doNubMatching && (set->getCount() > 0) ) {
IOService::catalogNewDrivers(set);
generation++;
}
IOUnlock( lock );
set->release();
tables->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
{
kmod_info_t * k_info;
if ( !moduleName )
return false;
k_info = kmod_lookupbyname_locked((char *)moduleName);
if ( !k_info ) {
kern_return_t ret;
IOLockLock(kld_lock);
if (kmod_load_function != 0) {
ret = kmod_load_function((char *)moduleName);
if ( ret != kIOReturnSuccess ) {
IOLog("IOCatalogue: %s cannot be loaded.\n", moduleName);
if (kernelLinkerPresent && remove_startup_extension_function) {
(*remove_startup_extension_function)(moduleName);
}
IOLockUnlock(kld_lock);
return false;
} else if (kernelLinkerPresent) {
IOLockUnlock(kld_lock);
return true;
} else {
IOLockUnlock(kld_lock);
return false;
}
} else {
IOLog("IOCatalogue: %s cannot be loaded "
"(kmod load function not set).\n",
moduleName);
}
IOLockUnlock(kld_lock);
return false;
}
if (k_info) {
kfree(k_info, sizeof(kmod_info_t));
}
return true;
}
bool IOCatalogue::isModuleLoaded( OSDictionary * driver ) const
{
OSString * moduleName = NULL;
if ( !driver )
return false;
moduleName = OSDynamicCast(OSString, driver->getObject(kModuleKey));
if ( moduleName )
return isModuleLoaded(moduleName);
return true;
}
void IOCatalogue::moduleHasLoaded( OSString * moduleName )
{
OSDictionary * dict;
dict = OSDictionary::withCapacity(2);
dict->setObject(kModuleKey, moduleName);
startMatching(dict);
dict->release();
}
void IOCatalogue::moduleHasLoaded( const char * moduleName )
{
OSString * name;
name = OSString::withCString(moduleName);
moduleHasLoaded(name);
name->release();
}
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;
}
static IOReturn _terminateDrivers( OSArray * array, OSDictionary * matching )
{
OSCollectionIterator * tables;
OSDictionary * dict;
OSIterator * iter;
OSArray * arrayCopy;
IOService * service;
IOReturn ret;
if ( !matching )
return kIOReturnBadArgument;
ret = kIOReturnSuccess;
dict = 0;
iter = IORegistryIterator::iterateOver(gIOServicePlane,
kIORegistryIterateRecursively);
if ( !iter )
return kIOReturnNoMemory;
UniqueProperties( 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();
if ( ret != kIOReturnSuccess )
return ret;
arrayCopy = OSArray::withCapacity(100);
if ( !arrayCopy )
return kIOReturnNoMemory;
tables = OSCollectionIterator::withCollection(arrayCopy);
arrayCopy->release();
if ( !tables )
return kIOReturnNoMemory;
arrayCopy->merge(array);
array->flushCollection();
tables->reset();
while ( (dict = (OSDictionary *)tables->getNextObject()) ) {
if ( dict->isEqualTo(matching, matching) )
continue;
array->setObject(dict);
}
tables->release();
return ret;
}
IOReturn IOCatalogue::terminateDrivers( OSDictionary * matching )
{
IOReturn ret;
ret = kIOReturnSuccess;
IOTakeLock( lock );
ret = _terminateDrivers(array, matching);
kernelTables->reset();
IOUnlock( lock );
return ret;
}
IOReturn IOCatalogue::terminateDriversForModule(
OSString * moduleName,
bool unload )
{
IOReturn ret;
OSDictionary * dict;
dict = OSDictionary::withCapacity(1);
if ( !dict )
return kIOReturnNoMemory;
dict->setObject(kModuleKey, moduleName);
IOTakeLock( lock );
ret = _terminateDrivers(array, dict);
kernelTables->reset();
if ( unload && ret == kIOReturnSuccess ) {
ret = unloadModule(moduleName);
}
IOUnlock( lock );
dict->release();
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 )
{
OSDictionary * dict;
OSOrderedSet * set;
if ( !matching )
return false;
set = OSOrderedSet::withCapacity(10, IOServiceOrdering,
(void *)gIOProbeScoreKey);
if ( !set )
return false;
IOTakeLock( lock );
kernelTables->reset();
while ( (dict = (OSDictionary *)kernelTables->getNextObject()) ) {
if ( dict->isEqualTo(matching, matching) )
AddNewImports(set, dict);
}
if ( set->getCount() > 0 ) {
IOService::catalogNewDrivers(set);
generation++;
}
IOUnlock( lock );
set->release();
return true;
}
void IOCatalogue::reset(void)
{
OSArray * tables;
OSDictionary * entry;
unsigned int count;
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);
}
}
kernelTables->reset();
IOUnlock( lock );
tables->release();
}
bool IOCatalogue::serialize(OSSerialize * s) const
{
bool ret;
if ( !s )
return false;
IOTakeLock( lock );
ret = array->serialize(s);
IOUnlock( lock );
return ret;
}
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;
}
IOLockUnlock(kld_lock);
return result;
}
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);
return result;
}
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;
IOLockLock(kld_lock);
if (!kernelLinkerPresent) {
result = KERN_SUCCESS;
goto finish;
}
IOLog("Jettisoning kernel linker.\n");
kernelLinkerPresent = 0;
kmod_load_function = &kmod_load_extension;
record_startup_extensions_function = 0;
add_from_mkext_function = 0;
remove_startup_extension_function = 0;
segment = getsegbynamefromheader(
&_mh_execute_header, "__KLD");
if (!segment) {
IOLog("error removing kernel linker: can't find __KLD segment\n");
result = KERN_FAILURE;
goto finish;
}
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);
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);
}
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);
}
finish:
IOLockUnlock(kld_lock);
return result;
}