#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 <security/mac_data.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 kModuleKey "CFBundleIdentifier"
#define super OSObject
OSDefineMetaClassAndStructors(IOCatalogue, OSObject)
#define CATALOGTEST 0
IOCatalogue * gIOCatalogue;
const OSSymbol * gIOClassKey;
const OSSymbol * gIOProbeScoreKey;
const OSSymbol * gIOModuleIdentifierKey;
OSSet * gIOCatalogModuleRequests;
OSSet * gIOCatalogCacheMisses;
OSSet * gIOCatalogROMMkexts;
IOLock * gIOCatalogLock;
IOLock * gIOKLDLock;
OSArray * gIOPrelinkedModules = 0;
extern "C" kern_return_t
kmod_create_internal(
kmod_info_t *info,
kmod_t *id);
extern "C" kern_return_t
kmod_destroy_internal(kmod_t id);
extern "C" kern_return_t
kmod_start_or_stop(
kmod_t id,
int start,
kmod_args_t *data,
mach_msg_type_number_t *dataCount);
extern "C" kern_return_t kmod_retain(kmod_t id);
extern "C" kern_return_t kmod_release(kmod_t id);
#if CONFIG_MACF_KEXT
#ifdef IOC_DEBUG
#define DPRINTF(x) printf x
#else
#define IOC_DEBUG
#define DPRINTF(x)
#endif
static bool
primitive_type(OSObject *obj)
{
const OSMetaClass *typeID;
typeID = OSTypeIDInst(obj);
if (typeID == OSTypeID(OSString) || typeID == OSTypeID(OSNumber) ||
typeID == OSTypeID(OSBoolean) || typeID == OSTypeID(OSData))
return(true);
else
return(false);
}
static int
primitive_type_length(OSObject *obj)
{
const OSMetaClass *typeID;
int len;
typeID = OSTypeIDInst(obj);
if (typeID == OSTypeID(OSString)) {
OSString * stringObj = OSDynamicCast(OSString, obj);
len = stringObj->getLength() + 1;
}
else if (typeID == OSTypeID(OSNumber)) {
len = sizeof("4294967295");
}
else if (typeID == OSTypeID(OSBoolean)) {
OSBoolean * boolObj = OSDynamicCast(OSBoolean, obj);
len = boolObj->isTrue() ? sizeof("true") : sizeof("false");
}
else if (typeID == OSTypeID(OSData)) {
OSData * dataObj = OSDynamicCast(OSData, obj);
len = dataObj->getLength();
}
else {
len = 0;
}
return(len);
}
static void
primitive_type_collect(struct mac_module_data_element *element, OSObject *value)
{
const OSMetaClass *typeID;
typeID = OSTypeIDInst(value);
if (typeID == OSTypeID(OSString)) {
OSString *stringObj = OSDynamicCast(OSString, value);
element->value_type = MAC_DATA_TYPE_PRIMITIVE;
element->value_size = stringObj->getLength() + 1;
DPRINTF(("osdict: string %s size %d\n",
stringObj->getCStringNoCopy(), element->value_size));
memcpy(element->value, stringObj->getCStringNoCopy(),
element->value_size);
} else if (typeID == OSTypeID(OSNumber)) {
OSNumber *numberObj = OSDynamicCast(OSNumber, value);
element->value_type = MAC_DATA_TYPE_PRIMITIVE;
element->value_size = sprintf(element->value, "%u",
numberObj->unsigned32BitValue()) + 1;
} else if (typeID == OSTypeID(OSBoolean)) {
OSBoolean *boolObj = OSDynamicCast(OSBoolean, value);
element->value_type = MAC_DATA_TYPE_PRIMITIVE;
if (boolObj->isTrue()) {
strcpy(element->value, "true");
element->value_size = 5;
} else {
strcpy(element->value, "false");
element->value_size = 6;
}
} else if (typeID == OSTypeID(OSData)) {
OSData *dataObj = OSDynamicCast(OSData, value);
element->value_type = MAC_DATA_TYPE_PRIMITIVE;
element->value_size = dataObj->getLength();
DPRINTF(("osdict: data size %d\n", dataObj->getLength()));
memcpy(element->value, dataObj->getBytesNoCopy(),
element->value_size);
}
}
struct mac_module_data *
osdict_encode(OSDictionary *dict)
{
const OSMetaClass * typeID; OSString * key = NULL; OSCollectionIterator * keyIterator = 0; struct mac_module_data * module_data = 0;
struct mac_module_data_element * element;
unsigned int strtabsize = 0;
unsigned int listtabsize = 0;
unsigned int dicttabsize = 0;
unsigned int nkeys = 0;
unsigned int datalen;
char *strtab = NULL;
char *listtab = NULL;
char *dicttab = NULL;
vm_offset_t data_addr;
keyIterator = OSCollectionIterator::withCollection(dict);
if (!keyIterator)
goto finish;
while ( (key = OSDynamicCast(OSString, keyIterator->getNextObject())) ) {
OSObject * value = dict->getObject(key);
if (!value)
continue;
typeID = OSTypeIDInst(value);
if (primitive_type(value)) {
strtabsize += primitive_type_length(value);
}
else if (typeID == OSTypeID(OSArray)) {
unsigned int k, cnt, nents;
OSArray *arrayObj = OSDynamicCast(OSArray, value);
nents = 0;
cnt = arrayObj->getCount();
for (k = 0; k < cnt; k++) {
value = arrayObj->getObject(k);
typeID = OSTypeIDInst(value);
if (primitive_type(value)) {
listtabsize += primitive_type_length(value);
nents++;
}
else if (typeID == OSTypeID(OSDictionary)) {
unsigned int dents;
OSDictionary *dictObj;
OSString *dictkey;
OSCollectionIterator *dictIterator;
dents = 0;
dictObj = OSDynamicCast(OSDictionary, value);
dictIterator = OSCollectionIterator::withCollection(dictObj);
if (!dictIterator)
goto finish;
while ((dictkey = OSDynamicCast(OSString,
dictIterator->getNextObject()))) {
OSObject *dictvalue;
dictvalue = dictObj->getObject(dictkey);
if (!dictvalue)
continue;
if (primitive_type(dictvalue)) {
strtabsize += primitive_type_length(dictvalue);
}
else {
continue;
}
strtabsize += dictkey->getLength() + 1;
dents++;
}
dictIterator->release();
if (dents-- > 0) {
dicttabsize += sizeof(struct mac_module_data_list) +
dents * sizeof(struct mac_module_data_element);
nents++;
}
}
else {
continue;
}
}
if (nents == 0)
continue;
listtabsize += sizeof(struct mac_module_data_list) +
(nents - 1) * sizeof(struct mac_module_data_element);
}
else {
continue;
}
strtabsize += key->getLength() + 1;
nkeys++;
}
if (nkeys == 0)
goto finish;
datalen = sizeof(struct mac_module_data) +
sizeof(mac_module_data_element) * (nkeys - 1) +
strtabsize + listtabsize + dicttabsize;
DPRINTF(("osdict: datalen %d strtabsize %d listtabsize %d dicttabsize %d\n",
datalen, strtabsize, listtabsize, dicttabsize));
if (kmem_alloc(kernel_map, &data_addr, datalen) != KERN_SUCCESS)
goto finish;
module_data = (mac_module_data *)data_addr;
module_data->base_addr = data_addr;
module_data->size = datalen;
module_data->count = nkeys;
strtab = (char *)&module_data->data[nkeys];
listtab = strtab + strtabsize;
dicttab = listtab + listtabsize;
DPRINTF(("osdict: data_addr %p strtab %p listtab %p dicttab %p end %p\n",
data_addr, strtab, listtab, dicttab, data_addr + datalen));
keyIterator->reset();
nkeys = 0;
element = &module_data->data[0];
DPRINTF(("osdict: element %p\n", element));
while ( (key = OSDynamicCast(OSString, keyIterator->getNextObject())) ) {
OSObject * value = dict->getObject(key);
if (!value)
continue;
DPRINTF(("osdict: element @%p\n", element));
element->key = strtab;
element->key_size = key->getLength() + 1;
DPRINTF(("osdict: key %s size %d @%p\n", key->getCStringNoCopy(), element->key_size, strtab));
memcpy(element->key, key->getCStringNoCopy(), element->key_size);
typeID = OSTypeIDInst(value);
if (primitive_type(value)) {
element->value = element->key + element->key_size;
DPRINTF(("osdict: primitive element value %p\n", element->value));
primitive_type_collect(element, value);
strtab += element->key_size + element->value_size;
DPRINTF(("osdict: new strtab %p\n", strtab));
}
else if (typeID == OSTypeID(OSArray)) {
unsigned int k, cnt, nents;
char *astrtab;
struct mac_module_data_list *arrayhd;
struct mac_module_data_element *ele;
OSArray *arrayObj = OSDynamicCast(OSArray, value);
element->value = listtab;
DPRINTF(("osdict: array element value %p\n", element->value));
element->value_type = MAC_DATA_TYPE_ARRAY;
arrayhd = (struct mac_module_data_list *)element->value;
arrayhd->type = 0;
DPRINTF(("osdict: arrayhd %p\n", arrayhd));
nents = 0;
astrtab = strtab + element->key_size;
ele = &(arrayhd->list[0]);
cnt = arrayObj->getCount();
for (k = 0; k < cnt; k++) {
value = arrayObj->getObject(k);
DPRINTF(("osdict: array ele %d @%p\n", nents, ele));
ele->key = NULL;
ele->key_size = 0;
typeID = OSTypeIDInst(value);
if (primitive_type(value)) {
if (arrayhd->type != 0 &&
arrayhd->type != MAC_DATA_TYPE_PRIMITIVE)
continue;
arrayhd->type = MAC_DATA_TYPE_PRIMITIVE;
ele->value = astrtab;
primitive_type_collect(ele, value);
astrtab += ele->value_size;
DPRINTF(("osdict: array new astrtab %p\n", astrtab));
}
else if (typeID == OSTypeID(OSDictionary)) {
unsigned int dents;
char *dstrtab;
OSDictionary *dictObj;
OSString *dictkey;
OSCollectionIterator *dictIterator;
struct mac_module_data_list *dicthd;
struct mac_module_data_element *dele;
if (arrayhd->type != 0 &&
arrayhd->type != MAC_DATA_TYPE_DICT)
continue;
dictObj = OSDynamicCast(OSDictionary, value);
dictIterator = OSCollectionIterator::withCollection(dictObj);
if (!dictIterator)
goto finish;
DPRINTF(("osdict: dict\n"));
ele->value = dicttab;
ele->value_type = MAC_DATA_TYPE_DICT;
dicthd = (struct mac_module_data_list *)ele->value;
DPRINTF(("osdict: dicthd %p\n", dicthd));
dstrtab = astrtab;
dents = 0;
while ((dictkey = OSDynamicCast(OSString,
dictIterator->getNextObject()))) {
OSObject *dictvalue;
dictvalue = dictObj->getObject(dictkey);
if (!dictvalue)
continue;
dele = &(dicthd->list[dents]);
DPRINTF(("osdict: dict ele %d @%p\n", dents, dele));
if (primitive_type(dictvalue)) {
dele->key = dstrtab;
dele->key_size = dictkey->getLength() + 1;
DPRINTF(("osdict: dictkey %s size %d @%p\n",
dictkey->getCStringNoCopy(), dictkey->getLength(), dstrtab));
memcpy(dele->key, dictkey->getCStringNoCopy(),
dele->key_size);
dele->value = dele->key + dele->key_size;
primitive_type_collect(dele, dictvalue);
dstrtab += dele->key_size + dele->value_size;
DPRINTF(("osdict: dict new dstrtab %p\n", dstrtab));
}
else {
continue;
}
dents++;
}
dictIterator->release();
if (dents == 0)
continue;
arrayhd->type = MAC_DATA_TYPE_DICT;
ele->value_size = sizeof(struct mac_module_data_list) +
(dents - 1) * sizeof(struct mac_module_data_element);
DPRINTF(("osdict: dict ele size %d ents %d\n", ele->value_size, dents));
dicttab += ele->value_size;
DPRINTF(("osdict: new dicttab %p\n", dicttab));
dicthd->count = dents;
astrtab = dstrtab;
}
else {
continue;
}
nents++;
ele++;
}
if (nents == 0)
continue;
element->value_size = sizeof(struct mac_module_data_list) +
(nents - 1) * sizeof(struct mac_module_data_element);
listtab += element->value_size;
DPRINTF(("osdict: new listtab %p\n", listtab));
arrayhd->count = nents;
strtab = astrtab;
DPRINTF(("osdict: new strtab %p\n", strtab));
}
else {
continue;
}
element++;
}
DPRINTF(("module_data list @%p, key %p value %p\n",
module_data, module_data->data[0].key, module_data->data[0].value));
finish:
if (keyIterator)
keyIterator->release();
return(module_data);
}
kmod_args_t
get_module_data(OSDictionary * kextPlist, mach_msg_type_number_t * datalen)
{
OSDictionary * kextModuleData = 0; struct mac_module_data * module_data = 0;
vm_map_copy_t copy = 0;
kextModuleData = OSDynamicCast(OSDictionary,
kextPlist->getObject("OSModuleData"));
if (!kextModuleData)
goto finish;
module_data = osdict_encode(kextModuleData);
if (!module_data)
goto finish;
*datalen = module_data->size;
vm_map_copyin(kernel_map, (vm_offset_t)module_data, *datalen, FALSE, ©);
kmem_free(kernel_map, (vm_offset_t)module_data, *datalen);
DPRINTF(("get_module_data: copy @ %p\n", copy));
finish:
return (kmod_args_t)copy;
}
#endif
static
kern_return_t start_prelink_module(UInt32 moduleIndex)
{
kern_return_t kr = KERN_SUCCESS;
UInt32 * togo;
SInt32 count, where, end;
UInt32 * prelink;
SInt32 next, lastDep;
OSData * data;
OSString * str;
OSDictionary * dict;
OSArray *
prelinkedModules = gIOPrelinkedModules;
togo = IONew(UInt32, prelinkedModules->getCount());
togo[0] = moduleIndex;
count = 1;
for (next = 0; next < count; next++)
{
dict = (OSDictionary *) prelinkedModules->getObject(togo[next]);
data = OSDynamicCast(OSData, dict->getObject("OSBundlePrelink"));
if (!data)
{
if (togo[next] == moduleIndex)
{
kr = KERN_FAILURE;
break;
}
continue;
}
prelink = (UInt32 *) data->getBytesNoCopy();
lastDep = OSReadBigInt32(prelink, 12);
for (SInt32 idx = OSReadBigInt32(prelink, 8); idx < lastDep; idx += sizeof(UInt32))
{
UInt32 depIdx = OSReadBigInt32(prelink, idx) - 1;
for (where = next + 1;
(where < count) && (togo[where] > depIdx);
where++) {}
if (where != count)
{
if (togo[where] == depIdx)
continue;
for (end = count; end != where; end--)
togo[end] = togo[end - 1];
}
count++;
togo[where] = depIdx;
}
}
if (KERN_SUCCESS != kr)
return kr;
for (next = (count - 1); next >= 0; next--)
{
dict = (OSDictionary *) prelinkedModules->getObject(togo[next]);
data = OSDynamicCast(OSData, dict->getObject("OSBundlePrelink"));
if (!data)
continue;
prelink = (UInt32 *) data->getBytesNoCopy();
kmod_t id;
kmod_info_t * kmod_info = (kmod_info_t *) OSReadBigInt32(prelink, 0);
kr = kmod_create_internal(kmod_info, &id);
if (KERN_SUCCESS != kr)
break;
lastDep = OSReadBigInt32(prelink, 12);
for (SInt32 idx = OSReadBigInt32(prelink, 8); idx < lastDep; idx += sizeof(UInt32))
{
OSDictionary * depDict;
kmod_info_t * depInfo;
depDict = (OSDictionary *) prelinkedModules->getObject(OSReadBigInt32(prelink, idx) - 1);
str = OSDynamicCast(OSString, depDict->getObject(kModuleKey));
depInfo = kmod_lookupbyname_locked(str->getCStringNoCopy());
if (depInfo)
{
kr = kmod_retain(KMOD_PACK_IDS(id, depInfo->id));
kfree(depInfo, sizeof(kmod_info_t));
} else
IOLog("%s: NO DEP %s\n", kmod_info->name, str->getCStringNoCopy());
}
dict->removeObject("OSBundlePrelink");
if (kmod_info->start)
kr = kmod_start_or_stop(kmod_info->id, 1, 0, 0);
}
IODelete(togo, UInt32, prelinkedModules->getCount());
return kr;
}
static
kern_return_t kmod_load_from_cache_sym(const OSSymbol * kmod_name)
{
OSArray * prelinkedModules = gIOPrelinkedModules;
kern_return_t result = KERN_FAILURE;
OSDictionary * dict;
OSObject * ident;
UInt32 idx;
if (!gIOPrelinkedModules)
return KERN_FAILURE;
for (idx = 0;
(dict = (OSDictionary *) prelinkedModules->getObject(idx));
idx++)
{
if ((ident = dict->getObject(kModuleKey))
&& kmod_name->isEqualTo(ident))
break;
}
if (dict)
{
if (kernelLinkerPresent && dict->getObject("OSBundleDefer"))
{
kmod_load_extension((char *) kmod_name->getCStringNoCopy());
result = kIOReturnOffline;
}
else
result = start_prelink_module(idx);
}
return result;
}
extern "C" Boolean kmod_load_request(const char * moduleName, Boolean make_request)
{
bool ret, cacheMiss = false;
kern_return_t kr;
const OSSymbol * sym = 0;
kmod_info_t * kmod_info;
if (!moduleName)
return false;
IOLockLock(gIOKLDLock);
do
{
ret = (0 != (kmod_info = kmod_lookupbyname_locked((char *)moduleName)));
if (ret) {
kfree(kmod_info, sizeof(kmod_info_t));
break;
}
sym = OSSymbol::withCString(moduleName);
if (!sym) {
ret = false;
break;
}
kr = kmod_load_from_cache_sym(sym);
ret = (kIOReturnSuccess == kr);
cacheMiss = !ret;
if (ret || !make_request || (kr == kIOReturnOffline))
break;
if (!kmod_load_function) {
IOLog("IOCatalogue: %s cannot be loaded "
"(kmod load function not set).\n",
moduleName);
ret = true;
break;
}
kr = 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);
}
ret = false;
} else if (kernelLinkerPresent) {
ret = true;
} else {
ret = false;
}
}
while (false);
IOLockUnlock(gIOKLDLock);
if (sym)
{
IOLockLock(gIOCatalogLock);
gIOCatalogModuleRequests->setObject(sym);
if (cacheMiss)
gIOCatalogCacheMisses->setObject(sym);
IOLockUnlock(gIOCatalogLock);
}
return ret;
}
extern "C" kern_return_t kmod_unload_cache(void)
{
OSArray * prelinkedModules = gIOPrelinkedModules;
kern_return_t result = KERN_FAILURE;
OSDictionary * dict;
UInt32 idx;
UInt32 * prelink;
OSData * data;
if (!gIOPrelinkedModules)
return KERN_SUCCESS;
IOLockLock(gIOKLDLock);
for (idx = 0;
(dict = (OSDictionary *) prelinkedModules->getObject(idx));
idx++)
{
data = OSDynamicCast(OSData, dict->getObject("OSBundlePrelink"));
if (!data)
continue;
prelink = (UInt32 *) data->getBytesNoCopy();
kmod_info_t * kmod_info = (kmod_info_t *) OSReadBigInt32(prelink, 0);
vm_offset_t
virt = ml_static_ptovirt(kmod_info->address);
if( virt) {
ml_static_mfree(virt, kmod_info->size);
}
}
gIOPrelinkedModules->release();
gIOPrelinkedModules = 0;
IOLockUnlock(gIOKLDLock);
return result;
}
extern "C" kern_return_t kmod_load_from_cache(const char * kmod_name)
{
kern_return_t kr;
const OSSymbol * sym = OSSymbol::withCStringNoCopy(kmod_name);
if (sym)
{
kr = kmod_load_from_cache_sym(sym);
sym->release();
}
else
kr = kIOReturnNoMemory;
return kr;
}
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 );
gIOModuleIdentifierKey = OSSymbol::withCStringNoCopy( kModuleKey );
gIOCatalogModuleRequests = OSSet::withCapacity(16);
gIOCatalogCacheMisses = OSSet::withCapacity(16);
gIOCatalogROMMkexts = OSSet::withCapacity(4);
assert( array && gIOClassKey && gIOProbeScoreKey
&& gIOModuleIdentifierKey && gIOCatalogModuleRequests);
gIOCatalogue = new IOCatalogue;
assert(gIOCatalogue);
rc = gIOCatalogue->init(array);
assert(rc);
array->release();
}
bool IOCatalogue::init(OSArray * initArray)
{
OSDictionary * dict;
if ( !super::init() )
return false;
generation = 1;
array = initArray;
array->retain();
kernelTables = OSCollectionIterator::withCollection( array );
gIOCatalogLock = IOLockAlloc();
gIOKLDLock = IOLockAlloc();
lock = gIOCatalogLock;
kld_lock = gIOKLDLock;
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
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 );
IOLockLock( &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++;
IOLockUnlock( &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 );
IOLockLock( 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();
IOLockUnlock( lock );
return( set );
}
OSOrderedSet * IOCatalogue::findDrivers( OSDictionary * matching,
SInt32 * generationCount)
{
OSDictionary * dict;
OSOrderedSet * set;
UniqueProperties(matching);
set = OSOrderedSet::withCapacity( 1, IOServiceOrdering,
(void *)gIOProbeScoreKey );
IOLockLock( lock );
kernelTables->reset();
while ( (dict = (OSDictionary *) kernelTables->getNextObject()) ) {
if ( dict->isEqualTo(matching, matching) )
set->setObject(dict);
}
*generationCount = getGenerationCount();
IOLockUnlock( lock );
return set;
}
static void AddNewImports( OSOrderedSet * set, OSDictionary * dict )
{
set->setObject(dict);
}
bool IOCatalogue::addDrivers(OSArray * drivers,
bool doNubMatching )
{
OSCollectionIterator * iter;
OSDictionary * dict;
OSOrderedSet * set;
OSArray * persons;
OSString * moduleName;
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;
}
IOLockLock( lock );
while ( (dict = (OSDictionary *) iter->getNextObject()) )
{
if ((moduleName = OSDynamicCast(OSString, dict->getObject("OSBundleModuleDemand"))))
{
IOLockUnlock( lock );
ret = kmod_load_request(moduleName->getCStringNoCopy(), false);
IOLockLock( lock );
ret = true;
}
else
{
SInt count;
UniqueProperties( dict );
count = array->getCount();
while ( count-- ) {
OSDictionary * driver;
driver = (OSDictionary *)array->getObject(count);
if (dict->isEqualTo(driver))
break;
}
if (count >= 0)
continue;
ret = array->setObject( dict );
if (!ret)
break;
AddNewImports( set, dict );
}
}
if (doNubMatching && (set->getCount() > 0)) {
IOService::catalogNewDrivers( set );
generation++;
}
IOLockUnlock( lock );
set->release();
iter->release();
return ret;
}
bool IOCatalogue::removeDrivers( OSDictionary * matching,
bool doNubMatching)
{
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 );
IOLockLock( 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++;
}
IOLockUnlock( 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
{
return (kmod_load_request(moduleName, true));
}
bool IOCatalogue::isModuleLoaded( OSDictionary * driver ) const
{
OSString * moduleName = NULL;
if ( !driver )
return false;
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 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) ) {
record_kext_unload(k_info->id);
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( 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;
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();
return ret;
}
static IOReturn _removeDrivers( OSArray * array, OSDictionary * matching )
{
OSCollectionIterator * tables;
OSDictionary * dict;
OSArray * arrayCopy;
IOReturn ret = kIOReturnSuccess;
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 = _terminateDrivers(matching);
IOLockLock( lock );
if (kIOReturnSuccess == ret)
ret = _removeDrivers(array, matching);
kernelTables->reset();
IOLockUnlock( lock );
return ret;
}
IOReturn IOCatalogue::terminateDriversForModule(
OSString * moduleName,
bool unload )
{
IOReturn ret;
OSDictionary * dict;
dict = OSDictionary::withCapacity(1);
if ( !dict )
return kIOReturnNoMemory;
dict->setObject(gIOModuleIdentifierKey, moduleName);
ret = _terminateDrivers(dict);
IOLockLock( lock );
if (kIOReturnSuccess == ret)
ret = _removeDrivers(array, dict);
kernelTables->reset();
if ( unload && ret == kIOReturnSuccess ) {
ret = unloadModule(moduleName);
}
IOLockUnlock( 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;
IOLockLock( lock );
kernelTables->reset();
while ( (dict = (OSDictionary *)kernelTables->getNextObject()) ) {
if ( dict->isEqualTo(matching, matching) )
AddNewImports(set, dict);
}
if ( set->getCount() > 0 ) {
IOService::catalogNewDrivers(set);
generation++;
}
IOLockUnlock( lock );
set->release();
return true;
}
void IOCatalogue::reset(void)
{
IOLog("Resetting IOCatalogue.\n");
}
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:
if (!array->serialize(s))
kr = kIOReturnNoMemory;
break;
case kIOCatalogGetModuleDemandList:
IOLockLock( lock );
if (!gIOCatalogModuleRequests->serialize(s))
kr = kIOReturnNoMemory;
IOLockUnlock( lock );
break;
case kIOCatalogGetCacheMissList:
IOLockLock( lock );
if (!gIOCatalogCacheMisses->serialize(s))
kr = kIOReturnNoMemory;
IOLockUnlock( lock );
break;
case kIOCatalogGetROMMkextList:
IOLockLock( lock );
if (!gIOCatalogROMMkexts || !gIOCatalogROMMkexts->getCount())
kr = kIOReturnNoResources;
else if (!gIOCatalogROMMkexts->serialize(s))
kr = kIOReturnNoMemory;
if (gIOCatalogROMMkexts)
{
gIOCatalogROMMkexts->release();
gIOCatalogROMMkexts = 0;
}
IOLockUnlock( lock );
break;
default:
kr = kIOReturnBadArgument;
break;
}
return kr;
}
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)
{
OSData * copyData;
bool result = false;
bool prelinked;
copyData = OSData::withData(mkext);
if (copyData)
{
struct section * infosect;
infosect = getsectbyname("__PRELINK", "__info");
prelinked = (infosect && infosect->addr && infosect->size);
IOLockLock(kld_lock);
if (gIOCatalogROMMkexts)
gIOCatalogROMMkexts->setObject(copyData);
if (prelinked) {
result = true;
} else if (kernelLinkerPresent && add_from_mkext_function) {
result = (*add_from_mkext_function)(copyData);
} else {
IOLog("Can't add startup extensions from archive; "
"kernel linker is not present.\n");
result = false;
}
IOLockUnlock(kld_lock);
copyData->release();
}
return result;
}
kern_return_t IOCatalogue::removeKernelLinker(void) {
kern_return_t result = KERN_SUCCESS;
struct segment_command * segmentLE, *segmentKLD;
boolean_t keepsyms = FALSE;
#if __ppc__ || __arm__
char * dt_segment_name;
void * segment_paddress;
int segment_size;
#endif
IOLockLock(kld_lock);
if (!kernelLinkerPresent) {
result = KERN_SUCCESS;
goto finish;
}
PE_parse_boot_argn("keepsyms", &keepsyms, sizeof (keepsyms));
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;
segmentKLD = getsegbyname("__KLD");
if (!segmentKLD) {
IOLog("error removing kernel linker: can't find __KLD segment\n");
result = KERN_FAILURE;
goto finish;
}
OSRuntimeUnloadCPPForSegment(segmentKLD);
#if __ppc__ || __arm__
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);
}
#elif __i386__
if (!keepsyms && segmentKLD->vmaddr && segmentKLD->vmsize)
ml_static_mfree(segmentKLD->vmaddr, segmentKLD->vmsize);
#else
#error arch
#endif
struct section * sect;
sect = getsectbyname("__PRELINK", "__symtab");
if (sect && sect->addr) {
ml_static_mfree(sect->addr, sect->size);
}
finish:
IOLockUnlock(kld_lock);
return result;
}
void IOCatalogue::disableExternalLinker(void) {
IOLockLock(gIOKLDLock);
if (kmod_load_function == &kmod_load_extension) {
kmod_load_function = NULL;
}
IOLockUnlock(gIOKLDLock);
}
extern "C"
void jettison_kernel_linker(void)
{
if (gIOCatalogue != NULL)
gIOCatalogue->removeKernelLinker();
}