Fix module loading for vkernel.
[dragonfly.git] / sys / kern / kern_linker.c
index 2eb6532..c1159f3 100644 (file)
@@ -24,7 +24,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/kern/kern_linker.c,v 1.41.2.3 2001/11/21 17:50:35 luigi Exp $
- * $DragonFly: src/sys/kern/kern_linker.c,v 1.21 2005/03/29 00:35:55 drhodus Exp $
+ * $DragonFly: src/sys/kern/kern_linker.c,v 1.38 2007/06/07 22:58:11 corecode Exp $
  */
 
 #include "opt_ddb.h"
 
 #include <vm/vm_zone.h>
 
+#ifdef _KERNEL_VIRTUAL
+#include <dlfcn.h>
+#endif
+
 #ifdef KLD_DEBUG
 int kld_debug = 0;
 #endif
@@ -66,12 +70,12 @@ static int next_file_id = 1;
 static void
 linker_init(void* arg)
 {
-    lockinit(&lock, 0, "klink", 0, 0);
+    lockinit(&lock, "klink", 0, 0);
     TAILQ_INIT(&classes);
     TAILQ_INIT(&linker_files);
 }
 
-SYSINIT(linker, SI_SUB_KLD, SI_ORDER_FIRST, linker_init, 0);
+SYSINIT(linker, SI_BOOT2_KLD, SI_ORDER_FIRST, linker_init, 0);
 
 int
 linker_add_class(const char* desc, void* priv,
@@ -79,7 +83,7 @@ linker_add_class(const char* desc, void* priv,
 {
     linker_class_t lc;
 
-    lc = malloc(sizeof(struct linker_class), M_LINKER, M_NOWAIT);
+    lc = kmalloc(sizeof(struct linker_class), M_LINKER, M_NOWAIT);
     if (!lc)
        return ENOMEM;
     bzero(lc, sizeof(*lc));
@@ -114,7 +118,7 @@ linker_file_sysinit(linker_file_t lf)
            moddata = (*sipp)->udata;
            error = module_register(moddata, lf);
            if (error) {
-               printf("linker_file_sysinit \"%s\" failed to register! %d\n",
+               kprintf("linker_file_sysinit \"%s\" failed to register! %d\n",
                    lf->filename, error);
                return error;
            }
@@ -146,7 +150,7 @@ linker_file_sysinit(linker_file_t lf)
      * Perform each task, and continue on to the next task.
      */
     for (sipp = start; sipp < stop; sipp++) {
-       if ((*sipp)->subsystem == SI_SUB_DUMMY)
+       if ((*sipp)->subsystem == SI_SPECIAL_DUMMY)
            continue;   /* skip dummy task(s)*/
 
        /* Call function */
@@ -194,7 +198,7 @@ linker_file_sysuninit(linker_file_t lf)
      * Perform each task, and continue on to the next task.
      */
     for (sipp = start; sipp < stop; sipp++) {
-       if ((*sipp)->subsystem == SI_SUB_DUMMY)
+       if ((*sipp)->subsystem == SI_SPECIAL_DUMMY)
            continue;   /* skip dummy task(s)*/
 
        /* Call function */
@@ -239,7 +243,7 @@ linker_load_file(const char* filename, linker_file_t* result)
     char *koname = NULL;
 
     /* Refuse to load modules if securelevel raised */
-    if (securelevel > 0)
+    if (securelevel > 0 || kernel_mem_readonly)
        return EPERM; 
 
     lf = linker_find_file_by_name(filename);
@@ -256,12 +260,12 @@ linker_load_file(const char* filename, linker_file_t* result)
        goto out;
     }
 
-    koname = malloc(strlen(filename) + 4, M_LINKER, M_WAITOK);
+    koname = kmalloc(strlen(filename) + 4, M_LINKER, M_WAITOK);
     if (koname == NULL) {
        error = ENOMEM;
        goto out;
     }
-    sprintf(koname, "%s.ko", filename);
+    ksprintf(koname, "%s.ko", filename);
     lf = NULL;
     foundfile = 0;
     for (lc = TAILQ_FIRST(&classes); lc; lc = TAILQ_NEXT(lc, link)) {
@@ -306,7 +310,7 @@ linker_load_file(const char* filename, linker_file_t* result)
 
 out:
     if (koname)
-       free(koname, M_LINKER);
+       kfree(koname, M_LINKER);
     return error;
 }
 
@@ -321,23 +325,23 @@ linker_find_file_by_name(const char* filename)
        ;
     filename += i;
 
-    koname = malloc(strlen(filename) + 4, M_LINKER, M_WAITOK);
+    koname = kmalloc(strlen(filename) + 4, M_LINKER, M_WAITOK);
     if (koname == NULL)
        goto out;
-    sprintf(koname, "%s.ko", filename);
+    ksprintf(koname, "%s.ko", filename);
 
-    lockmgr(&lock, LK_SHARED, NULL, curthread);
+    lockmgr(&lock, LK_SHARED);
     for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link)) {
        if (!strcmp(lf->filename, koname))
            break;
        if (!strcmp(lf->filename, filename))
            break;
     }
-    lockmgr(&lock, LK_RELEASE, NULL, curthread);
+    lockmgr(&lock, LK_RELEASE);
 
 out:
     if (koname)
-       free(koname, M_LINKER);
+       kfree(koname, M_LINKER);
     return lf;
 }
 
@@ -346,11 +350,11 @@ linker_find_file_by_id(int fileid)
 {
     linker_file_t lf = 0;
 
-    lockmgr(&lock, LK_SHARED, NULL, curthread);
+    lockmgr(&lock, LK_SHARED);
     for (lf = TAILQ_FIRST(&linker_files); lf; lf = TAILQ_NEXT(lf, link))
        if (lf->id == fileid)
            break;
-    lockmgr(&lock, LK_RELEASE, NULL, curthread);
+    lockmgr(&lock, LK_RELEASE);
 
     return lf;
 }
@@ -369,9 +373,9 @@ linker_make_file(const char* pathname, void* priv, struct linker_file_ops* ops)
        filename = pathname;
 
     KLD_DPF(FILE, ("linker_make_file: new file, filename=%s\n", filename));
-    lockmgr(&lock, LK_EXCLUSIVE, NULL, curthread);
+    lockmgr(&lock, LK_EXCLUSIVE);
     namelen = strlen(filename) + 1;
-    lf = malloc(sizeof(struct linker_file) + namelen, M_LINKER, M_WAITOK);
+    lf = kmalloc(sizeof(struct linker_file) + namelen, M_LINKER, M_WAITOK);
     if (!lf)
        goto out;
     bzero(lf, sizeof(*lf));
@@ -392,7 +396,7 @@ linker_make_file(const char* pathname, void* priv, struct linker_file_ops* ops)
     TAILQ_INSERT_TAIL(&linker_files, lf, link);
 
 out:
-    lockmgr(&lock, LK_RELEASE, NULL, curthread);
+    lockmgr(&lock, LK_RELEASE);
     return lf;
 }
 
@@ -405,11 +409,11 @@ linker_file_unload(linker_file_t file)
     int i;
 
     /* Refuse to unload modules if securelevel raised */
-    if (securelevel > 0)
+    if (securelevel > 0 || kernel_mem_readonly)
        return EPERM; 
 
     KLD_DPF(FILE, ("linker_file_unload: lf->refs=%d\n", file->refs));
-    lockmgr(&lock, LK_EXCLUSIVE, NULL, curthread);
+    lockmgr(&lock, LK_EXCLUSIVE);
     if (file->refs == 1) {
        KLD_DPF(FILE, ("linker_file_unload: file is unloading, informing modules\n"));
        /*
@@ -430,7 +434,8 @@ linker_file_unload(linker_file_t file)
            if ((error = module_unload(mod)) != 0) {
                KLD_DPF(FILE, ("linker_file_unload: module %x vetoes unload\n",
                               mod));
-               lockmgr(&lock, LK_RELEASE, NULL, curthread);
+               lockmgr(&lock, LK_RELEASE);
+               file->refs--;
                goto out;
            }
 
@@ -452,14 +457,14 @@ linker_file_unload(linker_file_t file)
             mod; 
             mod = module_getfnext(mod)
        ) {
-           printf("linker_file_unload: module %p still has refs!\n", mod);
+           kprintf("linker_file_unload: module %p still has refs!\n", mod);
        }
        --file->refs;
     }
 
     file->refs--;
     if (file->refs > 0) {
-       lockmgr(&lock, LK_RELEASE, NULL, curthread);
+       lockmgr(&lock, LK_RELEASE);
        goto out;
     }
 
@@ -470,20 +475,20 @@ linker_file_unload(linker_file_t file)
     }
 
     TAILQ_REMOVE(&linker_files, file, link);
-    lockmgr(&lock, LK_RELEASE, NULL, curthread);
+    lockmgr(&lock, LK_RELEASE);
 
     for (i = 0; i < file->ndeps; i++)
        linker_file_unload(file->deps[i]);
-    free(file->deps, M_LINKER);
+    kfree(file->deps, M_LINKER);
 
     for (cp = STAILQ_FIRST(&file->common); cp;
         cp = STAILQ_FIRST(&file->common)) {
        STAILQ_REMOVE(&file->common, cp, common_symbol, link);
-       free(cp, M_LINKER);
+       kfree(cp, M_LINKER);
     }
 
     file->ops->unload(file);
-    free(file, M_LINKER);
+    kfree(file, M_LINKER);
 
 out:
     return error;
@@ -494,7 +499,7 @@ linker_file_add_dependancy(linker_file_t file, linker_file_t dep)
 {
     linker_file_t* newdeps;
 
-    newdeps = malloc((file->ndeps + 1) * sizeof(linker_file_t*),
+    newdeps = kmalloc((file->ndeps + 1) * sizeof(linker_file_t*),
                     M_LINKER, M_WAITOK);
     if (newdeps == NULL)
        return ENOMEM;
@@ -502,7 +507,7 @@ linker_file_add_dependancy(linker_file_t file, linker_file_t dep)
 
     if (file->deps) {
        bcopy(file->deps, newdeps, file->ndeps * sizeof(linker_file_t*));
-       free(file->deps, M_LINKER);
+       kfree(file->deps, M_LINKER);
     }
     file->deps = newdeps;
     file->deps[file->ndeps] = dep;
@@ -600,7 +605,7 @@ linker_file_lookup_symbol(linker_file_t file, const char* name, int deps, caddr_
         * Round the symbol size up to align.
         */
        common_size = (common_size + sizeof(int) - 1) & -sizeof(int);
-       cp = malloc(sizeof(struct common_symbol)
+       cp = kmalloc(sizeof(struct common_symbol)
                    + common_size
                    + strlen(name) + 1,
                    M_LINKER, M_WAITOK);
@@ -621,6 +626,14 @@ linker_file_lookup_symbol(linker_file_t file, const char* name, int deps, caddr_
        return 0;
     }
 
+#ifdef _KERNEL_VIRTUAL
+    *raddr = dlsym(RTLD_NEXT, name);
+    if (*raddr != NULL) {
+       KLD_DPF(SYM, ("linker_file_lookup_symbol: found dlsym=%x\n", *raddr));
+       return 0;
+    }
+#endif
+
     KLD_DPF(SYM, ("linker_file_lookup_symbol: fail\n"));
     return ENOENT;
 }
@@ -698,7 +711,7 @@ linker_ddb_symbol_values(c_linker_sym_t sym, linker_symval_t *symval)
  */
 
 int
-kldload(struct kldload_args *uap)
+sys_kldload(struct kldload_args *uap)
 {
     struct thread *td = curthread;
     char* filename = NULL, *modulename;
@@ -707,13 +720,13 @@ kldload(struct kldload_args *uap)
 
     uap->sysmsg_result = -1;
 
-    if (securelevel > 0)       /* redundant, but that's OK */
+    if (securelevel > 0 || kernel_mem_readonly)        /* redundant, but that's OK */
        return EPERM;
 
     if ((error = suser(td)) != 0)
        return error;
 
-    filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
+    filename = kmalloc(MAXPATHLEN, M_TEMP, M_WAITOK);
     if ((error = copyinstr(uap->file, filename, MAXPATHLEN, NULL)) != 0)
        goto out;
 
@@ -736,18 +749,18 @@ kldload(struct kldload_args *uap)
 
 out:
     if (filename)
-       free(filename, M_TEMP);
+       kfree(filename, M_TEMP);
     return error;
 }
 
 int
-kldunload(struct kldunload_args *uap)
+sys_kldunload(struct kldunload_args *uap)
 {
     struct thread *td = curthread;
     linker_file_t lf;
     int error = 0;
 
-    if (securelevel > 0)       /* redundant, but that's OK */
+    if (securelevel > 0 || kernel_mem_readonly)        /* redundant, but that's OK */
        return EPERM;
 
     if ((error = suser(td)) != 0)
@@ -757,7 +770,7 @@ kldunload(struct kldunload_args *uap)
     if (lf) {
        KLD_DPF(FILE, ("kldunload: lf->userrefs=%d\n", lf->userrefs));
        if (lf->userrefs == 0) {
-           printf("linkerunload: attempt to unload file that was loaded by the kernel\n");
+           kprintf("linkerunload: attempt to unload file that was loaded by the kernel\n");
            error = EBUSY;
            goto out;
        }
@@ -773,7 +786,7 @@ out:
 }
 
 int
-kldfind(struct kldfind_args *uap)
+sys_kldfind(struct kldfind_args *uap)
 {
     char *filename = NULL, *modulename;
     linker_file_t lf;
@@ -781,7 +794,7 @@ kldfind(struct kldfind_args *uap)
 
     uap->sysmsg_result = -1;
 
-    filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
+    filename = kmalloc(MAXPATHLEN, M_TEMP, M_WAITOK);
     if ((error = copyinstr(uap->file, filename, MAXPATHLEN, NULL)) != 0)
        goto out;
 
@@ -797,12 +810,12 @@ kldfind(struct kldfind_args *uap)
 
 out:
     if (filename)
-       free(filename, M_TEMP);
+       kfree(filename, M_TEMP);
     return error;
 }
 
 int
-kldnext(struct kldnext_args *uap)
+sys_kldnext(struct kldnext_args *uap)
 {
     linker_file_t lf;
     int error = 0;
@@ -828,7 +841,7 @@ kldnext(struct kldnext_args *uap)
 }
 
 int
-kldstat(struct kldstat_args *uap)
+sys_kldstat(struct kldstat_args *uap)
 {
     linker_file_t lf;
     int error = 0;
@@ -875,7 +888,7 @@ out:
 }
 
 int
-kldfirstmod(struct kldfirstmod_args *uap)
+sys_kldfirstmod(struct kldfirstmod_args *uap)
 {
     linker_file_t lf;
     int error = 0;
@@ -893,7 +906,7 @@ kldfirstmod(struct kldfirstmod_args *uap)
 }
 
 int
-kldsym(struct kldsym_args *uap)
+sys_kldsym(struct kldsym_args *uap)
 {
     char *symstr = NULL;
     c_linker_sym_t sym;
@@ -909,7 +922,7 @@ kldsym(struct kldsym_args *uap)
        goto out;
     }
 
-    symstr = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
+    symstr = kmalloc(MAXPATHLEN, M_TEMP, M_WAITOK);
     if ((error = copyinstr(lookup.symname, symstr, MAXPATHLEN, NULL)) != 0)
        goto out;
 
@@ -941,7 +954,7 @@ kldsym(struct kldsym_args *uap)
     }
 out:
     if (symstr)
-       free(symstr, M_TEMP);
+       kfree(symstr, M_TEMP);
     return error;
 }
 
@@ -975,8 +988,7 @@ find_mod_metadata(const char *modname)
        if (mdt->md_type != MDT_MODULE)
            continue;
        if (strlen(mdt->md_cval) == len &&
-           strncmp(mdt->md_cval, modname, len) == 0
-       ) {
+           strncmp(mdt->md_cval, modname, len) == 0) {
            return(mdt);
        }
     }
