]> git.saurik.com Git - apple/xnu.git/blobdiff - libsa/catalogue.cpp
xnu-1228.5.20.tar.gz
[apple/xnu.git] / libsa / catalogue.cpp
index 1528cc634caa1dc14fb0f2c83d46580de6e8a6db..886094ab6d1dcb802002ba72d4267c46d1bfe0d8 100644 (file)
@@ -1,28 +1,35 @@
 /*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2007 Apple 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@
  */
 #include <libkern/c++/OSContainers.h>
 #include <IOKit/IODeviceTreeSupport.h>
 #include <IOKit/IORegistryEntry.h>
 #include <IOKit/IOCatalogue.h>
+#include <IOKit/IOKitKeysPrivate.h>
 #include <libkern/c++/OSUnserialize.h>
 #include <libkern/OSByteOrder.h>
 #include <libsa/catalogue.h>
@@ -33,94 +40,378 @@ extern "C" {
 #include <mach/kmod.h>
 #include <libsa/mkext.h>
 #include <libsa/vers_rsrc.h>
+#include <mach-o/loader.h>
 };
 
 #include <IOKit/IOLib.h>
 
 #include <IOKit/assert.h>
 
-
 extern "C" {
 extern void IODTFreeLoaderInfo( char *key, void *infoAddr, int infoSize );
-extern kern_return_t host_info(host_t host,
-    host_flavor_t flavor,
-    host_info_t info,
-    mach_msg_type_number_t  *count);
-extern int check_cpu_subtype(cpu_subtype_t cpu_subtype);
-
-extern IOLock * kld_lock;
+// extern kern_return_t host_info(host_t host,
+//     host_flavor_t flavor,
+//     host_info_t info,
+//     mach_msg_type_number_t  *count);
+extern int grade_binary(cpu_type_t exectype, cpu_subtype_t execsubtype);
+// Return the address of the named Mach-O segment from the currently
+// executing 32 bit kernel, or NULL.
+extern struct segment_command *getsegbyname(char *seg_name);
+// Return the address of the named section from the named Mach-O segment
+// from the currently executing 32 bit kernel, or NULL.
+extern struct section *getsectbyname(const char *segname, const char *sectname);
 };
 
-
 #define LOG_DELAY()
 
+#if 0
 #define VTYELLOW   "\033[33m"
 #define VTRESET    "\033[0m"
-
+#else
+#define VTYELLOW   ""
+#define VTRESET    ""
+#endif
 
 /*********************************************************************
 *********************************************************************/
 static OSDictionary * gStartupExtensions = 0;
+static OSArray * gBootLoaderObjects = 0;
+extern OSArray * gIOPrelinkedModules;
 
 OSDictionary * getStartupExtensions(void) {
     if (gStartupExtensions) {
         return gStartupExtensions;
     }
     gStartupExtensions = OSDictionary::withCapacity(1);
-    if (!gStartupExtensions) {
-        IOLog("Error: Couldn't allocate "
-            "startup extensions dictionary.\n");
-        LOG_DELAY();
-    }
+    assert (gStartupExtensions);
+
     return gStartupExtensions;
 }
 
+/* This array holds objects that are needed to be held around during
+ * boot before kextd starts up. Currently it contains OSData objects
+ * copied from OF entries for mkext archives in device ROMs. Because
+ * the Device Tree support code dumps these after initially handing
+ * them to us, we have to be able to clean them up later.
+ */
+OSArray * getBootLoaderObjects(void) {
+    if (gBootLoaderObjects) {
+        return gBootLoaderObjects;
+    }
+    gBootLoaderObjects = OSArray::withCapacity(1);
+    assert (gBootLoaderObjects);
+
+    return gBootLoaderObjects;
+}
 
 /*********************************************************************
 * This function checks that a driver dict has all the required
 * entries and does a little bit of value checking too.
+*
+* index is nonnegative if the index of an entry from an mkext
+* archive.
 *********************************************************************/
