/* * Copyright (c) 1998-2006 Apple Computer, Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * 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. * * 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * Copyright (c) 1998 Apple Computer, 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 extern "C" { int IODTGetLoaderInfo( char *key, void **infoAddr, int *infoSize ); extern void IODTFreeLoaderInfo( char *key, void *infoAddr, int infoSize ); /* operates on 32 bit segments */ extern void OSRuntimeUnloadCPPForSegment(struct segment_command * segment); }; /***** * 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; }; /***** * 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; #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 /* MAC Framework support */ /* * define IOC_DEBUG to display run-time debugging information * #define IOC_DEBUG 1 */ #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"); /* UINT32_MAX */ } 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); } } /********************************************************************* * This function takes an OSDictionary and returns a struct mac_module_data * list. *********************************************************************/ struct mac_module_data * osdict_encode(OSDictionary *dict) { const OSMetaClass * typeID; // don't release OSString * key = NULL; // don't release OSCollectionIterator * keyIterator = 0; // must release 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; /* Iterate over OSModuleData to figure out total size */ while ( (key = OSDynamicCast(OSString, keyIterator->getNextObject())) ) { // Get the key's value and determine its type 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; /* Only handle primitive types here. */ } /* * Allow for the "arraynnn/" prefix in the key length. */ 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; /* Skip everything else. */ } } if (nents == 0) continue; listtabsize += sizeof(struct mac_module_data_list) + (nents - 1) * sizeof(struct mac_module_data_element); } else { continue; /* skip anything else */ } strtabsize += key->getLength() + 1; nkeys++; } if (nkeys == 0) goto finish; /* * Allocate and fill in the module data structures. */ 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())) ) { // Get the key's value and determine its type OSObject * value = dict->getObject(key); if (!value) continue; /* Store key */ 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)) { /* Store 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; /* Only handle primitive types here. */ } 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; /* Skip everything else. */ } 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; /* skip anything else */ } 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); } /********************************************************************* * This function takes a plist and looks for an OSModuleData dictionary. * If it is found, an encoded copy is returned. *********************************************************************/ kmod_args_t get_module_data(OSDictionary * kextPlist, mach_msg_type_number_t * datalen) { OSDictionary * kextModuleData = 0; // don't release 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; /* * Make a CoW copy of data and free the original. The copy is * consumed by a call to vm_map_copyout() in kmod_start_or_stop(). */ 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 /* MAC */ 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) { // already started or no code 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; } /********************************************************************* * This is a function that IOCatalogue calls in order to load a kmod. *********************************************************************/ 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; /* 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(gIOKLDLock); do { // Is the module already loaded? 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 the module hasn't been loaded, then load it. 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 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); } ret = false; } else if (kernelLinkerPresent) { // If kern linker is here, the driver is actually loaded, // so return true. ret = true; } else { // kern linker isn't here, a request has been queued // but the module isn't necessarily loaded yet, so stall. 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(); } // Initialize the IOCatalog object. 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; } // 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(); } #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 ); } // Is personality already in the catalog? 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()) ) { /* 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(); IOLockUnlock( lock ); 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. 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 ); // 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)) break; } if (count >= 0) // its a dup continue; ret = array->setObject( dict ); if (!ret) break; AddNewImports( set, dict ); } } // Start device matching. if (doNubMatching && (set->getCount() > 0)) { IOService::catalogNewDrivers( set ); generation++; } IOLockUnlock( lock ); set->release(); iter->release(); return ret; } // 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; 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()) ) { /* 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; } array->setObject(dict); } // Start device matching. if ( doNubMatching && (set->getCount() > 0) ) { IOService::catalogNewDrivers(set); generation++; } IOLockUnlock( lock ); set->release(); tables->release(); return true; } // Return the generation count. 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)); } // Check to see if module has been loaded already. bool IOCatalogue::isModuleLoaded( OSDictionary * driver ) const { OSString * moduleName = NULL; if ( !driver ) return false; moduleName = OSDynamicCast(OSString, driver->getObject(gIOModuleIdentifierKey)); if ( moduleName ) return isModuleLoaded(moduleName); /* If a personality doesn't hold the "CFBundleIdentifier" key * it is assumed to be an "in-kernel" driver. */ return true; } // This function is called after a module has been loaded. 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 ); // terminate instances. do { iter->reset(); while( (service = (IOService *)iter->getNextObject()) ) { dict = service->getPropertyTable(); if ( !dict ) continue; /* Terminate only for 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; 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; // remove configs from catalog. 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()) ) { /* 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; 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(); // Unload the module itself. if ( unload && ret == kIOReturnSuccess ) { // Do kmod stop first. 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()) ) { /* 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); } // Start device matching. 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; } /********************************************************************* * This function operates on sections retrieved from the currently running * 32 bit mach kernel. *********************************************************************/ bool IOCatalogue::addExtensionsFromArchive(OSData * mkext) { OSData * copyData; bool result = false; bool prelinked; /* The mkext we've been handed (or the data it references) can go away, * so we need to make a local copy to keep around as long as it might * be needed. */ 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; } /********************************************************************* * 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. * * The segments it operates on are strictly 32 bit segments. *********************************************************************/ 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 /* 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; } PE_parse_boot_argn("keepsyms", &keepsyms, sizeof (keepsyms)); IOLog("Jettisoning kernel linker.\n"); kernelLinkerPresent = 0; /* Set the kmod_load_extension function as the means for loading * a kernel extension. */ kmod_load_function = &kmod_load_extension; record_startup_extensions_function = 0; add_from_mkext_function = 0; remove_startup_extension_function = 0; /* 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. */ 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__ /* 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); } #elif __i386__ /* On x86, use the mapping data from the segment load command to * unload KLD directly, unless the keepsyms boot-arg was enabled. * This may invalidate any assumptions about "avail_start" * defining the lower bound for valid physical addresses. */ 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: /* This must be the very last thing done before returning. */ IOLockUnlock(kld_lock); return result; } /********************************************************************* * This function stops the catalogue from making kextd requests during * shutdown. *********************************************************************/ void IOCatalogue::disableExternalLinker(void) { IOLockLock(gIOKLDLock); /* If kmod_load_extension (the kextd requester function) is in use, * disable new module requests. */ 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(); }