@@ -1004,11 +1016,11 @@ linker_preload(void* arg)
        modname = (char *)preload_search_info(modptr, MODINFO_NAME);
        modtype = (char *)preload_search_info(modptr, MODINFO_TYPE);
        if (modname == NULL) {
-           printf("Preloaded module at %p does not have a name!\n", modptr);
+           kprintf("Preloaded module at %p does not have a name!\n", modptr);
            continue;
        }
        if (modtype == NULL) {
-           printf("Preloaded module at %p does not have a type!\n", modptr);
+           kprintf("Preloaded module at %p does not have a type!\n", modptr);
            continue;
        }
 
@@ -1016,15 +1028,16 @@ linker_preload(void* arg)
         * This is a hack at the moment, but what's in FreeBSD-5 is even 
         * worse so I'd rather the hack.
         */
-       printf("Preloaded %s \"%s\" at %p", modtype, modname, modptr);
+       kprintf("Preloaded %s \"%s\" at %p", modtype, modname, modptr);
        if (find_mod_metadata(modname)) {
-           printf(" (ignored, already in static kernel)\n");
+           kprintf(" (ignored, already in static kernel)\n");
            continue;
        }
-       printf(".\n");
+       kprintf(".\n");
 
        lf = linker_find_file_by_name(modname);
        if (lf) {
+           lf->refs++;
            lf->userrefs++;
            continue;
        }
