URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [uclinux/] [uClinux-2.0.x/] [kernel/] [module.c] - Rev 1765
Compare with Previous | Blame | View Log
#include <linux/errno.h> #include <linux/kernel.h> #include <asm/segment.h> #include <linux/mm.h> /* defines GFP_KERNEL */ #include <linux/string.h> #include <linux/module.h> #include <linux/sched.h> #include <linux/malloc.h> #include <linux/config.h> #include <asm/pgtable.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 *)" * * 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) */ #ifdef CONFIG_MODULES /* a *big* #ifdef block... */ static struct module kernel_module; static struct module *module_list = &kernel_module; static int freeing_modules; /* true if some modules are marked for deletion */ static struct module *find_module( const char *name); static int get_mod_name( char *user_name, char *buf); static int free_modules( void); 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; kernel_module.symtab = &symbol_table; kernel_module.state = MOD_RUNNING; /* Hah! */ kernel_module.name = ""; } /* * Allocate space for a module. */ asmlinkage unsigned long sys_create_module(char *module_name, unsigned long 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() || securelevel > 0) return -EPERM; if (module_name == NULL || size == 0) return -EINVAL; if ((error = get_mod_name(module_name, name)) != 0) return error; if (find_module(name) != NULL) { return -EEXIST; } if ((mp = (struct module*) kmalloc(sspace, GFP_KERNEL)) == NULL) { return -ENOMEM; } 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; } 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; * (long *) addr = 0; /* set use count to zero */ module_list = mp; /* 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; } /* * Initialize a module. */ asmlinkage int sys_init_module(char *module_name, char *code, unsigned codesize, struct mod_routines *routines, struct symbol_table *symtab) { struct module *mp; struct symbol_table *newtab; char name[MOD_MAX_NAME]; int error; struct mod_routines rt; if (!suser() || securelevel > 0) return -EPERM; #ifdef __i386__ /* A little bit of protection... we "know" where the user stack is... */ if (symtab && ((unsigned long)symtab > 0xb0000000)) { printk(KERN_WARNING "warning: you are using an old insmod, no symbols will be inserted!\n"); symtab = NULL; } #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); memcpy_fromfs(&rt, routines, sizeof rt); 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; memcpy_fromfs((char *)mp->addr + sizeof (long), code, codesize); 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); mp->cleanup = rt.cleanup; /* 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; if ((error = verify_area(VERIFY_READ, &symtab->size, sizeof(symtab->size)))) return error; size = get_user(&symtab->size); if ((newtab = (struct symbol_table*) kmalloc(size, GFP_KERNEL)) == NULL) { return -ENOMEM; } if ((error = verify_area(VERIFY_READ, symtab, size))) { kfree_s(newtab, size); return error; } memcpy_fromfs((char *)(newtab), symtab, size); /* sanity check */ legal_start = sizeof(struct symbol_table) + newtab->n_symbols * sizeof(struct internal_symbol) + newtab->n_refs * sizeof(struct module_ref); 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; } /* 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; } ref->next = ref->module->ref; ref->module->ref = ref; ref->module = mp; } } flush_pages_to_ram((unsigned long)mp->addr, (codesize+sizeof(long)+PAGE_SIZE-1)/PAGE_SIZE); GET_USE_COUNT(mp) += 1; if ((*rt.init)() != 0) { GET_USE_COUNT(mp) = 0; return -EBUSY; } GET_USE_COUNT(mp) -= 1; mp->state = MOD_RUNNING; return 0; } asmlinkage int sys_delete_module(char *module_name) { struct module *mp; char name[MOD_MAX_NAME]; int error; if (!suser() || securelevel > 0) 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(); } } } } return 0; } /* * 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) { struct internal_symbol *from; struct kernel_sym isym; struct kernel_sym *to; struct module *mp = module_list; int i; int nmodsyms = 0; 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; if ((i = verify_area(VERIFY_WRITE, to, nmodsyms * sizeof(*table)))) return i; /* 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); memcpy_tofs(to, &isym, sizeof isym); ++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); memcpy_tofs(to, &isym, sizeof isym); } } } } } return nmodsyms; } /* * Copy the name of a module from user space. */ int get_mod_name(char *user_name, char *buf) { int i; i = 0; for (i = 0 ; (buf[i] = get_user(user_name + i)) != '\0' ; ) { if (++i >= MOD_MAX_NAME) return -E2BIG; } return 0; } /* * Look for a module by name, ignoring modules marked for deletion. */ struct module * find_module( const char *name) { struct module *mp; for (mp = module_list ; mp ; mp = mp->next) { if (mp->state == MOD_DELETED) continue; if (!strcmp(mp->name, name)) break; } return mp; } static void drop_refs(struct module *mp) { struct module *step; struct module_ref *prev; 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; } } } /* * Try to free modules which have been marked for deletion. Returns nonzero * if a module was actually freed. */ int free_modules( void) { 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; } } } return did_deletion; } /* * Called by the /proc file system to return a current list of modules. */ int get_module_list(char *buf) { 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++ = ']'; } 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++; } *p++ = '\n'; } return p - buf; } /* * Called by the /proc file system to return a current list of ksyms. */ int get_ksyms_list(char *buf, char **start, off_t offset, int length) { struct module *mp; struct internal_symbol *sym; 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; } } } leave_the_loop: *start = buf + (offset - begin); len -= (offset - begin); if (len > length) len = length; return len; } /* * Gets the address for a symbol in the given module. If modname is * NULL, it looks for the name in any registered symbol table. If the * modname is an empty string, it looks for the symbol in kernel exported * symbol tables. */ void *get_module_symbol(char *modname, char *symname) { struct module *mp; struct internal_symbol *sym; int i; for (mp = module_list; mp; mp = mp->next) { if (((modname == NULL) || (strcmp(mp->name, modname) == 0)) && (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) { if (strcmp(sym->name, symname) == 0) { return sym->addr; } } } } return NULL; } /* * 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. */ #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) { 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) break; } if (mp == &kernel_module) { /* Aha! Called from an "internal" module */ if (!intab) return 0; /* or -ESILLY_PROGRAMMER :-) */ /* 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; /* link it in _after_ the resident symbol table */ mp->next = kernel_module.next; kernel_module.next = mp; return 0; } /* else ******** Called from a loadable module **********/ /* * This call should _only_ be done in the context of the * call to init_module i.e. when loading the module!! * Or else... */ /* Any table there before? */ if ((oldtab = mp->symtab) == (struct symbol_table*)0) { /* No, just insert it! */ mp->symtab = intab; return 0; } /* else ****** we have to replace the module symbol table ******/ 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; } /* 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 */ /* the input symbol table space does not include the string table */ /* (it does for symbol tables that insmod creates) */ 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; } /* copy up to, and including, the new symbols */ memcpy(newtab, intab, SYMSIZ + intab->n_symbols * INTSIZ); newtab->size = size; newtab->n_refs = oldtab->n_refs; /* copy references */ memcpy( ((char *)newtab) + SYMSIZ + intab->n_symbols * INTSIZ, ((char *)oldtab) + SYMSIZ + oldtab->n_symbols * INTSIZ, oldtab->n_refs * REFSIZ); /* relink references from the old table to the new one */ /* pointer to the first reference entry in newtab! Really! */ newref = (struct module_ref*) &(newtab->symbol[newtab->n_symbols]); /* check for reference links from previous modules */ for ( link = module_list; link && (link != &kernel_module); link = link->next) { if (link->ref && (link->ref->module == mp)) link->ref = newref++; } mp->symtab = newtab; /* all references (if any) have been handled */ /* if the old table was kmalloc-ed, drop it */ if (oldtab->size > 0) kfree_s(oldtab, oldtab->size); return 0; } #else /* CONFIG_MODULES */ /* Dummy syscalls for people who don't want modules */ asmlinkage unsigned long sys_create_module(void) { return -ENOSYS; } asmlinkage int sys_init_module(void) { return -ENOSYS; } asmlinkage int sys_delete_module(void) { return -ENOSYS; } asmlinkage int sys_get_kernel_syms(void) { return -ENOSYS; } int register_symtab_from(struct symbol_table *intab, long *from) { return 0; } #endif /* CONFIG_MODULES */