Loading...
--- xnu/xnu-124.13/libsa/catalogue.cpp
+++ xnu/xnu-201/libsa/catalogue.cpp
@@ -39,7 +39,6 @@
#include <IOKit/assert.h>
-
extern "C" {
extern void IODTFreeLoaderInfo( char *key, void *infoAddr, int infoSize );
extern kern_return_t host_info(host_t host,
@@ -47,8 +46,6 @@
host_info_t info,
mach_msg_type_number_t *count);
extern int check_cpu_subtype(cpu_subtype_t cpu_subtype);
-
-extern IOLock * kld_lock;
};
@@ -61,6 +58,7 @@
/*********************************************************************
*********************************************************************/
static OSDictionary * gStartupExtensions = 0;
+static OSArray * gBootLoaderObjects = 0;
OSDictionary * getStartupExtensions(void) {
if (gStartupExtensions) {
@@ -73,6 +71,25 @@
LOG_DELAY();
}
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);
+ if (! gBootLoaderObjects) {
+ IOLog("Error: Couldn't allocate "
+ "bootstrap objects array.\n");
+ LOG_DELAY();
+ }
+ return gBootLoaderObjects;
}
@@ -380,6 +397,11 @@
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
@@ -492,9 +514,14 @@
/* It's perfectly okay for a KEXT to have no executable.
* Check that moduleAddr is nonzero before attempting to
* get one.
+ *
+ * NOTE: The driverCode object is created "no-copy", so
+ * it doesn't own that memory. The memory must be freed
+ * separately from the OSData object (see
+ * clearStartupExtensionsAndLoaderInfo() at the end of this file).
*/
if (dataBuffer->moduleAddr && dataBuffer->moduleLength) {
- driverCode = OSData::withBytes(dataBuffer->moduleAddr,
+ driverCode = OSData::withBytesNoCopy(dataBuffer->moduleAddr,
dataBuffer->moduleLength);
if (!driverCode) {
IOLog("Error: Couldn't allocate data object "
@@ -512,12 +539,6 @@
finish:
- /* Free the memory for this extension that was set up
- * by bootx.
- */
- IODTFreeLoaderInfo(memory_map_name, (void *)driverInfo->paddr,
- (int)driverInfo->length);
-
// do not release bootxDriverDataObject
// do not release driverName
@@ -542,14 +563,17 @@
/*********************************************************************
* 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) {
-
- int result = 1;
- u_int8_t * uncompressed_file = 0; // don't free; owned by OSData obj
- OSData * uncompressedFile = 0; // don't release
+static bool uncompressFile(u_int8_t *base_address, mkext_file * fileinfo,
+ /* out */ OSData ** file) {
+
+ bool result = true;
+ kern_return_t kern_result;
+ u_int8_t * uncompressed_file = 0; // kmem_free() on error
+ OSData * uncompressedFile = 0; // returned
size_t uncompressed_size = 0;
size_t offset = OSSwapBigToHostInt32(fileinfo->offset);
@@ -568,12 +592,13 @@
}
// Add 1 for '\0' to terminate XML string!
- uncompressed_file = (u_int8_t *)kalloc(realsize + 1);
- if (!uncompressed_file) {
+ kern_result = kmem_alloc(kernel_map, (vm_offset_t *)&uncompressed_file,
+ realsize + 1);
+ if (kern_result != KERN_SUCCESS) {
IOLog("Error: Couldn't allocate data buffer "
"to uncompress file.\n");
LOG_DELAY();
- result = 0;
+ result = false;
goto finish;
}
@@ -583,7 +608,7 @@
IOLog("Error: Couldn't allocate data object "
"to uncompress file.\n");
LOG_DELAY();
- result = 0;
+ result = false;
goto finish;
}
@@ -595,7 +620,7 @@
IOLog("Error: Uncompressed file is not the length "
"recorded.\n");
LOG_DELAY();
- result = 0;
+ result = false;
goto finish;
}
} else {
@@ -608,12 +633,24 @@
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) {
+
+ MkextEntryInfo *info = (MkextEntryInfo *) compData->getBytesNoCopy();
+
+ return uncompressFile((u_int8_t *) info->base_address,
+ info->fileinfo, file);
}
@@ -639,6 +676,9 @@
OSString * moduleName = 0; // don't release
OSString * errorString = NULL; // must release
+ OSData * moduleInfo = 0; // must release
+ MkextEntryInfo module_info;
+
mkext_data = (mkext_header *)mkext_file_info->paddr;
if (OSSwapBigToHostInt32(mkext_data->magic) != MKEXT_MAGIC ||
@@ -739,18 +779,16 @@
&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(
@@ -765,16 +803,14 @@
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;
+ continue;
}
}
@@ -815,20 +851,37 @@
driverDict->setObject("plist", driverPlist);
- if (!uncompressFile((u_int8_t *)mkext_data, module_file,
- &driverCode)) {
-
- IOLog("Error: couldn't uncompress module file "
- "%d from multikext archive.\n", i);
- LOG_DELAY();
- result = false;
- goto finish; // or just continue?
- }
-
- /* It's okay for there to be no module
+ /*****
+ * 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 (driverCode) {
- driverDict->setObject("code", driverCode);
+ if (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,
@@ -871,18 +924,16 @@
if (driverPlistDataObject) 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,
@@ -918,9 +969,6 @@
if (!result && extensions) {
extensions->flushCollection();
}
-
- IODTFreeLoaderInfo(memory_map_name, (void *)mkext_file_info->paddr,
- (int)mkext_file_info->length);
return result;
}
@@ -1012,18 +1060,21 @@
/*********************************************************************
* 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
+ OSData * localMkextDataObject = 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
@@ -1033,6 +1084,14 @@
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 "
@@ -1042,8 +1101,25 @@
goto finish;
}
- mkext_file_info.paddr = (UInt32)mkextDataObject->getBytesNoCopy();
- mkext_file_info.length = mkextDataObject->getLength();
+ /* 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();
+
+ /* Save the local mkext data object so that we can deallocate it later.
+ */
+ bootLoaderObjects->setObject(localMkextDataObject);
+ localMkextDataObject->release();
result = extractExtensionsFromArchive(&mkext_file_info, extensions);
if (!result) {
@@ -1091,8 +1167,6 @@
}
if (extensions) extensions->release();
-
- IOLockUnlock(kld_lock);
return result;
}
@@ -1127,8 +1201,6 @@
OSDictionary * newDriverDict = NULL; // must release
OSDictionary * driverPlist = NULL; // don't release
- IOLockLock(kld_lock);
-
IOLog("Recording startup extensions.\n");
LOG_DELAY();
@@ -1339,6 +1411,7 @@
IOLog("Error: Failed to record startup extensions.\n");
LOG_DELAY();
} else {
+#if DEBUG
keyIterator = OSCollectionIterator::withCollection(
startupExtensions);
@@ -1353,6 +1426,7 @@
keyIterator->release();
keyIterator = 0;
}
+#endif DEBUG
}
if (newDriverDict) newDriverDict->release();
@@ -1361,7 +1435,6 @@
if (mkextExtensions) mkextExtensions->release();
if (startupExtensions) startupExtensions->release();
- IOLockUnlock(kld_lock);
return result;
}
@@ -1382,8 +1455,6 @@
OSCollectionIterator * keyIterator = NULL; // must release
OSString * key = NULL; // don't release
- IOLockLock(kld_lock);
-
startupExtensions = getStartupExtensions();
if (!startupExtensions) goto finish;
@@ -1432,7 +1503,143 @@
finish:
if (keyIterator) keyIterator->release();
-
- IOLockUnlock(kld_lock);
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;
+ MemoryMapFileInfo * driverInfo = 0;
+
+ bootxDriverDataObject = OSDynamicCast(OSData,
+ propertyDict->getObject(keyValue));
+ // don't release bootxDriverDataObject
+
+ if (!bootxDriverDataObject) {
+ continue;
+ }
+ driverInfo = (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;
+}