@@ -1050,7 +1063,7 @@ linker_preload(void* arg)
                        moddata = (*sipp)->udata;
                        error = module_register(moddata, lf);
                        if (error)
-                           printf("Preloaded %s \"%s\" failed to register: %d\n",
+                           kprintf("Preloaded %s \"%s\" failed to register: %d\n",
                                modtype, modname, error);
                    }
                }
@@ -1061,7 +1074,7 @@ linker_preload(void* arg)
     }
 }
 
-SYSINIT(preload, SI_SUB_KLD, SI_ORDER_MIDDLE, linker_preload, 0);
+SYSINIT(preload, SI_BOOT2_KLD, SI_ORDER_MIDDLE, linker_preload, 0);
 
 /*
  * Search for a not-loaded module by name.
@@ -1078,17 +1091,18 @@ SYSINIT(preload, SI_SUB_KLD, SI_ORDER_MIDDLE, linker_preload, 0);
  * character as a separator to be consistent with the bootloader.
  */
 
-static char linker_path[MAXPATHLEN] = "/;/boot/;/modules/";
+static char linker_path[MAXPATHLEN] = "/;/boot;/modules";
 
 SYSCTL_STRING(_kern, OID_AUTO, module_path, CTLFLAG_RW, linker_path,
              sizeof(linker_path), "module load search path");
