/*
- * 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.
* 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 <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IOService.h>
#include <mach/kmod.h>
#include <mach-o/mach_header.h>
#include <kern/host.h>
+#include <security/mac_data.h>
};
#include <IOKit/IOLib.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);
+/* operates on 32 bit segments */
+extern void OSRuntimeUnloadCPPForSegment(struct segment_command * segment);
};
*/
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 )
{
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);
// Initialize the IOCatalog object.
bool IOCatalogue::init(OSArray * initArray)
{
- IORegistryEntry * entry;
OSDictionary * dict;
if ( !super::init() )
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())) {
thread_call_func_delayed( ping, this, deadline );
#endif
- entry = IORegistryEntry::getRegistryRoot();
- if ( entry )
- entry->setProperty(kIOCatalogueKey, this);
-
return true;
}
set = OSOrderedSet::withCapacity( 1 );
- IOTakeLock( &self->lock );
+ IOLockLock( &self->lock );
for( newLimit = 0; newLimit < kDriversPerIter; newLimit++) {
table = (OSDictionary *) self->array->getObject(
hackLimit += newLimit;
self->generation++;
- IOUnlock( &self->lock );
+ IOLockUnlock( &self->lock );
if( kDriversPerIter == newLimit) {
AbsoluteTime deadline;
if( !set )
return( 0 );
- IOTakeLock( lock );
+ IOLockLock( lock );
kernelTables->reset();
#if CATALOGTEST
*generationCount = getGenerationCount();
- IOUnlock( lock );
+ IOLockUnlock( lock );
return( set );
}
set = OSOrderedSet::withCapacity( 1, IOServiceOrdering,
(void *)gIOProbeScoreKey );
- IOTakeLock( lock );
+ IOLockLock( lock );
kernelTables->reset();
while ( (dict = (OSDictionary *) kernelTables->getNextObject()) ) {
set->setObject(dict);
}
*generationCount = getGenerationCount();
- IOUnlock( lock );
+ IOLockUnlock( lock );
return set;
}
// 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;
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();
// 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;
UniqueProperties( matching );
- IOTakeLock( lock );
+ IOLockLock( lock );
kernelTables->reset();
arrayCopy->merge(array);
array->flushCollection();
IOService::catalogNewDrivers(set);
generation++;
}
- IOUnlock( lock );
+ IOLockUnlock( lock );
set->release();
tables->release();
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.
if ( !driver )
return false;
- moduleName = OSDynamicCast(OSString, driver->getObject(kModuleKey));
+ moduleName = OSDynamicCast(OSString, driver->getObject(gIOModuleIdentifierKey));
if ( moduleName )
return isModuleLoaded(moduleName);
OSDictionary * dict;
dict = OSDictionary::withCapacity(2);
- dict->setObject(kModuleKey, moduleName);
+ dict->setObject(gIOModuleIdentifierKey, moduleName);
startMatching(dict);
dict->release();
}
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) ) {
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;
} 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 )
{
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;
}
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.
ret = unloadModule(moduleName);
}
- IOUnlock( lock );
+ IOLockUnlock( lock );
dict->release();
if ( !set )
return false;
- IOTakeLock( lock );
+ IOLockLock( lock );
kernelTables->reset();
while ( (dict = (OSDictionary *)kernelTables->getNextObject()) ) {
generation++;
}
- IOUnlock( lock );
+ IOLockUnlock( lock );
set->release();
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;
}
/*********************************************************************
+* 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.
*/
goto finish;
}
+ PE_parse_boot_argn("keepsyms", &keepsyms, sizeof (keepsyms));
+
IOLog("Jettisoning kernel linker.\n");
kernelLinkerPresent = 0;
* 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";
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.
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();
+}