+IOCatalogue * gIOCatalogue;
+const OSSymbol * gIOClassKey;
+const OSSymbol * gIOProbeScoreKey;
+const OSSymbol * gIOModuleIdentifierKey;
+OSSet * gIOCatalogModuleRequests;
+OSSet * gIOCatalogCacheMisses;
+OSSet * gIOCatalogROMMkexts;
+IOLock * gIOCatalogLock;
+IOLock * gIOKLDLock;
+
+/*********************************************************************
+*********************************************************************/
+
+OSArray * gIOPrelinkedModules = 0;
+
+extern "C" kern_return_t
+kmod_create_internal(
+ kmod_info_t *info,
+ kmod_t *id);
+
+extern "C" kern_return_t
+kmod_destroy_internal(kmod_t id);
+
+extern "C" kern_return_t
+kmod_start_or_stop(
+ kmod_t id,
+ int start,
+ kmod_args_t *data,
+ mach_msg_type_number_t *dataCount);
+
+extern "C" kern_return_t kmod_retain(kmod_t id);
+extern "C" kern_return_t kmod_release(kmod_t id);
+
+static
+kern_return_t start_prelink_module(UInt32 moduleIndex)
+{
+ kern_return_t kr = KERN_SUCCESS;
+ UInt32 * togo;
+ SInt32 count, where, end;
+ UInt32 * prelink;
+ SInt32 next, lastDep;
+ OSData * data;
+ OSString * str;
+ OSDictionary * dict;
+
+ OSArray *
+ prelinkedModules = gIOPrelinkedModules;
+
+ togo = IONew(UInt32, prelinkedModules->getCount());
+ togo[0] = moduleIndex;
+ count = 1;
+
+ for (next = 0; next < count; next++)
+ {
+ dict = (OSDictionary *) prelinkedModules->getObject(togo[next]);
+
+ data = OSDynamicCast(OSData, dict->getObject("OSBundlePrelink"));
+ if (!data)
+ {
+ // already started or no code
+ if (togo[next] == moduleIndex)
+ {
+ kr = KERN_FAILURE;
+ break;
+ }
+ continue;
+ }
+ prelink = (UInt32 *) data->getBytesNoCopy();
+ lastDep = OSReadBigInt32(prelink, 12);
+ for (SInt32 idx = OSReadBigInt32(prelink, 8); idx < lastDep; idx += sizeof(UInt32))
+ {
+ UInt32 depIdx = OSReadBigInt32(prelink, idx) - 1;
+
+ for (where = next + 1;
+ (where < count) && (togo[where] > depIdx);
+ where++) {}
+
+ if (where != count)
+ {
+ if (togo[where] == depIdx)
+ continue;
+ for (end = count; end != where; end--)
+ togo[end] = togo[end - 1];
+ }
+ count++;
+ togo[where] = depIdx;
+ }
+ }
+
+ if (KERN_SUCCESS != kr)
+ return kr;
+
+ for (next = (count - 1); next >= 0; next--)
+ {
+ dict = (OSDictionary *) prelinkedModules->getObject(togo[next]);
+
+ data = OSDynamicCast(OSData, dict->getObject("OSBundlePrelink"));
+ if (!data)
+ continue;
+ prelink = (UInt32 *) data->getBytesNoCopy();
+
+ kmod_t id;
+ kmod_info_t * kmod_info = (kmod_info_t *) OSReadBigInt32(prelink, 0);
+
+ kr = kmod_create_internal(kmod_info, &id);
+ if (KERN_SUCCESS != kr)
+ break;
+
+ lastDep = OSReadBigInt32(prelink, 12);
+ for (SInt32 idx = OSReadBigInt32(prelink, 8); idx < lastDep; idx += sizeof(UInt32))
+ {
+ OSDictionary * depDict;
+ kmod_info_t * depInfo;
+
+ depDict = (OSDictionary *) prelinkedModules->getObject(OSReadBigInt32(prelink, idx) - 1);
+ str = OSDynamicCast(OSString, depDict->getObject(kModuleKey));
+ depInfo = kmod_lookupbyname_locked(str->getCStringNoCopy());
+ if (depInfo)
+ {
+ kr = kmod_retain(KMOD_PACK_IDS(id, depInfo->id));
+ kfree((vm_offset_t) depInfo, sizeof(kmod_info_t));
+ } else
+ IOLog("%s: NO DEP %s\n", kmod_info->name, str->getCStringNoCopy());
+ }
+ dict->removeObject("OSBundlePrelink");
+
+ if (kmod_info->start)
+ kr = kmod_start_or_stop(kmod_info->id, 1, 0, 0);
+ }
+
+ IODelete(togo, UInt32, prelinkedModules->getCount());
+
+ return kr;
+}
+
+/*********************************************************************
+* This is a function that IOCatalogue calls in order to load a kmod.
+*********************************************************************/
+
+static
+kern_return_t kmod_load_from_cache_sym(const OSSymbol * kmod_name)
+{
+ OSArray * prelinkedModules = gIOPrelinkedModules;
+ kern_return_t result = KERN_FAILURE;
+ OSDictionary * dict;
+ OSObject * ident;
+ UInt32 idx;
+
+ if (!gIOPrelinkedModules)
+ return KERN_FAILURE;
+
+ for (idx = 0;
+ (dict = (OSDictionary *) prelinkedModules->getObject(idx));
+ idx++)
+ {
+ if ((ident = dict->getObject(kModuleKey))
+ && kmod_name->isEqualTo(ident))
+ break;
+ }
+ if (dict)
+ {
+ if (kernelLinkerPresent && dict->getObject("OSBundleDefer"))
+ {
+ kmod_load_extension((char *) kmod_name->getCStringNoCopy());
+ result = kIOReturnOffline;
+ }
+ else
+ result = start_prelink_module(idx);
+ }
+
+ return result;
+}
+
+extern "C" Boolean kmod_load_request(const char * moduleName, Boolean make_request)
+{
+ bool ret, cacheMiss = false;
+ kern_return_t kr;
+ const OSSymbol * sym = 0;
+ kmod_info_t * kmod_info;
+
+ if (!moduleName)
+ return false;
+
+ /* To make sure this operation completes even if a bad extension needs
+ * to be removed, take the kld lock for this whole block, spanning the
+ * kmod_load_function() and remove_startup_extension_function() calls.
+ */
+ IOLockLock(gIOKLDLock);
+ do
+ {
+ // Is the module already loaded?
+ ret = (0 != (kmod_info = kmod_lookupbyname_locked((char *)moduleName)));
+ if (ret) {
+ kfree((vm_offset_t) kmod_info, sizeof(kmod_info_t));
+ break;
+ }
+ sym = OSSymbol::withCString(moduleName);
+ if (!sym) {
+ ret = false;
+ break;
+ }
+
+ kr = kmod_load_from_cache_sym(sym);
+ ret = (kIOReturnSuccess == kr);
+ cacheMiss = !ret;
+ if (ret || !make_request || (kr == kIOReturnOffline))
+ break;
+
+ // If the module hasn't been loaded, then load it.
+ if (!kmod_load_function) {
+ IOLog("IOCatalogue: %s cannot be loaded "
+ "(kmod load function not set).\n",
+ moduleName);
+ break;
+ }
+
+ kr = kmod_load_function((char *)moduleName);
+
+ if (ret != kIOReturnSuccess) {
+ IOLog("IOCatalogue: %s cannot be loaded.\n", moduleName);
+
+ /* If the extension couldn't be loaded this time,
+ * make it unavailable so that no more requests are
+ * made in vain. This also enables other matching
+ * extensions to have a chance.
+ */
+ if (kernelLinkerPresent && remove_startup_extension_function) {
+ (*remove_startup_extension_function)(moduleName);
+ }
+ ret = false;
+
+ } else if (kernelLinkerPresent) {
+ // If kern linker is here, the driver is actually loaded,
+ // so return true.
+ ret = true;
+
+ } else {
+ // kern linker isn't here, a request has been queued
+ // but the module isn't necessarily loaded yet, so stall.
+ ret = false;
+ }
+ }
+ while (false);
+
+ IOLockUnlock(gIOKLDLock);
+
+ if (sym)
+ {
+ IOLockLock(gIOCatalogLock);
+ gIOCatalogModuleRequests->setObject(sym);
+ if (cacheMiss)
+ gIOCatalogCacheMisses->setObject(sym);
+ IOLockUnlock(gIOCatalogLock);
+ }
+
+ return ret;
+}
+
+extern "C" kern_return_t kmod_unload_cache(void)
+{
+ OSArray * prelinkedModules = gIOPrelinkedModules;
+ kern_return_t result = KERN_FAILURE;
+ OSDictionary * dict;
+ UInt32 idx;
+ UInt32 * prelink;
+ OSData * data;
+
+ if (!gIOPrelinkedModules)
+ return KERN_SUCCESS;
+
+ IOLockLock(gIOKLDLock);
+ for (idx = 0;
+ (dict = (OSDictionary *) prelinkedModules->getObject(idx));
+ idx++)
+ {
+ data = OSDynamicCast(OSData, dict->getObject("OSBundlePrelink"));
+ if (!data)
+ continue;
+ prelink = (UInt32 *) data->getBytesNoCopy();
+
+ kmod_info_t * kmod_info = (kmod_info_t *) OSReadBigInt32(prelink, 0);
+ vm_offset_t
+ virt = ml_static_ptovirt(kmod_info->address);
+ if( virt) {
+ ml_static_mfree(virt, kmod_info->size);
+ }
+ }
+
+ gIOPrelinkedModules->release();
+ gIOPrelinkedModules = 0;
+
+ IOLockUnlock(gIOKLDLock);
+
+ return result;
+}
+
+extern "C" kern_return_t kmod_load_from_cache(const char * kmod_name)
+{
+ kern_return_t kr;
+ const OSSymbol * sym = OSSymbol::withCStringNoCopy(kmod_name);
+
+ if (sym)
+ {
+ kr = kmod_load_from_cache_sym(sym);
+ sym->release();
+ }
+ else
+ kr = kIOReturnNoMemory;
+
+ return kr;
+}
+
+/*********************************************************************
+*********************************************************************/