+TUNABLE_STR("module_path", linker_path, sizeof(linker_path));
 
 static char *
 linker_strdup(const char *str)
 {
     char       *result;
 
-    if ((result = malloc((strlen(str) + 1), M_LINKER, M_WAITOK)) != NULL)
+    if ((result = kmalloc((strlen(str) + 1), M_LINKER, M_WAITOK)) != NULL)
        strcpy(result, str);
     return(result);
 }
@@ -1098,6 +1112,8 @@ linker_search_path(const char *name)
 {
     struct nlookupdata nd;
     char               *cp, *ep, *result;
+    size_t             name_len, prefix_len;
+    int                        sep;
     int                        error;
     enum vtype         type;
 
@@ -1107,17 +1123,28 @@ linker_search_path(const char *name)
 
     /* traverse the linker path */
     cp = linker_path;
+    name_len = strlen(name);
     for (;;) {
 
        /* find the end of this component */
        for (ep = cp; (*ep != 0) && (*ep != ';'); ep++)
            ;
-       result = malloc((strlen(name) + (ep - cp) + 1), M_LINKER, M_WAITOK);
-       if (result == NULL)     /* actually ENOMEM */
-           return(NULL);
+       prefix_len = ep - cp;
+       /* if this component doesn't end with a slash, add one */
+       if (ep == cp || *(ep - 1) != '/')
+           sep = 1;
+       else
+           sep = 0;
+
+       /*
+        * +2 : possible separator, plus terminator.
+        */
+       result = kmalloc(prefix_len + name_len + 2, M_LINKER, M_WAITOK);
 
-       strncpy(result, cp, ep - cp);
-       strcpy(result + (ep - cp), name);
+       strncpy(result, cp, prefix_len);
+       if (sep)
+           result[prefix_len++] = '/';
+       strcpy(result + prefix_len, name);
 
        /*
         * Attempt to open the file, and return the path if we succeed and it's
@@ -1134,7 +1161,7 @@ linker_search_path(const char *name)
            }
        }
        nlookup_done(&nd);
-       free(result, M_LINKER);
+       kfree(result, M_LINKER);
 
        if (*ep == 0)
            break;