]> git.saurik.com Git - apple/xnu.git/blobdiff - iokit/Kernel/IOCatalogue.cpp
xnu-1228.12.14.tar.gz
[apple/xnu.git] / iokit / Kernel / IOCatalogue.cpp
index 14dabbd65021c07d917c237f6bb76e022ebdfb6f..7ca1e8c466a410f28d6c16ed0da5eefaa7719375 100644 (file)
@@ -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. 
  * 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>
@@ -36,6 +48,7 @@ extern "C" {
 #include <mach/kmod.h>
 #include <mach-o/mach_header.h>
 #include <kern/host.h>
+#include <security/mac_data.h>
 };
 
 #include <IOKit/IOLib.h>
@@ -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, &copy);
+    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();
+}