#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>
extern "C" {
#include <machine/machine_routines.h>
#include <mach/host_info.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 int grade_binary(cpu_type_t exectype, cpu_subtype_t execsubtype);
extern struct segment_command *getsegbyname(char *seg_name);
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);
assert (gStartupExtensions);
return gStartupExtensions;
}
OSArray * getBootLoaderObjects(void) {
if (gBootLoaderObjects) {
return gBootLoaderObjects;
}
gBootLoaderObjects = OSArray::withCapacity(1);
assert (gBootLoaderObjects);
return gBootLoaderObjects;
}
bool validateExtensionDict(OSDictionary * extension, int index) {
bool result = true;
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; OSObject * rawValue = NULL; OSString * stringValue = NULL; OSBoolean * booleanValue = NULL; OSDictionary * personalities = NULL; OSDictionary * libraries = NULL; OSCollectionIterator * keyIterator = NULL; OSString * key = NULL; VERS_version vers;
VERS_version compatible_vers;
char namep[16];
if (!OSDynamicCast(OSDictionary, extension)) {
not_a_dict = true;
result = false;
goto finish;
}
bundleIdentifier = OSDynamicCast(OSString,
extension->getObject("CFBundleIdentifier"));
if (!bundleIdentifier) {
id_missing = true;
result = false;
goto finish;
}
if (bundleIdentifier->getLength() >= KMOD_MAX_NAME) {
result = false;
goto finish;
}
stringValue = OSDynamicCast(OSString,
extension->getObject("CFBundlePackageType"));
if (!stringValue) {
result = false;
goto finish;
}
if (!stringValue->isEqualTo("KEXT")) {
result = false;
goto finish;
}
stringValue = OSDynamicCast(OSString,
extension->getObject("CFBundleVersion"));
if (!stringValue) {
result = false;
goto finish;
}
vers = VERS_parse_string(stringValue->getCStringNoCopy());
if (vers < 0) {
result = false;
goto finish;
}
rawValue = extension->getObject("OSBundleCompatibleVersion");
if (rawValue) {
stringValue = OSDynamicCast(OSString, rawValue);
if (!stringValue) {
result = false;
goto finish;
}
compatible_vers = VERS_parse_string(stringValue->getCStringNoCopy());
if (compatible_vers < 0) {
result = false;
goto finish;
}
if (compatible_vers > vers) {
result = false;
goto finish;
}
}
rawValue = extension->getObject("CFBundleExecutable");
if (rawValue) {
stringValue = OSDynamicCast(OSString, rawValue);
if (!stringValue || stringValue->getLength() == 0) {
result = false;
goto finish;
}
has_executable = true;
}
rawValue = extension->getObject("OSKernelResource");
if (rawValue) {
booleanValue = OSDynamicCast(OSBoolean, rawValue);
if (!booleanValue) {
result = false;
goto finish;
}
is_kernel_resource = booleanValue->isTrue();
}
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;
personality = OSDynamicCast(OSDictionary,
personalities->getObject(key));
if (!personality) {
result = false;
goto finish;
}
if (!OSDynamicCast(OSString, personality->getObject("IOClass"))) {
result = false;
goto finish;
}
if (!OSDynamicCast(OSString,
personality->getObject("IOProviderClass"))) {
result = false;
goto finish;
}
rawValue = personality->getObject("CFBundleIdentifier");
if (!rawValue) {
personality->setObject("CFBundleIdentifier", bundleIdentifier);
} else {
OSString * personalityID = NULL; personalityID = OSDynamicCast(OSString, rawValue);
if (!personalityID) {
result = false;
goto finish;
} else {
if (personalityID->getLength() >= KMOD_MAX_NAME) {
result = false;
goto finish;
}
}
}
rawValue = personality->getObject("IOKitDebug");
if (rawValue && !OSDynamicCast(OSNumber, rawValue)) {
result = false;
goto finish;
}
}
keyIterator->release();
keyIterator = NULL;
}
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;
if (key->getLength() >= KMOD_MAX_NAME) {
result = false;
goto finish;
}
libraryVersion = OSDynamicCast(OSString,
libraries->getObject(key));
if (!libraryVersion) {
result = false;
goto finish;
}
vers = VERS_parse_string(libraryVersion->getCStringNoCopy());
if (vers < 0) {
result = false;
goto finish;
}
}
keyIterator->release();
keyIterator = NULL;
}
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_argn("-x", namep, sizeof (namep))) {
ineligible_for_safe_boot = true;
result = false;
goto finish;
}
finish:
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;
}
OSDictionary * compareExtensionVersions(
OSDictionary * incumbent,
OSDictionary * candidate) {
OSDictionary * winner = NULL;
OSDictionary * incumbentPlist = NULL;
OSDictionary * candidatePlist = NULL;
OSString * incumbentName = NULL;
OSString * candidateName = NULL;
OSString * incumbentVersionString = NULL;
OSString * candidateVersionString = NULL;
VERS_version incumbent_vers = 0;
VERS_version candidate_vers = 0;
incumbentPlist = OSDynamicCast(OSDictionary,
incumbent->getObject("plist"));
candidatePlist = OSDynamicCast(OSDictionary,
candidate->getObject("plist"));
if (!incumbentPlist || !candidatePlist) {
IOLog("compareExtensionVersions() called with invalid "
"extension dictionaries.\n");
LOG_DELAY();
winner = NULL;
goto finish;
}
incumbentName = OSDynamicCast(OSString,
incumbentPlist->getObject("CFBundleIdentifier"));
candidateName = OSDynamicCast(OSString,
candidatePlist->getObject("CFBundleIdentifier"));
incumbentVersionString = OSDynamicCast(OSString,
incumbentPlist->getObject("CFBundleVersion"));
candidateVersionString = OSDynamicCast(OSString,
candidatePlist->getObject("CFBundleVersion"));
if (!incumbentName || !candidateName ||
!incumbentVersionString || !candidateVersionString) {
IOLog("compareExtensionVersions() called with invalid "
"extension dictionaries.\n");
LOG_DELAY();
winner = NULL;
goto finish;
}
if (strcmp(incumbentName->getCStringNoCopy(),
candidateName->getCStringNoCopy())) {
IOLog("compareExtensionVersions() called with different "
"extension names (%s and %s).\n",
incumbentName->getCStringNoCopy(),
candidateName->getCStringNoCopy());
LOG_DELAY();
winner = NULL;
goto finish;
}
incumbent_vers = VERS_parse_string(incumbentVersionString->getCStringNoCopy());
if (incumbent_vers < 0) {
IOLog(VTYELLOW "Error parsing version string for extension %s (%s)\n"
VTRESET,
incumbentName->getCStringNoCopy(),
incumbentVersionString->getCStringNoCopy());
LOG_DELAY();
winner = NULL;
goto finish;
}
candidate_vers = VERS_parse_string(candidateVersionString->getCStringNoCopy());
if (candidate_vers < 0) {
IOLog(VTYELLOW "Error parsing version string for extension %s (%s)\n"
VTRESET,
candidateName->getCStringNoCopy(),
candidateVersionString->getCStringNoCopy());
LOG_DELAY();
winner = NULL;
goto finish;
}
if (0 == strcmp("com.apple.driver.AppleIntelCPUPowerManagement",
incumbentName->getCStringNoCopy())) {
if(0 == strcmp(incumbentVersionString->getCStringNoCopy(), "51.0.0")) {
IOLog(VTYELLOW "Skipping duplicate extension \"%s\" with "
" version (%s -> %s).\n" VTRESET,
candidateName->getCStringNoCopy(),
candidateVersionString->getCStringNoCopy(),
incumbentVersionString->getCStringNoCopy());
winner = incumbent;
goto finish;
} else if (0 == strcmp(candidateVersionString->getCStringNoCopy(), "51.0.0")) {
IOLog(VTYELLOW "Skipping duplicate extension \"%s\" with "
" version (%s -> %s).\n" VTRESET,
candidateName->getCStringNoCopy(),
incumbentVersionString->getCStringNoCopy(),
candidateVersionString->getCStringNoCopy());
winner = candidate;
goto finish;
}
}
if (candidate_vers > incumbent_vers) {
IOLog(VTYELLOW "Replacing extension \"%s\" with newer version "
"(%s -> %s).\n" VTRESET,
incumbentName->getCStringNoCopy(),
incumbentVersionString->getCStringNoCopy(),
candidateVersionString->getCStringNoCopy());
LOG_DELAY();
winner = candidate;
goto finish;
} else {
IOLog(VTYELLOW "Skipping duplicate extension \"%s\" with older/same "
" version (%s -> %s).\n" VTRESET,
candidateName->getCStringNoCopy(),
candidateVersionString->getCStringNoCopy(),
incumbentVersionString->getCStringNoCopy());
LOG_DELAY();
winner = incumbent;
goto finish;
}
finish:
return winner;
}
bool mergeExtensionDictionaries(OSDictionary * mergeInto,
OSDictionary * mergeFrom) {
bool result = true;
OSDictionary * mergeIntoCopy = NULL; OSDictionary * mergeFromCopy = NULL; OSCollectionIterator * keyIterator = NULL; OSString * key;
mergeIntoCopy = OSDictionary::withDictionary(mergeInto,
mergeInto->getCount() + 1);
if (!mergeIntoCopy) {
IOLog("Error: Failed to copy 'into' extensions dictionary "
"for merge.\n");
LOG_DELAY();
result = false;
goto finish;
}
mergeFromCopy = OSDictionary::withDictionary(mergeFrom,
mergeFrom->getCount() + 1);
if (!mergeFromCopy) {
IOLog("Error: Failed to copy 'from' extensions dictionary "
"for merge.\n");
LOG_DELAY();
result = false;
goto finish;
}
keyIterator = OSCollectionIterator::withCollection(mergeFrom);
if (!keyIterator) {
IOLog("Error: Failed to allocate iterator for extensions.\n");
LOG_DELAY();
result = false;
goto finish;
}
while ((key = OSDynamicCast(OSString, keyIterator->getNextObject()))) {
OSDictionary * incumbentExt = OSDynamicCast(OSDictionary,
mergeIntoCopy->getObject(key));
OSDictionary * candidateExt = OSDynamicCast(OSDictionary,
mergeFrom->getObject(key));
if (!incumbentExt) {
if (!mergeIntoCopy->setObject(key, candidateExt)) {
IOLog("mergeExtensionDictionaries(): Failed to add "
"identifier %s\n",
key->getCStringNoCopy());
LOG_DELAY();
result = false;
goto finish;
}
} else {
OSDictionary * mostRecentExtension =
compareExtensionVersions(incumbentExt, candidateExt);
if (mostRecentExtension == incumbentExt) {
mergeFromCopy->removeObject(key);
} else if (mostRecentExtension == candidateExt) {
if (!mergeIntoCopy->setObject(key, candidateExt)) {
IOLog("mergeExtensionDictionaries(): Failed to add "
"identifier %s\n",
key->getCStringNoCopy());
LOG_DELAY();
result = false;
goto finish;
}
} else {
IOLog("mergeExtensionDictionaries(): Error comparing "
"versions of duplicate extensions %s.\n",
key->getCStringNoCopy());
LOG_DELAY();
continue;
}
}
}
finish:
if (result) {
mergeInto->flushCollection();
mergeInto->merge(mergeIntoCopy);
mergeFrom->flushCollection();
mergeFrom->merge(mergeFromCopy);
}
if (mergeIntoCopy) mergeIntoCopy->release();
if (mergeFromCopy) mergeFromCopy->release();
if (keyIterator) keyIterator->release();
return result;
}
#define BOOTX_KEXT_PREFIX "Driver-"
#define BOOTX_MULTIKEXT_PREFIX "DriversPackage-"
typedef struct MemoryMapFileInfo {
UInt32 paddr;
UInt32 length;
} MemoryMapFileInfo;
typedef struct BootxDriverInfo {
char *plistAddr;
long plistLength;
void *moduleAddr;
long moduleLength;
} BootxDriverInfo;
typedef struct MkextEntryInfo {
vm_address_t base_address;
mkext_file * fileinfo;
} MkextEntryInfo;
OSDictionary * readExtension(OSDictionary * propertyDict,
const char * memory_map_name) {
int error = 0;
OSData * bootxDriverDataObject = NULL;
OSDictionary * driverPlist = NULL;
OSString * driverName = NULL;
OSData * driverCode = NULL;
OSString * errorString = NULL;
OSDictionary * driverDict = NULL;
const MemoryMapFileInfo * driverInfo = 0;
BootxDriverInfo * dataBuffer;
kmod_info_t * loaded_kmod = NULL;
bootxDriverDataObject = OSDynamicCast(OSData,
propertyDict->getObject(memory_map_name));
if (!bootxDriverDataObject) {
IOLog("Error: No driver data object "
"for device tree entry \"%s\".\n",
memory_map_name);
LOG_DELAY();
error = 1;
goto finish;
}
driverDict = OSDictionary::withCapacity(2);
if (!driverDict) {
IOLog("Error: Couldn't allocate dictionary "
"for device tree entry \"%s\".\n", memory_map_name);
LOG_DELAY();
error = 1;
goto finish;
}
driverInfo = (const MemoryMapFileInfo *)
bootxDriverDataObject->getBytesNoCopy(0,
sizeof(MemoryMapFileInfo));
#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);
LOG_DELAY();
error = 1;
goto finish;
}
driverPlist = OSDynamicCast(OSDictionary,
OSUnserializeXML(dataBuffer->plistAddr, &errorString));
if (!driverPlist) {
IOLog("Error: Couldn't read XML property list "
"for device tree entry \"%s\".\n", memory_map_name);
LOG_DELAY();
if (errorString) {
IOLog("XML parse error: %s.\n",
errorString->getCStringNoCopy());
LOG_DELAY();
}
error = 1;
goto finish;
}
driverName = OSDynamicCast(OSString,
driverPlist->getObject("CFBundleIdentifier")); if (!driverName) {
IOLog("Error: Device tree entry \"%s\" has "
"no \"CFBundleIdentifier\" property.\n", memory_map_name);
LOG_DELAY();
error = 1;
goto finish;
}
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",
driverName->getCStringNoCopy(),
loaded_kmod->name);
LOG_DELAY();
error = 1;
goto finish;
}
if (!validateExtensionDict(driverPlist, -1)) {
error = 1;
goto finish;
}
driverDict->setObject("plist", driverPlist);
if (dataBuffer->moduleAddr && dataBuffer->moduleLength) {
driverCode = OSData::withBytesNoCopy(dataBuffer->moduleAddr,
dataBuffer->moduleLength);
if (!driverCode) {
IOLog("Error: Couldn't allocate data object "
"to hold code for device tree entry \"%s\".\n",
memory_map_name);
LOG_DELAY();
error = 1;
goto finish;
}
if (driverCode) {
driverDict->setObject("code", driverCode);
}
}
finish:
if (loaded_kmod) {
kfree(loaded_kmod, sizeof(kmod_info_t));
}
if (driverPlist) {
driverPlist->release();
}
if (errorString) {
errorString->release();
}
if (driverCode) {
driverCode->release();
}
if (error) {
if (driverDict) {
driverDict->release();
driverDict = NULL;
}
}
return driverDict;
}
static bool uncompressFile(u_int8_t *base_address, mkext_file * fileinfo,
OSData ** file) {
bool result = true;
kern_return_t kern_result;
u_int8_t * uncompressed_file = 0; OSData * uncompressedFile = 0; size_t uncompressed_size = 0;
size_t offset = OSSwapBigToHostInt32(fileinfo->offset);
size_t compsize = OSSwapBigToHostInt32(fileinfo->compsize);
size_t realsize = OSSwapBigToHostInt32(fileinfo->realsize);
time_t modifiedsecs = OSSwapBigToHostInt32(fileinfo->modifiedsecs);
*file = 0;
if (offset == 0 && compsize == 0 &&
realsize == 0 && modifiedsecs == 0) {
goto finish;
}
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 = false;
goto finish;
}
uncompressedFile = OSData::withBytesNoCopy(uncompressed_file,
realsize + 1);
if (!uncompressedFile) {
IOLog("Error: Couldn't allocate data object "
"to uncompress file.\n");
LOG_DELAY();
result = false;
goto finish;
}
if (compsize != 0) {
uncompressed_size = decompress_lzss(uncompressed_file,
base_address + offset,
compsize);
if (uncompressed_size != realsize) {
IOLog("Error: Uncompressed file is not the length "
"recorded.\n");
LOG_DELAY();
result = false;
goto finish;
}
uncompressed_file[uncompressed_size] = '\0';
} else {
bcopy(base_address + offset, uncompressed_file,
realsize);
uncompressed_file[realsize] = '\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, OSData ** file) {
const MkextEntryInfo *info = (const MkextEntryInfo *) compData->getBytesNoCopy();
return uncompressFile((u_int8_t *) info->base_address,
info->fileinfo, file);
}
bool extractExtensionsFromArchive(const MemoryMapFileInfo * mkext_file_info,
bool vaddr,
OSDictionary * extensions) {
bool result = true;
u_int8_t * crc_address = 0;
u_int32_t checksum;
mkext_header * mkext_data = 0; mkext_kext * onekext_data = 0; mkext_file * plist_file = 0; mkext_file * module_file = 0; kmod_info_t * loaded_kmod = 0;
OSData * driverPlistDataObject = 0; OSDictionary * driverPlist = 0; OSData * driverCode = 0; OSDictionary * driverDict = 0; OSString * moduleName = 0; OSString * errorString = NULL;
OSData * moduleInfo = 0; MkextEntryInfo module_info;
IORegistryEntry * root;
OSData * checksumObj;
if (vaddr) {
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) {
IOLog("Error: Extension archive has invalid magic or signature.\n");
LOG_DELAY();
result = false;
goto finish;
}
if (OSSwapBigToHostInt32(mkext_data->length) != mkext_file_info->length) {
IOLog("Error: Mismatch between extension archive & "
"recorded length.\n");
LOG_DELAY();
result = false;
goto finish;
}
crc_address = (u_int8_t *)&mkext_data->version;
checksum = adler32(crc_address,
(unsigned int)mkext_data +
OSSwapBigToHostInt32(mkext_data->length) - (unsigned int)crc_address);
if (OSSwapBigToHostInt32(mkext_data->adler32) != checksum) {
IOLog("Error: Extension archive has a bad checksum.\n");
LOG_DELAY();
result = false;
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 (OSSwapBigToHostInt32(mkext_data->cputype) != (UInt32)CPU_TYPE_ANY) {
kern_return_t kresult = KERN_FAILURE;
host_basic_info_data_t hostinfo;
host_info_t hostinfo_ptr = (host_info_t)&hostinfo;
mach_msg_type_number_t count = sizeof(hostinfo)/sizeof(integer_t);
kresult = host_info((host_t)1, HOST_BASIC_INFO,
hostinfo_ptr, &count);
if (kresult != KERN_SUCCESS) {
IOLog("Error: Couldn't get current host info.\n");
LOG_DELAY();
result = false;
goto finish;
}
if ((UInt32)hostinfo.cpu_type !=
OSSwapBigToHostInt32(mkext_data->cputype)) {
IOLog("Error: Extension archive doesn't contain software "
"for this computer's CPU type.\n");
LOG_DELAY();
result = false;
goto finish;
}
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();
result = false;
goto finish;
}
}
for (unsigned int i = 0;
i < OSSwapBigToHostInt32(mkext_data->numkexts);
i++) {
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;
}
if (driverPlist) {
driverPlist->release();
driverPlist = NULL;
}
if (driverCode) {
driverCode->release();
driverCode = NULL;
}
if (driverDict) {
driverDict->release();
driverDict = NULL;
}
if (errorString) {
errorString->release();
errorString = NULL;
}
onekext_data = &mkext_data->kext[i];
plist_file = &onekext_data->plist;
module_file = &onekext_data->module;
if (!uncompressFile((u_int8_t *)mkext_data, plist_file,
&driverPlistDataObject)) {
IOLog("Error: couldn't uncompress plist file "
"from multikext archive entry %d.\n", i);
LOG_DELAY();
continue;
}
if (!driverPlistDataObject) {
IOLog("Error: No property list present "
"for multikext archive entry %d.\n", i);
LOG_DELAY();
continue;
} else {
driverPlist = OSDynamicCast(OSDictionary,
OSUnserializeXML(
(const char *)driverPlistDataObject->getBytesNoCopy(),
&errorString));
if (!driverPlist) {
IOLog("Error: Couldn't read XML property list "
"for multikext archive entry %d.\n", i);
LOG_DELAY();
if (errorString) {
IOLog("XML parse error: %s.\n",
errorString->getCStringNoCopy());
LOG_DELAY();
}
continue;
}
if (!validateExtensionDict(driverPlist, i)) {
continue;
}
}
moduleName = OSDynamicCast(OSString,
driverPlist->getObject("CFBundleIdentifier")); if (!moduleName) {
IOLog("Error: Multikext archive entry %d has "
"no \"CFBundleIdentifier\" property.\n", i);
LOG_DELAY();
continue; }
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",
moduleName->getCStringNoCopy(),
loaded_kmod->name);
continue;
}
driverDict = OSDictionary::withCapacity(2);
if (!driverDict) {
IOLog("Error: Couldn't allocate dictionary "
"for multikext archive entry %d.\n", i);
LOG_DELAY();
result = false;
goto finish;
}
driverDict->setObject("plist", driverPlist);
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;
}
module_info.base_address = (vm_address_t)mkext_data;
module_info.fileinfo = module_file;
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,
extensions->getObject(moduleName));
if (!incumbentExt) {
extensions->setObject(moduleName, driverDict);
} else {
OSDictionary * mostRecentExtension =
compareExtensionVersions(incumbentExt, driverDict);
if (mostRecentExtension == incumbentExt) {
} else if (mostRecentExtension == driverDict) {
if (!extensions->setObject(moduleName, driverDict)) {
IOLog("extractExtensionsFromArchive(): Failed to add "
"identifier %s\n",
moduleName->getCStringNoCopy());
LOG_DELAY();
result = false;
goto finish;
}
} else {
IOLog("extractExtensionsFromArchive(): Error comparing "
"versions of duplicate extensions %s.\n",
moduleName->getCStringNoCopy());
LOG_DELAY();
continue;
}
}
}
finish:
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 (moduleInfo) moduleInfo->release();
if (driverDict) driverDict->release();
if (errorString) errorString->release();
return result;
}
bool readExtensions(OSDictionary * propertyDict,
const char * memory_map_name,
OSDictionary * extensions) {
bool result = true;
OSData * mkextDataObject = 0; const MemoryMapFileInfo * mkext_file_info = 0;
mkextDataObject = OSDynamicCast(OSData,
propertyDict->getObject(memory_map_name));
if (!mkextDataObject) {
IOLog("Error: No mkext data object "
"for device tree entry \"%s\".\n",
memory_map_name);
LOG_DELAY();
result = false;
goto finish;
}
mkext_file_info = (const MemoryMapFileInfo *)mkextDataObject->getBytesNoCopy();
if (!mkext_file_info) {
result = false;
goto finish;
}
result = extractExtensionsFromArchive(mkext_file_info, false , extensions);
finish:
if (!result && extensions) {
extensions->flushCollection();
}
return result;
}
bool addPersonalities(OSDictionary * extensions) {
bool result = true;
OSCollectionIterator * keyIterator = NULL; OSString * key; OSDictionary * driverDict = NULL; OSDictionary * driverPlist = NULL; OSDictionary * thisDriverPersonalities = NULL; OSArray * allDriverPersonalities = NULL;
allDriverPersonalities = OSArray::withCapacity(1);
if (!allDriverPersonalities) {
IOLog("Error: Couldn't allocate personality dictionary.\n");
LOG_DELAY();
result = false;
goto finish;
}
keyIterator = OSCollectionIterator::withCollection(extensions);
if (!keyIterator) {
IOLog("Error: Couldn't allocate iterator to record personalities.\n");
LOG_DELAY();
result = false;
goto finish;
}
while ( ( key = OSDynamicCast(OSString,
keyIterator->getNextObject() ))) {
driverDict = OSDynamicCast(OSDictionary,
extensions->getObject(key));
driverPlist = OSDynamicCast(OSDictionary,
driverDict->getObject("plist"));
thisDriverPersonalities = OSDynamicCast(OSDictionary,
driverPlist->getObject("IOKitPersonalities"));
if (thisDriverPersonalities) {
OSCollectionIterator * pIterator;
OSString * locakKey;
pIterator = OSCollectionIterator::withCollection(
thisDriverPersonalities);
if (!pIterator) {
IOLog("Error: Couldn't allocate iterator "
"to record extension personalities.\n");
LOG_DELAY();
continue;
}
while ( (locakKey = OSDynamicCast(OSString,
pIterator->getNextObject())) ) {
OSDictionary * personality = OSDynamicCast(
OSDictionary,
thisDriverPersonalities->getObject(locakKey));
if (personality) {
allDriverPersonalities->setObject(personality);
}
}
pIterator->release();
}
}
gIOCatalogue->addDrivers(allDriverPersonalities, false);
finish:
if (allDriverPersonalities) allDriverPersonalities->release();
if (keyIterator) keyIterator->release();
return result;
}
bool addExtensionsFromArchive(OSData * mkextDataObject) {
bool result = true;
OSDictionary * startupExtensions = NULL; OSArray * bootLoaderObjects = NULL; OSDictionary * extensions = NULL; MemoryMapFileInfo mkext_file_info;
OSCollectionIterator * keyIterator = NULL; OSString * key = NULL;
startupExtensions = getStartupExtensions();
if (!startupExtensions) {
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;
}
extensions = OSDictionary::withCapacity(2);
if (!extensions) {
IOLog("Error: Couldn't allocate dictionary to unpack "
"extension archive.\n");
LOG_DELAY();
result = false;
goto finish;
}
mkext_file_info.paddr = (UInt32)mkextDataObject->getBytesNoCopy();
mkext_file_info.length = mkextDataObject->getLength();
bootLoaderObjects->setObject(mkextDataObject);
result = extractExtensionsFromArchive(&mkext_file_info, true , extensions);
if (!result) {
IOLog("Error: Failed to extract extensions from archive.\n");
LOG_DELAY();
result = false;
goto finish;
}
result = mergeExtensionDictionaries(startupExtensions, extensions);
if (!result) {
IOLog("Error: Failed to merge new extensions into existing set.\n");
LOG_DELAY();
goto finish;
}
result = addPersonalities(extensions);
if (!result) {
IOLog("Error: Failed to add personalities for extensions extracted "
"from archive.\n");
LOG_DELAY();
result = false;
goto finish;
}
finish:
if (!result) {
IOLog("Error: Failed to record extensions from archive.\n");
LOG_DELAY();
} else {
keyIterator = OSCollectionIterator::withCollection(
extensions);
if (keyIterator) {
while ( (key = OSDynamicCast(OSString,
keyIterator->getNextObject())) ) {
IOLog("Added extension \"%s\" from archive.\n",
key->getCStringNoCopy());
LOG_DELAY();
}
keyIterator->release();
}
}
if (extensions) extensions->release();
return result;
}
bool recordStartupExtensions(void) {
bool result = true;
OSDictionary * startupExtensions = NULL; OSDictionary * existingExtensions = NULL; OSDictionary * mkextExtensions = NULL; IORegistryEntry * bootxMemoryMap = NULL; OSDictionary * propertyDict = NULL; OSCollectionIterator * keyIterator = NULL; OSString * key = NULL;
OSDictionary * newDriverDict = NULL; OSDictionary * driverPlist = NULL;
struct section * infosect;
struct section * symsect;
unsigned int prelinkedCount = 0;
existingExtensions = getStartupExtensions();
if (!existingExtensions) {
IOLog("Error: There is no dictionary for startup extensions.\n");
LOG_DELAY();
result = false;
goto finish;
}
startupExtensions = OSDictionary::withCapacity(1);
if (!startupExtensions) {
IOLog("Error: Couldn't allocate dictionary "
"to record startup extensions.\n");
LOG_DELAY();
result = false;
goto finish;
}
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 };
OSString * moduleName = OSDynamicCast(OSString,
propertyDict->getObject("CFBundleIdentifier"));
if (!moduleName) {
IOLog("Error: Prelinked module entry has "
"no \"CFBundleIdentifier\" property.\n");
LOG_DELAY();
continue;
}
newDriverDict = OSDictionary::withCapacity(4);
assert(newDriverDict);
newDriverDict->setObject("plist", propertyDict);
startupExtensions->setObject(moduleName, newDriverDict);
newDriverDict->release();
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);
data = OSData::withBytesNoCopy((void *) kmod_info->address,
symsect->addr + symsect->size - kmod_info->address);
newDriverDict->setObject("code", data);
data->release();
prelinkedCount++;
continue;
}
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);
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", gIODTPlane );
if (!bootxMemoryMap) {
IOLog("Error: Couldn't read booter memory map.\n");
LOG_DELAY();
result = false;
goto finish;
}
propertyDict = bootxMemoryMap->dictionaryWithProperties();
if (!propertyDict) {
IOLog("Error: Couldn't get property dictionary "
"from memory map.\n");
LOG_DELAY();
result = false;
goto finish;
}
keyIterator = OSCollectionIterator::withCollection(propertyDict);
if (!keyIterator) {
IOLog("Error: Couldn't allocate iterator for driver images.\n");
LOG_DELAY();
result = false;
goto finish;
}
while ( (key = OSDynamicCast(OSString,
keyIterator->getNextObject())) ) {
if (newDriverDict) {
newDriverDict->release();
newDriverDict = NULL;
}
if (mkextExtensions) {
mkextExtensions->release();
mkextExtensions = NULL;
}
const char * keyValue = key->getCStringNoCopy();
if ( !strncmp(keyValue, BOOTX_KEXT_PREFIX,
strlen(BOOTX_KEXT_PREFIX)) ) {
newDriverDict = readExtension(propertyDict, keyValue);
if (!newDriverDict) {
IOLog("Error: Couldn't read data "
"for device tree entry \"%s\".\n", keyValue);
LOG_DELAY();
continue;
}
driverPlist = OSDynamicCast(OSDictionary,
newDriverDict->getObject("plist"));
if (!driverPlist) {
IOLog("Error: Extension in device tree entry \"%s\" "
"has no property list.\n", keyValue);
LOG_DELAY();
continue;
}
OSString * moduleName = OSDynamicCast(OSString,
driverPlist->getObject("CFBundleIdentifier"));
if (!moduleName) {
IOLog("Error: Device tree entry \"%s\" has "
"no \"CFBundleIdentifier\" property.\n", keyValue);
LOG_DELAY();
continue;
}
OSDictionary * incumbentExt = OSDynamicCast(OSDictionary,
startupExtensions->getObject(moduleName));
if (!incumbentExt) {
startupExtensions->setObject(moduleName, newDriverDict);
} else {
OSDictionary * mostRecentExtension =
compareExtensionVersions(incumbentExt, newDriverDict);
if (mostRecentExtension == incumbentExt) {
} else if (mostRecentExtension == newDriverDict) {
if (!startupExtensions->setObject(moduleName,
newDriverDict)) {
IOLog("recordStartupExtensions(): Failed to add "
"identifier %s\n",
moduleName->getCStringNoCopy());
LOG_DELAY();
result = false;
goto finish;
}
} else {
IOLog("recordStartupExtensions(): Error comparing "
"versions of duplicate extensions %s.\n",
moduleName->getCStringNoCopy());
LOG_DELAY();
continue;
}
}
} else if ( !strncmp(keyValue, BOOTX_MULTIKEXT_PREFIX,
strlen(BOOTX_MULTIKEXT_PREFIX)) ) {
mkextExtensions = OSDictionary::withCapacity(10);
if (!mkextExtensions) {
IOLog("Error: Couldn't allocate dictionary to unpack "
"multi-extension archive.\n");
LOG_DELAY();
result = false;
goto finish; }
if (!readExtensions(propertyDict, keyValue, mkextExtensions)) {
IOLog("Error: Couldn't unpack multi-extension archive.\n");
LOG_DELAY();
continue;
} else {
if (!mergeExtensionDictionaries(startupExtensions,
mkextExtensions)) {
IOLog("Error: Failed to merge new extensions into "
"existing set.\n");
LOG_DELAY();
result = false;
goto finish; }
}
}
}
if (!mergeExtensionDictionaries(existingExtensions, startupExtensions)) {
IOLog("Error: Failed to merge new extensions into existing set.\n");
LOG_DELAY();
result = false;
goto finish;
}
result = addPersonalities(startupExtensions);
if (!result) {
IOLog("Error: Failed to add personalities for extensions extracted "
"from archive.\n");
LOG_DELAY();
result = false;
goto finish;
}
finish:
if (keyIterator) {
keyIterator->release();
keyIterator = 0;
}
if (!result) {
IOLog("Error: Failed to record startup extensions.\n");
LOG_DELAY();
} else {
#if DEBUG
keyIterator = OSCollectionIterator::withCollection(
startupExtensions);
if (keyIterator) {
while ( (key = OSDynamicCast(OSString,
keyIterator->getNextObject())) ) {
IOLog("Found extension \"%s\".\n",
key->getCStringNoCopy());
LOG_DELAY();
}
keyIterator->release();
keyIterator = 0;
}
#endif
}
if (newDriverDict) newDriverDict->release();
if (propertyDict) propertyDict->release();
if (bootxMemoryMap) bootxMemoryMap->release();
if (mkextExtensions) mkextExtensions->release();
if (startupExtensions) startupExtensions->release();
return result;
}
void removeStartupExtension(const char * extensionName) {
OSDictionary * startupExtensions = NULL; OSDictionary * extensionDict = NULL; OSDictionary * extensionPlist = NULL; OSDictionary * extensionPersonalities = NULL; OSDictionary * personality = NULL; OSCollectionIterator * keyIterator = NULL; OSString * key = NULL;
startupExtensions = getStartupExtensions();
if (!startupExtensions) goto finish;
extensionDict = OSDynamicCast(OSDictionary,
startupExtensions->getObject(extensionName));
if (!extensionDict) goto finish;
extensionPlist = OSDynamicCast(OSDictionary,
extensionDict->getObject("plist"));
if (!extensionPlist) goto finish;
extensionPersonalities = OSDynamicCast(OSDictionary,
extensionPlist->getObject("IOKitPersonalities"));
if (!extensionPersonalities) goto finish;
keyIterator = OSCollectionIterator::withCollection(
extensionPersonalities);
if (!keyIterator) {
IOLog("Error: Couldn't allocate iterator to scan"
" personalities for %s.\n", extensionName);
LOG_DELAY();
}
while ((key = OSDynamicCast(OSString, keyIterator->getNextObject()))) {
personality = OSDynamicCast(OSDictionary,
extensionPersonalities->getObject(key));
if (personality) {
gIOCatalogue->removeDrivers(personality, true);
}
}
startupExtensions->removeObject(extensionName);
finish:
if (keyIterator) keyIterator->release();
return;
}
void clearStartupExtensionsAndLoaderInfo(void)
{
OSDictionary * startupExtensions = NULL; OSArray * bootLoaderObjects = NULL;
IORegistryEntry * bootxMemoryMap = NULL; OSDictionary * propertyDict = NULL; OSCollectionIterator * keyIterator = NULL; OSString * key = NULL;
bootLoaderObjects = getBootLoaderObjects();
if (bootLoaderObjects) {
bootLoaderObjects->release();
}
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; }
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:
bootxMemoryMap =
IORegistryEntry::fromPath(
"/chosen/memory-map", gIODTPlane );
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));
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();
return;
}