277 lines
5.8 KiB
C
277 lines
5.8 KiB
C
#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>
|
|
|
|
struct module *module_list = NULL;
|
|
int freeing_modules; /* true if some modules are marked for deletion */
|
|
|
|
struct module *find_module( const char *name);
|
|
int get_mod_name( char *user_name, char *buf);
|
|
int free_modules( void);
|
|
|
|
/*
|
|
* Allocate space for a module.
|
|
*/
|
|
asmlinkage int
|
|
sys_create_module(char *module_name, unsigned long size)
|
|
{
|
|
int npages;
|
|
void* addr;
|
|
int len;
|
|
char name[MOD_MAX_NAME];
|
|
char *savename;
|
|
struct module *mp;
|
|
int error;
|
|
|
|
if (!suser())
|
|
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;
|
|
}
|
|
len = strlen(name) + 1;
|
|
if ((savename = (char*) kmalloc(len, GFP_KERNEL)) == NULL)
|
|
return -ENOMEM;
|
|
memcpy(savename, name, len);
|
|
if ((mp = (struct module*) kmalloc(sizeof *mp, GFP_KERNEL)) == NULL) {
|
|
kfree(savename);
|
|
return -ENOMEM;
|
|
}
|
|
npages = (size + sizeof (int) + 4095) / 4096;
|
|
if ((addr = vmalloc(npages * 4096)) == 0) {
|
|
kfree_s(mp, sizeof *mp);
|
|
kfree(savename);
|
|
return -ENOMEM;
|
|
}
|
|
mp->name = savename;
|
|
mp->size = npages;
|
|
mp->addr = addr;
|
|
mp->state = MOD_UNINITIALIZED;
|
|
* (int *) addr = 0; /* set use count to zero */
|
|
mp->cleanup = NULL;
|
|
mp->next = module_list;
|
|
module_list = mp;
|
|
printk("module `%s' (%lu pages @ 0x%08lx) created\n",
|
|
mp->name, (unsigned long) mp->size, (unsigned long) mp->addr);
|
|
return (int) addr;
|
|
}
|
|
|
|
/*
|
|
* Initialize a module.
|
|
*/
|
|
asmlinkage int
|
|
sys_init_module(char *module_name, char *code, unsigned codesize,
|
|
struct mod_routines *routines)
|
|
{
|
|
struct module *mp;
|
|
char name[MOD_MAX_NAME];
|
|
int error;
|
|
struct mod_routines rt;
|
|
|
|
if (!suser())
|
|
return -EPERM;
|
|
/*
|
|
* First reclaim any memory from dead modules that where not
|
|
* freed when deleted. Should I think be done by timers when
|
|
* the module was deleted - Jon.
|
|
*/
|
|
free_modules();
|
|
|
|
if ((error = get_mod_name(module_name, name)) != 0)
|
|
return error;
|
|
printk( "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 + sizeof (int) + 4095) / 4096 > mp->size)
|
|
return -EINVAL;
|
|
memcpy_fromfs((char *)mp->addr + sizeof (int), code, codesize);
|
|
memset((char *)mp->addr + sizeof (int) + codesize, 0,
|
|
mp->size * 4096 - (codesize + sizeof (int)));
|
|
printk( " init entry @ 0x%08lx, cleanup entry @ 0x%08lx\n",
|
|
(unsigned long) rt.init, (unsigned long) rt.cleanup);
|
|
mp->cleanup = rt.cleanup;
|
|
if ((*rt.init)() != 0)
|
|
return -EBUSY;
|
|
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())
|
|
return -EPERM;
|
|
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->state == MOD_RUNNING)
|
|
(*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.
|
|
*/
|
|
asmlinkage int
|
|
sys_get_kernel_syms(struct kernel_sym *table)
|
|
{
|
|
struct symbol {
|
|
unsigned long addr;
|
|
char *name;
|
|
};
|
|
extern int symbol_table_size;
|
|
extern struct symbol symbol_table[];
|
|
int i;
|
|
struct symbol *from;
|
|
struct kernel_sym *to;
|
|
struct kernel_sym sym;
|
|
|
|
if (table != NULL) {
|
|
from = symbol_table;
|
|
to = table;
|
|
i = verify_area(VERIFY_WRITE, to, symbol_table_size * sizeof *table);
|
|
if (i)
|
|
return i;
|
|
for (i = symbol_table_size ; --i >= 0 ; ) {
|
|
sym.value = from->addr;
|
|
strncpy(sym.name, from->name, sizeof sym.name);
|
|
memcpy_tofs(to, &sym, sizeof sym);
|
|
from++, to++;
|
|
}
|
|
}
|
|
return symbol_table_size;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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_fs_byte(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;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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) {
|
|
freeing_modules = 1;
|
|
mpp = &mp->next;
|
|
} else { /* delete it */
|
|
*mpp = mp->next;
|
|
vfree(mp->addr);
|
|
kfree(mp->name);
|
|
kfree_s(mp, sizeof *mp);
|
|
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;
|
|
char *q;
|
|
int i;
|
|
struct module *mp;
|
|
char size[32];
|
|
|
|
p = buf;
|
|
for (mp = module_list ; mp ; mp = mp->next) {
|
|
if (p - buf > 4096 - 100)
|
|
break; /* avoid overflowing buffer */
|
|
q = mp->name;
|
|
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++ = '\n';
|
|
}
|
|
return p - buf;
|
|
}
|