summaryrefslogtreecommitdiffstats
path: root/kernel/module.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
committer <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
commit19c9bba94152148523ba0f7ef7cffe3d45656b11 (patch)
tree40b1cb534496a7f1ca0f5c314a523c69f1fee464 /kernel/module.c
parent7206675c40394c78a90e74812bbdbf8cf3cca1be (diff)
Import of Linux/MIPS 2.1.36
Diffstat (limited to 'kernel/module.c')
-rw-r--r--kernel/module.c1404
1 files changed, 789 insertions, 615 deletions
diff --git a/kernel/module.c b/kernel/module.c
index 09cee93b7..885539b5c 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -4,789 +4,963 @@
#include <linux/string.h>
#include <linux/module.h>
#include <linux/sched.h>
-#include <linux/malloc.h>
-#include <linux/vmalloc.h>
#include <linux/config.h>
-
#include <asm/uaccess.h>
+#include <linux/vmalloc.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+
/*
* Originally by Anonymous (as far as I know...)
* Linux version by Bas Laarhoven <bas@vimec.nl>
* 0.99.14 version by Jon Tombs <jon@gtex02.us.es>,
- *
* Heavily modified by Bjorn Ekwall <bj0rn@blox.se> May 1994 (C)
- * This source is covered by the GNU GPL, the same as all kernel sources.
- *
- * Features:
- * - Supports stacked modules (removable only of there are no dependents).
- * - Supports table of symbols defined by the modules.
- * - Supports /proc/ksyms, showing value, name and owner of all
- * the symbols defined by all modules (in stack order).
- * - Added module dependencies information into /proc/modules
- * - Supports redefines of all symbols, for streams-like behaviour.
- * - Compatible with older versions of insmod.
- *
- * New addition in December 1994: (Bjorn Ekwall, idea from Jacques Gelinas)
- * - Externally callable function:
- *
- * "int register_symtab(struct symbol_table *)"
+ * Rewritten by Richard Henderson <rth@tamu.edu> Dec 1996
*
- * This function can be called from within the kernel,
- * and ALSO from loadable modules.
- * The goal is to assist in modularizing the kernel even more,
- * and finally: reducing the number of entries in ksyms.c
- * since every subsystem should now be able to decide and
- * control exactly what symbols it wants to export, locally!
- *
- * On 1-Aug-95: <Matti.Aarnio@utu.fi> altered code to use same style as
- * do /proc/net/XXX "files". Namely allow more than 4kB
- * (or what the block size is) output.
- *
- * - Use dummy syscall functions for users who disable all
- * module support. Similar to kernel/sys.c (Paul Gortmaker)
+ * This source is covered by the GNU GPL, the same as all kernel sources.
*/
#ifdef CONFIG_MODULES /* a *big* #ifdef block... */
-static struct module kernel_module;
-struct module *module_list = &kernel_module;
+extern struct module_symbol __start___ksymtab[];
+extern struct module_symbol __stop___ksymtab[];
-static int freeing_modules; /* true if some modules are marked for deletion */
+extern const struct exception_table_entry __start___ex_table[];
+extern const struct exception_table_entry __stop___ex_table[];
-static struct module *find_module( const char *name);
-static int free_modules( void);
+static struct module kernel_module =
+{
+ sizeof(struct module), /* size_of_struct */
+ NULL, /* next */
+ "", /* name */
+ 0, /* size */
+ 1, /* usecount */
+ MOD_RUNNING, /* flags */
+ 0, /* nsyms -- to filled in in init_modules */
+ 0, /* ndeps */
+ __start___ksymtab, /* syms */
+ NULL, /* deps */
+ NULL, /* refs */
+ NULL, /* init */
+ NULL, /* cleanup */
+ __start___ex_table, /* ex_table_start */
+ __stop___ex_table, /* ex_table_end */
+ /* Rest are NULL */
+};
+
+struct module *module_list = &kernel_module;
+
+static long get_mod_name(const char *user_name, char **buf);
+static void put_mod_name(char *buf);
+static struct module *find_module(const char *name);
+static void free_module(struct module *);
-extern struct symbol_table symbol_table; /* in kernel/ksyms.c */
/*
* Called at boot time
*/
-void init_modules(void) {
- struct internal_symbol *sym;
- int i;
- for (i = 0, sym = symbol_table.symbol; sym->name; ++sym, ++i)
- ;
- symbol_table.n_symbols = i;
+void init_modules(void)
+{
+ kernel_module.nsyms = __stop___ksymtab - __start___ksymtab;
- kernel_module.symtab = &symbol_table;
- kernel_module.state = MOD_RUNNING; /* Hah! */
- kernel_module.name = "";
+#ifdef __alpha__
+ __asm__("stq $29,%0" : "=m"(kernel_module.gp));
+#endif
}
-
/*
* Copy the name of a module from user space.
*/
-inline int
-get_mod_name(char *user_name, char *buf)
+
+static inline long
+get_mod_name(const char *user_name, char **buf)
{
- /* Should return -EBIG instead of -EFAULT when the name
- is too long, but that we couldn't detect real faults then.
- Maybe strncpy_from_user() should return -EBIG, when
- the source string is too long. */
- return strncpy_from_user(buf, user_name, MOD_MAX_NAME);
+ unsigned long page;
+ long retval;
+
+ if ((unsigned long)user_name >= TASK_SIZE)
+ return -EFAULT;
+
+ page = __get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ retval = strncpy_from_user((char *)page, user_name, PAGE_SIZE);
+ if (retval > 0) {
+ if (retval < PAGE_SIZE) {
+ *buf = (char *)page;
+ return retval;
+ }
+ retval = -ENAMETOOLONG;
+ } else if (!retval)
+ retval = -EINVAL;
+
+ free_page(page);
+ return retval;
}
+static inline void
+put_mod_name(char *buf)
+{
+ free_page((unsigned long)buf);
+}
/*
* Allocate space for a module.
*/
+
asmlinkage unsigned long
-sys_create_module(char *module_name, unsigned long size)
+sys_create_module(const char *name_user, size_t size)
{
- struct module *mp;
- void* addr;
- int error;
- int npages;
- int sspace = sizeof(struct module) + MOD_MAX_NAME;
- char name[MOD_MAX_NAME];
-
- if (!suser())
- return -EPERM;
- if (module_name == NULL || size == 0)
- return -EINVAL;
- if ((error = get_mod_name(module_name, name)) < 0)
- return error;
+ char *name;
+ long namelen, error;
+ struct module *mod;
+
+ lock_kernel();
+ if (!suser()) {
+ error = -EPERM;
+ goto err0;
+ }
+ if ((namelen = get_mod_name(name_user, &name)) < 0) {
+ error = namelen;
+ goto err0;
+ }
+ if (size < sizeof(struct module)+namelen) {
+ error = -EINVAL;
+ goto err1;
+ }
if (find_module(name) != NULL) {
- return -EEXIST;
+ error = -EEXIST;
+ goto err1;
}
-
- if ((mp = (struct module*) kmalloc(sspace, GFP_KERNEL)) == NULL) {
- return -ENOMEM;
+ if ((mod = (struct module *)vmalloc(size)) == NULL) {
+ error = -ENOMEM;
+ goto err1;
}
- strcpy((char *)(mp + 1), name); /* why not? */
- npages = (size + sizeof (long) + PAGE_SIZE - 1) / PAGE_SIZE;
- if ((addr = vmalloc(npages * PAGE_SIZE)) == 0) {
- kfree_s(mp, sspace);
- return -ENOMEM;
- }
+ memset(mod, 0, sizeof(*mod));
+ mod->size_of_struct = sizeof(*mod);
+ mod->next = module_list;
+ mod->name = (char *)(mod + 1);
+ mod->size = size;
+ memcpy((char*)(mod+1), name, namelen+1);
- mp->next = module_list;
- mp->ref = NULL;
- mp->symtab = NULL;
- mp->name = (char *)(mp + 1);
- mp->size = npages;
- mp->addr = addr;
- mp->state = MOD_UNINITIALIZED;
- mp->cleanup = NULL;
- mp->exceptinfo.start = NULL;
- mp->exceptinfo.stop = NULL;
+ put_mod_name(name);
- * (long *) addr = 0; /* set use count to zero */
- module_list = mp; /* link it in */
+ module_list = mod; /* link it in */
- pr_debug("module `%s' (%lu pages @ 0x%08lx) created\n",
- mp->name, (unsigned long) mp->size, (unsigned long) mp->addr);
- return (unsigned long) addr;
+ error = (long) mod;
+ goto err0;
+err1:
+ put_mod_name(name);
+err0:
+ unlock_kernel();
+ return error;
}
-
/*
* Initialize a module.
*/
+
asmlinkage int
-sys_init_module(char *module_name, char *code, unsigned codesize,
- struct mod_routines *routines,
- struct symbol_table *symtab)
+sys_init_module(const char *name_user, struct module *mod_user)
{
- struct module *mp;
- struct symbol_table *newtab;
- char name[MOD_MAX_NAME];
- int error;
- struct mod_routines rt;
+ struct module mod_tmp, *mod;
+ char *name, *n_name;
+ long namelen, n_namelen, i, error = -EPERM;
+ unsigned long mod_user_size;
+ struct module_ref *dep;
+ lock_kernel();
if (!suser())
- return -EPERM;
+ goto err0;
+ if ((namelen = get_mod_name(name_user, &name)) < 0) {
+ error = namelen;
+ goto err0;
+ }
+ if ((mod = find_module(name)) == NULL) {
+ error = -ENOENT;
+ goto err1;
+ }
+
+ /* Check module header size. We allow a bit of slop over the
+ size we are familiar with to cope with a version of insmod
+ for a newer kernel. But don't over do it. */
+ if ((error = get_user(mod_user_size, &mod_user->size_of_struct)) != 0)
+ goto err1;
+ if (mod_user_size < (unsigned long)&((struct module *)0L)->persist_start
+ || mod_user_size > sizeof(struct module) + 16*sizeof(void*)) {
+ printk(KERN_ERR "init_module: Invalid module header size.\n"
+ KERN_ERR "A new version of the modutils is likely "
+ "needed.\n");
+ error = -EINVAL;
+ goto err1;
+ }
+
+ /* Hold the current contents while we play with the user's idea
+ of righteousness. */
+ mod_tmp = *mod;
+
+ error = copy_from_user(mod, mod_user, sizeof(struct module));
+ if (error) {
+ error = -EFAULT;
+ goto err2;
+ }
+
+ /* Sanity check the size of the module. */
+ error = -EINVAL;
+
+ if (mod->size > mod_tmp.size) {
+ printk(KERN_ERR "init_module: Size of initialized module "
+ "exceeds size of created module.\n");
+ goto err2;
+ }
+
+ /* Make sure all interesting pointers are sane. */
-#ifdef __i386__
- /* A little bit of protection... we "know" where the user stack is... */
+#define bound(p, n, m) ((unsigned long)(p) >= (unsigned long)(m+1) && \
+ (unsigned long)((p)+(n)) <= (unsigned long)(m) + (m)->size)
- if (symtab && ((unsigned long)symtab > 0xb0000000)) {
- printk(KERN_WARNING "warning: you are using an old insmod, no symbols will be inserted!\n");
- symtab = NULL;
+ if (!bound(mod->name, namelen, mod)) {
+ printk(KERN_ERR "init_module: mod->name out of bounds.\n");
+ goto err2;
+ }
+ if (mod->nsyms && !bound(mod->syms, mod->nsyms, mod)) {
+ printk(KERN_ERR "init_module: mod->syms out of bounds.\n");
+ goto err2;
+ }
+ if (mod->ndeps && !bound(mod->deps, mod->ndeps, mod)) {
+ printk(KERN_ERR "init_module: mod->deps out of bounds.\n");
+ goto err2;
+ }
+ if (mod->init && !bound(mod->init, 0, mod)) {
+ printk(KERN_ERR "init_module: mod->init out of bounds.\n");
+ goto err2;
+ }
+ if (mod->cleanup && !bound(mod->cleanup, 0, mod)) {
+ printk(KERN_ERR "init_module: mod->cleanup out of bounds.\n");
+ goto err2;
+ }
+ if (mod->ex_table_start > mod->ex_table_end
+ || (mod->ex_table_start &&
+ !((unsigned long)mod->ex_table_start >= (unsigned long)(mod+1)
+ && ((unsigned long)mod->ex_table_end
+ < (unsigned long)mod + mod->size)))
+ || (((unsigned long)mod->ex_table_start
+ - (unsigned long)mod->ex_table_end)
+ % sizeof(struct exception_table_entry))) {
+ printk(KERN_ERR "init_module: mod->ex_table_* invalid.\n");
+ goto err2;
+ }
+ if (mod->flags & ~MOD_AUTOCLEAN) {
+ printk(KERN_ERR "init_module: mod->flags invalid.\n");
+ goto err2;
+ }
+#ifdef __alpha__
+ if (!bound(mod->gp - 0x8000, 0, mod)) {
+ printk(KERN_ERR "init_module: mod->gp out of bounds.\n");
+ goto err2;
}
#endif
- if ((error = get_mod_name(module_name, name)) < 0)
- return error;
- pr_debug("initializing module `%s', %d (0x%x) bytes\n",
- name, codesize, codesize);
- if (copy_from_user(&rt, routines, sizeof rt))
- return -EFAULT;
- if ((mp = find_module(name)) == NULL)
- return -ENOENT;
- if (codesize & MOD_AUTOCLEAN) {
- /*
- * set autoclean marker from codesize...
- * set usage count to "zero"
- */
- codesize &= ~MOD_AUTOCLEAN;
- GET_USE_COUNT(mp) = MOD_AUTOCLEAN;
- }
- if ((codesize + sizeof (long) + PAGE_SIZE - 1) / PAGE_SIZE > mp->size)
- return -EINVAL;
- if (copy_from_user((char *)mp->addr + sizeof (long), code, codesize))
- return -EFAULT;
- memset((char *)mp->addr + sizeof (long) + codesize, 0,
- mp->size * PAGE_SIZE - (codesize + sizeof (long)));
- pr_debug("module init entry = 0x%08lx, cleanup entry = 0x%08lx\n",
- (unsigned long) rt.init, (unsigned long) rt.cleanup);
- if (rt.signature != MODULE_2_1_7_SIG){
- printk ("Older insmod used with kernel 2.1.7 +\n");
- return -EINVAL;
+ if (mod_member_present(mod, can_unload)
+ && mod->can_unload && !bound(mod->can_unload, 0, mod)) {
+ printk(KERN_ERR "init_module: mod->can_unload out of bounds.\n");
+ goto err2;
}
- mp->cleanup = rt.cleanup;
- mp->exceptinfo = rt.exceptinfo;
-
- /* update kernel symbol table */
- if (symtab) { /* symtab == NULL means no new entries to handle */
- struct internal_symbol *sym;
- struct module_ref *ref;
- int size;
- int i;
- int legal_start;
-
- error = get_user(size, &symtab->size);
- if (error)
- return error;
- if ((newtab = (struct symbol_table*) kmalloc(size, GFP_KERNEL)) == NULL) {
- return -ENOMEM;
- }
- if (copy_from_user((char *)(newtab), symtab, size)) {
- kfree_s(newtab, size);
- return -EFAULT;
- }
+#undef bound
- /* sanity check */
- legal_start = sizeof(struct symbol_table) +
- newtab->n_symbols * sizeof(struct internal_symbol) +
- newtab->n_refs * sizeof(struct module_ref);
+ /* Check that the user isn't doing something silly with the name. */
- if ((newtab->n_symbols < 0) || (newtab->n_refs < 0) || (legal_start > size)) {
- printk(KERN_WARNING "Rejecting illegal symbol table (n_symbols=%d,n_refs=%d)\n",
- newtab->n_symbols, newtab->n_refs);
- kfree_s(newtab, size);
- return -EINVAL;
- }
+ if ((n_namelen = get_mod_name(mod->name - (unsigned long)mod
+ + (unsigned long)mod_user,
+ &n_name)) < 0) {
+ error = n_namelen;
+ goto err2;
+ }
+ if (namelen != n_namelen || strcmp(n_name, mod_tmp.name) != 0) {
+ printk(KERN_ERR "init_module: changed module name to "
+ "`%s' from `%s'\n",
+ n_name, mod_tmp.name);
+ goto err3;
+ }
- /* relocate name pointers, index referred from start of table */
- for (sym = &(newtab->symbol[0]), i = 0; i < newtab->n_symbols; ++sym, ++i) {
- if ((unsigned long)sym->name < legal_start || size <= (unsigned long)sym->name) {
- printk(KERN_WARNING "Rejecting illegal symbol table\n");
- kfree_s(newtab, size);
- return -EINVAL;
- }
- /* else */
- sym->name += (long)newtab;
- }
- mp->symtab = newtab;
-
- /* Update module references.
- * On entry, from "insmod", ref->module points to
- * the referenced module!
- * Now it will point to the current module instead!
- * The ref structure becomes the first link in the linked
- * list of references to the referenced module.
- * Also, "sym" from above, points to the first ref entry!!!
- */
- for (ref = (struct module_ref *)sym, i = 0;
- i < newtab->n_refs; ++ref, ++i) {
-
- /* Check for valid reference */
- struct module *link = module_list;
- while (link && (ref->module != link))
- link = link->next;
-
- if (link == (struct module *)0) {
- printk(KERN_WARNING "Non-module reference! Rejected!\n");
- return -EINVAL;
- }
+ /* Ok, that's about all the sanity we can stomach; copy the rest. */
- ref->next = ref->module->ref;
- ref->module->ref = ref;
- ref->module = mp;
- }
+ if (copy_from_user(mod+1, mod_user+1, mod->size-sizeof(*mod))) {
+ error = -EFAULT;
+ goto err3;
}
- GET_USE_COUNT(mp) += 1;
- if ((*rt.init)() != 0) {
- GET_USE_COUNT(mp) = 0;
- return -EBUSY;
+ /* Update module references. */
+ mod->next = mod_tmp.next;
+ mod->refs = NULL;
+ for (i = 0, dep = mod->deps; i < mod->ndeps; ++i, ++dep) {
+ struct module *o, *d = dep->dep;
+
+ /* Make sure the indicated dependancies are really modules. */
+ if (d == mod) {
+ printk(KERN_ERR "init_module: self-referential "
+ "dependancy in mod->deps.\n");
+ goto err3;
+ }
+
+ for (o = module_list; o != &kernel_module; o = o->next)
+ if (o == d) goto found_dep;
+
+ printk(KERN_ERR "init_module: found dependancy that is "
+ "(no longer?) a module.\n");
+ goto err3;
+
+ found_dep:
+ dep->ref = mod;
+ dep->next_ref = d->refs;
+ d->refs = dep;
+ /* Being referenced by a dependant module counts as a
+ use as far as kerneld is concerned. */
+ d->flags |= MOD_USED_ONCE;
}
- GET_USE_COUNT(mp) -= 1;
- mp->state = MOD_RUNNING;
- return 0;
+ /* Free our temporary memory. */
+ put_mod_name(n_name);
+ put_mod_name(name);
+
+ /* Initialize the module. */
+ mod->usecount = 1;
+ if (mod->init && mod->init() != 0) {
+ mod->usecount = 0;
+ error = -EBUSY;
+ goto err0;
+ }
+ mod->usecount--;
+
+ /* And set it running. */
+ mod->flags |= MOD_RUNNING;
+ error = 0;
+ goto err0;
+
+err3:
+ put_mod_name(n_name);
+err2:
+ *mod = mod_tmp;
+err1:
+ put_mod_name(name);
+err0:
+ unlock_kernel();
+ return error;
}
asmlinkage int
-sys_delete_module(char *module_name)
+sys_delete_module(const char *name_user)
{
- struct module *mp;
- char name[MOD_MAX_NAME];
- int error;
+ struct module *mod, *next;
+ char *name;
+ long error = -EPERM;
+ lock_kernel();
if (!suser())
- return -EPERM;
- /* else */
- if (module_name != NULL) {
- if ((error = get_mod_name(module_name, name)) < 0)
- return error;
- if ((mp = find_module(name)) == NULL)
- return -ENOENT;
- if ((mp->ref != NULL) ||
- ((GET_USE_COUNT(mp) & ~(MOD_AUTOCLEAN | MOD_VISITED)) != 0))
- return -EBUSY;
- GET_USE_COUNT(mp) &= ~(MOD_AUTOCLEAN | MOD_VISITED);
- if (mp->state == MOD_RUNNING)
- (*mp->cleanup)();
- mp->state = MOD_DELETED;
- free_modules();
- }
- /* for automatic reaping */
- else {
- struct module *mp_next;
- for (mp = module_list; mp != &kernel_module; mp = mp_next) {
- mp_next = mp->next;
- if ((mp->ref == NULL) && (mp->state == MOD_RUNNING) &&
- ((GET_USE_COUNT(mp) & ~MOD_VISITED) == MOD_AUTOCLEAN)) {
- if ((GET_USE_COUNT(mp) & MOD_VISITED)) {
- /* Don't reap until one "cycle" after last _use_ */
- GET_USE_COUNT(mp) &= ~MOD_VISITED;
- }
- else {
- GET_USE_COUNT(mp) &= ~(MOD_AUTOCLEAN | MOD_VISITED);
- (*mp->cleanup)();
- mp->state = MOD_DELETED;
- free_modules();
- }
- }
+ goto out;
+
+ if (name_user) {
+ if ((error = get_mod_name(name_user, &name)) < 0)
+ goto out;
+ if (error == 0) {
+ error = -EINVAL;
+ put_mod_name(name);
+ goto out;
+ }
+ error = -ENOENT;
+ if ((mod = find_module(name)) == NULL) {
+ put_mod_name(name);
+ goto out;
+ }
+ put_mod_name(name);
+ error = -EBUSY;
+ if (mod->refs != NULL || __MOD_IN_USE(mod))
+ goto out;
+
+ free_module(mod);
+ error = 0;
+ goto out;
+ }
+
+ /* Do automatic reaping */
+ for (mod = module_list; mod != &kernel_module; mod = next) {
+ next = mod->next;
+ if (mod->refs == NULL &&
+ ((mod->flags
+ & (MOD_AUTOCLEAN|MOD_RUNNING|MOD_DELETED|MOD_USED_ONCE))
+ == (MOD_AUTOCLEAN|MOD_RUNNING|MOD_USED_ONCE)) &&
+ !__MOD_IN_USE(mod)) {
+ if (mod->flags & MOD_VISITED)
+ mod->flags &= ~MOD_VISITED;
+ else
+ free_module(mod);
}
}
- return 0;
+ error = 0;
+out:
+ unlock_kernel();
+ return error;
}
+/* Query various bits about modules. */
-/*
- * Copy the kernel symbol table to user space. If the argument is null,
- * just return the size of the table.
- *
- * Note that the transient module symbols are copied _first_,
- * in lifo order!!!
- *
- * The symbols to "insmod" are according to the "old" format: struct kernel_sym,
- * which is actually quite handy for this purpose.
- * Note that insmod inserts a struct symbol_table later on...
- * (as that format is quite handy for the kernel...)
- *
- * For every module, the first (pseudo)symbol copied is the module name
- * and the address of the module struct.
- * This lets "insmod" keep track of references, and build the array of
- * struct module_refs in the symbol table.
- * The format of the module name is "#module", so that "insmod" can easily
- * notice when a module name comes along. Also, this will make it possible
- * to use old versions of "insmod", albeit with reduced functionality...
- * The "kernel" module has an empty name.
- */
-asmlinkage int
-sys_get_kernel_syms(struct kernel_sym *table)
+static int
+qm_modules(char *buf, size_t bufsize, size_t *ret)
{
- struct internal_symbol *from;
- struct kernel_sym isym;
- struct kernel_sym *to;
- struct module *mp = module_list;
- int i;
- int nmodsyms = 0;
- int err;
+ struct module *mod;
+ size_t nmod, space, len;
- for (mp = module_list; mp; mp = mp->next) {
- if (mp->symtab && mp->symtab->n_symbols) {
- /* include the count for the module name! */
- nmodsyms += mp->symtab->n_symbols + 1;
- }
- else
- /* include the count for the module name! */
- nmodsyms += 1; /* return modules without symbols too */
- }
-
- if (table != NULL) {
- to = table;
-
- /* copy all module symbols first (always LIFO order) */
- for (mp = module_list; mp; mp = mp->next) {
- if (mp->state == MOD_RUNNING) {
- /* magic: write module info as a pseudo symbol */
- isym.value = (unsigned long)mp;
- sprintf(isym.name, "#%s", mp->name);
- err = copy_to_user(to, &isym, sizeof isym);
- if (err)
- return -EFAULT;
- ++to;
-
- if (mp->symtab != NULL) {
- for (i = mp->symtab->n_symbols,
- from = mp->symtab->symbol;
- i > 0; --i, ++from, ++to) {
-
- isym.value = (unsigned long)from->addr;
- strncpy(isym.name, from->name, sizeof isym.name);
- err = copy_to_user(to, &isym, sizeof isym);
- if (err)
- return -EFAULT;
- }
- }
- }
- }
+ nmod = space = 0;
+
+ for (mod=module_list; mod != &kernel_module; mod=mod->next, ++nmod) {
+ len = strlen(mod->name)+1;
+ if (len > bufsize)
+ goto calc_space_needed;
+ if (copy_to_user(buf, mod->name, len))
+ return -EFAULT;
+ buf += len;
+ bufsize -= len;
+ space += len;
}
- return nmodsyms;
+ if (put_user(nmod, ret))
+ return -EFAULT;
+ else
+ return 0;
+
+calc_space_needed:
+ space += len;
+ while ((mod = mod->next) != &kernel_module)
+ space += strlen(mod->name)+1;
+
+ if (put_user(space, ret))
+ return -EFAULT;
+ else
+ return -ENOSPC;
}
-/*
- * Look for a module by name, ignoring modules marked for deletion.
- */
-struct module *
-find_module( const char *name)
+static int
+qm_deps(struct module *mod, char *buf, size_t bufsize, size_t *ret)
{
- struct module *mp;
+ size_t i, space, len;
- for (mp = module_list ; mp ; mp = mp->next) {
- if (mp->state == MOD_DELETED)
- continue;
- if (!strcmp(mp->name, name))
- break;
+ if (mod == &kernel_module)
+ return -EINVAL;
+ if ((mod->flags & (MOD_RUNNING | MOD_DELETED)) != MOD_RUNNING)
+ if (put_user(0, ret))
+ return -EFAULT;
+ else
+ return 0;
+
+ space = 0;
+ for (i = 0; i < mod->ndeps; ++i) {
+ const char *dep_name = mod->deps[i].dep->name;
+
+ len = strlen(dep_name)+1;
+ if (len > bufsize)
+ goto calc_space_needed;
+ if (copy_to_user(buf, dep_name, len))
+ return -EFAULT;
+ buf += len;
+ bufsize -= len;
+ space += len;
}
- return mp;
+
+ if (put_user(i, ret))
+ return -EFAULT;
+ else
+ return 0;
+
+calc_space_needed:
+ space += len;
+ while (++i < mod->ndeps)
+ space += strlen(mod->deps[i].dep->name)+1;
+
+ if (put_user(space, ret))
+ return -EFAULT;
+ else
+ return -ENOSPC;
}
-static void
-drop_refs(struct module *mp)
+static int
+qm_refs(struct module *mod, char *buf, size_t bufsize, size_t *ret)
{
- struct module *step;
- struct module_ref *prev;
+ size_t nrefs, space, len;
struct module_ref *ref;
- for (step = module_list; step; step = step->next) {
- for (prev = ref = step->ref; ref; ref = prev->next) {
- if (ref->module == mp) {
- if (ref == step->ref)
- step->ref = ref->next;
- else
- prev->next = ref->next;
- break; /* every module only references once! */
- }
- else
- prev = ref;
- }
+ if (mod == &kernel_module)
+ return -EINVAL;
+ if ((mod->flags & (MOD_RUNNING | MOD_DELETED)) != MOD_RUNNING)
+ if (put_user(0, ret))
+ return -EFAULT;
+ else
+ return 0;
+
+ space = 0;
+ for (nrefs = 0, ref = mod->refs; ref ; ++nrefs, ref = ref->next_ref) {
+ const char *ref_name = ref->ref->name;
+
+ len = strlen(ref_name)+1;
+ if (len > bufsize)
+ goto calc_space_needed;
+ if (copy_to_user(buf, ref_name, len))
+ return -EFAULT;
+ buf += len;
+ bufsize -= len;
+ space += len;
}
+
+ if (put_user(nrefs, ret))
+ return -EFAULT;
+ else
+ return 0;
+
+calc_space_needed:
+ space += len;
+ while ((ref = ref->next_ref) != NULL)
+ space += strlen(ref->ref->name)+1;
+
+ if (put_user(space, ret))
+ return -EFAULT;
+ else
+ return -ENOSPC;
}
-/*
- * Try to free modules which have been marked for deletion. Returns nonzero
- * if a module was actually freed.
- */
-int
-free_modules( void)
+static int
+qm_symbols(struct module *mod, char *buf, size_t bufsize, size_t *ret)
{
- struct module *mp;
- struct module **mpp;
- int did_deletion;
-
- did_deletion = 0;
- freeing_modules = 0;
- mpp = &module_list;
- while ((mp = *mpp) != NULL) {
- if (mp->state != MOD_DELETED) {
- mpp = &mp->next;
- } else {
- if ((GET_USE_COUNT(mp) != 0) || (mp->ref != NULL)) {
- freeing_modules = 1;
- mpp = &mp->next;
- } else { /* delete it */
- *mpp = mp->next;
- if (mp->symtab) {
- if (mp->symtab->n_refs)
- drop_refs(mp);
- if (mp->symtab->size)
- kfree_s(mp->symtab, mp->symtab->size);
- }
- vfree(mp->addr);
- kfree_s(mp, sizeof(struct module) + MOD_MAX_NAME);
- did_deletion = 1;
- }
- }
+ size_t i, space, len;
+ struct module_symbol *s;
+ char *strings;
+ unsigned long *vals;
+
+ if ((mod->flags & (MOD_RUNNING | MOD_DELETED)) != MOD_RUNNING)
+ if (put_user(0, ret))
+ return -EFAULT;
+ else
+ return 0;
+
+ space = mod->nsyms * 2*sizeof(void *);
+
+ i = len = 0;
+ s = mod->syms;
+
+ if (space > bufsize)
+ goto calc_space_needed;
+
+ if (!access_ok(VERIFY_WRITE, buf, space))
+ return -EFAULT;
+
+ bufsize -= space;
+ vals = (unsigned long *)buf;
+ strings = buf+space;
+
+ for (; i < mod->nsyms ; ++i, ++s, vals += 2) {
+ len = strlen(s->name)+1;
+ if (len > bufsize)
+ goto calc_space_needed;
+
+ if (copy_to_user(strings, s->name, len)
+ || __put_user(s->value, vals+0)
+ || __put_user(space, vals+1))
+ return -EFAULT;
+
+ strings += len;
+ bufsize -= len;
+ space += len;
}
- return did_deletion;
+
+ if (put_user(i, ret))
+ return -EFAULT;
+ else
+ return 0;
+
+calc_space_needed:
+ for (; i < mod->nsyms; ++i, ++s)
+ space += strlen(s->name)+1;
+
+ if (put_user(space, ret))
+ return -EFAULT;
+ else
+ return -ENOSPC;
}
+static int
+qm_info(struct module *mod, char *buf, size_t bufsize, size_t *ret)
+{
+ int error = 0;
-/*
- * Called by the /proc file system to return a current list of modules.
- */
-int get_module_list(char *buf)
+ if (mod == &kernel_module)
+ return -EINVAL;
+
+ if (sizeof(struct module_info) <= bufsize) {
+ struct module_info info;
+ info.addr = (unsigned long)mod;
+ info.size = mod->size;
+ info.flags = mod->flags;
+ info.usecount = (mod_member_present(mod, can_unload)
+ && mod->can_unload ? -1 : mod->usecount);
+
+ if (copy_to_user(buf, &info, sizeof(struct module_info)))
+ return -EFAULT;
+ } else
+ error = -ENOSPC;
+
+ if (put_user(sizeof(struct module_info), ret))
+ return -EFAULT;
+
+ return error;
+}
+
+asmlinkage int
+sys_query_module(const char *name_user, int which, char *buf, size_t bufsize,
+ size_t *ret)
{
- char *p;
- const char *q;
- int i;
- struct module *mp;
- struct module_ref *ref;
- char size[32];
-
- p = buf;
- /* Do not show the kernel pseudo module */
- for (mp = module_list ; mp && mp->next; mp = mp->next) {
- if (p - buf > 4096 - 100)
- break; /* avoid overflowing buffer */
- q = mp->name;
- if (*q == '\0' && mp->size == 0 && mp->ref == NULL)
- continue; /* don't list modules for kernel syms */
- i = 20;
- while (*q) {
- *p++ = *q++;
- i--;
- }
- sprintf(size, "%d", mp->size);
- i -= strlen(size);
- if (i <= 0)
- i = 1;
- while (--i >= 0)
- *p++ = ' ';
- q = size;
- while (*q)
- *p++ = *q++;
- if (mp->state == MOD_UNINITIALIZED)
- q = " (uninitialized)";
- else if (mp->state == MOD_RUNNING)
- q = "";
- else if (mp->state == MOD_DELETED)
- q = " (deleted)";
- else
- q = " (bad state)";
- while (*q)
- *p++ = *q++;
-
- *p++ = '\t';
- if ((ref = mp->ref) != NULL) {
- *p++ = '[';
- for (; ref; ref = ref->next) {
- q = ref->module->name;
- while (*q)
- *p++ = *q++;
- if (ref->next)
- *p++ = ' ';
- }
- *p++ = ']';
+ struct module *mod;
+ int err;
+
+ lock_kernel();
+ if (name_user == NULL)
+ mod = &kernel_module;
+ else {
+ long namelen;
+ char *name;
+
+ if ((namelen = get_mod_name(name_user, &name)) < 0) {
+ err = namelen;
+ goto out;
}
- if (mp->state == MOD_RUNNING) {
- sprintf(size,"\t%ld%s",
- GET_USE_COUNT(mp) & ~(MOD_AUTOCLEAN | MOD_VISITED),
- ((GET_USE_COUNT(mp) & MOD_AUTOCLEAN)?
- " (autoclean)":""));
- q = size;
- while (*q)
- *p++ = *q++;
+ err = -ENOENT;
+ if (namelen == 0)
+ mod = &kernel_module;
+ else if ((mod = find_module(name)) == NULL) {
+ put_mod_name(name);
+ goto out;
}
- *p++ = '\n';
+ put_mod_name(name);
}
- return p - buf;
-}
+ switch (which)
+ {
+ case 0:
+ err = 0;
+ break;
+ case QM_MODULES:
+ err = qm_modules(buf, bufsize, ret);
+ break;
+ case QM_DEPS:
+ err = qm_deps(mod, buf, bufsize, ret);
+ break;
+ case QM_REFS:
+ err = qm_refs(mod, buf, bufsize, ret);
+ break;
+ case QM_SYMBOLS:
+ err = qm_symbols(mod, buf, bufsize, ret);
+ break;
+ case QM_INFO:
+ err = qm_info(mod, buf, bufsize, ret);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+out:
+ unlock_kernel();
+ return err;
+}
/*
- * Called by the /proc file system to return a current list of ksyms.
+ * Copy the kernel symbol table to user space. If the argument is
+ * NULL, just return the size of the table.
+ *
+ * This call is obsolete. New programs should use query_module+QM_SYMBOLS
+ * which does not arbitrarily limit the length of symbols.
*/
-int get_ksyms_list(char *buf, char **start, off_t offset, int length)
+
+asmlinkage int
+sys_get_kernel_syms(struct kernel_sym *table)
{
- struct module *mp;
- struct internal_symbol *sym;
+ struct module *mod;
int i;
- char *p = buf;
- int len = 0; /* code from net/ipv4/proc.c */
- off_t pos = 0;
- off_t begin = 0;
- for (mp = module_list; mp; mp = mp->next) {
- if ((mp->state == MOD_RUNNING) &&
- (mp->symtab != NULL) &&
- (mp->symtab->n_symbols > 0)) {
- for (i = mp->symtab->n_symbols,
- sym = mp->symtab->symbol;
- i > 0; --i, ++sym) {
-
- p = buf + len;
- if (mp->name[0]) {
- len += sprintf(p, "%08lx %s\t[%s]\n",
- (long)sym->addr,
- sym->name, mp->name);
- } else {
- len += sprintf(p, "%08lx %s\n",
- (long)sym->addr,
- sym->name);
- }
- pos = begin + len;
- if (pos < offset) {
- len = 0;
- begin = pos;
- }
- pos = begin + len;
- if (pos > offset+length)
- goto leave_the_loop;
- }
+ lock_kernel();
+ for (mod = module_list, i = 0; mod; mod = mod->next) {
+ /* include the count for the module name! */
+ i += mod->nsyms + 1;
+ }
+
+ if (table == NULL)
+ goto out;
+
+ for (mod = module_list, i = 0; mod; mod = mod->next) {
+ struct kernel_sym ksym;
+ struct module_symbol *msym;
+ unsigned int j;
+
+ if ((mod->flags & (MOD_RUNNING|MOD_DELETED)) != MOD_RUNNING)
+ continue;
+
+ /* magic: write module info as a pseudo symbol */
+ ksym.value = (unsigned long)mod;
+ ksym.name[0] = '#';
+ strncpy(ksym.name+1, mod->name, sizeof(ksym.name)-1);
+ ksym.name[sizeof(ksym.name)-1] = '\0';
+
+ if (copy_to_user(table, &ksym, sizeof(ksym)) != 0)
+ goto out;
+ ++i, ++table;
+
+ if (mod->nsyms == 0)
+ continue;
+
+ for (j = 0, msym = mod->syms; j < mod->nsyms; ++j, ++msym) {
+ ksym.value = msym->value;
+ strncpy(ksym.name, msym->name, sizeof(ksym.name));
+ ksym.name[sizeof(ksym.name)-1] = '\0';
+
+ if (copy_to_user(table, &ksym, sizeof(ksym)) != 0)
+ goto out;
+ ++i, ++table;
}
}
- leave_the_loop:
- *start = buf + (offset - begin);
- len -= (offset - begin);
- if (len > length)
- len = length;
- return len;
+out:
+ unlock_kernel();
+ return i;
}
/*
- * Rules:
- * - The new symbol table should be statically allocated, or else you _have_
- * to set the "size" field of the struct to the number of bytes allocated.
- *
- * - The strings that name the symbols will not be copied, maybe the pointers
- *
- * - For a loadable module, the function should only be called in the
- * context of init_module
- *
- * Those are the only restrictions! (apart from not being reentrant...)
- *
- * If you want to remove a symbol table for a loadable module,
- * the call looks like: "register_symtab(0)".
- *
- * The look of the code is mostly dictated by the format of
- * the frozen struct symbol_table, due to compatibility demands.
+ * Look for a module by name, ignoring modules marked for deletion.
*/
-#define INTSIZ sizeof(struct internal_symbol)
-#define REFSIZ sizeof(struct module_ref)
-#define SYMSIZ sizeof(struct symbol_table)
-#define MODSIZ sizeof(struct module)
-static struct symbol_table nulltab;
-int
-register_symtab_from(struct symbol_table *intab, long *from)
+static struct module *
+find_module(const char *name)
{
- struct module *mp;
- struct module *link;
- struct symbol_table *oldtab;
- struct symbol_table *newtab;
- struct module_ref *newref;
- int size;
-
- if (intab && (intab->n_symbols == 0)) {
- struct internal_symbol *sym;
- /* How many symbols, really? */
-
- for (sym = intab->symbol; sym->name; ++sym)
- intab->n_symbols +=1;
- }
-
- for (mp = module_list; mp != &kernel_module; mp = mp->next) {
- /*
- * "from" points to "mod_use_count_" (== start of module)
- * or is == 0 if called from a non-module
- */
- if ((unsigned long)(mp->addr) == (unsigned long)from)
+ struct module *mod;
+
+ for (mod = module_list; mod ; mod = mod->next) {
+ if (mod->flags & MOD_DELETED)
+ continue;
+ if (!strcmp(mod->name, name))
break;
}
- if (mp == &kernel_module) {
- /* Aha! Called from an "internal" module */
- if (!intab)
- return 0; /* or -ESILLY_PROGRAMMER :-) */
+ return mod;
+}
- /* create a pseudo module! */
- if (!(mp = (struct module*) kmalloc(MODSIZ, GFP_KERNEL))) {
- /* panic time! */
- printk(KERN_ERR "Out of memory for new symbol table!\n");
- return -ENOMEM;
- }
- /* else OK */
- memset(mp, 0, MODSIZ);
- mp->state = MOD_RUNNING; /* Since it is resident... */
- mp->name = ""; /* This is still the "kernel" symbol table! */
- mp->symtab = intab;
+/*
+ * Free the given module.
+ */
- /* link it in _after_ the resident symbol table */
- mp->next = kernel_module.next;
- kernel_module.next = mp;
+static void
+free_module(struct module *mod)
+{
+ struct module_ref *dep;
+ unsigned i;
- return 0;
- }
+ /* Let the module clean up. */
- /* else ******** Called from a loadable module **********/
+ mod->flags |= MOD_DELETED;
+ if (mod->flags & MOD_RUNNING) {
+ mod->cleanup();
+ mod->flags &= ~MOD_RUNNING;
+ }
- /*
- * This call should _only_ be done in the context of the
- * call to init_module i.e. when loading the module!!
- * Or else...
- */
+ /* Remove the module from the dependancy lists. */
- /* Any table there before? */
- if ((oldtab = mp->symtab) == (struct symbol_table*)0) {
- /* No, just insert it! */
- mp->symtab = intab;
- return 0;
+ for (i = 0, dep = mod->deps; i < mod->ndeps; ++i, ++dep) {
+ struct module_ref **pp;
+ for (pp = &dep->dep->refs; *pp != dep; pp = &(*pp)->next_ref)
+ continue;
+ *pp = dep->next_ref;
}
- /* else ****** we have to replace the module symbol table ******/
+ /* And from the main module list. */
- if (oldtab->n_refs == 0) { /* no problems! */
- mp->symtab = intab;
- /* if the old table was kmalloc-ed, drop it */
- if (oldtab->size > 0)
- kfree_s(oldtab, oldtab->size);
-
- return 0;
+ if (mod == module_list) {
+ module_list = mod->next;
+ } else {
+ struct module *p;
+ for (p = module_list; p->next != mod; p = p->next)
+ continue;
+ p->next = mod->next;
}
- /* else */
- /***** The module references other modules... insmod said so! *****/
- /* We have to allocate a new symbol table, or we lose them! */
- if (intab == (struct symbol_table*)0)
- intab = &nulltab; /* easier code with zeroes in place */
+ /* And free the memory. */
- /* the input symbol table space does not include the string table */
- /* (it does for symbol tables that insmod creates) */
+ vfree(mod);
+}
- if (!(newtab = (struct symbol_table*)kmalloc(
- size = SYMSIZ + intab->n_symbols * INTSIZ +
- oldtab->n_refs * REFSIZ,
- GFP_KERNEL))) {
- /* panic time! */
- printk(KERN_ERR "Out of memory for new symbol table!\n");
- return -ENOMEM;
- }
+/*
+ * Called by the /proc file system to return a current list of modules.
+ */
- /* copy up to, and including, the new symbols */
- memcpy(newtab, intab, SYMSIZ + intab->n_symbols * INTSIZ);
+int get_module_list(char *p)
+{
+ size_t left = PAGE_SIZE;
+ struct module *mod;
+ char tmpstr[64];
+ struct module_ref *ref;
- newtab->size = size;
- newtab->n_refs = oldtab->n_refs;
+ for (mod = module_list; mod != &kernel_module; mod = mod->next) {
+ long len;
+ const char *q;
+
+#define safe_copy_str(str, len) \
+ do { \
+ if (left < len) \
+ goto fini; \
+ memcpy(p, str, len); p += len, left -= len; \
+ } while (0)
+#define safe_copy_cstr(str) safe_copy_str(str, sizeof(str)-1)
+
+ len = strlen(mod->name);
+ safe_copy_str(mod->name, len);
+
+ if ((len = 20 - len) > 0) {
+ if (left < len)
+ goto fini;
+ memset(p, ' ', len);
+ p += len;
+ left -= len;
+ }
- /* copy references */
- memcpy( ((char *)newtab) + SYMSIZ + intab->n_symbols * INTSIZ,
- ((char *)oldtab) + SYMSIZ + oldtab->n_symbols * INTSIZ,
- oldtab->n_refs * REFSIZ);
+ len = sprintf(tmpstr, "%8lu", mod->size);
+ safe_copy_str(tmpstr, len);
- /* relink references from the old table to the new one */
+ if (mod->flags & MOD_RUNNING) {
+ len = sprintf(tmpstr, "%4ld",
+ (mod_member_present(mod, can_unload)
+ && mod->can_unload
+ ? -1 : mod->usecount));
+ safe_copy_str(tmpstr, len);
+ }
- /* pointer to the first reference entry in newtab! Really! */
- newref = (struct module_ref*) &(newtab->symbol[newtab->n_symbols]);
+ if (mod->flags & MOD_DELETED)
+ safe_copy_cstr(" (deleted)");
+ else if (mod->flags & MOD_RUNNING) {
+ if (mod->flags & MOD_AUTOCLEAN)
+ safe_copy_cstr(" (autoclean)");
+ if (!(mod->flags & MOD_USED_ONCE))
+ safe_copy_cstr(" (unused)");
+ } else
+ safe_copy_cstr(" (uninitialized)");
+
+ if ((ref = mod->refs) != NULL) {
+ safe_copy_cstr(" [");
+ while (1) {
+ q = ref->ref->name;
+ len = strlen(q);
+ safe_copy_str(q, len);
+
+ if ((ref = ref->next_ref) != NULL)
+ safe_copy_cstr(" ");
+ else
+ break;
+ }
+ safe_copy_cstr("]");
+ }
- /* check for reference links from previous modules */
- for ( link = module_list;
- link && (link != &kernel_module);
- link = link->next) {
+ safe_copy_cstr("\n");
- if (link->ref && (link->ref->module == mp))
- link->ref = newref++;
+#undef safe_copy_str
+#undef safe_copy_cstr
}
- mp->symtab = newtab;
+fini:
+ return PAGE_SIZE - left;
+}
- /* all references (if any) have been handled */
+/*
+ * Called by the /proc file system to return a current list of ksyms.
+ */
- /* if the old table was kmalloc-ed, drop it */
- if (oldtab->size > 0)
- kfree_s(oldtab, oldtab->size);
+int
+get_ksyms_list(char *buf, char **start, off_t offset, int length)
+{
+ struct module *mod;
+ char *p = buf;
+ int len = 0; /* code from net/ipv4/proc.c */
+ off_t pos = 0;
+ off_t begin = 0;
+
+ for (mod = module_list; mod; mod = mod->next) {
+ unsigned i;
+ struct module_symbol *sym;
- return 0;
+ if (!(mod->flags & MOD_RUNNING) || (mod->flags & MOD_DELETED))
+ continue;
+
+ for (i = mod->nsyms, sym = mod->syms; i > 0; --i, ++sym) {
+ p = buf + len;
+ if (*mod->name) {
+ len += sprintf(p, "%0*lx %s\t[%s]\n",
+ (int)(2*sizeof(void*)),
+ sym->value, sym->name,
+ mod->name);
+ } else {
+ len += sprintf(p, "%0*lx %s\n",
+ (int)(2*sizeof(void*)),
+ sym->value, sym->name);
+ }
+ pos = begin + len;
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ pos = begin + len;
+ if (pos > offset+length)
+ goto leave_the_loop;
+ }
+ }
+leave_the_loop:
+ *start = buf + (offset - begin);
+ len -= (offset - begin);
+ if (len > length)
+ len = length;
+ return len;
}
#else /* CONFIG_MODULES */
/* Dummy syscalls for people who don't want modules */
-asmlinkage unsigned long sys_create_module(void)
+asmlinkage unsigned long
+sys_create_module(const char *name_user, size_t size)
{
return -ENOSYS;
}
-asmlinkage int sys_init_module(void)
+asmlinkage int
+sys_init_module(const char *name_user, struct module *mod_user)
{
return -ENOSYS;
}
-asmlinkage int sys_delete_module(void)
+asmlinkage int
+sys_delete_module(const char *name_user)
{
return -ENOSYS;
}
-asmlinkage int sys_get_kernel_syms(void)
+asmlinkage int
+sys_query_module(const char *name_user, int which, char *buf, size_t bufsize,
+ size_t *ret)
{
+ /* Let the program know about the new interface. Not that
+ it'll do them much good. */
+ if (which == 0)
+ return 0;
+
return -ENOSYS;
}
-int register_symtab_from(struct symbol_table *intab, long *from)
+asmlinkage int
+sys_get_kernel_syms(struct kernel_sym *table)
{
- return 0;
+ return -ENOSYS;
}
#endif /* CONFIG_MODULES */
-