summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_module.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/kern_module.c')
-rw-r--r--sys/kern/kern_module.c372
1 files changed, 372 insertions, 0 deletions
diff --git a/sys/kern/kern_module.c b/sys/kern/kern_module.c
new file mode 100644
index 0000000..f3651d2
--- /dev/null
+++ b/sys/kern/kern_module.c
@@ -0,0 +1,372 @@
+/*-
+ * Copyright (c) 1997 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/eventhandler.h>
+#include <sys/malloc.h>
+#include <sys/sysproto.h>
+#include <sys/sysent.h>
+#include <sys/module.h>
+#include <sys/linker.h>
+#include <sys/proc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+static MALLOC_DEFINE(M_MODULE, "module", "module data structures");
+
+typedef TAILQ_HEAD(, module) modulelist_t;
+struct module {
+ TAILQ_ENTRY(module) link; /* chain together all modules */
+ TAILQ_ENTRY(module) flink; /* all modules in a file */
+ struct linker_file* file; /* file which contains this module */
+ int refs; /* reference count */
+ int id; /* unique id number */
+ char *name; /* module name */
+ modeventhand_t handler; /* event handler */
+ void *arg; /* argument for handler */
+ modspecific_t data; /* module specific data */
+};
+
+#define MOD_EVENT(mod, type) (mod)->handler((mod), (type), (mod)->arg)
+
+static modulelist_t modules;
+static int nextid = 1;
+
+static void module_shutdown(void*, int);
+
+static int
+modevent_nop(module_t mod, int what, void* arg)
+{
+ return 0;
+}
+
+
+static void
+module_init(void* arg)
+{
+ TAILQ_INIT(&modules);
+ EVENTHANDLER_REGISTER(shutdown_post_sync, module_shutdown, NULL,
+ SHUTDOWN_PRI_DEFAULT);
+}
+
+SYSINIT(module, SI_SUB_KLD, SI_ORDER_FIRST, module_init, 0);
+
+static void
+module_shutdown(void* arg1, int arg2)
+{
+ module_t mod;
+
+ TAILQ_FOREACH(mod, &modules, link)
+ MOD_EVENT(mod, MOD_SHUTDOWN);
+}
+
+void
+module_register_init(const void *arg)
+{
+ const moduledata_t* data = (const moduledata_t*) arg;
+ int error;
+ module_t mod;
+
+ mod = module_lookupbyname(data->name);
+ if (mod == NULL)
+ panic("module_register_init: module named %s not found\n", data->name);
+ error = MOD_EVENT(mod, MOD_LOAD);
+ if (error) {
+ MOD_EVENT(mod, MOD_UNLOAD);
+ module_release(mod);
+ printf("module_register_init: MOD_LOAD (%s, %lx, %p) error %d\n",
+ data->name, (u_long)(uintfptr_t)data->evhand, data->priv, error);
+ }
+}
+
+int
+module_register(const moduledata_t *data, linker_file_t container)
+{
+ size_t namelen;
+ module_t newmod;
+
+ newmod = module_lookupbyname(data->name);
+ if (newmod != NULL) {
+ printf("module_register: module %s already exists!\n", data->name);
+ return EEXIST;
+ }
+ namelen = strlen(data->name) + 1;
+ newmod = (module_t) malloc(sizeof(struct module) + namelen,
+ M_MODULE, M_WAITOK);
+ if (newmod == 0)
+ return ENOMEM;
+
+ newmod->refs = 1;
+ newmod->id = nextid++;
+ newmod->name = (char *) (newmod + 1);
+ strcpy(newmod->name, data->name);
+ newmod->handler = data->evhand ? data->evhand : modevent_nop;
+ newmod->arg = data->priv;
+ bzero(&newmod->data, sizeof(newmod->data));
+ TAILQ_INSERT_TAIL(&modules, newmod, link);
+
+ if (container)
+ TAILQ_INSERT_TAIL(&container->modules, newmod, flink);
+ newmod->file = container;
+
+ return 0;
+}
+
+void
+module_reference(module_t mod)
+{
+ MOD_DPF(REFS, ("module_reference: before, refs=%d\n", mod->refs));
+
+ mod->refs++;
+}
+
+void
+module_release(module_t mod)
+{
+ if (mod->refs <= 0)
+ panic("module_release: bad reference count");
+
+ MOD_DPF(REFS, ("module_release: before, refs=%d\n", mod->refs));
+
+ mod->refs--;
+ if (mod->refs == 0) {
+ TAILQ_REMOVE(&modules, mod, link);
+ if (mod->file) {
+ TAILQ_REMOVE(&mod->file->modules, mod, flink);
+ }
+ free(mod, M_MODULE);
+ }
+}
+
+module_t
+module_lookupbyname(const char* name)
+{
+ module_t mod;
+
+ TAILQ_FOREACH(mod, &modules, link) {
+ if (!strcmp(mod->name, name))
+ return mod;
+ }
+
+ return 0;
+}
+
+module_t
+module_lookupbyid(int modid)
+{
+ module_t mod;
+
+ TAILQ_FOREACH(mod, &modules, link) {
+ if (mod->id == modid)
+ return mod;
+ }
+
+ return 0;
+}
+
+int
+module_unload(module_t mod)
+{
+ return MOD_EVENT(mod, MOD_UNLOAD);
+}
+
+int
+module_getid(module_t mod)
+{
+ return mod->id;
+}
+
+module_t
+module_getfnext(module_t mod)
+{
+ return TAILQ_NEXT(mod, flink);
+}
+
+void
+module_setspecific(module_t mod, modspecific_t *datap)
+{
+ mod->data = *datap;
+}
+
+/*
+ * Syscalls.
+ */
+/*
+ * MPSAFE
+ */
+int
+modnext(struct thread *td, struct modnext_args *uap)
+{
+ module_t mod;
+ int error = 0;
+
+ mtx_lock(&Giant);
+
+ td->td_retval[0] = -1;
+ if (SCARG(uap, modid) == 0) {
+ mod = TAILQ_FIRST(&modules);
+ if (mod)
+ td->td_retval[0] = mod->id;
+ else
+ error = ENOENT;
+ goto done2;
+ }
+
+ mod = module_lookupbyid(SCARG(uap, modid));
+ if (mod == NULL) {
+ error = ENOENT;
+ goto done2;
+ }
+
+ if (TAILQ_NEXT(mod, link))
+ td->td_retval[0] = TAILQ_NEXT(mod, link)->id;
+ else
+ td->td_retval[0] = 0;
+done2:
+ mtx_unlock(&Giant);
+ return (error);
+}
+
+/*
+ * MPSAFE
+ */
+int
+modfnext(struct thread *td, struct modfnext_args *uap)
+{
+ module_t mod;
+ int error;
+
+ td->td_retval[0] = -1;
+
+ mtx_lock(&Giant);
+
+ mod = module_lookupbyid(SCARG(uap, modid));
+ if (mod == NULL) {
+ error = ENOENT;
+ } else {
+ error = 0;
+ if (TAILQ_NEXT(mod, flink))
+ td->td_retval[0] = TAILQ_NEXT(mod, flink)->id;
+ else
+ td->td_retval[0] = 0;
+ }
+ mtx_unlock(&Giant);
+ return (error);
+}
+
+struct module_stat_v1 {
+ int version; /* set to sizeof(struct module_stat) */
+ char name[MAXMODNAME];
+ int refs;
+ int id;
+};
+
+/*
+ * MPSAFE
+ */
+int
+modstat(struct thread *td, struct modstat_args *uap)
+{
+ module_t mod;
+ int error = 0;
+ int namelen;
+ int version;
+ struct module_stat* stat;
+
+ mtx_lock(&Giant);
+
+ mod = module_lookupbyid(SCARG(uap, modid));
+ if (mod == NULL) {
+ error = ENOENT;
+ goto out;
+ }
+
+ stat = SCARG(uap, stat);
+
+ /*
+ * Check the version of the user's structure.
+ */
+ if ((error = copyin(&stat->version, &version, sizeof(version))) != 0)
+ goto out;
+ if (version != sizeof(struct module_stat_v1)
+ && version != sizeof(struct module_stat)) {
+ error = EINVAL;
+ goto out;
+ }
+
+ namelen = strlen(mod->name) + 1;
+ if (namelen > MAXMODNAME)
+ namelen = MAXMODNAME;
+ if ((error = copyout(mod->name, &stat->name[0], namelen)) != 0)
+ goto out;
+
+ if ((error = copyout(&mod->refs, &stat->refs, sizeof(int))) != 0)
+ goto out;
+ if ((error = copyout(&mod->id, &stat->id, sizeof(int))) != 0)
+ goto out;
+
+ /*
+ * >v1 stat includes module data.
+ */
+ if (version == sizeof(struct module_stat)) {
+ if ((error = copyout(&mod->data, &stat->data, sizeof(mod->data))) != 0)
+ goto out;
+ }
+
+ td->td_retval[0] = 0;
+
+out:
+ mtx_unlock(&Giant);
+ return error;
+}
+
+/*
+ * MPSAFE
+ */
+int
+modfind(struct thread *td, struct modfind_args *uap)
+{
+ int error = 0;
+ char name[MAXMODNAME];
+ module_t mod;
+
+ if ((error = copyinstr(SCARG(uap, name), name, sizeof name, 0)) != 0)
+ goto out;
+
+ mtx_lock(&Giant);
+ mod = module_lookupbyname(name);
+ if (mod == NULL)
+ error = ENOENT;
+ else
+ td->td_retval[0] = mod->id;
+ mtx_unlock(&Giant);
+out:
+ return error;
+}
OpenPOWER on IntegriCloud