-bool validateExtensionDict(OSDictionary * extension) {
+bool validateExtensionDict(OSDictionary * extension, int index) {
 
     bool result = true;
-    OSString * name;         // do not release
-    OSString * stringValue;  // do not release
-    UInt32 vers;
+    bool not_a_dict = false;
+    bool id_missing = false;
+    bool is_kernel_resource = false;
+    bool has_executable = false;
+    bool ineligible_for_safe_boot = false;
+    OSString * bundleIdentifier = NULL;    // do not release
+    OSObject * rawValue = NULL;            // do not release
+    OSString * stringValue = NULL;         // do not release
+    OSBoolean * booleanValue = NULL;       // do not release
+    OSDictionary * personalities = NULL;   // do not release
+    OSDictionary * libraries = NULL;       // do not release
+    OSCollectionIterator * keyIterator = NULL;  // must release
+    OSString * key = NULL;                 // do not release
+    VERS_version vers;
+    VERS_version compatible_vers;
+    char namep[16];      // unused but needed for PE_parse_boot_arg()
+
+    // Info dict is a dictionary
+    if (!OSDynamicCast(OSDictionary, extension)) {
+        not_a_dict = true;
+        result = false;
+        goto finish;
+    }
 
-    name = OSDynamicCast(OSString,
+    // CFBundleIdentifier is a string - REQUIRED
+    bundleIdentifier = OSDynamicCast(OSString,
         extension->getObject("CFBundleIdentifier"));
-    if (!name) {
-        IOLog(VTYELLOW "Extension has no \"CFBundleIdentifier\" property.\n"
-            VTRESET);
-        LOG_DELAY();
+    if (!bundleIdentifier) {
+        id_missing = true;
+        result = false;
+        goto finish;
+    }
+
+    // Length of CFBundleIdentifier is not >= KMOD_MAX_NAME
+    if (bundleIdentifier->getLength() >= KMOD_MAX_NAME) {
+        result = false;
+        goto finish;
+    }
+
+    // CFBundlePackageType is "KEXT" - REQUIRED
+    stringValue = OSDynamicCast(OSString,
+        extension->getObject("CFBundlePackageType"));
+    if (!stringValue) {
+        result = false;
+        goto finish;
+    }
+    if (!stringValue->isEqualTo("KEXT")) {
         result = false;
         goto finish;
     }
 
+    // CFBundleVersion is a string - REQUIRED
     stringValue = OSDynamicCast(OSString,
         extension->getObject("CFBundleVersion"));
     if (!stringValue) {
-        IOLog(VTYELLOW "Extension \"%s\" has no \"CFBundleVersion\" "
-            "property.\n" VTRESET,
-            name->getCStringNoCopy());
-        LOG_DELAY();
         result = false;
         goto finish;
     }
-    if (!VERS_parse_string(stringValue->getCStringNoCopy(),
-        &vers)) {
-        IOLog(VTYELLOW "Extension \"%s\" has an invalid "
-            "\"CFBundleVersion\" property.\n" VTRESET,
-            name->getCStringNoCopy());
-        LOG_DELAY();
+    // CFBundleVersion is of valid form
+    vers = VERS_parse_string(stringValue->getCStringNoCopy());
+    if (vers < 0) {
+        result = false;
+        goto finish;
+    }
+
+    // OSBundleCompatibleVersion is a string - OPTIONAL
+    rawValue = extension->getObject("OSBundleCompatibleVersion");
+    if (rawValue) {
+        stringValue = OSDynamicCast(OSString, rawValue);
+        if (!stringValue) {
+            result = false;
+            goto finish;
+        }
+
+        // OSBundleCompatibleVersion is of valid form
+        compatible_vers = VERS_parse_string(stringValue->getCStringNoCopy());
+        if (compatible_vers < 0) {
+            result = false;
+            goto finish;
+        }
+
+        // OSBundleCompatibleVersion <= CFBundleVersion
+        if (compatible_vers > vers) {
+            result = false;
+            goto finish;
+        }
+    }
+
+    // CFBundleExecutable is a string - OPTIONAL
+    rawValue = extension->getObject("CFBundleExecutable");
+    if (rawValue) {
+        stringValue = OSDynamicCast(OSString, rawValue);
+        if (!stringValue || stringValue->getLength() == 0) {
+            result = false;
+            goto finish;
+        }
+        has_executable = true;
+    }
+
+    // OSKernelResource is a boolean value - OPTIONAL
+    rawValue = extension->getObject("OSKernelResource");
+    if (rawValue) {
+        booleanValue = OSDynamicCast(OSBoolean, rawValue);
+        if (!booleanValue) {
+            result = false;
+            goto finish;
+        }
+        is_kernel_resource = booleanValue->isTrue();
+    }
+
+    // IOKitPersonalities is a dictionary - OPTIONAL
+    rawValue = extension->getObject("IOKitPersonalities");
+    if (rawValue) {
+        personalities = OSDynamicCast(OSDictionary, rawValue);
+        if (!personalities) {
+            result = false;
+            goto finish;
+        }
+
+        keyIterator = OSCollectionIterator::withCollection(personalities);
+        if (!keyIterator) {
+            IOLog("Error: Failed to allocate iterator for personalities.\n");
+            LOG_DELAY();
+            result = false;
+            goto finish;
+        }
+
+        while ((key = OSDynamicCast(OSString, keyIterator->getNextObject()))) {
+            OSDictionary * personality = NULL;  // do not release
+
+            // Each personality is a dictionary
+            personality = OSDynamicCast(OSDictionary,
+                personalities->getObject(key));
+            if (!personality) {
+                result = false;
+                goto finish;
+            }
+
+            //   IOClass exists as a string - REQUIRED
+            if (!OSDynamicCast(OSString, personality->getObject("IOClass"))) {
+                result = false;
+                goto finish;
+            }
+
+            //   IOProviderClass exists as a string - REQUIRED
+            if (!OSDynamicCast(OSString,
+                personality->getObject("IOProviderClass"))) {
+
+                result = false;
+                goto finish;
+            }
+
+            // CFBundleIdentifier is a string - OPTIONAL - INSERT IF ABSENT!
+            rawValue = personality->getObject("CFBundleIdentifier");
+            if (!rawValue) {
+                personality->setObject("CFBundleIdentifier", bundleIdentifier);
+            } else {
+                OSString * personalityID = NULL;    // do not release
+                personalityID = OSDynamicCast(OSString, rawValue);
+                if (!personalityID) {
+                    result = false;
+                    goto finish;
+                } else {
+                    // Length of CFBundleIdentifier is not >= KMOD_MAX_NAME
+                    if (personalityID->getLength() >= KMOD_MAX_NAME) {
+                        result = false;
+                        goto finish;
+                    }
+                }
+            }
+
+            // IOKitDebug is a number - OPTIONAL
+            rawValue = personality->getObject("IOKitDebug");
+            if (rawValue && !OSDynamicCast(OSNumber, rawValue)) {
+                result = false;
+                goto finish;
+            }
+        }
+
+        keyIterator->release();
+        keyIterator = NULL;
+    }
+
+
+    // OSBundleLibraries is a dictionary - REQUIRED if
+    // not kernel resource & has executable
+    //
+    rawValue = extension->getObject("OSBundleLibraries");
+    if (!rawValue && !is_kernel_resource && has_executable) {
+        result = false;
+        goto finish;
+    }
+
+    if (rawValue) {
+        libraries = OSDynamicCast(OSDictionary, rawValue);
+        if (!libraries) {
+            result = false;
+            goto finish;
+        }
+
+        keyIterator = OSCollectionIterator::withCollection(libraries);
+        if (!keyIterator) {
+            IOLog("Error: Failed to allocate iterator for libraries.\n");
+            LOG_DELAY();
+            result = false;
+            goto finish;
+        }
+
+        while ((key = OSDynamicCast(OSString,
+            keyIterator->getNextObject()))) {
+
+            OSString * libraryVersion = NULL;  // do not release
+
+            // Each key's length is not >= KMOD_MAX_NAME
+            if (key->getLength() >= KMOD_MAX_NAME) {
+                result = false;
+                goto finish;
+            }
+
+            libraryVersion = OSDynamicCast(OSString,
+                libraries->getObject(key));
+            if (!libraryVersion) {
+                result = false;
+                goto finish;
+            }
+
+            // Each value is a valid version string
+            vers = VERS_parse_string(libraryVersion->getCStringNoCopy());
+            if (vers < 0) {
+                result = false;
+                goto finish;
+            }
+        }
+
+        keyIterator->release();
+        keyIterator = NULL;
+    }
+
+    // OSBundleRequired, if present, must have a legal value.
+    // If it is not present and if we are safe-booting,
+    // then the kext is not eligible.
+    //
+    rawValue = extension->getObject("OSBundleRequired");
+    if (rawValue) {
+        stringValue = OSDynamicCast(OSString, rawValue);
+        if (!stringValue) {
+            result = false;
+            goto finish;
+        }
+        if (!stringValue->isEqualTo("Root") &&
+            !stringValue->isEqualTo("Local-Root") &&
+            !stringValue->isEqualTo("Network-Root") &&
+            !stringValue->isEqualTo("Safe Boot") &&
+            !stringValue->isEqualTo("Console")) {
+
+            result = false;
+            goto finish;
+        }
+
+    } else if (PE_parse_boot_arg("-x", namep)) { /* safe boot */
+        ineligible_for_safe_boot = true;
         result = false;
         goto finish;
     }
 
 
 finish:
-    // FIXME: Make return real result after kext conversion
-    return true;
+    if (keyIterator)   keyIterator->release();
+
+    if (!result) {
+        if (ineligible_for_safe_boot) {
+            IOLog(VTYELLOW "Skipping extension \"%s\" during safe boot "
+                "(no OSBundleRequired property)\n"
+                VTRESET,
+                bundleIdentifier->getCStringNoCopy());
+        } else if (not_a_dict) {
+            if (index > -1) {
+                IOLog(VTYELLOW "mkext entry %d: " VTRESET, index);
+            } else {
+                IOLog(VTYELLOW "kernel extension " VTRESET);
+            }
+            IOLog(VTYELLOW "info dictionary isn't a dictionary\n"
+                VTRESET);
+        } else if (id_missing) {
+            if (index > -1) {
+                IOLog(VTYELLOW "mkext entry %d: " VTRESET, index);
+            } else {
+                IOLog(VTYELLOW "kernel extension " VTRESET);
+            }
+            IOLog(VTYELLOW "\"CFBundleIdentifier\" property is "
+                "missing or not a string\n"
+                VTRESET);
+        } else {
+            IOLog(VTYELLOW "kernel extension \"%s\": info dictionary is invalid\n"
+                VTRESET, bundleIdentifier->getCStringNoCopy());
+        }
+        LOG_DELAY();
+    }
 
     return result;
 }
@@ -140,8 +431,8 @@ OSDictionary * compareExtensionVersions(
     OSString * candidateName = NULL;
     OSString * incumbentVersionString = NULL;
     OSString * candidateVersionString = NULL;
-    UInt32 incumbent_vers = 0;
-    UInt32 candidate_vers = 0;
+    VERS_version incumbent_vers = 0;
+    VERS_version candidate_vers = 0;
 
     incumbentPlist = OSDynamicCast(OSDictionary,
         incumbent->getObject("plist"));
@@ -187,8 +478,8 @@ OSDictionary * compareExtensionVersions(
         goto finish;
     }
 
-    if (!VERS_parse_string(incumbentVersionString->getCStringNoCopy(),
-        &incumbent_vers)) {
+    incumbent_vers = VERS_parse_string(incumbentVersionString->getCStringNoCopy());
+    if (incumbent_vers < 0) {
 
         IOLog(VTYELLOW "Error parsing version string for extension %s (%s)\n"
             VTRESET,
@@ -199,8 +490,8 @@ OSDictionary * compareExtensionVersions(
         goto finish;
     }
 
-    if (!VERS_parse_string(candidateVersionString->getCStringNoCopy(),
-        &candidate_vers)) {
+    candidate_vers = VERS_parse_string(candidateVersionString->getCStringNoCopy());
+    if (candidate_vers < 0) {
 
         IOLog(VTYELLOW "Error parsing version string for extension %s (%s)\n"
             VTRESET,
@@ -380,6 +671,11 @@ typedef struct BootxDriverInfo {
     long  moduleLength;
 } BootxDriverInfo;
 
+typedef struct MkextEntryInfo {
+    vm_address_t  base_address;
+    mkext_file  * fileinfo;
+} MkextEntryInfo;
+
 
 /*********************************************************************
 * This private function reads the data for a single extension from
@@ -398,12 +694,11 @@ OSDictionary * readExtension(OSDictionary * propertyDict,
     OSString             * errorString = NULL;
     OSDictionary         * driverDict = NULL;
 
-    MemoryMapFileInfo * driverInfo = 0;
+    const MemoryMapFileInfo * driverInfo = 0;
     BootxDriverInfo * dataBuffer;
 
     kmod_info_t          * loaded_kmod = NULL;
 
-
     bootxDriverDataObject = OSDynamicCast(OSData,
         propertyDict->getObject(memory_map_name));
     // don't release bootxDriverDataObject
@@ -426,11 +721,19 @@ OSDictionary * readExtension(OSDictionary * propertyDict,
         goto finish;
     }
 
-    driverInfo = (MemoryMapFileInfo *)
+    driverInfo = (const MemoryMapFileInfo *)
         bootxDriverDataObject->getBytesNoCopy(0,
         sizeof(MemoryMapFileInfo));
-    dataBuffer = (BootxDriverInfo *)ml_static_ptovirt(
-        driverInfo->paddr);
+#if defined (__ppc__) || defined (__arm__)
+    dataBuffer = (BootxDriverInfo *)ml_static_ptovirt(driverInfo->paddr);
+#elif defined (__i386__)
+    dataBuffer = (BootxDriverInfo *)ml_boot_ptovirt(driverInfo->paddr);
+    dataBuffer->plistAddr = (char *)ml_boot_ptovirt((vm_address_t)dataBuffer->plistAddr);
+    if (dataBuffer->moduleAddr)
+      dataBuffer->moduleAddr = (void *)ml_boot_ptovirt((vm_address_t)dataBuffer->moduleAddr);
+#else
+#error unsupported architecture
+#endif
     if (!dataBuffer) {
         IOLog("Error: No data buffer "
         "for device tree entry \"%s\".\n", memory_map_name);
@@ -468,7 +771,7 @@ OSDictionary * readExtension(OSDictionary * propertyDict,
    /* Check if kmod is already loaded and is a real loadable one (has
     * an address).
     */
-    loaded_kmod = kmod_lookupbyname(driverName->getCStringNoCopy());
+    loaded_kmod = kmod_lookupbyname_locked(driverName->getCStringNoCopy());
     if (loaded_kmod && loaded_kmod->address) {
         IOLog("Skipping new extension \"%s\"; an extension named "
             "\"%s\" is already loaded.\n",
@@ -479,10 +782,8 @@ OSDictionary * readExtension(OSDictionary * propertyDict,
         goto finish;
     }
 
-    if (!validateExtensionDict(driverPlist)) {
-        IOLog("Error: Failed to validate property list "
-            "for device tree entry \"%s\".\n", memory_map_name);
-        LOG_DELAY();
+    if (!validateExtensionDict(driverPlist, -1)) {
+        // validateExtensionsDict() logs an error
         error = 1;
         goto finish;
     }
@@ -492,9 +793,14 @@ OSDictionary * readExtension(OSDictionary * propertyDict,
    /* It's perfectly okay for a KEXT to have no executable.
     * Check that moduleAddr is nonzero before attempting to
     * get one.
+    *
+    * NOTE: The driverCode object is created "no-copy", so
+    * it doesn't own that memory. The memory must be freed
+    * separately from the OSData object (see
+    * clearStartupExtensionsAndLoaderInfo() at the end of this file).
     */
     if (dataBuffer->moduleAddr && dataBuffer->moduleLength) {
-        driverCode = OSData::withBytes(dataBuffer->moduleAddr,
+        driverCode = OSData::withBytesNoCopy(dataBuffer->moduleAddr,
             dataBuffer->moduleLength);
         if (!driverCode) {
             IOLog("Error: Couldn't allocate data object "
@@ -512,11 +818,9 @@ OSDictionary * readExtension(OSDictionary * propertyDict,
 
 finish:
 
-   /* Free the memory for this extension that was set up
-    * by bootx.
-    */
-    IODTFreeLoaderInfo(memory_map_name, (void *)driverInfo->paddr,
-        (int)driverInfo->length);
+    if (loaded_kmod) {
+        kfree(loaded_kmod, sizeof(kmod_info_t));
+    }
 
     // do not release bootxDriverDataObject
     // do not release driverName
@@ -542,14 +846,17 @@ finish:
 
 /*********************************************************************
 * Used to uncompress a single file entry in an mkext archive.
+*
+* The OSData returned does not own its memory! You must deallocate
+* that memory using kmem_free() before releasing the OSData().
 *********************************************************************/
-int uncompressFile(u_int8_t * base_address,
-    mkext_file * fileinfo,
-    /* out */ OSData ** file) {
+static bool uncompressFile(u_int8_t *base_address, mkext_file * fileinfo,
+                          /* out */ OSData ** file) {
 
-    int result = 1;
-    u_int8_t * uncompressed_file = 0;   // don't free; owned by OSData obj
-    OSData * uncompressedFile = 0;  // don't release
+    bool result = true;
+    kern_return_t kern_result;
+    u_int8_t * uncompressed_file = 0; // kmem_free() on error
+    OSData * uncompressedFile = 0;    // returned
     size_t uncompressed_size = 0;
 
     size_t offset = OSSwapBigToHostInt32(fileinfo->offset);
@@ -568,12 +875,13 @@ int uncompressFile(u_int8_t * base_address,
     }
 
     // Add 1 for '\0' to terminate XML string!
-    uncompressed_file = (u_int8_t *)kalloc(realsize + 1);
-    if (!uncompressed_file) {
+    kern_result = kmem_alloc(kernel_map, (vm_offset_t *)&uncompressed_file,
+        realsize + 1);
+    if (kern_result != KERN_SUCCESS) {
         IOLog("Error: Couldn't allocate data buffer "
               "to uncompress file.\n");
         LOG_DELAY();
-        result = 0;
+        result = false;
         goto finish;
     }
 
@@ -583,7 +891,7 @@ int uncompressFile(u_int8_t * base_address,
         IOLog("Error: Couldn't allocate data object "
               "to uncompress file.\n");
         LOG_DELAY();
-        result = 0;
+        result = false;
         goto finish;
     }
 
@@ -595,19 +903,24 @@ int uncompressFile(u_int8_t * base_address,
             IOLog("Error: Uncompressed file is not the length "
                   "recorded.\n");
             LOG_DELAY();
-            result = 0;
+            result = false;
             goto finish;
         }
+        uncompressed_file[uncompressed_size] = '\0';
     } else {
         bcopy(base_address + offset, uncompressed_file,
-            compsize);
+            realsize);
+        uncompressed_file[realsize] = '\0';
     }
-    uncompressed_file[uncompressed_size] = '\0';
 
     *file = uncompressedFile;
 
 finish:
     if (!result) {
+        if (uncompressed_file) {
+            kmem_free(kernel_map, (vm_address_t)uncompressed_file,
+                realsize + 1);
+        }
         if (uncompressedFile) {
             uncompressedFile->release();
             *file = 0;
@@ -616,12 +929,21 @@ finish:
     return result;
 }
 
+bool uncompressModule(OSData *compData, /* out */ OSData ** file) {
+
+    const MkextEntryInfo *info = (const MkextEntryInfo *) compData->getBytesNoCopy();
+
+    return uncompressFile((u_int8_t *) info->base_address, 
+                         info->fileinfo, file);
+}
+
 
 /*********************************************************************
 * Does the work of pulling extensions out of an mkext archive located
 * in memory.
 *********************************************************************/
-bool extractExtensionsFromArchive(MemoryMapFileInfo * mkext_file_info,
+bool extractExtensionsFromArchive(const MemoryMapFileInfo * mkext_file_info,
+    bool vaddr,
     OSDictionary * extensions) {
 
     bool result = true;
@@ -632,6 +954,8 @@ bool extractExtensionsFromArchive(MemoryMapFileInfo * mkext_file_info,
     mkext_kext   * onekext_data = 0; // don't free
     mkext_file   * plist_file = 0;   // don't free
     mkext_file   * module_file = 0;  // don't free
+    kmod_info_t  * loaded_kmod = 0;  // must free
+
     OSData       * driverPlistDataObject = 0; // must release
     OSDictionary * driverPlist = 0;  // must release
     OSData       * driverCode = 0;   // must release
@@ -639,7 +963,24 @@ bool extractExtensionsFromArchive(MemoryMapFileInfo * mkext_file_info,
     OSString     * moduleName = 0;   // don't release
     OSString     * errorString = NULL;  // must release
 
-    mkext_data = (mkext_header *)mkext_file_info->paddr;
+    OSData         * moduleInfo = 0;  // must release
+    MkextEntryInfo   module_info;
+
+    IORegistryEntry * root;
+    OSData * checksumObj;
+
+    if (vaddr) {
+       // addExtensionsFromArchive passes a kernel virtual address
+       mkext_data = (mkext_header *)mkext_file_info->paddr;
+    } else {
+#if defined (__ppc__) || defined (__arm__)
+       mkext_data = (mkext_header *)ml_static_ptovirt(mkext_file_info->paddr);
+#elif defined (__i386__)
+       mkext_data = (mkext_header *)ml_boot_ptovirt(mkext_file_info->paddr);
+#else
+#error unsupported architecture
+#endif
+    }
 
     if (OSSwapBigToHostInt32(mkext_data->magic) != MKEXT_MAGIC ||
         OSSwapBigToHostInt32(mkext_data->signature) != MKEXT_SIGN) {
@@ -669,6 +1010,16 @@ bool extractExtensionsFromArchive(MemoryMapFileInfo * mkext_file_info,
         goto finish;
     }
 
+    root = IORegistryEntry::getRegistryRoot();
+    assert(root);
+    checksumObj = OSData::withBytes((void *)&checksum,
+        sizeof(checksum));
+    assert(checksumObj);
+    if (checksumObj) {
+        root->setProperty(kIOStartupMkextCRC, checksumObj);
+        checksumObj->release();
+    }
+
    /* If the MKEXT archive isn't fat, check that the CPU type & subtype
     * match that of the running kernel.
     */
@@ -695,7 +1046,8 @@ bool extractExtensionsFromArchive(MemoryMapFileInfo * mkext_file_info,
             result = false;
             goto finish;
         }
-        if (!check_cpu_subtype(OSSwapBigToHostInt32(mkext_data->cpusubtype))) {
+        if (!grade_binary(OSSwapBigToHostInt32(mkext_data->cputype),
+                         OSSwapBigToHostInt32(mkext_data->cpusubtype))) {
             IOLog("Error: Extension archive doesn't contain software "
                 "for this computer's CPU subtype.\n");
             LOG_DELAY();
@@ -708,9 +1060,16 @@ bool extractExtensionsFromArchive(MemoryMapFileInfo * mkext_file_info,
          i < OSSwapBigToHostInt32(mkext_data->numkexts);
          i++) {
 
-        kmod_info_t * loaded_kmod = 0;
+        if (loaded_kmod) {
+            kfree(loaded_kmod, sizeof(kmod_info_t));
+            loaded_kmod = 0;
+        }
 
         if (driverPlistDataObject) {
+            kmem_free(kernel_map,
+                (unsigned int)driverPlistDataObject->getBytesNoCopy(),
+                driverPlistDataObject->getLength());
+
             driverPlistDataObject->release();
             driverPlistDataObject = NULL;
         }
@@ -739,22 +1098,20 @@ bool extractExtensionsFromArchive(MemoryMapFileInfo * mkext_file_info,
             &driverPlistDataObject)) {
 
             IOLog("Error: couldn't uncompress plist file "
-                "%d from multikext archive.\n", i);
+                "from multikext archive entry %d.\n", i);
             LOG_DELAY();
-            result = false;
-            goto finish;  // or just continue?
+            continue;
         }
 
         if (!driverPlistDataObject) {
             IOLog("Error: No property list present "
                 "for multikext archive entry %d.\n", i);
             LOG_DELAY();
-            result = false;
-            goto finish;  // or just continue?
+            continue;
         } else {
             driverPlist = OSDynamicCast(OSDictionary,
                 OSUnserializeXML(
-                    (char *)driverPlistDataObject->getBytesNoCopy(),
+                    (const char *)driverPlistDataObject->getBytesNoCopy(),
                     &errorString));
             if (!driverPlist) {
                 IOLog("Error: Couldn't read XML property list "
@@ -765,16 +1122,12 @@ bool extractExtensionsFromArchive(MemoryMapFileInfo * mkext_file_info,
                         errorString->getCStringNoCopy());
                     LOG_DELAY();
                 }
-                result = false;
-                goto finish;  // or just continue?
+                continue;
             }
 
-            if (!validateExtensionDict(driverPlist)) {
-                IOLog("Error: Failed to validate property list "
-                      "for multikext archive entry %d.\n", i);
-                LOG_DELAY();
-                result = false;
-                goto finish;
+            if (!validateExtensionDict(driverPlist, i)) {
+                // validateExtensionsDict() logs an error
+                continue;
             }
 
         }
@@ -794,7 +1147,7 @@ bool extractExtensionsFromArchive(MemoryMapFileInfo * mkext_file_info,
        /* Check if kmod is already loaded and is a real loadable one (has
         * an address).
         */
-        loaded_kmod = kmod_lookupbyname(moduleName->getCStringNoCopy());
+        loaded_kmod = kmod_lookupbyname_locked(moduleName->getCStringNoCopy());
         if (loaded_kmod && loaded_kmod->address) {
             IOLog("Skipping new extension \"%s\"; an extension named "
                 "\"%s\" is already loaded.\n",
@@ -815,20 +1168,37 @@ bool extractExtensionsFromArchive(MemoryMapFileInfo * mkext_file_info,
 
         driverDict->setObject("plist", driverPlist);
 
-        if (!uncompressFile((u_int8_t *)mkext_data, module_file,
-            &driverCode)) {
+       /*****
+        * Prepare an entry to hold the mkext entry info for the
+        * compressed binary module, if there is one. If all four fields
+        * of the module entry are zero, there isn't one.
+        */
+        if (!(loaded_kmod && loaded_kmod->address) && (OSSwapBigToHostInt32(module_file->offset) ||
+            OSSwapBigToHostInt32(module_file->compsize) ||
+            OSSwapBigToHostInt32(module_file->realsize) ||
+            OSSwapBigToHostInt32(module_file->modifiedsecs))) {
+
+            moduleInfo = OSData::withCapacity(sizeof(MkextEntryInfo));
+            if (!moduleInfo) {
+                IOLog("Error: Couldn't allocate data object "
+                      "for multikext archive entry %d.\n", i);
+                LOG_DELAY();
+                result = false;
+                goto finish;
+            }
 
-            IOLog("Error: couldn't uncompress module file "
-                "%d from multikext archive.\n", i);
-            LOG_DELAY();
-            result = false;
-            goto finish;  // or just continue?
-        }
+            module_info.base_address = (vm_address_t)mkext_data;
+            module_info.fileinfo = module_file;
 
-       /* It's okay for there to be no module
-        */
-        if (driverCode) {
-            driverDict->setObject("code", driverCode);
+            if (!moduleInfo->appendBytes(&module_info, sizeof(module_info))) {
+                IOLog("Error: Couldn't record info "
+                      "for multikext archive entry %d.\n", i);
+                LOG_DELAY();
+                result = false;
+                goto finish;
+            }
+
+            driverDict->setObject("compressedCode", moduleInfo);
         }
 
         OSDictionary * incumbentExt = OSDynamicCast(OSDictionary,
@@ -869,20 +1239,24 @@ bool extractExtensionsFromArchive(MemoryMapFileInfo * mkext_file_info,
 
 finish:
 
-    if (driverPlistDataObject) driverPlistDataObject->release();
+    if (loaded_kmod) kfree(loaded_kmod, sizeof(kmod_info_t));
+    if (driverPlistDataObject) {
+        kmem_free(kernel_map,
+            (unsigned int)driverPlistDataObject->getBytesNoCopy(),
+            driverPlistDataObject->getLength());
+        driverPlistDataObject->release();
+    }
     if (driverPlist) driverPlist->release();
-    if (driverCode) driverCode->release();
-    if (driverDict) driverDict->release();
+    if (driverCode)  driverCode->release();
+    if (moduleInfo)  moduleInfo->release();
+    if (driverDict)  driverDict->release();
     if (errorString) errorString->release();
 
     return result;
 }
 
-
 /*********************************************************************
-* Unlike with single KEXTs, a failure to read any member of a
-* multi-KEXT archive is considered a failure for all. We want to
-* take no chances unpacking a single, compressed archive of drivers.
+*
 *********************************************************************/
 bool readExtensions(OSDictionary * propertyDict,
     const char * memory_map_name,
@@ -890,7 +1264,7 @@ bool readExtensions(OSDictionary * propertyDict,
 
     bool result = true;
     OSData * mkextDataObject = 0;      // don't release
-    MemoryMapFileInfo * mkext_file_info = 0; // don't free
+    const MemoryMapFileInfo * mkext_file_info = 0; // don't free
 
     mkextDataObject = OSDynamicCast(OSData,
         propertyDict->getObject(memory_map_name));
@@ -905,13 +1279,13 @@ bool readExtensions(OSDictionary * propertyDict,
         goto finish;
     }
 
-    mkext_file_info = (MemoryMapFileInfo *)mkextDataObject->getBytesNoCopy();
+    mkext_file_info = (const MemoryMapFileInfo *)mkextDataObject->getBytesNoCopy();
     if (!mkext_file_info) {
         result = false;
         goto finish;
     }
 
-    result = extractExtensionsFromArchive(mkext_file_info, extensions);
+    result = extractExtensionsFromArchive(mkext_file_info, false /*physical*/, extensions);
 
 finish:
 
@@ -919,9 +1293,6 @@ finish:
         extensions->flushCollection();
     }
 
-    IODTFreeLoaderInfo(memory_map_name, (void *)mkext_file_info->paddr,
-        (int)mkext_file_info->length);
-
     return result;
 }
 
@@ -972,7 +1343,7 @@ bool addPersonalities(OSDictionary * extensions) {
 
         if (thisDriverPersonalities) {
             OSCollectionIterator * pIterator;
-            OSString * key;
+            OSString * locakKey;
             pIterator = OSCollectionIterator::withCollection(
                 thisDriverPersonalities);
             if (!pIterator) {
@@ -981,12 +1352,12 @@ bool addPersonalities(OSDictionary * extensions) {
                 LOG_DELAY();
                 continue;
             }
-            while ( (key = OSDynamicCast(OSString,
+            while ( (locakKey = OSDynamicCast(OSString,
                      pIterator->getNextObject())) ) {
 
                 OSDictionary * personality = OSDynamicCast(
                     OSDictionary,
-                    thisDriverPersonalities->getObject(key));
+                    thisDriverPersonalities->getObject(locakKey));
                 if (personality) {
                     allDriverPersonalities->setObject(personality);
                 }
@@ -1012,22 +1383,32 @@ finish:
 
 /*********************************************************************
 * Called from IOCatalogue to add extensions from an mkext archive.
+* This function makes a copy of the mkext object passed in because
+* the device tree support code dumps it after calling us (indirectly
+* through the IOCatalogue).
 *********************************************************************/
 bool addExtensionsFromArchive(OSData * mkextDataObject) {
     bool result = true;
 
     OSDictionary * startupExtensions = NULL;  // don't release
+    OSArray      * bootLoaderObjects = NULL;  // don't release
     OSDictionary * extensions = NULL;         // must release
     MemoryMapFileInfo mkext_file_info;
     OSCollectionIterator * keyIterator = NULL;   // must release
     OSString             * key = NULL;           // don't release
 
-    IOLockLock(kld_lock);
-
     startupExtensions = getStartupExtensions();
     if (!startupExtensions) {
-        IOLog("Can't record extension archive; there is no
-            extensions dictionary.\n");
+        IOLog("Can't record extension archive; there is no"
+            " extensions dictionary.\n");
+        LOG_DELAY();
+        result = false;
+        goto finish;
+    }
+
+    bootLoaderObjects = getBootLoaderObjects();
+    if (! bootLoaderObjects) {
+        IOLog("Error: Couldn't allocate array to hold temporary objects.\n");
         LOG_DELAY();
         result = false;
         goto finish;
@@ -1045,7 +1426,11 @@ bool addExtensionsFromArchive(OSData * mkextDataObject) {
     mkext_file_info.paddr = (UInt32)mkextDataObject->getBytesNoCopy();
     mkext_file_info.length = mkextDataObject->getLength();
 
-    result = extractExtensionsFromArchive(&mkext_file_info, extensions);
+   /* Save the local mkext data object so that we can deallocate it later.
+    */
+    bootLoaderObjects->setObject(mkextDataObject);
+
+    result = extractExtensionsFromArchive(&mkext_file_info, true /*virtual*/, extensions);
     if (!result) {
         IOLog("Error: Failed to extract extensions from archive.\n");
         LOG_DELAY();
@@ -1092,8 +1477,6 @@ finish:
 
     if (extensions) extensions->release();
 
-    IOLockUnlock(kld_lock);
-
     return result;
 }
 
@@ -1114,6 +1497,7 @@ finish:
 * a single extension is not considered fatal, and this function
 * will simply skip the problematic extension to try the next one.
 *********************************************************************/
+
 bool recordStartupExtensions(void) {
     bool result = true;
     OSDictionary         * startupExtensions = NULL; // must release
@@ -1127,10 +1511,9 @@ bool recordStartupExtensions(void) {
     OSDictionary * newDriverDict = NULL;  // must release
     OSDictionary * driverPlist = NULL; // don't release
 
-    IOLockLock(kld_lock);
-
-    IOLog("Recording startup extensions.\n");
-    LOG_DELAY();
+    struct section * infosect;
+    struct section * symsect;
+    unsigned int     prelinkedCount = 0;
 
     existingExtensions = getStartupExtensions();
     if (!existingExtensions) {
@@ -1149,6 +1532,92 @@ bool recordStartupExtensions(void) {
         goto finish;
     }
 
+    // --
+    // add any prelinked modules as startup extensions
+
+    infosect   = getsectbyname("__PRELINK", "__info");
+    symsect    = getsectbyname("__PRELINK", "__symtab");
+    if (infosect && infosect->addr && infosect->size 
+     && symsect && symsect->addr && symsect->size) do
+    {
+       gIOPrelinkedModules = OSDynamicCast(OSArray,
+           OSUnserializeXML((const char *) infosect->addr, NULL));
+
+       if (!gIOPrelinkedModules)
+           break;
+       for( unsigned int idx = 0; 
+               (propertyDict = OSDynamicCast(OSDictionary, gIOPrelinkedModules->getObject(idx)));
+               idx++)
+       {
+           enum { kPrelinkReservedCount = 4 };
+
+           /* Get the extension's module name. This is used to record
+            * the extension. Do *not* release the moduleName.
+            */
+            OSString * moduleName = OSDynamicCast(OSString,
+                propertyDict->getObject("CFBundleIdentifier"));
+            if (!moduleName) {
+                IOLog("Error: Prelinked module entry has "
+                    "no \"CFBundleIdentifier\" property.\n");
+                LOG_DELAY();
+                continue;
+            }
+
+           /* Add the kext, & its plist.
+            */
+           newDriverDict = OSDictionary::withCapacity(4);
+           assert(newDriverDict);
+           newDriverDict->setObject("plist", propertyDict);
+           startupExtensions->setObject(moduleName, newDriverDict);
+           newDriverDict->release();
+
+           /* Add the code if present.
+            */
+           OSData * data = OSDynamicCast(OSData, propertyDict->getObject("OSBundlePrelink"));
+            if (data) {
+               if (data->getLength() < (kPrelinkReservedCount * sizeof(UInt32))) {
+                   IOLog("Error: Prelinked module entry has "
+                       "invalid \"OSBundlePrelink\" property.\n");
+                   LOG_DELAY();
+                   continue;
+               }
+               const UInt32 * prelink;
+               prelink = (const UInt32 *) data->getBytesNoCopy();
+               kmod_info_t * kmod_info = (kmod_info_t *) OSReadBigInt32(prelink, 0);
+               // end of "file" is end of symbol sect
+               data = OSData::withBytesNoCopy((void *) kmod_info->address,
+                           symsect->addr + symsect->size - kmod_info->address);
+               newDriverDict->setObject("code", data);
+               data->release();
+               prelinkedCount++;
+                continue;
+            }
+           /* Add the symbols if present.
+            */
+           OSNumber * num = OSDynamicCast(OSNumber, propertyDict->getObject("OSBundlePrelinkSymbols"));
+            if (num) {
+               UInt32 offset = num->unsigned32BitValue();
+               data = OSData::withBytesNoCopy((void *) (symsect->addr + offset), symsect->size - offset);
+               newDriverDict->setObject("code", data);
+               data->release();
+               prelinkedCount++;
+                continue;
+           }
+       } 
+       if (gIOPrelinkedModules)
+           IOLog("%d prelinked modules\n", prelinkedCount);
+
+       // free __info
+       vm_offset_t
+       virt = ml_static_ptovirt(infosect->addr);
+       if( virt) {
+           ml_static_mfree(virt, infosect->size);
+       }
+       newDriverDict = NULL;
+    }
+    while (false);
+    // --
+
     bootxMemoryMap =
         IORegistryEntry::fromPath(
             "/chosen/memory-map", // path
@@ -1182,7 +1651,6 @@ bool recordStartupExtensions(void) {
 
     while ( (key = OSDynamicCast(OSString,
              keyIterator->getNextObject())) ) {
-
        /* Clear newDriverDict & mkextExtensions upon entry to the loop,
         * handling both successful and unsuccessful iterations.
         */
@@ -1309,7 +1777,7 @@ bool recordStartupExtensions(void) {
 
         // Do not release key.
 
-    } /* while ( (key = OSDynamicCast(OSString, ... */
+    } /* while ( (key = OSDynamicCast(OSString, ...) ) ) */
 
     if (!mergeExtensionDictionaries(existingExtensions, startupExtensions)) {
         IOLog("Error: Failed to merge new extensions into existing set.\n");
@@ -1339,6 +1807,7 @@ finish:
         IOLog("Error: Failed to record startup extensions.\n");
         LOG_DELAY();
     } else {
+#if DEBUG
         keyIterator = OSCollectionIterator::withCollection(
             startupExtensions);
 
@@ -1353,6 +1822,7 @@ finish:
             keyIterator->release();
             keyIterator = 0;
         }
+#endif /* DEBUG */
     }
 
     if (newDriverDict)     newDriverDict->release();
@@ -1361,7 +1831,6 @@ finish:
     if (mkextExtensions)   mkextExtensions->release();
     if (startupExtensions) startupExtensions->release();
 
-    IOLockUnlock(kld_lock);
     return result;
 }
 
@@ -1382,8 +1851,6 @@ void removeStartupExtension(const char * extensionName) {
     OSCollectionIterator * keyIterator = NULL;    // must release
     OSString     * key = NULL;                    // don't release
 
-    IOLockLock(kld_lock);
-
     startupExtensions = getStartupExtensions();
     if (!startupExtensions) goto finish;
 
@@ -1412,8 +1879,8 @@ void removeStartupExtension(const char * extensionName) {
     keyIterator = OSCollectionIterator::withCollection(
         extensionPersonalities);
     if (!keyIterator) {
-        IOLog("Error: Couldn't allocate iterator to scan
-            personalities for %s.\n", extensionName);
+        IOLog("Error: Couldn't allocate iterator to scan"
+            personalities for %s.\n", extensionName);
         LOG_DELAY();
     }
 
@@ -1432,7 +1899,143 @@ void removeStartupExtension(const char * extensionName) {
 finish:
 
     if (keyIterator) keyIterator->release();
+    return;
+}
+
+/*********************************************************************
+* FIXME: This function invalidates the globals gStartupExtensions and
+* FIXME: ...gBootLoaderObjects without setting them to NULL. Since
+* FIXME: ...the code itself is immediately unloaded, there may not be
+* FIXME: ...any reason to worry about that!
+*********************************************************************/
+void clearStartupExtensionsAndLoaderInfo(void)
+{
+    OSDictionary * startupExtensions = NULL;  // must release
+    OSArray      * bootLoaderObjects = NULL;  // must release
+
+    IORegistryEntry      * bootxMemoryMap = NULL;    // must release
+    OSDictionary         * propertyDict = NULL;      // must release
+    OSCollectionIterator * keyIterator = NULL;       // must release
+    OSString             * key = NULL;               // don't release
+
+   /*****
+    * Drop any temporarily held data objects.
+    */
+    bootLoaderObjects = getBootLoaderObjects();
+    if (bootLoaderObjects) {
+        bootLoaderObjects->release();
+    }
+
+   /****
+    * If any "code" entries in driver dictionaries are accompanied
+    * by "compressedCode" entries, then those data objects were
+    * created based of of kmem_alloc()'ed memory, which must be
+    * freed specially.
+    */
+    startupExtensions = getStartupExtensions();
+    if (startupExtensions) {
+        keyIterator =
+            OSCollectionIterator::withCollection(startupExtensions);
+        if (!keyIterator) {
+            IOLog("Error: Couldn't allocate iterator for startup "
+                "extensions.\n");
+            LOG_DELAY();
+            goto memory_map;  // bail to the memory_map label
+        }
+
+        while ( (key = OSDynamicCast(OSString,
+                 keyIterator->getNextObject())) ) {
+
+            OSDictionary * driverDict = 0;
+            OSData * codeData = 0;
+
+            driverDict = OSDynamicCast(OSDictionary,
+                startupExtensions->getObject(key));
+            if (driverDict) {
+                codeData = OSDynamicCast(OSData,
+                    driverDict->getObject("code"));
+
+                if (codeData &&
+                    driverDict->getObject("compressedCode")) {
+
+                    kmem_free(kernel_map,
+                       (unsigned int)codeData->getBytesNoCopy(),
+                        codeData->getLength());
+                }
+            }
+        }
+
+        keyIterator->release();
+        startupExtensions->release();
+    }
+
+memory_map:
+
+   /****
+    * Go through the device tree's memory map and remove any driver
+    * data entries.
+    */
+    bootxMemoryMap =
+        IORegistryEntry::fromPath(
+            "/chosen/memory-map", // path
+            gIODTPlane            // plane
+            );
+    // return value is retained so be sure to release it
+
+    if (!bootxMemoryMap) {
+        IOLog("Error: Couldn't read booter memory map.\n");
+        LOG_DELAY();
+        goto finish;
+    }
+
+    propertyDict = bootxMemoryMap->dictionaryWithProperties();
+    if (!propertyDict) {
+        IOLog("Error: Couldn't get property dictionary "
+            "from memory map.\n");
+        LOG_DELAY();
+        goto finish;
+    }
+
+    keyIterator = OSCollectionIterator::withCollection(propertyDict);
+    if (!keyIterator) {
+        IOLog("Error: Couldn't allocate iterator for driver images.\n");
+        LOG_DELAY();
+        goto finish;
+    }
+
+    while ( (key = OSDynamicCast(OSString,
+             keyIterator->getNextObject())) ) {
+
+        const char * keyValue = key->getCStringNoCopy();
+
+        if ( !strncmp(keyValue, BOOTX_KEXT_PREFIX,
+                  strlen(BOOTX_KEXT_PREFIX)) ||
+             !strncmp(keyValue, BOOTX_MULTIKEXT_PREFIX,
+                  strlen(BOOTX_MULTIKEXT_PREFIX)) ) {
+
+            OSData            * bootxDriverDataObject = NULL;
+            const MemoryMapFileInfo * driverInfo = 0;
+
+            bootxDriverDataObject = OSDynamicCast(OSData,
+                propertyDict->getObject(keyValue));
+            // don't release bootxDriverDataObject
+
+            if (!bootxDriverDataObject) {
+                continue;
+            }
+            driverInfo = (const MemoryMapFileInfo *)
+                bootxDriverDataObject->getBytesNoCopy(0,
+                sizeof(MemoryMapFileInfo));
+            IODTFreeLoaderInfo((char *)keyValue,
+                (void *)driverInfo->paddr,
+                (int)driverInfo->length);
+        }
+    }
+
+finish:
+    if (bootxMemoryMap) bootxMemoryMap->release();
+    if (propertyDict)   propertyDict->release();
+    if (keyIterator)    keyIterator->release();
 
-    IOLockUnlock(kld_lock);
     return;
 }