]> git.saurik.com Git - apple/xnu.git/blobdiff - libsa/kmod.cpp
xnu-517.9.5.tar.gz
[apple/xnu.git] / libsa / kmod.cpp
index 4d18d4399a31e68f1c21c34b5ab771ddbf5d1946..97675dddb65c19afed28e6a2f76f98f61068db87 100644 (file)
@@ -59,8 +59,8 @@ kmod_start_or_stop(
 extern kern_return_t kmod_retain(kmod_t id);
 extern kern_return_t kmod_release(kmod_t id);
 
-extern void flush_dcache(vm_offset_t addr, unsigned cnt, int phys);
-extern void invalidate_icache(vm_offset_t addr, unsigned cnt, int phys);
+extern void flush_dcache64(addr64_t addr, unsigned cnt, int phys);
+extern void invalidate_icache64(addr64_t addr, unsigned cnt, int phys);
 };
 
 
@@ -167,6 +167,72 @@ bool verifyCompatibility(OSString * extName, OSString * requiredVersion)
     return true;
 }
 
+/*********************************************************************
+*********************************************************************/
+static
+Boolean kextIsADependency(OSString * name) {
+    Boolean result = true;
+    OSDictionary * extensionsDict = 0;    // don't release
+    OSDictionary * extDict = 0;           // don't release
+    OSDictionary * extPlist = 0;          // don't release
+    OSBoolean * isKernelResourceObj = 0;  // don't release
+    OSData * driverCode = 0;              // don't release
+    OSData * compressedCode = 0;          // don't release
+
+    extensionsDict = getStartupExtensions();
+    if (!extensionsDict) {
+        IOLog("kextIsADependency(): No extensions dictionary.\n");
+        LOG_DELAY();
+        result = false;
+        goto finish;
+    }
+    
+
+    extDict = OSDynamicCast(OSDictionary,
+        extensionsDict->getObject(name));
+    if (!extDict) {
+        IOLog("kextIsADependency(): "
+           "Extension \"%s\" cannot be found.\n",
+           name->getCStringNoCopy());
+        LOG_DELAY();
+        result = false;
+        goto finish;
+    }
+
+    extPlist = OSDynamicCast(OSDictionary, extDict->getObject("plist"));
+    if (!extPlist) {
+        IOLog("getDependencyListForKmod(): "
+            "Extension \"%s\" has no property list.\n",
+            name->getCStringNoCopy());
+        LOG_DELAY();
+        result = false;
+        goto finish;
+    }
+
+   /* A kext that is a kernel component is still a dependency, as there
+    * are fake kmod entries for them.
+    */
+    isKernelResourceObj = OSDynamicCast(OSBoolean,
+        extPlist->getObject("OSKernelResource"));
+    if (isKernelResourceObj && isKernelResourceObj->isTrue()) {
+        result = true;
+        goto finish;
+    }
+
+    driverCode = OSDynamicCast(OSData, extDict->getObject("code"));
+    compressedCode = OSDynamicCast(OSData,
+        extDict->getObject("compressedCode"));
+
+    if (!driverCode && !compressedCode) {
+        result = false;
+        goto finish;
+    }
+
+finish:
+
+    return result;
+}
+
 /*********************************************************************
 * This function builds a uniqued, in-order list of modules that need
 * to be loaded in order for kmod_name to be successfully loaded. This
@@ -182,9 +248,6 @@ OSArray * getDependencyListForKmod(const char * kmod_name) {
     OSDictionary * extPlist;       // don't release
     OSString     * extName;        // don't release
     OSArray      * dependencyList = NULL; // return value, caller releases
-    OSBoolean * isKernelResourceObj = 0; // don't release
-    bool isKernelResource = false;
-    bool declaresExecutable = false;
     unsigned int   i;
 
    /* These are used to remove duplicates from the dependency list.
@@ -255,27 +318,6 @@ OSArray * getDependencyListForKmod(const char * kmod_name) {
         goto finish;
     }
 
-   /* A kext that's not a kernel extension and declares no executable has nothing
-    * to load, so just return an empty array.
-    */
-    isKernelResourceObj = OSDynamicCast(OSBoolean,
-        extPlist->getObject("OSKernelResource"));
-    if (isKernelResourceObj && isKernelResourceObj->isTrue()) {
-        isKernelResource = true;
-    } else {
-        isKernelResource = false;
-    }
-
-    if (extPlist->getObject("CFBundleExecutable")) {
-        declaresExecutable = true;
-    } else {
-        declaresExecutable = false;
-    }
-
-    if (!isKernelResource && !declaresExecutable) {
-        error = 0;
-        goto finish;
-    }
 
    /* Okay, let's get started.
     */
