⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 module.c

📁 Linux2.4.20针对三星公司的s3c2410开发板的内核改造。
💻 C
📖 第 1 页 / 共 2 页
字号:
#include <linux/config.h>#include <linux/mm.h>#include <linux/module.h>#include <asm/module.h>#include <asm/uaccess.h>#include <linux/vmalloc.h>#include <linux/smp_lock.h>#include <asm/pgalloc.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/kmod.h>#include <linux/seq_file.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) * Rewritten by Richard Henderson <rth@tamu.edu> Dec 1996 * Add MOD_INITIALIZING Keith Owens <kaos@ocs.com.au> Nov 1999 * Add kallsyms support, Keith Owens <kaos@ocs.com.au> Apr 2000 * Add asm/module support, IA64 has special requirements.  Keith Owens <kaos@ocs.com.au> Sep 2000 * Fix assorted bugs in module verification.  Keith Owens <kaos@ocs.com.au> Sep 2000 * Fix sys_init_module race, Andrew Morton <andrewm@uow.edu.au> Oct 2000 *     http://www.uwsg.iu.edu/hypermail/linux/kernel/0008.3/0379.html * Replace xxx_module_symbol with inter_module_xxx.  Keith Owens <kaos@ocs.com.au> Oct 2000 * Add a module list lock for kernel fault race fixing. Alan Cox <alan@redhat.com> * * This source is covered by the GNU GPL, the same as all kernel sources. */#if defined(CONFIG_MODULES) || defined(CONFIG_KALLSYMS)extern struct module_symbol __start___ksymtab[];extern struct module_symbol __stop___ksymtab[];extern const struct exception_table_entry __start___ex_table[];extern const struct exception_table_entry __stop___ex_table[];extern const char __start___kallsyms[] __attribute__ ((weak));extern const char __stop___kallsyms[] __attribute__ ((weak));struct module kernel_module ={	size_of_struct:		sizeof(struct module),	name: 			"",	uc:	 		{ATOMIC_INIT(1)},	flags:			MOD_RUNNING,	syms:			__start___ksymtab,	ex_table_start:		__start___ex_table,	ex_table_end:		__stop___ex_table,	kallsyms_start:		__start___kallsyms,	kallsyms_end:		__stop___kallsyms,};struct module *module_list = &kernel_module;#endif	/* defined(CONFIG_MODULES) || defined(CONFIG_KALLSYMS) *//* inter_module functions are always available, even when the kernel is * compiled without modules.  Consumers of inter_module_xxx routines * will always work, even when both are built into the kernel, this * approach removes lots of #ifdefs in mainline code. */static struct list_head ime_list = LIST_HEAD_INIT(ime_list);static spinlock_t ime_lock = SPIN_LOCK_UNLOCKED;static int kmalloc_failed;/* *	This lock prevents modifications that might race the kernel fault *	fixups. It does not prevent reader walks that the modules code *	does. The kernel lock does that. * *	Since vmalloc fault fixups occur in any context this lock is taken *	irqsave at all times. */ spinlock_t modlist_lock = SPIN_LOCK_UNLOCKED;/** * inter_module_register - register a new set of inter module data. * @im_name: an arbitrary string to identify the data, must be unique * @owner: module that is registering the data, always use THIS_MODULE * @userdata: pointer to arbitrary userdata to be registered * * Description: Check that the im_name has not already been registered, * complain if it has.  For new data, add it to the inter_module_entry * list. */void inter_module_register(const char *im_name, struct module *owner, const void *userdata){	struct list_head *tmp;	struct inter_module_entry *ime, *ime_new;	if (!(ime_new = kmalloc(sizeof(*ime), GFP_KERNEL))) {		/* Overloaded kernel, not fatal */		printk(KERN_ERR			"Aiee, inter_module_register: cannot kmalloc entry for '%s'\n",			im_name);		kmalloc_failed = 1;		return;	}	memset(ime_new, 0, sizeof(*ime_new));	ime_new->im_name = im_name;	ime_new->owner = owner;	ime_new->userdata = userdata;	spin_lock(&ime_lock);	list_for_each(tmp, &ime_list) {		ime = list_entry(tmp, struct inter_module_entry, list);		if (strcmp(ime->im_name, im_name) == 0) {			spin_unlock(&ime_lock);			kfree(ime_new);			/* Program logic error, fatal */			printk(KERN_ERR "inter_module_register: duplicate im_name '%s'", im_name);			BUG();		}	}	list_add(&(ime_new->list), &ime_list);	spin_unlock(&ime_lock);}/** * inter_module_unregister - unregister a set of inter module data. * @im_name: an arbitrary string to identify the data, must be unique * * Description: Check that the im_name has been registered, complain if * it has not.  For existing data, remove it from the * inter_module_entry list. */void inter_module_unregister(const char *im_name){	struct list_head *tmp;	struct inter_module_entry *ime;	spin_lock(&ime_lock);	list_for_each(tmp, &ime_list) {		ime = list_entry(tmp, struct inter_module_entry, list);		if (strcmp(ime->im_name, im_name) == 0) {			list_del(&(ime->list));			spin_unlock(&ime_lock);			kfree(ime);			return;		}	}	spin_unlock(&ime_lock);	if (kmalloc_failed) {		printk(KERN_ERR			"inter_module_unregister: no entry for '%s', "			"probably caused by previous kmalloc failure\n",			im_name);		return;	}	else {		/* Program logic error, fatal */		printk(KERN_ERR "inter_module_unregister: no entry for '%s'", im_name);		BUG();	}}/** * inter_module_get - return arbitrary userdata from another module. * @im_name: an arbitrary string to identify the data, must be unique * * Description: If the im_name has not been registered, return NULL. * Try to increment the use count on the owning module, if that fails * then return NULL.  Otherwise return the userdata. */const void *inter_module_get(const char *im_name){	struct list_head *tmp;	struct inter_module_entry *ime;	const void *result = NULL;	spin_lock(&ime_lock);	list_for_each(tmp, &ime_list) {		ime = list_entry(tmp, struct inter_module_entry, list);		if (strcmp(ime->im_name, im_name) == 0) {			if (try_inc_mod_count(ime->owner))				result = ime->userdata;			break;		}	}	spin_unlock(&ime_lock);	return(result);}/** * inter_module_get_request - im get with automatic request_module. * @im_name: an arbitrary string to identify the data, must be unique * @modname: module that is expected to register im_name * * Description: If inter_module_get fails, do request_module then retry. */const void *inter_module_get_request(const char *im_name, const char *modname){	const void *result = inter_module_get(im_name);	if (!result) {		request_module(modname);		result = inter_module_get(im_name);	}	return(result);}/** * inter_module_put - release use of data from another module. * @im_name: an arbitrary string to identify the data, must be unique * * Description: If the im_name has not been registered, complain, * otherwise decrement the use count on the owning module. */void inter_module_put(const char *im_name){	struct list_head *tmp;	struct inter_module_entry *ime;	spin_lock(&ime_lock);	list_for_each(tmp, &ime_list) {		ime = list_entry(tmp, struct inter_module_entry, list);		if (strcmp(ime->im_name, im_name) == 0) {			if (ime->owner)				__MOD_DEC_USE_COUNT(ime->owner);			spin_unlock(&ime_lock);			return;		}	}	spin_unlock(&ime_lock);	printk(KERN_ERR "inter_module_put: no entry for '%s'", im_name);	BUG();}#if defined(CONFIG_MODULES)	/* The rest of the source */static long get_mod_name(const char *user_name, char **buf);static void put_mod_name(char *buf);struct module *find_module(const char *name);void free_module(struct module *, int tag_freed);/* * Called at boot time */void __init init_modules(void){	kernel_module.nsyms = __stop___ksymtab - __start___ksymtab;	arch_init_modules(&kernel_module);}/* * Copy the name of a module from user space. */static inline longget_mod_name(const char *user_name, char **buf){	unsigned long page;	long retval;	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 voidput_mod_name(char *buf){	free_page((unsigned long)buf);}/* * Allocate space for a module. */asmlinkage unsigned longsys_create_module(const char *name_user, size_t size){	char *name;	long namelen, error;	struct module *mod;	unsigned long flags;	if (!capable(CAP_SYS_MODULE))		return -EPERM;	lock_kernel();	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) {		error = -EEXIST;		goto err1;	}	if ((mod = (struct module *)module_map(size)) == NULL) {		error = -ENOMEM;		goto err1;	}	memset(mod, 0, sizeof(*mod));	mod->size_of_struct = sizeof(*mod);	mod->name = (char *)(mod + 1);	mod->size = size;	memcpy((char*)(mod+1), name, namelen+1);	put_mod_name(name);	spin_lock_irqsave(&modlist_lock, flags);	mod->next = module_list;	module_list = mod;	/* link it in */	spin_unlock_irqrestore(&modlist_lock, flags);	error = (long) mod;	goto err0;err1:	put_mod_name(name);err0:	unlock_kernel();	return error;}/* * Initialize a module. */asmlinkage longsys_init_module(const char *name_user, struct module *mod_user){	struct module mod_tmp, *mod;	char *name, *n_name, *name_tmp = NULL;	long namelen, n_namelen, i, error;	unsigned long mod_user_size;	struct module_ref *dep;	if (!capable(CAP_SYS_MODULE))		return -EPERM;	lock_kernel();	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;	name_tmp = kmalloc(strlen(mod->name) + 1, GFP_KERNEL);	/* Where's kstrdup()? */	if (name_tmp == NULL) {		error = -ENOMEM;		goto err1;	}	strcpy(name_tmp, mod->name);	error = copy_from_user(mod, mod_user, mod_user_size);	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.  */	if (!mod_bound(mod->name, namelen, mod)) {		printk(KERN_ERR "init_module: mod->name out of bounds.\n");		goto err2;	}	if (mod->nsyms && !mod_bound(mod->syms, mod->nsyms, mod)) {		printk(KERN_ERR "init_module: mod->syms out of bounds.\n");		goto err2;	}	if (mod->ndeps && !mod_bound(mod->deps, mod->ndeps, mod)) {		printk(KERN_ERR "init_module: mod->deps out of bounds.\n");		goto err2;	}	if (mod->init && !mod_bound(mod->init, 0, mod)) {		printk(KERN_ERR "init_module: mod->init out of bounds.\n");		goto err2;	}	if (mod->cleanup && !mod_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 + mod->size_of_struct)		  && ((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;	}	if (mod_member_present(mod, can_unload)	    && mod->can_unload && !mod_bound(mod->can_unload, 0, mod)) {		printk(KERN_ERR "init_module: mod->can_unload out of bounds.\n");		goto err2;	}	if (mod_member_present(mod, kallsyms_end)) {	    if (mod->kallsyms_end &&		(!mod_bound(mod->kallsyms_start, 0, mod) ||		 !mod_bound(mod->kallsyms_end, 0, mod))) {		printk(KERN_ERR "init_module: mod->kallsyms out of bounds.\n");		goto err2;	    }	    if (mod->kallsyms_start > mod->kallsyms_end) {		printk(KERN_ERR "init_module: mod->kallsyms invalid.\n");		goto err2;	    }	}	if (mod_member_present(mod, archdata_end)) {	    if (mod->archdata_end &&		(!mod_bound(mod->archdata_start, 0, mod) ||		 !mod_bound(mod->archdata_end, 0, mod))) {		printk(KERN_ERR "init_module: mod->archdata out of bounds.\n");		goto err2;	    }	    if (mod->archdata_start > mod->archdata_end) {		printk(KERN_ERR "init_module: mod->archdata invalid.\n");		goto err2;	    }	}	if (mod_member_present(mod, kernel_data) && mod->kernel_data) {	    printk(KERN_ERR "init_module: mod->kernel_data must be zero.\n");	    goto err2;	}	/* Check that the user isn't doing something silly with the name.  */	if ((n_namelen = get_mod_name(mod->name - (unsigned long)mod				      + (unsigned long)mod_user,				      &n_name)) < 0) {		printk(KERN_ERR "init_module: get_mod_name failure.\n");		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;	}	/* Ok, that's about all the sanity we can stomach; copy the rest.  */	if (copy_from_user((char *)mod+mod_user_size,			   (char *)mod_user+mod_user_size,			   mod->size-mod_user_size)) {		error = -EFAULT;		goto err3;	}	if (module_arch_init(mod))		goto err3;	/* On some machines it is necessary to do something here	   to make the I and D caches consistent.  */	flush_icache_range((unsigned long)mod, (unsigned long)mod + mod->size);	mod->next = mod_tmp.next;	mod->refs = NULL;	/* Sanity check the module's dependents */	for (i = 0, dep = mod->deps; i < mod->ndeps; ++i, ++dep) {		struct module *o, *d = dep->dep;		/* Make sure the indicated dependencies are really modules.  */		if (d == mod) {			printk(KERN_ERR "init_module: self-referential "					"dependency in mod->deps.\n");			goto err3;		}		/* Scan the current modules for this dependency */		for (o = module_list; o != &kernel_module && o != d; o = o->next)			;		if (o != d) {			printk(KERN_ERR "init_module: found dependency that is "				"(no longer?) a module.\n");			goto err3;		}	}	/* Update module references.  */	for (i = 0, dep = mod->deps; i < mod->ndeps; ++i, ++dep) {		struct module *d = dep->dep;		dep->ref = mod;		dep->next_ref = d->refs;		d->refs = dep;		/* Being referenced by a dependent module counts as a		   use as far as kmod is concerned.  */		d->flags |= MOD_USED_ONCE;	}	/* Free our temporary memory.  */	put_mod_name(n_name);	put_mod_name(name);	/* Initialize the module.  */	atomic_set(&mod->uc.usecount,1);	mod->flags |= MOD_INITIALIZING;	if (mod->init && (error = mod->init()) != 0) {		atomic_set(&mod->uc.usecount,0);		mod->flags &= ~MOD_INITIALIZING;		if (error > 0)	/* Buggy module */			error = -EBUSY;		goto err0;	}	atomic_dec(&mod->uc.usecount);	/* And set it running.  */	mod->flags = (mod->flags | MOD_RUNNING) & ~MOD_INITIALIZING;	error = 0;	goto err0;err3:	put_mod_name(n_name);err2:	*mod = mod_tmp;	strcpy((char *)mod->name, name_tmp);	/* We know there is room for this */err1:	put_mod_name(name);err0:	unlock_kernel();	kfree(name_tmp);	return error;}static spinlock_t unload_lock = SPIN_LOCK_UNLOCKED;int try_inc_mod_count(struct module *mod){	int res = 1;	if (mod) {		spin_lock(&unload_lock);		if (mod->flags & MOD_DELETED)			res = 0;		else			__MOD_INC_USE_COUNT(mod);		spin_unlock(&unload_lock);	}	return res;}asmlinkage longsys_delete_module(const char *name_user){	struct module *mod, *next;	char *name;	long error;	int something_changed;	if (!capable(CAP_SYS_MODULE))		return -EPERM;	lock_kernel();	if (name_user) {		if ((error = get_mod_name(name_user, &name)) < 0)			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)			goto out;		spin_lock(&unload_lock);		if (!__MOD_IN_USE(mod)) {			mod->flags |= MOD_DELETED;			spin_unlock(&unload_lock);			free_module(mod, 0);			error = 0;		} else {			spin_unlock(&unload_lock);		}		goto out;	}	/* Do automatic reaping */restart:	something_changed = 0;		for (mod = module_list; mod != &kernel_module; mod = next) {		next = mod->next;		spin_lock(&unload_lock);		if (mod->refs == NULL		    && (mod->flags & MOD_AUTOCLEAN)		    && (mod->flags & MOD_RUNNING)		    && !(mod->flags & MOD_DELETED)		    && (mod->flags & MOD_USED_ONCE)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -