X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/de355530ae67247cbd0da700edb3a2a1dae884c2..13fec9890cf095cc781fdf7b8917cb03bf32dd4c:/libsa/catalogue.cpp diff --git a/libsa/catalogue.cpp b/libsa/catalogue.cpp index e9c301939..554fa5a3a 100644 --- a/libsa/catalogue.cpp +++ b/libsa/catalogue.cpp @@ -33,6 +33,7 @@ extern "C" { #include #include #include +#include }; #include @@ -41,35 +42,42 @@ extern "C" { 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 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(char *segname, 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; } @@ -84,60 +92,307 @@ OSArray * getBootLoaderObjects(void) { return gBootLoaderObjects; } gBootLoaderObjects = OSArray::withCapacity(1); - if (! gBootLoaderObjects) { - IOLog("Error: Couldn't allocate " - "bootstrap objects array.\n"); - LOG_DELAY(); - } + 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; + 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; + + // 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 is a legal value - *not* required at boot time + // so we can do install CDs and the like with mkext files containing + // all normally-used drivers. + 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; + } + + } + finish: - // FIXME: Make return real result after kext conversion - return true; + if (keyIterator) keyIterator->release(); + + if (!result) { + 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; } @@ -157,8 +412,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")); @@ -204,8 +459,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, @@ -216,8 +471,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, @@ -450,8 +705,17 @@ OSDictionary * readExtension(OSDictionary * propertyDict, driverInfo = (MemoryMapFileInfo *) bootxDriverDataObject->getBytesNoCopy(0, sizeof(MemoryMapFileInfo)); +#if defined (__ppc__) dataBuffer = (BootxDriverInfo *)ml_static_ptovirt( - driverInfo->paddr); + driverInfo->paddr); +#elif defined (__i386__) + dataBuffer = (BootxDriverInfo *)driverInfo->paddr; + dataBuffer->plistAddr = ml_static_ptovirt(dataBuffer->plistAddr); + if (dataBuffer->moduleAddr) + dataBuffer->moduleAddr = ml_static_ptovirt(dataBuffer->moduleAddr); +#else +#error unsupported architecture +#endif if (!dataBuffer) { IOLog("Error: No data buffer " "for device tree entry \"%s\".\n", memory_map_name); @@ -500,10 +764,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; } @@ -539,7 +801,7 @@ OSDictionary * readExtension(OSDictionary * propertyDict, finish: if (loaded_kmod) { - kfree(loaded_kmod, sizeof(kmod_info_t)); + kfree((unsigned int)loaded_kmod, sizeof(kmod_info_t)); } // do not release bootxDriverDataObject @@ -685,8 +947,14 @@ bool extractExtensionsFromArchive(MemoryMapFileInfo * mkext_file_info, OSData * moduleInfo = 0; // must release MkextEntryInfo module_info; - mkext_data = (mkext_header *)mkext_file_info->paddr; +#if defined (__ppc__) + mkext_data = (mkext_header *)mkext_file_info->paddr; +#elif defined (__i386__) + mkext_data = (mkext_header *)ml_static_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"); @@ -741,7 +1009,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(); @@ -755,11 +1024,15 @@ bool extractExtensionsFromArchive(MemoryMapFileInfo * mkext_file_info, i++) { if (loaded_kmod) { - kfree(loaded_kmod, sizeof(kmod_info_t)); + kfree((unsigned int)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; } @@ -815,10 +1088,8 @@ bool extractExtensionsFromArchive(MemoryMapFileInfo * mkext_file_info, continue; } - if (!validateExtensionDict(driverPlist)) { - IOLog("Error: Failed to validate property list " - "for multikext archive entry %d.\n", i); - LOG_DELAY(); + if (!validateExtensionDict(driverPlist, i)) { + // validateExtensionsDict() logs an error continue; } @@ -865,10 +1136,10 @@ bool extractExtensionsFromArchive(MemoryMapFileInfo * mkext_file_info, * compressed binary module, if there is one. If all four fields * of the module entry are zero, there isn't one. */ - if (OSSwapBigToHostInt32(module_file->offset) || + if (!(loaded_kmod && loaded_kmod->address) && (OSSwapBigToHostInt32(module_file->offset) || OSSwapBigToHostInt32(module_file->compsize) || OSSwapBigToHostInt32(module_file->realsize) || - OSSwapBigToHostInt32(module_file->modifiedsecs)) { + OSSwapBigToHostInt32(module_file->modifiedsecs))) { moduleInfo = OSData::withCapacity(sizeof(MkextEntryInfo)); if (!moduleInfo) { @@ -931,8 +1202,13 @@ bool extractExtensionsFromArchive(MemoryMapFileInfo * mkext_file_info, finish: - if (loaded_kmod) kfree(loaded_kmod, sizeof(kmod_info_t)); - if (driverPlistDataObject) driverPlistDataObject->release(); + if (loaded_kmod) kfree((unsigned int)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(); @@ -1030,7 +1306,7 @@ bool addPersonalities(OSDictionary * extensions) { if (thisDriverPersonalities) { OSCollectionIterator * pIterator; - OSString * key; + OSString * locakKey; pIterator = OSCollectionIterator::withCollection( thisDriverPersonalities); if (!pIterator) { @@ -1039,12 +1315,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); } @@ -1079,7 +1355,6 @@ bool addExtensionsFromArchive(OSData * mkextDataObject) { OSDictionary * startupExtensions = NULL; // don't release OSArray * bootLoaderObjects = NULL; // don't release - OSData * localMkextDataObject = NULL; // don't release OSDictionary * extensions = NULL; // must release MemoryMapFileInfo mkext_file_info; OSCollectionIterator * keyIterator = NULL; // must release @@ -1111,25 +1386,12 @@ bool addExtensionsFromArchive(OSData * mkextDataObject) { goto finish; } - /* The mkext we've been handed (or the data it references) can go away, - * so we need to make a local copy to keep around as long as it might - * be needed. - */ - localMkextDataObject = OSData::withData(mkextDataObject); - if (!localMkextDataObject) { - IOLog("Error: Couldn't copy extension archive.\n"); - LOG_DELAY(); - result = false; - goto finish; - } - - mkext_file_info.paddr = (UInt32)localMkextDataObject->getBytesNoCopy(); - mkext_file_info.length = localMkextDataObject->getLength(); + mkext_file_info.paddr = (UInt32)mkextDataObject->getBytesNoCopy(); + mkext_file_info.length = mkextDataObject->getLength(); /* Save the local mkext data object so that we can deallocate it later. */ - bootLoaderObjects->setObject(localMkextDataObject); - localMkextDataObject->release(); + bootLoaderObjects->setObject(mkextDataObject); result = extractExtensionsFromArchive(&mkext_file_info, extensions); if (!result) { @@ -1198,6 +1460,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 @@ -1211,8 +1474,9 @@ bool recordStartupExtensions(void) { OSDictionary * newDriverDict = NULL; // must release OSDictionary * driverPlist = NULL; // don't release - IOLog("Recording startup extensions.\n"); - LOG_DELAY(); + struct section * infosect; + struct section * symsect; + unsigned int prelinkedCount = 0; existingExtensions = getStartupExtensions(); if (!existingExtensions) { @@ -1231,6 +1495,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; + } + UInt32 * prelink; + prelink = (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 @@ -1435,7 +1785,7 @@ finish: keyIterator->release(); keyIterator = 0; } -#endif DEBUG +#endif /* DEBUG */ } if (newDriverDict) newDriverDict->release();