/*
- * 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>
#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;
}
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"));
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,
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,
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
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
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);
/* 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",
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;
}
/* 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 "
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
/*********************************************************************
* 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);
}
// 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;
}
IOLog("Error: Couldn't allocate data object "
"to uncompress file.\n");
LOG_DELAY();
- result = 0;
+ result = false;
goto finish;
}
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;
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;
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
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) {
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.
*/
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();
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;
}
&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 "
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;
}
}
/* 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",
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,
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,
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));
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:
extensions->flushCollection();
}
- IODTFreeLoaderInfo(memory_map_name, (void *)mkext_file_info->paddr,
- (int)mkext_file_info->length);
-
return result;
}
if (thisDriverPersonalities) {
OSCollectionIterator * pIterator;
- OSString * key;
+ OSString * locakKey;
pIterator = OSCollectionIterator::withCollection(
thisDriverPersonalities);
if (!pIterator) {
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);
}
/*********************************************************************
* 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;
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();
if (extensions) extensions->release();
- IOLockUnlock(kld_lock);
-
return result;
}
* 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
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) {
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
while ( (key = OSDynamicCast(OSString,
keyIterator->getNextObject())) ) {
-
/* Clear newDriverDict & mkextExtensions upon entry to the loop,
* handling both successful and unsuccessful iterations.
*/
// 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");
IOLog("Error: Failed to record startup extensions.\n");
LOG_DELAY();
} else {
+#if DEBUG
keyIterator = OSCollectionIterator::withCollection(
startupExtensions);
keyIterator->release();
keyIterator = 0;
}
+#endif /* DEBUG */
}
if (newDriverDict) newDriverDict->release();
if (mkextExtensions) mkextExtensions->release();
if (startupExtensions) startupExtensions->release();
- IOLockUnlock(kld_lock);
return result;
}
OSCollectionIterator * keyIterator = NULL; // must release
OSString * key = NULL; // don't release
- IOLockLock(kld_lock);
-
startupExtensions = getStartupExtensions();
if (!startupExtensions) goto finish;
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();
}
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;
}