summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/kern/kern_module.c
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2013-10-09 22:42:09 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2013-10-10 09:06:58 +0200
commitbceabc95c1c85d793200446fa85f1ddc6313ea29 (patch)
tree973c8bd8deca9fd69913f2895cc91e0e6114d46c /freebsd/sys/kern/kern_module.c
parentAdd FreeBSD sources as a submodule (diff)
downloadrtems-libbsd-bceabc95c1c85d793200446fa85f1ddc6313ea29.tar.bz2
Move files to match FreeBSD layout
Diffstat (limited to 'freebsd/sys/kern/kern_module.c')
-rw-r--r--freebsd/sys/kern/kern_module.c551
1 files changed, 551 insertions, 0 deletions
diff --git a/freebsd/sys/kern/kern_module.c b/freebsd/sys/kern/kern_module.c
new file mode 100644
index 00000000..0476efef
--- /dev/null
+++ b/freebsd/sys/kern/kern_module.c
@@ -0,0 +1,551 @@
+#include <freebsd/machine/rtems-bsd-config.h>
+
+/*-
+ * 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.
+ */
+
+#include <freebsd/local/opt_compat.h>
+
+#include <freebsd/sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <freebsd/sys/param.h>
+#include <freebsd/sys/kernel.h>
+#include <freebsd/sys/systm.h>
+#include <freebsd/sys/eventhandler.h>
+#include <freebsd/sys/malloc.h>
+#include <freebsd/sys/sysproto.h>
+#include <freebsd/sys/sysent.h>
+#include <freebsd/sys/proc.h>
+#include <freebsd/sys/lock.h>
+#include <freebsd/sys/mutex.h>
+#include <freebsd/sys/reboot.h>
+#include <freebsd/sys/sx.h>
+#include <freebsd/sys/module.h>
+#include <freebsd/sys/linker.h>
+
+static MALLOC_DEFINE(M_MODULE, "module", "module data structures");
+
+struct module {
+ TAILQ_ENTRY(module) link; /* chain together all modules */
+#ifndef __rtems__
+ TAILQ_ENTRY(module) flink; /* all modules in a file */
+ struct linker_file *file; /* file which contains this module */
+ int refs; /* reference count */
+#endif /* __rtems__ */
+ 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 TAILQ_HEAD(modulelist, module) modules;
+struct sx modules_sx;
+static int nextid = 1;
+#ifndef __rtems__
+static void module_shutdown(void *, int);
+#endif /* __rtems__ */
+
+static int
+modevent_nop(module_t mod, int what, void *arg)
+{
+
+ switch(what) {
+ case MOD_LOAD:
+ return (0);
+ case MOD_UNLOAD:
+ return (EBUSY);
+ default:
+ return (EOPNOTSUPP);
+ }
+}
+
+static void
+module_init(void *arg)
+{
+
+ sx_init(&modules_sx, "module subsystem sx lock");
+ TAILQ_INIT(&modules);
+#ifndef __rtems__
+ EVENTHANDLER_REGISTER(shutdown_final, module_shutdown, NULL,
+ SHUTDOWN_PRI_DEFAULT);
+#endif /* __rtems__ */
+}
+
+SYSINIT(module, SI_SUB_KLD, SI_ORDER_FIRST, module_init, 0);
+
+#ifndef __rtems__
+static void
+module_shutdown(void *arg1, int arg2)
+{
+ module_t mod;
+
+ if (arg2 & RB_NOSYNC)
+ return;
+ mtx_lock(&Giant);
+ MOD_SLOCK;
+ TAILQ_FOREACH_REVERSE(mod, &modules, modulelist, link)
+ MOD_EVENT(mod, MOD_SHUTDOWN);
+ MOD_SUNLOCK;
+ mtx_unlock(&Giant);
+}
+#endif /* __rtems__ */
+
+void
+module_register_init(const void *arg)
+{
+ const moduledata_t *data = (const moduledata_t *)arg;
+ int error;
+ module_t mod;
+
+ mtx_lock(&Giant);
+ MOD_SLOCK;
+ mod = module_lookupbyname(data->name);
+ if (mod == NULL)
+ panic("module_register_init: module named %s not found\n",
+ data->name);
+ MOD_SUNLOCK;
+ error = MOD_EVENT(mod, MOD_LOAD);
+ if (error) {
+ MOD_EVENT(mod, MOD_UNLOAD);
+ MOD_XLOCK;
+ module_release(mod);
+ MOD_XUNLOCK;
+ printf("module_register_init: MOD_LOAD (%s, %p, %p) error"
+ " %d\n", data->name, (void *)data->evhand, data->priv,
+ error);
+ } else {
+#ifndef __rtems__
+ MOD_XLOCK;
+ if (mod->file) {
+ /*
+ * Once a module is succesfully loaded, move
+ * it to the head of the module list for this
+ * linker file. This resorts the list so that
+ * when the kernel linker iterates over the
+ * modules to unload them, it will unload them
+ * in the reverse order they were loaded.
+ */
+ TAILQ_REMOVE(&mod->file->modules, mod, flink);
+ TAILQ_INSERT_HEAD(&mod->file->modules, mod, flink);
+ }
+ MOD_XUNLOCK;
+#endif /* __rtems__ */
+ }
+ mtx_unlock(&Giant);
+}
+
+int
+module_register(const moduledata_t *data, linker_file_t container)
+{
+ size_t namelen;
+ module_t newmod;
+
+ MOD_XLOCK;
+ newmod = module_lookupbyname(data->name);
+ if (newmod != NULL) {
+ MOD_XUNLOCK;
+ printf("module_register: module %s already exists!\n",
+ data->name);
+ return (EEXIST);
+ }
+ namelen = strlen(data->name) + 1;
+ newmod = malloc(sizeof(struct module) + namelen, M_MODULE, M_WAITOK);
+ if (newmod == NULL) {
+ MOD_XUNLOCK;
+ return (ENOMEM);
+ }
+#ifndef __rtems__
+ newmod->refs = 1;
+#endif /* __rtems__ */
+ 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)
+#ifndef __rtems__
+ TAILQ_INSERT_TAIL(&container->modules, newmod, flink);
+ newmod->file = container;
+#else /* __rtems__ */
+ BSD_PANIC("not supported");
+#endif /* __rtems__ */
+ MOD_XUNLOCK;
+ return (0);
+}
+
+#ifndef __rtems__
+void
+module_reference(module_t mod)
+{
+
+ MOD_XLOCK_ASSERT;
+
+ MOD_DPF(REFS, ("module_reference: before, refs=%d\n", mod->refs));
+ mod->refs++;
+}
+#endif /* __rtems__ */
+
+void
+module_release(module_t mod)
+{
+
+ MOD_XLOCK_ASSERT;
+
+#ifndef __rtems__
+ 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) {
+#endif /* __rtems__ */
+ TAILQ_REMOVE(&modules, mod, link);
+#ifndef __rtems__
+ if (mod->file)
+ TAILQ_REMOVE(&mod->file->modules, mod, flink);
+#endif /* __rtems__ */
+ free(mod, M_MODULE);
+#ifndef __rtems__
+ }
+#endif /* __rtems__ */
+}
+
+module_t
+module_lookupbyname(const char *name)
+{
+ module_t mod;
+ int err;
+
+ MOD_LOCK_ASSERT;
+
+ TAILQ_FOREACH(mod, &modules, link) {
+ err = strcmp(mod->name, name);
+ if (err == 0)
+ return (mod);
+ }
+ return (NULL);
+}
+
+#ifndef __rtems__
+module_t
+module_lookupbyid(int modid)
+{
+ module_t mod;
+
+ MOD_LOCK_ASSERT;
+
+ TAILQ_FOREACH(mod, &modules, link)
+ if (mod->id == modid)
+ return(mod);
+ return (NULL);
+}
+
+int
+module_quiesce(module_t mod)
+{
+ int error;
+
+ mtx_lock(&Giant);
+ error = MOD_EVENT(mod, MOD_QUIESCE);
+ mtx_unlock(&Giant);
+ if (error == EOPNOTSUPP || error == EINVAL)
+ error = 0;
+ return (error);
+}
+
+int
+module_unload(module_t mod)
+{
+ int error;
+
+ mtx_lock(&Giant);
+ error = MOD_EVENT(mod, MOD_UNLOAD);
+ mtx_unlock(&Giant);
+ return (error);
+}
+
+int
+module_getid(module_t mod)
+{
+
+ MOD_LOCK_ASSERT;
+ return (mod->id);
+}
+
+module_t
+module_getfnext(module_t mod)
+{
+
+ MOD_LOCK_ASSERT;
+ return (TAILQ_NEXT(mod, flink));
+}
+
+const char *
+module_getname(module_t mod)
+{
+
+ MOD_LOCK_ASSERT;
+ return (mod->name);
+}
+
+void
+module_setspecific(module_t mod, modspecific_t *datap)
+{
+
+ MOD_XLOCK_ASSERT;
+ mod->data = *datap;
+}
+
+linker_file_t
+module_file(module_t mod)
+{
+
+ return (mod->file);
+}
+
+/*
+ * Syscalls.
+ */
+int
+modnext(struct thread *td, struct modnext_args *uap)
+{
+ module_t mod;
+ int error = 0;
+
+ td->td_retval[0] = -1;
+
+ MOD_SLOCK;
+ if (uap->modid == 0) {
+ mod = TAILQ_FIRST(&modules);
+ if (mod)
+ td->td_retval[0] = mod->id;
+ else
+ error = ENOENT;
+ goto done2;
+ }
+ mod = module_lookupbyid(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:
+ MOD_SUNLOCK;
+ return (error);
+}
+
+int
+modfnext(struct thread *td, struct modfnext_args *uap)
+{
+ module_t mod;
+ int error;
+
+ td->td_retval[0] = -1;
+
+ MOD_SLOCK;
+ mod = module_lookupbyid(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;
+ }
+ MOD_SUNLOCK;
+ return (error);
+}
+
+struct module_stat_v1 {
+ int version; /* set to sizeof(struct module_stat) */
+ char name[MAXMODNAME];
+ int refs;
+ int id;
+};
+
+int
+modstat(struct thread *td, struct modstat_args *uap)
+{
+ module_t mod;
+ modspecific_t data;
+ int error = 0;
+ int id, namelen, refs, version;
+ struct module_stat *stat;
+ char *name;
+
+ MOD_SLOCK;
+ mod = module_lookupbyid(uap->modid);
+ if (mod == NULL) {
+ MOD_SUNLOCK;
+ return (ENOENT);
+ }
+ id = mod->id;
+ refs = mod->refs;
+ name = mod->name;
+ data = mod->data;
+ MOD_SUNLOCK;
+ stat = uap->stat;
+
+ /*
+ * Check the version of the user's structure.
+ */
+ if ((error = copyin(&stat->version, &version, sizeof(version))) != 0)
+ return (error);
+ if (version != sizeof(struct module_stat_v1)
+ && version != sizeof(struct module_stat))
+ return (EINVAL);
+ namelen = strlen(mod->name) + 1;
+ if (namelen > MAXMODNAME)
+ namelen = MAXMODNAME;
+ if ((error = copyout(name, &stat->name[0], namelen)) != 0)
+ return (error);
+
+ if ((error = copyout(&refs, &stat->refs, sizeof(int))) != 0)
+ return (error);
+ if ((error = copyout(&id, &stat->id, sizeof(int))) != 0)
+ return (error);
+
+ /*
+ * >v1 stat includes module data.
+ */
+ if (version == sizeof(struct module_stat))
+ if ((error = copyout(&data, &stat->data,
+ sizeof(data))) != 0)
+ return (error);
+ td->td_retval[0] = 0;
+ return (error);
+}
+
+int
+modfind(struct thread *td, struct modfind_args *uap)
+{
+ int error = 0;
+ char name[MAXMODNAME];
+ module_t mod;
+
+ if ((error = copyinstr(uap->name, name, sizeof name, 0)) != 0)
+ return (error);
+
+ MOD_SLOCK;
+ mod = module_lookupbyname(name);
+ if (mod == NULL)
+ error = ENOENT;
+ else
+ td->td_retval[0] = module_getid(mod);
+ MOD_SUNLOCK;
+ return (error);
+}
+#endif /* __rtems__ */
+
+MODULE_VERSION(kernel, __FreeBSD_version);
+
+#ifdef COMPAT_FREEBSD32
+#include <freebsd/sys/mount.h>
+#include <freebsd/sys/socket.h>
+#include <freebsd/compat/freebsd32/freebsd32_util.h>
+#include <freebsd/compat/freebsd32/freebsd32.h>
+#include <freebsd/compat/freebsd32/freebsd32_proto.h>
+
+typedef union modspecific32 {
+ int intval;
+ u_int32_t uintval;
+ int longval;
+ u_int32_t ulongval;
+} modspecific32_t;
+
+struct module_stat32 {
+ int version;
+ char name[MAXMODNAME];
+ int refs;
+ int id;
+ modspecific32_t data;
+};
+
+int
+freebsd32_modstat(struct thread *td, struct freebsd32_modstat_args *uap)
+{
+ module_t mod;
+ modspecific32_t data32;
+ int error = 0;
+ int id, namelen, refs, version;
+ struct module_stat32 *stat32;
+ char *name;
+
+ MOD_SLOCK;
+ mod = module_lookupbyid(uap->modid);
+ if (mod == NULL) {
+ MOD_SUNLOCK;
+ return (ENOENT);
+ }
+
+ id = mod->id;
+ refs = mod->refs;
+ name = mod->name;
+ CP(mod->data, data32, intval);
+ CP(mod->data, data32, uintval);
+ CP(mod->data, data32, longval);
+ CP(mod->data, data32, ulongval);
+ MOD_SUNLOCK;
+ stat32 = uap->stat;
+
+ if ((error = copyin(&stat32->version, &version, sizeof(version))) != 0)
+ return (error);
+ if (version != sizeof(struct module_stat_v1)
+ && version != sizeof(struct module_stat32))
+ return (EINVAL);
+ namelen = strlen(mod->name) + 1;
+ if (namelen > MAXMODNAME)
+ namelen = MAXMODNAME;
+ if ((error = copyout(name, &stat32->name[0], namelen)) != 0)
+ return (error);
+
+ if ((error = copyout(&refs, &stat32->refs, sizeof(int))) != 0)
+ return (error);
+ if ((error = copyout(&id, &stat32->id, sizeof(int))) != 0)
+ return (error);
+
+ /*
+ * >v1 stat includes module data.
+ */
+ if (version == sizeof(struct module_stat32))
+ if ((error = copyout(&data32, &stat32->data,
+ sizeof(data32))) != 0)
+ return (error);
+ td->td_retval[0] = 0;
+ return (error);
+}
+#endif