@@ -368,28 +410,6 @@ OSArray * getDependencyListForKmod(const char * kmod_name) {
                     goto finish;
                 }
 
-               /* Don't add any entries that are not kernel resources and that declare no
-                * executable. Such kexts have nothing to load and so don't belong in the
-                * dependency list. Entries that are kernel resource *do* get added,
-                * however, because such kexts get fake kmod entries for reference counting.
-                */
-                isKernelResourceObj = OSDynamicCast(OSBoolean,
-                    curExtPlist->getObject("OSKernelResource"));
-                if (isKernelResourceObj && isKernelResourceObj->isTrue()) {
-                    isKernelResource = true;
-                } else {
-                    isKernelResource = false;
-                }
-                if (curExtPlist->getObject("CFBundleExecutable")) {
-                    declaresExecutable = true;
-                } else {
-                    declaresExecutable = false;
-                }
-
-                if (!isKernelResource && !declaresExecutable) {
-                    continue;
-                }
-
                 dependencyList->setObject(curDepName);
             }
 
@@ -426,7 +446,9 @@ OSArray * getDependencyListForKmod(const char * kmod_name) {
 
    /* Go backward through the original list, using the encounteredNames
     * dictionary to check for duplicates. We put originalList in as the
-    * value because we need some non-NULL value.
+    * value because we need some non-NULL value. Here we also drop any
+    * extensions that aren't proper dependencies (that is, any that are
+    * nonkernel kexts without code).
     */
     i = originalList->getCount();
 
@@ -437,7 +459,9 @@ OSArray * getDependencyListForKmod(const char * kmod_name) {
             OSString * item = OSDynamicCast(OSString,
                 originalList->getObject(i));
 
-            if ( ! encounteredNames->getObject(item) ) {
+            if ( (!encounteredNames->getObject(item)) &&
+                 kextIsADependency(item)) {
+
                 encounteredNames->setObject(item, originalList);
                 dependencyList->setObject(item);
             }
@@ -522,7 +546,7 @@ unsigned long address_for_loaded_kmod(
         return 0;
     }
 
-    round_headers_size = round_page(headers_size);
+    round_headers_size = round_page_32(headers_size);
     headers_pad = round_headers_size - headers_size;
 
     link_load_address = (unsigned long)g_current_kmod_info->address +
@@ -558,8 +582,8 @@ unsigned long alloc_for_kmod(
     unsigned long round_size;
     unsigned long headers_pad;
 
-    round_headers_size  = round_page(headers_size);
-    round_segments_size = round_page(size - headers_size);
+    round_headers_size  = round_page_32(headers_size);
+    round_segments_size = round_page_32(size - headers_size);
     round_size  = round_headers_size + round_segments_size;
     headers_pad = round_headers_size - headers_size;
 
@@ -672,55 +696,51 @@ int map_and_patch(const char * kmod_name) {
        return 0;
     }
 
+    ret = TRUE;
     if (!kld_file_patch_OSObjects(kmod_name)) {
         IOLog("map_and_patch(): "
               "Extension \"%s\" Error binding OSObjects.\n", kmod_name);
         LOG_DELAY();
-        return 0;
+        
+        // RY: Instead of returning here, set the return value.
+        // We still need to call kld_file_prepare_for_link because
+        // we might have patched files outside of the driver.  Don't
+        // worry, it will know to ignore the damaged file
+        ret = FALSE;
     }
 
     // Now repair any damage that the kld patcher may have done to the image
     kld_file_prepare_for_link();
 
-    return 1;
+    return ret;
 }
 
 /*********************************************************************
 *********************************************************************/
-bool verify_kmod(const char * kmod_name, kmod_info_t * kmod_info) {
+bool stamp_kmod(const char * kmod_name, kmod_info_t * kmod_info) {
     bool result = false;
     OSDictionary * extensionsDict = NULL;  // don't release
     OSDictionary * kmodDict = NULL;        // don't release
     OSDictionary * plist = NULL;           // don't release
     OSString     * versionString = NULL;   // don't release
-    UInt32 plist_vers;
-    UInt32 kmod_vers;
-
-    if (strncmp(kmod_name, kmod_info->name, sizeof(kmod_info->name))) {
-        IOLog("verify_kmod(): kmod loaded as \"%s\" has different "
-            "identifier \"%s\".\n", kmod_name, kmod_info->name);
-        LOG_DELAY();
-        result = false;
-        goto finish;
-    }
-
-    if (!VERS_parse_string(kmod_info->version,
-         &kmod_vers)) {
+    const char   * plist_version = NULL;   // don't free
 
-        IOLog("verify_kmod(): kmod \"%s\" has an invalid "
-            "version.\n", kmod_info->name);
+    if (strlen(kmod_name) + 1 > KMOD_MAX_NAME) {
+        IOLog("stamp_kmod(): Kext identifier \"%s\" is too long.\n",
+            kmod_name);
         LOG_DELAY();
         result = false;
         goto finish;
     }
 
+    strcpy(kmod_info->name, kmod_name);
 
    /* Get the dictionary of startup extensions.
     * This is keyed by module name.
     */
     extensionsDict = getStartupExtensions();
     if (!extensionsDict) {
-        IOLog("verify_kmod(): No extensions dictionary.\n");
+        IOLog("stamp_kmod(): No extensions dictionary.\n");
         LOG_DELAY();
         result = false;
         goto finish;
@@ -729,7 +749,7 @@ bool verify_kmod(const char * kmod_name, kmod_info_t * kmod_info) {
     kmodDict = OSDynamicCast(OSDictionary,
         extensionsDict->getObject(kmod_name));
     if (!kmodDict) {
-        IOLog("verify_kmod(): Can't find record for kmod \"%s\".\n",
+        IOLog("stamp_kmod(): Can't find record for kmod \"%s\".\n",
             kmod_name);
         LOG_DELAY();
         result = false;
@@ -739,17 +759,23 @@ bool verify_kmod(const char * kmod_name, kmod_info_t * kmod_info) {
     plist = OSDynamicCast(OSDictionary,
         kmodDict->getObject("plist"));
     if (!kmodDict) {
-        IOLog("verify_kmod(): Kmod \"%s\" has no property list.\n",
+        IOLog("stamp_kmod(): Kmod \"%s\" has no property list.\n",
             kmod_name);
         LOG_DELAY();
         result = false;
         goto finish;
     }
 
+   /*****
+    * Get the kext's version and stuff it into the kmod. This used
+    * to be a check that the kext & kmod had the same version, but
+    * now we just overwrite the kmod's version.
+    */
+
     versionString = OSDynamicCast(OSString,
         plist->getObject("CFBundleVersion"));
     if (!versionString) {
-        IOLog("verify_kmod(): Kmod \"%s\" has no \"CFBundleVersion\" "
+        IOLog("stamp_kmod(): Kmod \"%s\" has no \"CFBundleVersion\" "
             "property.\n",
             kmod_name);
         LOG_DELAY();
@@ -757,27 +783,24 @@ bool verify_kmod(const char * kmod_name, kmod_info_t * kmod_info) {
         goto finish;
     }
 
-    if (!VERS_parse_string(versionString->getCStringNoCopy(),
-         &plist_vers)) {
-
-        IOLog("verify_kmod(): Property list for kmod \"%s\" has "
-            "an invalid version.\n", kmod_info->name);
+    plist_version = versionString->getCStringNoCopy();
+    if (!plist_version) {
+        IOLog("stamp_kmod(): Can't get C string for kext version.\n");
         LOG_DELAY();
         result = false;
         goto finish;
     }
 
-    if (kmod_vers != plist_vers) {
-        IOLog("verify_kmod(): Kmod \"%s\" and its property list "
-            "claim different versions (%s & %s).\n",
-            kmod_info->name,
-            kmod_info->version,
-            versionString->getCStringNoCopy());
+    if (strlen(plist_version) + 1 > KMOD_MAX_NAME) {
+        IOLog("stamp_kmod(): Version \"%s\" of kext \"%s\" is too long.\n",
+            plist_version, kmod_name);
         LOG_DELAY();
         result = false;
         goto finish;
     }
 
+    strcpy(kmod_info->version, plist_version);
+
     result = true;
 
 finish:
@@ -808,7 +831,8 @@ kern_return_t load_kmod(OSArray * dependencyList) {
     struct mach_header * kmod_header;
     unsigned long kld_result;
     int           do_kld_unload = 0;
-    kmod_info_t * kmod_info;
+    kmod_info_t * kmod_info_freeme = 0;
+    kmod_info_t * kmod_info = 0;
     kmod_t        kmod_id;
 
 
@@ -836,8 +860,8 @@ kern_return_t load_kmod(OSArray * dependencyList) {
 
    /* If the requested kmod is already loaded, there's no work to do.
     */
-    kmod_info = kmod_lookupbyname(requested_kmod_name);
-    if (kmod_info) {
+    kmod_info_freeme = kmod_lookupbyname_locked(requested_kmod_name);
+    if (kmod_info_freeme) {
         // FIXME: Need to check for version mismatch if already loaded.
         result = KERN_SUCCESS;
         goto finish;
@@ -860,6 +884,9 @@ kern_return_t load_kmod(OSArray * dependencyList) {
         goto finish;
     }
 
+    bzero(kmod_dependencies, num_dependencies *
+        sizeof(kmod_info_t *));
+
     for (i = 0; i < num_dependencies; i++) {
 
         currentKmodName = OSDynamicCast(OSString,
@@ -876,7 +903,7 @@ kern_return_t load_kmod(OSArray * dependencyList) {
         const char * current_kmod_name = currentKmodName->getCStringNoCopy();
 
         // These globals are needed by the kld_address functions
-        g_current_kmod_info = kmod_lookupbyname(current_kmod_name);
+        g_current_kmod_info = kmod_lookupbyname_locked(current_kmod_name);
         g_current_kmod_name = current_kmod_name;
 
         if (!g_current_kmod_info) {
@@ -902,7 +929,7 @@ kern_return_t load_kmod(OSArray * dependencyList) {
             continue;
 
        if (!kld_file_merge_OSObjects(current_kmod_name)) {
-            IOLog("get_text_info_for_kmod(): Can't merge OSObjects \"%s\".\n",
+            IOLog("load_kmod(): Can't merge OSObjects \"%s\".\n",
                current_kmod_name);
             LOG_DELAY();
             result = KERN_FAILURE;
@@ -913,7 +940,7 @@ kern_return_t load_kmod(OSArray * dependencyList) {
            kld_file_getaddr(current_kmod_name, (long *) &kmod_size);
         if (!kmod_address) {
 
-            IOLog("get_text_info_for_kmod() failed for dependency kmod "
+            IOLog("load_kmod() failed for dependency kmod "
                 "\"%s\".\n", current_kmod_name);
             LOG_DELAY();
             result = KERN_FAILURE;
@@ -990,7 +1017,7 @@ kern_return_t load_kmod(OSArray * dependencyList) {
     // bcopy() is (from, to, length)
     bcopy((char *)kmod_header, (char *)link_buffer_address, link_header_size);
     bcopy((char *)kmod_header + link_header_size,
-        (char *)link_buffer_address + round_page(link_header_size),
+        (char *)link_buffer_address + round_page_32(link_header_size),
         link_load_size - link_header_size);
 
 
@@ -1005,8 +1032,8 @@ kern_return_t load_kmod(OSArray * dependencyList) {
     }
 
 
-    if (!verify_kmod(requested_kmod_name, kmod_info)) {
-        // verify_kmod() logs a meaningful message
+    if (!stamp_kmod(requested_kmod_name, kmod_info)) {
+        // stamp_kmod() logs a meaningful message
         result = KERN_FAILURE;
         goto finish;
     }
@@ -1018,13 +1045,13 @@ kern_return_t load_kmod(OSArray * dependencyList) {
     */
     kmod_info->address = link_buffer_address;
     kmod_info->size = link_buffer_size;
-    kmod_info->hdr_size = round_page(link_header_size);
+    kmod_info->hdr_size = round_page_32(link_header_size);
 
    /* We've written data and instructions, so *flush* the data cache
     * and *invalidate* the instruction cache.
     */
-    flush_dcache(link_buffer_address, link_buffer_size, false);
-    invalidate_icache(link_buffer_address, link_buffer_size, false);
+    flush_dcache64((addr64_t)link_buffer_address, link_buffer_size, false);
+    invalidate_icache64((addr64_t)link_buffer_address, link_buffer_size, false);
 
 
    /* Register the new kmod with the kernel proper.
@@ -1041,7 +1068,7 @@ kern_return_t load_kmod(OSArray * dependencyList) {
     IOLog("kmod id %d successfully created at 0x%lx, size %ld.\n",
         (unsigned int)kmod_id, link_buffer_address, link_buffer_size);
     LOG_DELAY();
-#endif DEBUG
+#endif /* DEBUG */
 
    /* Record dependencies for the newly-loaded kmod.
     */
@@ -1075,11 +1102,14 @@ kern_return_t load_kmod(OSArray * dependencyList) {
 
 finish:
 
+    if (kmod_info_freeme) {
+        kfree((unsigned int)kmod_info_freeme, sizeof(kmod_info_t));
+    }
+
    /* Only do a kld_unload_all() if at least one load happened.
     */
     if (do_kld_unload) {
         kld_unload_all(/* deallocate sets */ 1);
-
     }
 
    /* If the link failed, blow away the allocated link buffer.
@@ -1089,6 +1119,11 @@ finish:
     }
 
     if (kmod_dependencies) {
+        for (i = 0; i < num_dependencies; i++) {
+            if (kmod_dependencies[i]) {
+                kfree((unsigned int)kmod_dependencies[i], sizeof(kmod_info_t));
+            }
+        }
         kfree((unsigned int)kmod_dependencies,
             num_dependencies * sizeof(kmod_info_t *));
     }
@@ -1117,14 +1152,13 @@ finish:
 __private_extern__
 kern_return_t load_kernel_extension(char * kmod_name) {
     kern_return_t result = KERN_SUCCESS;
-    kmod_info_t * kmod_info;
+    kmod_info_t * kmod_info = 0;  // must free
     OSArray * dependencyList = NULL;     // must release
     OSArray * curDependencyList = NULL;  // must release
-    bool isKernelResource = false;
 
    /* See if the kmod is already loaded.
     */
-    kmod_info = kmod_lookupbyname(kmod_name);
+    kmod_info = kmod_lookupbyname_locked(kmod_name);
     if (kmod_info) {  // NOT checked
         result = KERN_SUCCESS;
         goto finish;
@@ -1180,6 +1214,10 @@ kern_return_t load_kernel_extension(char * kmod_name) {
 
 finish:
 
+    if (kmod_info) {
+        kfree((unsigned int)kmod_info, sizeof(kmod_info_t));
+    }
+
     if (dependencyList) {
         dependencyList->release();
         dependencyList = NULL;