diff options
Diffstat (limited to 'arch/powerpc/kernel/module.c')
| -rw-r--r-- | arch/powerpc/kernel/module.c | 116 | 
1 files changed, 116 insertions, 0 deletions
| diff --git a/arch/powerpc/kernel/module.c b/arch/powerpc/kernel/module.c new file mode 100644 index 000000000000..af07003573c4 --- /dev/null +++ b/arch/powerpc/kernel/module.c @@ -0,0 +1,116 @@ +/*  Kernel module help for powerpc. +    Copyright (C) 2001, 2003 Rusty Russell IBM Corporation. +    Copyright (C) 2008 Freescale Semiconductor, Inc. + +    This program is free software; you can redistribute it and/or modify +    it under the terms of the GNU General Public License as published by +    the Free Software Foundation; either version 2 of the License, or +    (at your option) any later version. + +    This program is distributed in the hope that it will be useful, +    but WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +    GNU General Public License for more details. + +    You should have received a copy of the GNU General Public License +    along with this program; if not, write to the Free Software +    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA +*/ +#include <linux/module.h> +#include <linux/elf.h> +#include <linux/moduleloader.h> +#include <linux/err.h> +#include <linux/vmalloc.h> +#include <linux/bug.h> +#include <asm/module.h> +#include <asm/uaccess.h> +#include <asm/firmware.h> +#include <linux/sort.h> + +#include "setup.h" + +LIST_HEAD(module_bug_list); + +void *module_alloc(unsigned long size) +{ +	if (size == 0) +		return NULL; + +	return vmalloc_exec(size); +} + +/* Free memory returned from module_alloc */ +void module_free(struct module *mod, void *module_region) +{ +	vfree(module_region); +	/* FIXME: If module_region == mod->init_region, trim exception +           table entries. */ +} + +static const Elf_Shdr *find_section(const Elf_Ehdr *hdr, +				    const Elf_Shdr *sechdrs, +				    const char *name) +{ +	char *secstrings; +	unsigned int i; + +	secstrings = (char *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; +	for (i = 1; i < hdr->e_shnum; i++) +		if (strcmp(secstrings+sechdrs[i].sh_name, name) == 0) +			return &sechdrs[i]; +	return NULL; +} + +int module_finalize(const Elf_Ehdr *hdr, +		const Elf_Shdr *sechdrs, struct module *me) +{ +	const Elf_Shdr *sect; +	int err; + +	err = module_bug_finalize(hdr, sechdrs, me); +	if (err) +		return err; + +	/* Apply feature fixups */ +	sect = find_section(hdr, sechdrs, "__ftr_fixup"); +	if (sect != NULL) +		do_feature_fixups(cur_cpu_spec->cpu_features, +				  (void *)sect->sh_addr, +				  (void *)sect->sh_addr + sect->sh_size); + +#ifdef CONFIG_PPC64 +	sect = find_section(hdr, sechdrs, "__fw_ftr_fixup"); +	if (sect != NULL) +		do_feature_fixups(powerpc_firmware_features, +				  (void *)sect->sh_addr, +				  (void *)sect->sh_addr + sect->sh_size); +#endif + +	sect = find_section(hdr, sechdrs, "__lwsync_fixup"); +	if (sect != NULL) +		do_lwsync_fixups(cur_cpu_spec->cpu_features, +				 (void *)sect->sh_addr, +				 (void *)sect->sh_addr + sect->sh_size); + +	return 0; +} + +void module_arch_cleanup(struct module *mod) +{ +	module_bug_cleanup(mod); +} + +struct bug_entry *module_find_bug(unsigned long bugaddr) +{ +	struct mod_arch_specific *mod; +	unsigned int i; +	struct bug_entry *bug; + +	list_for_each_entry(mod, &module_bug_list, bug_list) { +		bug = mod->bug_table; +		for (i = 0; i < mod->num_bugs; ++i, ++bug) +			if (bugaddr == bug->bug_addr) +				return bug; +	} +	return NULL; +} |