X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/9bccf70c0258c7cac2dcb80011b2a964d884c552..c910b4d9d2451126ae3917b931cd4390c11e1d52:/iokit/Kernel/IOCatalogue.cpp diff --git a/iokit/Kernel/IOCatalogue.cpp b/iokit/Kernel/IOCatalogue.cpp index 14dabbd65..7ca1e8c46 100644 --- a/iokit/Kernel/IOCatalogue.cpp +++ b/iokit/Kernel/IOCatalogue.cpp @@ -1,23 +1,29 @@ /* - * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 1998-2006 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * Copyright (c) 1998 Apple Computer, Inc. All rights reserved. @@ -25,6 +31,12 @@ * 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 @@ -36,6 +48,7 @@ extern "C" { #include #include #include +#include }; #include @@ -46,8 +59,8 @@ extern "C" { 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); +/* operates on 32 bit segments */ +extern void OSRuntimeUnloadCPPForSegment(struct segment_command * segment); }; @@ -75,17 +88,732 @@ void (*remove_startup_extension_function)(const char * name) = 0; */ int kernelLinkerPresent = 0; - -#define super OSObject #define kModuleKey "CFBundleIdentifier" +#define super OSObject OSDefineMetaClassAndStructors(IOCatalogue, OSObject) #define CATALOGTEST 0 -IOCatalogue * gIOCatalogue; -const OSSymbol * gIOClassKey; -const OSSymbol * gIOProbeScoreKey; +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 ) { @@ -123,9 +851,15 @@ void IOCatalogue::initialize( void ) errorString->release(); } - gIOClassKey = OSSymbol::withCStringNoCopy( kIOClassKey ); - gIOProbeScoreKey = OSSymbol::withCStringNoCopy( kIOProbeScoreKey ); - assert( array && gIOClassKey && gIOProbeScoreKey); + gIOClassKey = OSSymbol::withCStringNoCopy( kIOClassKey ); + gIOProbeScoreKey = OSSymbol::withCStringNoCopy( kIOProbeScoreKey ); + gIOModuleIdentifierKey = OSSymbol::withCStringNoCopy( kModuleKey ); + gIOCatalogModuleRequests = OSSet::withCapacity(16); + gIOCatalogCacheMisses = OSSet::withCapacity(16); + gIOCatalogROMMkexts = OSSet::withCapacity(4); + + assert( array && gIOClassKey && gIOProbeScoreKey + && gIOModuleIdentifierKey && gIOCatalogModuleRequests); gIOCatalogue = new IOCatalogue; assert(gIOCatalogue); @@ -137,7 +871,6 @@ void IOCatalogue::initialize( void ) // Initialize the IOCatalog object. bool IOCatalogue::init(OSArray * initArray) { - IORegistryEntry * entry; OSDictionary * dict; if ( !super::init() ) @@ -149,8 +882,11 @@ bool IOCatalogue::init(OSArray * initArray) array->retain(); kernelTables = OSCollectionIterator::withCollection( array ); - lock = IOLockAlloc(); - kld_lock = IOLockAlloc(); + gIOCatalogLock = IOLockAlloc(); + gIOKLDLock = IOLockAlloc(); + + lock = gIOCatalogLock; + kld_lock = gIOKLDLock; kernelTables->reset(); while( (dict = (OSDictionary *) kernelTables->getNextObject())) { @@ -166,10 +902,6 @@ bool IOCatalogue::init(OSArray * initArray) thread_call_func_delayed( ping, this, deadline ); #endif - entry = IORegistryEntry::getRegistryRoot(); - if ( entry ) - entry->setProperty(kIOCatalogueKey, this); - return true; } @@ -201,7 +933,7 @@ void IOCatalogue::ping( thread_call_param_t arg, thread_call_param_t) set = OSOrderedSet::withCapacity( 1 ); - IOTakeLock( &self->lock ); + IOLockLock( &self->lock ); for( newLimit = 0; newLimit < kDriversPerIter; newLimit++) { table = (OSDictionary *) self->array->getObject( @@ -223,7 +955,7 @@ void IOCatalogue::ping( thread_call_param_t arg, thread_call_param_t) hackLimit += newLimit; self->generation++; - IOUnlock( &self->lock ); + IOLockUnlock( &self->lock ); if( kDriversPerIter == newLimit) { AbsoluteTime deadline; @@ -245,7 +977,7 @@ OSOrderedSet * IOCatalogue::findDrivers( IOService * service, if( !set ) return( 0 ); - IOTakeLock( lock ); + IOLockLock( lock ); kernelTables->reset(); #if CATALOGTEST @@ -264,7 +996,7 @@ OSOrderedSet * IOCatalogue::findDrivers( IOService * service, *generationCount = getGenerationCount(); - IOUnlock( lock ); + IOLockUnlock( lock ); return( set ); } @@ -281,7 +1013,7 @@ OSOrderedSet * IOCatalogue::findDrivers( OSDictionary * matching, set = OSOrderedSet::withCapacity( 1, IOServiceOrdering, (void *)gIOProbeScoreKey ); - IOTakeLock( lock ); + IOLockLock( lock ); kernelTables->reset(); while ( (dict = (OSDictionary *) kernelTables->getNextObject()) ) { @@ -292,7 +1024,7 @@ OSOrderedSet * IOCatalogue::findDrivers( OSDictionary * matching, set->setObject(dict); } *generationCount = getGenerationCount(); - IOUnlock( lock ); + IOLockUnlock( lock ); return set; } @@ -308,12 +1040,13 @@ static void AddNewImports( OSOrderedSet * set, OSDictionary * dict ) // Add driver config tables to catalog and start matching process. bool IOCatalogue::addDrivers(OSArray * drivers, - bool doNubMatching = true ) + bool doNubMatching ) { OSCollectionIterator * iter; OSDictionary * dict; OSOrderedSet * set; OSArray * persons; + OSString * moduleName; bool ret; ret = true; @@ -332,44 +1065,56 @@ bool IOCatalogue::addDrivers(OSArray * drivers, return false; } - IOTakeLock( lock ); - while ( (dict = (OSDictionary *) iter->getNextObject()) ) { - UInt 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) ) { - array->removeObject(count); - break; - } - } - - ret = array->setObject( dict ); - if ( !ret ) - break; - - AddNewImports( set, dict ); + 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) ) { + if (doNubMatching && (set->getCount() > 0)) { IOService::catalogNewDrivers( set ); generation++; } - IOUnlock( lock ); + IOLockUnlock( lock ); set->release(); iter->release(); @@ -380,7 +1125,7 @@ bool IOCatalogue::addDrivers(OSArray * drivers, // Remove drivers from the catalog which match the // properties in the matching dictionary. bool IOCatalogue::removeDrivers( OSDictionary * matching, - bool doNubMatching = true) + bool doNubMatching) { OSCollectionIterator * tables; OSDictionary * dict; @@ -411,7 +1156,7 @@ bool IOCatalogue::removeDrivers( OSDictionary * matching, UniqueProperties( matching ); - IOTakeLock( lock ); + IOLockLock( lock ); kernelTables->reset(); arrayCopy->merge(array); array->flushCollection(); @@ -433,7 +1178,7 @@ bool IOCatalogue::removeDrivers( OSDictionary * matching, IOService::catalogNewDrivers(set); generation++; } - IOUnlock( lock ); + IOLockUnlock( lock ); set->release(); tables->release(); @@ -454,67 +1199,7 @@ bool IOCatalogue::isModuleLoaded( OSString * moduleName ) const bool IOCatalogue::isModuleLoaded( const char * moduleName ) const { - kmod_info_t * k_info; - - if ( !moduleName ) - return false; - - // Is the module already loaded? - k_info = kmod_lookupbyname_locked((char *)moduleName); - if ( !k_info ) { - kern_return_t ret; - - /* To make sure this operation completes even if a bad extension needs - * to be removed, take the kld lock for this whole block, spanning the - * kmod_load_function() and remove_startup_extension_function() calls. - */ - IOLockLock(kld_lock); - - // If the module hasn't been loaded, then load it. - if (kmod_load_function != 0) { - - ret = kmod_load_function((char *)moduleName); - - if ( ret != kIOReturnSuccess ) { - IOLog("IOCatalogue: %s cannot be loaded.\n", moduleName); - - /* If the extension couldn't be loaded this time, - * make it unavailable so that no more requests are - * made in vain. This also enables other matching - * extensions to have a chance. - */ - if (kernelLinkerPresent && remove_startup_extension_function) { - (*remove_startup_extension_function)(moduleName); - } - IOLockUnlock(kld_lock); - return false; - } else if (kernelLinkerPresent) { - // If kern linker is here, the driver is actually loaded, - // so return true. - IOLockUnlock(kld_lock); - return true; - } else { - // kern linker isn't here, a request has been queued - // but the module isn't necessarily loaded yet, so stall. - IOLockUnlock(kld_lock); - return false; - } - } else { - IOLog("IOCatalogue: %s cannot be loaded " - "(kmod load function not set).\n", - moduleName); - } - - IOLockUnlock(kld_lock); - return false; - } - - if (k_info) { - kfree(k_info, sizeof(kmod_info_t)); - } - - /* Lock wasn't taken if we get here. */ - return true; + return (kmod_load_request(moduleName, true)); } // Check to see if module has been loaded already. @@ -525,7 +1210,7 @@ bool IOCatalogue::isModuleLoaded( OSDictionary * driver ) const if ( !driver ) return false; - moduleName = OSDynamicCast(OSString, driver->getObject(kModuleKey)); + moduleName = OSDynamicCast(OSString, driver->getObject(gIOModuleIdentifierKey)); if ( moduleName ) return isModuleLoaded(moduleName); @@ -541,7 +1226,7 @@ void IOCatalogue::moduleHasLoaded( OSString * moduleName ) OSDictionary * dict; dict = OSDictionary::withCapacity(2); - dict->setObject(kModuleKey, moduleName); + dict->setObject(gIOModuleIdentifierKey, moduleName); startMatching(dict); dict->release(); } @@ -566,6 +1251,7 @@ IOReturn IOCatalogue::unloadModule( OSString * moduleName ) const 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) ) { @@ -584,12 +1270,10 @@ IOReturn IOCatalogue::unloadModule( OSString * moduleName ) const return ret; } -static IOReturn _terminateDrivers( OSArray * array, OSDictionary * matching ) +static IOReturn _terminateDrivers( OSDictionary * matching ) { - OSCollectionIterator * tables; OSDictionary * dict; OSIterator * iter; - OSArray * arrayCopy; IOService * service; IOReturn ret; @@ -628,9 +1312,17 @@ static IOReturn _terminateDrivers( OSArray * array, OSDictionary * matching ) } 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. - if ( ret != kIOReturnSuccess ) - return ret; arrayCopy = OSArray::withCapacity(100); if ( !arrayCopy ) @@ -666,11 +1358,12 @@ IOReturn IOCatalogue::terminateDrivers( OSDictionary * matching ) { IOReturn ret; - ret = kIOReturnSuccess; - IOTakeLock( lock ); - ret = _terminateDrivers(array, matching); + ret = _terminateDrivers(matching); + IOLockLock( lock ); + if (kIOReturnSuccess == ret) + ret = _removeDrivers(array, matching); kernelTables->reset(); - IOUnlock( lock ); + IOLockUnlock( lock ); return ret; } @@ -686,11 +1379,12 @@ IOReturn IOCatalogue::terminateDriversForModule( if ( !dict ) return kIOReturnNoMemory; - dict->setObject(kModuleKey, moduleName); + dict->setObject(gIOModuleIdentifierKey, moduleName); - IOTakeLock( lock ); - - ret = _terminateDrivers(array, dict); + ret = _terminateDrivers(dict); + IOLockLock( lock ); + if (kIOReturnSuccess == ret) + ret = _removeDrivers(array, dict); kernelTables->reset(); // Unload the module itself. @@ -699,7 +1393,7 @@ IOReturn IOCatalogue::terminateDriversForModule( ret = unloadModule(moduleName); } - IOUnlock( lock ); + IOLockUnlock( lock ); dict->release(); @@ -736,7 +1430,7 @@ bool IOCatalogue::startMatching( OSDictionary * matching ) if ( !set ) return false; - IOTakeLock( lock ); + IOLockLock( lock ); kernelTables->reset(); while ( (dict = (OSDictionary *)kernelTables->getNextObject()) ) { @@ -753,7 +1447,7 @@ bool IOCatalogue::startMatching( OSDictionary * matching ) generation++; } - IOUnlock( lock ); + IOLockUnlock( lock ); set->release(); @@ -762,42 +1456,65 @@ bool IOCatalogue::startMatching( OSDictionary * matching ) 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 super::serialize(s); +} - return ret; +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; } @@ -819,36 +1536,66 @@ bool IOCatalogue::recordStartupExtensions(void) { /********************************************************************* +* This function operates on sections retrieved from the currently running +* 32 bit mach kernel. *********************************************************************/ -bool IOCatalogue::addExtensionsFromArchive(OSData * mkext) { +bool IOCatalogue::addExtensionsFromArchive(OSData * mkext) +{ + OSData * copyData; bool result = false; + bool prelinked; - 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; + /* 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(); } - IOLockUnlock(kld_lock); 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; - extern struct mach_header _mh_execute_header; - struct segment_command * segment; + 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. */ @@ -863,6 +1610,8 @@ kern_return_t IOCatalogue::removeKernelLinker(void) { goto finish; } + PE_parse_boot_argn("keepsyms", &keepsyms, sizeof (keepsyms)); + IOLog("Jettisoning kernel linker.\n"); kernelLinkerPresent = 0; @@ -882,25 +1631,15 @@ kern_return_t IOCatalogue::removeKernelLinker(void) { * memory so that any cross-dependencies (not that there * should be any) are handled. */ - segment = getsegbynamefromheader( - &_mh_execute_header, "__KLD"); - if (!segment) { + segmentKLD = getsegbyname("__KLD"); + if (!segmentKLD) { 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); - + OSRuntimeUnloadCPPForSegment(segmentKLD); +#if __ppc__ || __arm__ /* Free the memory that was set up by bootx. */ dt_segment_name = "Kernel-__KLD"; @@ -908,14 +1647,24 @@ kern_return_t IOCatalogue::removeKernelLinker(void) { 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 - 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); + 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. @@ -924,3 +1673,26 @@ finish: 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(); +}