Name: New Module Loader Author: Rusty Russell Status: Experimental Section: Module Depends: Misc/rcu.patch.gz D: This patch is a rewrite of the kernel module code, simplifying it D: into an in-kernel module loader. Currently tested on i386, sparc64 D: and ppc. Note that this patch does not convert any modules, so D: modules cannot be unloaded, nor do modversions work, nor will their D: module parameters work: this is covered in the follow-up patches. D: To use it, you will need the trivial replacement module tools from D: http://www.kernel.org/pub/linux/people/rusty/module-init-tools-001.gz diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/arch/i386/config.in tmp/arch/i386/config.in --- linux-2.4.13-ac8/arch/i386/config.in Thu Nov 15 21:47:17 2001 +++ tmp/arch/i386/config.in Thu Nov 22 13:08:31 2001 @@ -22,7 +22,6 @@ comment 'Loadable module support' bool 'Enable loadable module support' CONFIG_MODULES if [ "$CONFIG_MODULES" = "y" ]; then - bool ' Set version information on all module symbols' CONFIG_MODVERSIONS bool ' Kernel module loader' CONFIG_KMOD fi endmenu @@ -418,6 +417,9 @@ bool ' Magic SysRq key' CONFIG_MAGIC_SYSRQ bool ' Spinlock debugging' CONFIG_DEBUG_SPINLOCK bool ' Verbose BUG() reporting (adds 70K)' CONFIG_DEBUG_BUGVERBOSE +fi +if [ "$CONFIG_MODULES" = "y" ]; then + bool 'Module unload support' CONFIG_MODULE_UNLOAD fi endmenu diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/arch/i386/kernel/Makefile tmp/arch/i386/kernel/Makefile --- linux-2.4.13-ac8/arch/i386/kernel/Makefile Thu Nov 15 21:47:17 2001 +++ tmp/arch/i386/kernel/Makefile Thu Nov 22 13:08:29 2001 @@ -41,5 +41,6 @@ obj-$(CONFIG_X86_LOCAL_APIC) += mpparse.o apic.o nmi.o obj-$(CONFIG_X86_IO_APIC) += io_apic.o acpitable.o obj-$(CONFIG_X86_VISWS_APIC) += visws_apic.o +obj-$(CONFIG_MODULES) += module.o include $(TOPDIR)/Rules.make diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/arch/i386/kernel/entry.S tmp/arch/i386/kernel/entry.S --- linux-2.4.13-ac8/arch/i386/kernel/entry.S Thu Nov 15 21:47:17 2001 +++ tmp/arch/i386/kernel/entry.S Thu Nov 22 13:08:31 2001 @@ -532,10 +532,10 @@ .long SYMBOL_NAME(sys_adjtimex) .long SYMBOL_NAME(sys_mprotect) /* 125 */ .long SYMBOL_NAME(sys_sigprocmask) - .long SYMBOL_NAME(sys_create_module) + .long SYMBOL_NAME(sys_ni_syscall) /* old create_module */ .long SYMBOL_NAME(sys_init_module) .long SYMBOL_NAME(sys_delete_module) - .long SYMBOL_NAME(sys_get_kernel_syms) /* 130 */ + .long SYMBOL_NAME(sys_ni_syscall) /* 130: old get_kernel_syms */ .long SYMBOL_NAME(sys_quotactl) .long SYMBOL_NAME(sys_getpgid) .long SYMBOL_NAME(sys_fchdir) @@ -572,7 +572,7 @@ .long SYMBOL_NAME(sys_setresuid16) .long SYMBOL_NAME(sys_getresuid16) /* 165 */ .long SYMBOL_NAME(sys_vm86) - .long SYMBOL_NAME(sys_query_module) + .long SYMBOL_NAME(sys_ni_syscall) /* old sys_query_module */ .long SYMBOL_NAME(sys_poll) .long SYMBOL_NAME(sys_nfsservctl) .long SYMBOL_NAME(sys_setresgid16) /* 170 */ diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/arch/i386/kernel/module.c tmp/arch/i386/kernel/module.c --- linux-2.4.13-ac8/arch/i386/kernel/module.c Thu Jan 1 10:00:00 1970 +++ tmp/arch/i386/kernel/module.c Thu Nov 22 13:08:31 2001 @@ -0,0 +1,118 @@ +/* Kernel module help for i386. + Copyright (C) 2001 Rusty Russell. + + 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 +#include +#include +#include +#include +#include + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(fmt , ...) +#endif + +static void *alloc_and_zero(unsigned long size) +{ + void *ret; + + ret = vmalloc(size); + if (!ret) ret = ERR_PTR(-ENOMEM); + else memset(ret, 0, size); + + return ret; +} + +/* Free memory returned from module_core_alloc/module_init_alloc */ +void module_free(void *module_region) +{ + vfree(module_region); +} + +void *module_core_alloc(const Elf32_Ehdr *hdr, + const Elf32_Shdr *sechdrs, + const char *secstrings, + struct module *module) +{ + return alloc_and_zero(module->core_size); +} + +void *module_init_alloc(const Elf32_Ehdr *hdr, + const Elf32_Shdr *sechdrs, + const char *secstrings, + struct module *module) +{ + return alloc_and_zero(module->init_size); +} + +int apply_relocate(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + unsigned int i; + Elf32_Rel *rel = (void *)sechdrs[relsec].sh_offset; + Elf32_Sym *sym; + uint32_t *location; + + DEBUGP("Applying relocate section %u to %u\n", relsec, + sechdrs[relsec].sh_info); + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { + /* This is where to make the change */ + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_offset + + rel[i].r_offset; + /* This is the symbol it is referring to */ + sym = (Elf32_Sym *)sechdrs[symindex].sh_offset + + ELF32_R_SYM(rel[i].r_info); + if (!sym->st_value) { + printk(KERN_WARNING "%s: Unknown symbol %s\n", + me->name, strtab + sym->st_name); + return -ENOENT; + } + + switch (ELF32_R_TYPE(rel[i].r_info)) { + case R_386_32: + /* We add the value into the location given */ + *location += sym->st_value; + break; + case R_386_PC32: + /* Add the value, subtract its postition */ + *location += sym->st_value - (uint32_t)location; + break; + default: + printk(KERN_ERR "module %s: Unknown relocation: %u\n", + me->name, ELF32_R_TYPE(rel[i].r_info)); + return -ENOEXEC; + } + } + return 0; +} + +int apply_relocate_add(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + printk(KERN_ERR "module %s: ADD RELOCATION unsupported\n", + me->name); + return -ENOEXEC; +} + diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/arch/i386/kernel/traps.c tmp/arch/i386/kernel/traps.c --- linux-2.4.13-ac8/arch/i386/kernel/traps.c Thu Nov 15 21:47:17 2001 +++ tmp/arch/i386/kernel/traps.c Thu Nov 22 13:08:31 2001 @@ -96,19 +96,20 @@ #ifdef CONFIG_MODULES -extern struct module *module_list; -extern struct module kernel_module; +extern struct list_head modules; static inline int kernel_text_address(unsigned long addr) { int retval = 0; + struct list_head *i; struct module *mod; if (addr >= (unsigned long) &_stext && addr <= (unsigned long) &_etext) return 1; - for (mod = module_list; mod != &kernel_module; mod = mod->next) { + list_for_each(i, &modules) { + mod = list_entry(i, struct module, list); /* mod_bound tests for addr being inside the vmalloc'ed * module area. Of course it'd be better to test only * for the .text subset... */ diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/arch/i386/mm/extable.c tmp/arch/i386/mm/extable.c --- linux-2.4.13-ac8/arch/i386/mm/extable.c Tue Sep 18 06:16:30 2001 +++ tmp/arch/i386/mm/extable.c Thu Nov 22 13:08:31 2001 @@ -44,15 +44,17 @@ return ret; #else unsigned long flags; - /* The kernel is the last "module" -- no need to treat it special. */ - struct module *mp; + struct list_head *i; + /* The kernel is the last "module" -- no need to treat it special. */ spin_lock_irqsave(&modlist_lock, flags); - for (mp = module_list; mp != NULL; mp = mp->next) { - if (mp->ex_table_start == NULL || !(mp->flags&(MOD_RUNNING|MOD_INITIALIZING))) + list_for_each(i, &extables) { + struct exception_table *ex + = list_entry(i, struct exception_table, list); + if (ex->num_entries == 0) continue; - ret = search_one_table(mp->ex_table_start, - mp->ex_table_end - 1, addr); + ret = search_one_table(ex->entry, + ex->entry + ex->num_entries - 1, addr); if (ret) break; } diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/arch/ppc/config.in tmp/arch/ppc/config.in --- linux-2.4.13-ac8/arch/ppc/config.in Thu Nov 15 21:47:17 2001 +++ tmp/arch/ppc/config.in Thu Nov 22 13:08:31 2001 @@ -20,7 +20,6 @@ comment 'Loadable module support' bool 'Enable loadable module support' CONFIG_MODULES if [ "$CONFIG_MODULES" = "y" ]; then - bool ' Set version information on all module symbols' CONFIG_MODVERSIONS bool ' Kernel module loader' CONFIG_KMOD fi endmenu @@ -387,4 +386,7 @@ bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ bool 'Include kgdb kernel debugger' CONFIG_KGDB bool 'Include xmon kernel debugger' CONFIG_XMON +if [ "$CONFIG_MODULES" = "y" ]; then + bool 'Module unload support' CONFIG_MODULE_UNLOAD +fi endmenu diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/arch/ppc/kernel/Makefile tmp/arch/ppc/kernel/Makefile --- linux-2.4.13-ac8/arch/ppc/kernel/Makefile Tue Aug 28 23:58:33 2001 +++ tmp/arch/ppc/kernel/Makefile Thu Nov 22 13:08:29 2001 @@ -36,7 +36,7 @@ ppc_htab.o semaphore.o syscalls.o \ align.o setup.o cputable.o obj-$(CONFIG_6xx) += l2cr.o -obj-$(CONFIG_MODULES) += ppc_ksyms.o +obj-$(CONFIG_MODULES) += module.o ppc_ksyms.o obj-$(CONFIG_POWER4) += xics.o obj-$(CONFIG_PCI) += pci.o pci-dma.o obj-$(CONFIG_KGDB) += ppc-stub.o diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/arch/ppc/kernel/misc.S tmp/arch/ppc/kernel/misc.S --- linux-2.4.13-ac8/arch/ppc/kernel/misc.S Tue Oct 9 03:39:18 2001 +++ tmp/arch/ppc/kernel/misc.S Thu Nov 22 13:08:31 2001 @@ -1040,10 +1040,10 @@ .long sys_adjtimex .long sys_mprotect /* 125 */ .long sys_sigprocmask - .long sys_create_module + .long sys_ni_syscall /* old sys_create_module */ .long sys_init_module .long sys_delete_module - .long sys_get_kernel_syms /* 130 */ + .long sys_ni_syscall /* old sys_get_kernel_syms */ /* 130 */ .long sys_quotactl .long sys_getpgid .long sys_fchdir @@ -1079,7 +1079,7 @@ .long sys_mremap .long sys_setresuid .long sys_getresuid /* 165 */ - .long sys_query_module + .long sys_ni_syscall /* old sys_query_module */ .long sys_poll .long sys_nfsservctl .long sys_setresgid diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/arch/ppc/kernel/module.c tmp/arch/ppc/kernel/module.c --- linux-2.4.13-ac8/arch/ppc/kernel/module.c Thu Jan 1 10:00:00 1970 +++ tmp/arch/ppc/kernel/module.c Thu Nov 22 13:08:31 2001 @@ -0,0 +1,259 @@ +/* Kernel module help for PPC. + Copyright (C) 2001 Rusty Russell. + + 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 +#include +#include +#include +#include +#include + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(fmt , ...) +#endif + +/* Count how many different relocations (different symbol, different + addend) */ +static unsigned int count_relocs(const Elf32_Rela *rela, unsigned int num) +{ + unsigned int i, j, ret = 0; + + /* Sure, this is order(n^2), but it's usually short, and not + time critical */ + for (i = 0; i < num; i++) { + for (j = 0; j < i; j++) { + /* If this addend appeared before, it's + already been counted */ + if (ELF32_R_SYM(rela[i].r_info) + == ELF32_R_SYM(rela[j].r_info) + && rela[i].r_addend == rela[j].r_addend) + break; + } + if (j == i) ret++; + } + return ret; +} + +static void *alloc_and_zero(unsigned long size) +{ + void *ret; + + ret = vmalloc(size); + if (!ret) ret = ERR_PTR(-ENOMEM); + else memset(ret, 0, size); + + return ret; +} + +/* Free memory returned from module_core_alloc/module_init_alloc */ +void module_free(void *module_region) +{ + vfree(module_region); +} + +/* Get the potential trampolines size required of the init and + non-init sections */ +static unsigned long get_plt_size(const Elf32_Ehdr *hdr, + const Elf32_Shdr *sechdrs, + const char *secstrings, + int is_init) +{ + unsigned long ret = 0; + unsigned i; + + /* Everything marked ALLOC (this includes the exported + symbols) */ + for (i = 1; i < hdr->e_shnum; i++) { + /* If it's called *.init*, and we're not init, we're + not interested */ + if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != 0) + != is_init) + continue; + + if (sechdrs[i].sh_type == SHT_RELA) { + DEBUGP("Found relocations in section %u\n", i); + DEBUGP("Ptr: %p. Number: %u\n", + (void *)hdr + sechdrs[i].sh_offset, + sechdrs[i].sh_size / sizeof(Elf32_Rela)); + ret += count_relocs((void *)hdr + + sechdrs[i].sh_offset, + sechdrs[i].sh_size + / sizeof(Elf32_Rela)) + * sizeof(struct ppc_plt_entry); + } + } + + return ret; +} + +void *module_core_alloc(const Elf32_Ehdr *hdr, + const Elf32_Shdr *sechdrs, + const char *secstrings, + struct module *module) +{ + module->arch.core_plt_offset = module->core_size; + + /* Realloc core size (this whole are needs to have the icache + flushed, including the PLTs on the end) */ + module->core_size += get_plt_size(hdr, sechdrs, secstrings, 0); + return alloc_and_zero(module->core_size); +} + +void *module_init_alloc(const Elf32_Ehdr *hdr, + const Elf32_Shdr *sechdrs, + const char *secstrings, + struct module *module) +{ + module->arch.init_plt_offset = module->init_size; + module->init_size += get_plt_size(hdr, sechdrs, secstrings, 1); + return alloc_and_zero(module->init_size); +} + +int apply_relocate(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *module) +{ + printk(KERN_ERR "%s: Non-ADD RELOCATION unsupported\n", + module->name); + return -ENOEXEC; +} + +static inline int entry_matches(struct ppc_plt_entry *entry, Elf32_Addr val) +{ + if (entry->jump[0] == 0x3d600000 + ((val + 0x8000) >> 16) + && entry->jump[1] == 0x396b0000 + (val & 0xffff)) + return 1; + return 0; +} + +/* Set up a trampoline in the PLT to bounce us to the distant function */ +static uint32_t do_plt_call(void *location, Elf32_Addr val, struct module *mod) +{ + struct ppc_plt_entry *entry; + + DEBUGP("Doing plt for %u\n", (unsigned int)location); + /* Init, or core PLT? */ + if (location >= mod->module_core + && location < mod->module_core + mod->arch.core_plt_offset) + entry = mod->module_core + mod->arch.core_plt_offset; + else + entry = mod->module_init + mod->arch.init_plt_offset; + + /* Find this entry, or if that fails, the next avail. entry */ + while (entry->jump[0]) { + if (entry_matches(entry, val)) return (uint32_t)entry; + entry++; + } + + /* Stolen from Paul Mackerras as well... */ + entry->jump[0] = 0x3d600000+((val+0x8000)>>16); /* lis r11,sym@ha */ + entry->jump[1] = 0x396b0000 + (val&0xffff); /* addi r11,r11,sym@l*/ + entry->jump[2] = 0x7d6903a6; /* mtctr r11 */ + entry->jump[3] = 0x4e800420; /* bctr */ + + return (uint32_t)entry; +} + +int apply_relocate_add(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *module) +{ + unsigned int i; + Elf32_Rela *rela = (void *)sechdrs[relsec].sh_offset; + Elf32_Sym *sym; + uint32_t *location; + uint32_t value; + + DEBUGP("Applying ADD relocate section %u to %u\n", relsec, + sechdrs[relsec].sh_info); + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) { + /* This is where to make the change */ + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_offset + + rela[i].r_offset; + /* This is the symbol it is referring to */ + sym = (Elf32_Sym *)sechdrs[symindex].sh_offset + + ELF32_R_SYM(rela[i].r_info); + if (!sym->st_value) { + printk(KERN_WARNING "%s: Unknown symbol %s\n", + module->name, strtab + sym->st_name); + return -ENOENT; + } + /* `Everything is relative'. */ + value = sym->st_value + rela[i].r_addend; + + switch (ELF32_R_TYPE(rela[i].r_info)) { + case R_PPC_ADDR32: + /* Simply set it */ + *(uint32_t *)location = value; + break; + + case R_PPC_ADDR16_LO: + /* Low half of the symbol */ + *(uint16_t *)location = value; + break; + + case R_PPC_ADDR16_HA: + /* Sign-adjusted lower 16 bits: PPC ELF ABI says: + (((x >> 16) + ((x & 0x8000) ? 1 : 0))) & 0xFFFF. + This is the same, only sane. + */ + *(uint16_t *)location = (value + 0x8000) >> 16; + break; + + case R_PPC_REL24: + if ((int)(value - (uint32_t)location) < -0x02000000 + || (int)(value - (uint32_t)location) >= 0x02000000) + value = do_plt_call(location, value, module); + + /* Only replace bits 2 through 26 */ + DEBUGP("REL24 value = %08X. location = %08X\n", + value, (uint32_t)location); + DEBUGP("Location before: %08X.\n", + *(uint32_t *)location); + *(uint32_t *)location + = (*(uint32_t *)location & ~0x03fffffc) + | ((value - (uint32_t)location) + & 0x03fffffc); + DEBUGP("Location after: %08X.\n", + *(uint32_t *)location); + DEBUGP("ie. jump to %08X+%08X = %08X\n", + *(uint32_t *)location & 0x03fffffc, + (uint32_t)location, + (*(uint32_t *)location & 0x03fffffc) + + (uint32_t)location); + break; + + case R_PPC_REL32: + /* 32-bit relative jump. */ + *(uint32_t *)location = value - (uint32_t)location; + break; + + default: + printk("%s: unknown ADD relocation: %u\n", + module->name, + ELF32_R_TYPE(rela[i].r_info)); + return -ENOEXEC; + } + } + return 0; +} diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/arch/ppc/mm/extable.c tmp/arch/ppc/mm/extable.c --- linux-2.4.13-ac8/arch/ppc/mm/extable.c Tue May 22 10:04:47 2001 +++ tmp/arch/ppc/mm/extable.c Thu Nov 22 13:08:31 2001 @@ -80,16 +80,22 @@ ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr); if (ret) return ret; #else - /* The kernel is the last "module" -- no need to treat it special. */ - struct module *mp; - for (mp = module_list; mp != NULL; mp = mp->next) { - if (mp->ex_table_start == NULL) + unsigned long flags; + struct list_head *i; + + /* The kernel is the last "module" -- no need to treat it special. */ + spin_lock_irqsave(&modlist_lock, flags); + list_for_each(i, &extables) { + struct exception_table *ex + = list_entry(i, struct exception_table, list); + if (ex->num_entries == 0) continue; - ret = search_one_table(mp->ex_table_start, - mp->ex_table_end - 1, addr); + ret = search_one_table(ex->entry, + ex->entry + ex->num_entries - 1, addr); if (ret) - return ret; + break; } + spin_unlock_irqrestore(&modlist_lock, flags); #endif return 0; diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/arch/sparc/config.in tmp/arch/sparc/config.in --- linux-2.4.13-ac8/arch/sparc/config.in Thu Nov 15 21:47:18 2001 +++ tmp/arch/sparc/config.in Thu Nov 22 13:08:31 2001 @@ -267,4 +267,7 @@ comment 'Kernel hacking' bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ +if [ "$CONFIG_MODULES" = "y" ]; then + bool 'Module unload support' CONFIG_MODULE_UNLOAD +fi endmenu diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/arch/sparc64/config.in tmp/arch/sparc64/config.in --- linux-2.4.13-ac8/arch/sparc64/config.in Thu Nov 15 21:47:18 2001 +++ tmp/arch/sparc64/config.in Thu Nov 22 13:08:31 2001 @@ -13,7 +13,6 @@ comment 'Loadable module support' bool 'Enable loadable module support' CONFIG_MODULES if [ "$CONFIG_MODULES" = "y" ]; then - bool ' Set version information on all symbols for modules' CONFIG_MODVERSIONS bool ' Kernel module loader' CONFIG_KMOD fi endmenu @@ -290,4 +289,7 @@ bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ #bool 'ECache flush trap support at ta 0x72' CONFIG_EC_FLUSH_TRAP +if [ "$CONFIG_MODULES" = "y" ]; then + bool 'Module unload support' CONFIG_MODULE_UNLOAD +fi endmenu diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/arch/sparc64/kernel/module.c tmp/arch/sparc64/kernel/module.c --- linux-2.4.13-ac8/arch/sparc64/kernel/module.c Thu Jan 1 10:00:00 1970 +++ tmp/arch/sparc64/kernel/module.c Thu Nov 22 13:08:31 2001 @@ -0,0 +1,217 @@ +/* Kernel module help for Sparc 64. + Copyright (C) 2001 Rusty Russell. + + Sparc64 module region code stolen from old arch/sparc64/mm/modutil.c, + Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + Based upon code written by Linus Torvalds and others. + + 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#define MODULES_VADDR 0x0000000001000000ULL /* Where to map modules */ +#define MODULES_LEN 0x000000007f000000ULL +#define MODULES_END 0x0000000080000000ULL + +static struct vm_struct * modvmlist = NULL; + +void module_free(void *module_region) +{ + struct vm_struct **p, *tmp; + + if (!module_region) + return; + if ((PAGE_SIZE-1) & (unsigned long)module_region) { + printk("Trying to free module with bad address (%p)\n", + module_region); + return; + } + for (p = &modvmlist ; (tmp = *p) ; p = &tmp->next) { + if (tmp->addr == module_region) { + *p = tmp->next; + vmfree_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size); + kfree(tmp); + return; + } + } + printk("Trying to free nonexistent module vm area (%p)\n", + module_region); +} + +/* Special module allocator: since sparc64 uses the "medlow" arg to + GCC, functions are assumed to be in the low 32 bits of memory. + Hence we need to allocate space for modules within the bottom 4GB, + even if we have more memory, otherwise we'd need trampolines ala + PPC --RR */ +void *module_map(unsigned long size) +{ + void * addr; + struct vm_struct **p, *tmp, *area; + + size = PAGE_ALIGN(size); + if (!size || size > MODULES_LEN) return NULL; + + addr = (void *) MODULES_VADDR; + for (p = &modvmlist; (tmp = *p) ; p = &tmp->next) { + if (size + (unsigned long) addr < (unsigned long) tmp->addr) + break; + addr = (void *) (tmp->size + (unsigned long) tmp->addr); + } + if ((unsigned long) addr + size >= MODULES_END) return NULL; + + area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL); + if (!area) return NULL; + area->size = size + PAGE_SIZE; + area->addr = addr; + area->next = *p; + *p = area; + + if (vmalloc_area_pages(VMALLOC_VMADDR(addr), size, GFP_KERNEL, PAGE_KERNEL)) { + module_free(addr); + return NULL; + } + return addr; +} + +static void *alloc_and_zero(unsigned long size) +{ + void *ret; + + ret = module_map(size); + if (!ret) ret = ERR_PTR(-ENOMEM); + else memset(ret, 0, size); + + return ret; +} + +void *module_core_alloc(const Elf64_Ehdr *hdr, + const Elf64_Shdr *sechdrs, + const char *secstrings, + struct module *module) +{ + return alloc_and_zero(module->core_size); +} + +void *module_init_alloc(const Elf64_Ehdr *hdr, + const Elf64_Shdr *sechdrs, + const char *secstrings, + struct module *module) +{ + return alloc_and_zero(module->init_size); +} + +int apply_relocate(Elf64_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *module) +{ + printk(KERN_ERR "%s: Non-ADD RELOCATION unsupported\n", + module->name); + return -ENOEXEC; +} + +int apply_relocate_add(Elf64_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + unsigned int i; + Elf64_Rela *rela = (void *)sechdrs[relsec].sh_offset; + Elf64_Sym *sym; + uint64_t *location; + uint64_t value; + + printk("Applying ADD relocate section %u to %u\n", relsec, + sechdrs[relsec].sh_info); + + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) { + /* This is where to make the change */ + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_offset + + rela[i].r_offset; + /* This is the symbol it is referring to */ + sym = (Elf64_Sym *)sechdrs[symindex].sh_offset + + ELF64_R_SYM(rela[i].r_info); + if (!sym->st_value) { + printk("%s: Unknown symbol %s\n", + me->name, strtab + sym->st_name); + return -ENOENT; + } + /* `Everything is relative'. */ + value = sym->st_value + rela[i].r_addend; + + switch (ELF64_R_TYPE(rela[i].r_info)) { + case R_SPARC_32: + ((unsigned char *) location)[0] = value >> 24; + ((unsigned char *) location)[1] = value >> 16; + ((unsigned char *) location)[2] = value >> 8; + ((unsigned char *) location)[3] = value; + break; + + case R_SPARC_WDISP30: + *((unsigned int *)location) + = (*((unsigned int *)location) & ~0x3fffffff) + | (((value - (uint64_t)location)>>2 + & 0x3fffffff)); + break; + + case R_SPARC_WDISP22: + *((unsigned int *)location) + = (*((unsigned int *)location) & ~0x3fffff) + | (((value - (uint64_t)location)>>2 + & 0x3fffff)); + break; + + case R_SPARC_HI22: + *((unsigned int *)location) + = (*((unsigned int *)location) & ~0x3fffff) + | (value >> 10); + break; + + case R_SPARC_LO10: + *((unsigned int *)location) + = (*((unsigned int *)location) & ~0x3ff) + | (value & 0x3ff); + break; + + case R_SPARC_64: + ((unsigned char *) location) [0] = value >> 56; + ((unsigned char *) location) [1] = value >> 48; + ((unsigned char *) location) [2] = value >> 40; + ((unsigned char *) location) [3] = value >> 32; + ((unsigned char *) location) [4] = value >> 24; + ((unsigned char *) location) [5] = value >> 16; + ((unsigned char *) location) [6] = value >> 8; + ((unsigned char *) location) [7] = value; + break; + + default: + printk("Unknown ADD relocation: %lu\n", + (unsigned long)ELF64_R_TYPE(rela[i].r_info)); + return -ENOEXEC; + } + } + return 0; +} diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/arch/sparc64/kernel/sys_sparc32.c tmp/arch/sparc64/kernel/sys_sparc32.c --- linux-2.4.13-ac8/arch/sparc64/kernel/sys_sparc32.c Thu Nov 15 21:47:18 2001 +++ tmp/arch/sparc64/kernel/sys_sparc32.c Thu Nov 22 13:08:31 2001 @@ -3081,418 +3081,41 @@ #ifdef CONFIG_MODULES -extern asmlinkage unsigned long sys_create_module(const char *name_user, size_t size); - -asmlinkage unsigned long sys32_create_module(const char *name_user, __kernel_size_t32 size) -{ - return sys_create_module(name_user, (size_t)size); -} - -extern asmlinkage int sys_init_module(const char *name_user, struct module *mod_user); - -/* Hey, when you're trying to init module, take time and prepare us a nice 64bit - * module structure, even if from 32bit modutils... Why to pollute kernel... :)) - */ -asmlinkage int sys32_init_module(const char *name_user, struct module *mod_user) -{ - return sys_init_module(name_user, mod_user); -} - -extern asmlinkage int sys_delete_module(const char *name_user); - -asmlinkage int sys32_delete_module(const char *name_user) -{ - return sys_delete_module(name_user); -} - -struct module_info32 { - u32 addr; - u32 size; - u32 flags; - s32 usecount; -}; - -/* Query various bits about modules. */ - -static inline long -get_mod_name(const char *user_name, char **buf) -{ - unsigned long page; - long retval; - - if ((unsigned long)user_name >= TASK_SIZE - && !segment_eq(get_fs (), KERNEL_DS)) - 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); -} - -static __inline__ struct module *find_module(const char *name) -{ - struct module *mod; - - for (mod = module_list; mod ; mod = mod->next) { - if (mod->flags & MOD_DELETED) - continue; - if (!strcmp(mod->name, name)) - break; - } - - return mod; -} - -static int -qm_modules(char *buf, size_t bufsize, __kernel_size_t32 *ret) -{ - struct module *mod; - size_t nmod, space, len; - - nmod = space = 0; - - for (mod = module_list; mod->next != NULL; 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; - } - - if (put_user(nmod, ret)) - return -EFAULT; - else - return 0; - -calc_space_needed: - space += len; - while ((mod = mod->next)->next != NULL) - space += strlen(mod->name)+1; - - if (put_user(space, ret)) - return -EFAULT; - else - return -ENOSPC; -} - -static int -qm_deps(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) -{ - size_t i, space, len; - - if (mod->next == NULL) - return -EINVAL; - if (!MOD_CAN_QUERY(mod)) - return put_user(0, ret); - - 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 put_user(i, ret); - -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 int -qm_refs(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) -{ - size_t nrefs, space, len; - struct module_ref *ref; - - if (mod->next == NULL) - return -EINVAL; - if (!MOD_CAN_QUERY(mod)) - 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; -} - -static inline int -qm_symbols(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) -{ - size_t i, space, len; - struct module_symbol *s; - char *strings; - unsigned *vals; - - if (!MOD_CAN_QUERY(mod)) - if (put_user(0, ret)) - return -EFAULT; - else - return 0; - - space = mod->nsyms * 2*sizeof(u32); - - 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 *)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; - } - - 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 inline int -qm_info(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) -{ - int error = 0; - - if (mod->next == NULL) - return -EINVAL; - - if (sizeof(struct module_info32) <= bufsize) { - struct module_info32 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 : atomic_read(&mod->uc.usecount)); - - if (copy_to_user(buf, &info, sizeof(struct module_info32))) - return -EFAULT; - } else - error = -ENOSPC; - - if (put_user(sizeof(struct module_info32), ret)) - return -EFAULT; - - return error; -} +extern asmlinkage int sys_init_module(const char *uname, + void *umod, + unsigned long len, + const char *uargs); -asmlinkage int sys32_query_module(char *name_user, int which, char *buf, __kernel_size_t32 bufsize, u32 ret) +asmlinkage int sys32_init_module(const char *uname, + void *umod, + unsigned int len, + const char *uargs) { - struct module *mod; - int err; - - lock_kernel(); - if (name_user == 0) { - /* This finds "kernel_module" which is not exported. */ - for(mod = module_list; mod->next != NULL; mod = mod->next) - ; - } else { - long namelen; - char *name; - - if ((namelen = get_mod_name(name_user, &name)) < 0) { - err = namelen; - goto out; - } - err = -ENOENT; - if (namelen == 0) { - /* This finds "kernel_module" which is not exported. */ - for(mod = module_list; mod->next != NULL; mod = mod->next) - ; - } else if ((mod = find_module(name)) == NULL) { - put_mod_name(name); - goto out; - } - put_mod_name(name); - } - - switch (which) - { - case 0: - err = 0; - break; - case QM_MODULES: - err = qm_modules(buf, bufsize, (__kernel_size_t32 *)AA(ret)); - break; - case QM_DEPS: - err = qm_deps(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); - break; - case QM_REFS: - err = qm_refs(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); - break; - case QM_SYMBOLS: - err = qm_symbols(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); - break; - case QM_INFO: - err = qm_info(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); - break; - default: - err = -EINVAL; - break; - } -out: - unlock_kernel(); - return err; + return sys_init_module(uname, umod, len, uargs); } -struct kernel_sym32 { - u32 value; - char name[60]; -}; - -extern asmlinkage int sys_get_kernel_syms(struct kernel_sym *table); +extern asmlinkage long sys_delete_module(const char *name_user, + unsigned int flags); -asmlinkage int sys32_get_kernel_syms(struct kernel_sym32 *table) +asmlinkage int sys32_delete_module(const char *name_user, unsigned int flags) { - int len, i; - struct kernel_sym *tbl; - mm_segment_t old_fs; - - len = sys_get_kernel_syms(NULL); - if (!table) return len; - tbl = kmalloc (len * sizeof (struct kernel_sym), GFP_KERNEL); - if (!tbl) return -ENOMEM; - old_fs = get_fs(); - set_fs (KERNEL_DS); - sys_get_kernel_syms(tbl); - set_fs (old_fs); - for (i = 0; i < len; i++, table++) { - if (put_user (tbl[i].value, &table->value) || - copy_to_user (table->name, tbl[i].name, 60)) - break; - } - kfree (tbl); - return i; + return sys_delete_module(name_user, flags); } #else /* CONFIG_MODULES */ -asmlinkage unsigned long -sys32_create_module(const char *name_user, size_t size) -{ - return -ENOSYS; -} - -asmlinkage int -sys32_init_module(const char *name_user, struct module *mod_user) -{ - return -ENOSYS; -} - -asmlinkage int -sys32_delete_module(const char *name_user) -{ - return -ENOSYS; -} - -asmlinkage int -sys32_query_module(const char *name_user, int which, char *buf, size_t bufsize, - size_t *ret) +asmlinkage int sys32_init_module(const char *uname, + void *umod, + unsigned int len, + const char *uargs) { - /* Let the program know about the new interface. Not that - it'll do them much good. */ - if (which == 0) - return 0; - return -ENOSYS; } -asmlinkage int -sys32_get_kernel_syms(struct kernel_sym *table) +asmlinkage int sys32_delete_module(const char *name_user, unsigned int flags) { return -ENOSYS; } - #endif /* CONFIG_MODULES */ #if defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE) @@ -4001,12 +3624,6 @@ return sys_pwrite(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); } -extern asmlinkage ssize_t sys_readahead(int fd, loff_t offset, size_t count); - -asmlinkage ssize_t32 sys32_readahead(int fd, u32 offhi, u32 offlo, s32 count) -{ - return sys_readahead(fd, ((loff_t)AA(offhi) << 32) | AA(offlo), count); -} extern asmlinkage ssize_t sys_sendfile(int out_fd, int in_fd, off_t *offset, size_t count); diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/arch/sparc64/kernel/systbls.S tmp/arch/sparc64/kernel/systbls.S --- linux-2.4.13-ac8/arch/sparc64/kernel/systbls.S Mon Oct 22 03:36:54 2001 +++ tmp/arch/sparc64/kernel/systbls.S Thu Nov 22 13:08:31 2001 @@ -55,15 +55,15 @@ .word sys32_quotactl, sys_nis_syscall, sys32_mount, sys_ustat, sys_nis_syscall /*170*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_getdents .word sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall -/*180*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_sigpending, sys32_query_module +/*180*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_sigpending, sys_nis_syscall .word sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sparc64_newuname /*190*/ .word sys32_init_module, sparc64_personality, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall .word sys_nis_syscall, sys_nis_syscall, sys_getppid, sys32_sigaction, sys_sgetmask /*200*/ .word sys_ssetmask, sys_sigsuspend, sys32_newlstat, sys_uselib, old32_readdir - .word sys32_readahead, sys32_socketcall, sys_syslog, sys_nis_syscall, sys_nis_syscall + .word sys_nis_syscall, sys32_socketcall, sys_syslog, sys_nis_syscall, sys_nis_syscall /*210*/ .word sys_nis_syscall, sys_nis_syscall, sys_waitpid, sys_swapoff, sys32_sysinfo .word sys32_ipc, sys32_sigreturn, sys_clone, sys_nis_syscall, sys32_adjtimex -/*220*/ .word sys32_sigprocmask, sys32_create_module, sys32_delete_module, sys32_get_kernel_syms, sys_getpgid +/*220*/ .word sys32_sigprocmask, sys_nis_syscall, sys32_delete_module, sys_nis_syscall, sys_getpgid .word sys32_bdflush, sys32_sysfs, sys_nis_syscall, sys32_setfsuid16, sys32_setfsgid16 /*230*/ .word sys32_select, sys_time, sys_nis_syscall, sys_stime, sys_nis_syscall .word sys_nis_syscall, sys_llseek, sys_mlock, sys_munlock, sys_mlockall @@ -114,15 +114,15 @@ .word sys_quotactl, sys_nis_syscall, sys_mount, sys_ustat, sys_nis_syscall /*170*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_getdents .word sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall -/*180*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_query_module +/*180*/ .word sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall .word sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sparc64_newuname /*190*/ .word sys_init_module, sparc64_personality, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall .word sys_nis_syscall, sys_nis_syscall, sys_getppid, sys_nis_syscall, sys_sgetmask /*200*/ .word sys_ssetmask, sys_nis_syscall, sys_newlstat, sys_uselib, sys_nis_syscall - .word sys_readahead, sys_socketcall, sys_syslog, sys_nis_syscall, sys_nis_syscall + .word sys_nis_syscall, sys_socketcall, sys_syslog, sys_nis_syscall, sys_nis_syscall /*210*/ .word sys_nis_syscall, sys_nis_syscall, sys_waitpid, sys_swapoff, sys_sysinfo .word sys_ipc, sys_nis_syscall, sys_clone, sys_nis_syscall, sys_adjtimex -/*220*/ .word sys_nis_syscall, sys_create_module, sys_delete_module, sys_get_kernel_syms, sys_getpgid +/*220*/ .word sys_nis_syscall, sys_nis_syscall, sys_delete_module, sys_nis_syscall, sys_getpgid .word sys_bdflush, sys_sysfs, sys_nis_syscall, sys_setfsuid, sys_setfsgid /*230*/ .word sys_select, sys_nis_syscall, sys_nis_syscall, sys_stime, sys_nis_syscall .word sys_nis_syscall, sys_llseek, sys_mlock, sys_munlock, sys_mlockall diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/arch/sparc64/mm/extable.c tmp/arch/sparc64/mm/extable.c --- linux-2.4.13-ac8/arch/sparc64/mm/extable.c Wed Aug 8 01:30:50 2001 +++ tmp/arch/sparc64/mm/extable.c Thu Nov 22 13:08:31 2001 @@ -56,15 +56,18 @@ __stop___ex_table-1, addr, g2); return ret; #else - /* The kernel is the last "module" -- no need to treat it special. */ - struct module *mp; + struct list_head *i; + /* The kernel is the last "module" -- no need to treat it special. */ spin_lock_irqsave(&modlist_lock, flags); - for (mp = module_list; mp != NULL; mp = mp->next) { - if (mp->ex_table_start == NULL || !(mp->flags & (MOD_RUNNING | MOD_INITIALIZING))) + list_for_each(i, &extables) { + struct exception_table *ex + = list_entry(i, struct exception_table, list); + if (ex->num_entries == 0) continue; - ret = search_one_table(mp->ex_table_start, - mp->ex_table_end-1, addr, g2); + ret = search_one_table(ex->entry, + ex->entry + ex->num_entries - 1, + addr, g2); if (ret) break; } diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/arch/sparc64/mm/modutil.c tmp/arch/sparc64/mm/modutil.c --- linux-2.4.13-ac8/arch/sparc64/mm/modutil.c Wed Aug 29 00:09:44 2001 +++ tmp/arch/sparc64/mm/modutil.c Thu Jan 1 10:00:00 1970 @@ -1,69 +0,0 @@ -/* $Id: modutil.c,v 1.9 2001/08/14 22:10:56 davem Exp $ - * arch/sparc64/mm/modutil.c - * - * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) - * Based upon code written by Linus Torvalds and others. - */ - -#include -#include - -#include -#include - -#define MODULES_VADDR 0x0000000001000000ULL /* Where to map modules */ -#define MODULES_LEN 0x000000007f000000ULL -#define MODULES_END 0x0000000080000000ULL - -static struct vm_struct * modvmlist = NULL; - -void module_unmap (void * addr) -{ - struct vm_struct **p, *tmp; - - if (!addr) - return; - if ((PAGE_SIZE-1) & (unsigned long) addr) { - printk("Trying to unmap module with bad address (%p)\n", addr); - return; - } - for (p = &modvmlist ; (tmp = *p) ; p = &tmp->next) { - if (tmp->addr == addr) { - *p = tmp->next; - vmfree_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size); - kfree(tmp); - return; - } - } - printk("Trying to unmap nonexistent module vm area (%p)\n", addr); -} - -void * module_map (unsigned long size) -{ - void * addr; - struct vm_struct **p, *tmp, *area; - - size = PAGE_ALIGN(size); - if (!size || size > MODULES_LEN) return NULL; - - addr = (void *) MODULES_VADDR; - for (p = &modvmlist; (tmp = *p) ; p = &tmp->next) { - if (size + (unsigned long) addr < (unsigned long) tmp->addr) - break; - addr = (void *) (tmp->size + (unsigned long) tmp->addr); - } - if ((unsigned long) addr + size >= MODULES_END) return NULL; - - area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL); - if (!area) return NULL; - area->size = size + PAGE_SIZE; - area->addr = addr; - area->next = *p; - *p = area; - - if (vmalloc_area_pages(VMALLOC_VMADDR(addr), size, GFP_KERNEL, PAGE_KERNEL)) { - module_unmap(addr); - return NULL; - } - return addr; -} diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/drivers/char/ftape/compressor/zftape-compress.c tmp/drivers/char/ftape/compressor/zftape-compress.c --- linux-2.4.13-ac8/drivers/char/ftape/compressor/zftape-compress.c Fri Sep 14 08:21:32 2001 +++ tmp/drivers/char/ftape/compressor/zftape-compress.c Thu Nov 22 13:08:31 2001 @@ -1266,13 +1266,6 @@ "Compression routines for zftape. Uses the lzrw3 algorithm by Ross Williams"); MODULE_LICENSE("GPL"); -#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18) -static int can_unload(void) -{ - return keep_module_locked ? -EBUSY : 0; -} -#endif - /* Called by modules package when installing the driver */ int init_module(void) @@ -1282,10 +1275,12 @@ #if LINUX_VERSION_CODE < KERNEL_VER(2,1,18) register_symtab(0); /* remove global ftape symbols */ #else +#if 0 /* FIXME --RR */ if (!mod_member_present(&__this_module, can_unload)) return -EBUSY; __this_module.can_unload = can_unload; EXPORT_NO_SYMBOLS; +#endif #endif result = zft_compressor_init(); keep_module_locked = 0; diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/drivers/char/ftape/zftape/zftape-init.c tmp/drivers/char/ftape/zftape/zftape-init.c --- linux-2.4.13-ac8/drivers/char/ftape/zftape/zftape-init.c Fri Sep 14 08:21:32 2001 +++ tmp/drivers/char/ftape/zftape/zftape-init.c Thu Nov 22 13:08:31 2001 @@ -399,20 +399,26 @@ #ifdef MODULE + +#if 0 /* FIXME --RR */ /* Called by modules package before trying to unload the module */ static int can_unload(void) { return (GET_USE_COUNT(THIS_MODULE)||zft_dirty()||busy_flag)?-EBUSY:0; } +#endif + /* Called by modules package when installing the driver */ int init_module(void) { +#if 0 /*FIXME --RR*/ if (!mod_member_present(&__this_module, can_unload)) { return -EBUSY; } __this_module.can_unload = can_unload; +#endif return zft_init(); } @@ -450,3 +456,5 @@ } #endif /* MODULE */ + +module_init(init_module); diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/fs/proc/proc_misc.c tmp/fs/proc/proc_misc.c --- linux-2.4.13-ac8/fs/proc/proc_misc.c Thu Nov 15 21:47:34 2001 +++ tmp/fs/proc/proc_misc.c Thu Nov 22 13:10:24 2001 @@ -294,18 +294,6 @@ int len = get_module_list(page); return proc_calc_metrics(page, start, off, count, eof, len); } - -extern struct seq_operations ksyms_op; -static int ksyms_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &ksyms_op); -} -static struct file_operations proc_ksyms_operations = { - open: ksyms_open, - read: seq_read, - llseek: seq_lseek, - release: seq_release, -}; #endif static int kstat_read_proc(char *page, char **start, off_t off, @@ -619,9 +607,6 @@ entry = create_proc_entry("mounts", 0, NULL); if (entry) entry->proc_fops = &proc_mounts_operations; - entry = create_proc_entry("ksyms", 0, NULL); - if (entry) - entry->proc_fops = &proc_ksyms_operations; proc_root_kcore = create_proc_entry("kcore", S_IRUSR, NULL); if (proc_root_kcore) { proc_root_kcore->proc_fops = &proc_kcore_operations; diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/include/asm-i386/module.h tmp/include/asm-i386/module.h --- linux-2.4.13-ac8/include/asm-i386/module.h Fri Sep 14 09:33:03 2001 +++ tmp/include/asm-i386/module.h Thu Nov 22 13:08:31 2001 @@ -1,12 +1,11 @@ #ifndef _ASM_I386_MODULE_H #define _ASM_I386_MODULE_H -/* - * This file contains the i386 architecture specific module code. - */ - -#define module_map(x) vmalloc(x) -#define module_unmap(x) vfree(x) -#define module_arch_init(x) (0) -#define arch_init_modules(x) do { } while (0) +/* x86 is simple */ +struct mod_arch_specific +{ +}; +#define Elf_Shdr Elf32_Shdr +#define Elf_Sym Elf32_Sym +#define Elf_Ehdr Elf32_Ehdr #endif /* _ASM_I386_MODULE_H */ diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/include/asm-ppc/module.h tmp/include/asm-ppc/module.h --- linux-2.4.13-ac8/include/asm-ppc/module.h Fri Sep 14 09:33:03 2001 +++ tmp/include/asm-ppc/module.h Thu Nov 22 13:08:31 2001 @@ -1,15 +1,34 @@ -/* - * BK Id: SCCS/s.module.h 1.5 05/17/01 18:14:25 cort - */ #ifndef _ASM_PPC_MODULE_H #define _ASM_PPC_MODULE_H -/* - * This file contains the PPC architecture specific module code. - */ +/* Module stuff for PPC. (C) 2001 Rusty Russell */ -#define module_map(x) vmalloc(x) -#define module_unmap(x) vfree(x) -#define module_arch_init(x) (0) +/* Thanks to Paul M for explaining this. + + PPC can only do rel jumps += 32MB, and often the kernel and other + modules are furthur away than this. So, we jump to a table of + trampolines attached to the module (the Procedure Linkage Table) + whenever that happens. +*/ + +struct ppc_plt_entry +{ + /* 16 byte jump instruction sequence (4 instructions) */ + unsigned int jump[4]; +}; + +struct mod_arch_specific +{ + /* How much of the core is actually taken up with core (then + we know the rest is for the PLT */ + unsigned int core_plt_offset; + + /* Same for init */ + unsigned int init_plt_offset; +}; + +#define Elf_Shdr Elf32_Shdr +#define Elf_Sym Elf32_Sym +#define Elf_Ehdr Elf32_Ehdr #define arch_init_modules(x) do { } while (0) #endif /* _ASM_PPC_MODULE_H */ diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/include/asm-sparc64/module.h tmp/include/asm-sparc64/module.h --- linux-2.4.13-ac8/include/asm-sparc64/module.h Fri Sep 14 09:33:03 2001 +++ tmp/include/asm-sparc64/module.h Thu Nov 22 13:08:31 2001 @@ -1,12 +1,14 @@ #ifndef _ASM_SPARC64_MODULE_H #define _ASM_SPARC64_MODULE_H -/* - * This file contains the sparc64 architecture specific module code. - */ +/* Sparc64 is simple */ +struct mod_arch_specific +{ +}; -extern void * module_map (unsigned long size); -extern void module_unmap (void *addr); -#define module_arch_init(x) (0) -#define arch_init_modules(x) do { } while (0) +#define Elf_Shdr Elf64_Shdr +#define Elf_Sym Elf64_Sym +#define Elf_Ehdr Elf64_Ehdr +/* Used by ioctl32 as well */ +void *module_map(unsigned long size); #endif /* _ASM_SPARC64_MODULE_H */ diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/include/linux/elf.h tmp/include/linux/elf.h --- linux-2.4.13-ac8/include/linux/elf.h Mon Nov 19 16:30:36 2001 +++ tmp/include/linux/elf.h Thu Nov 22 13:08:31 2001 @@ -194,6 +194,9 @@ #define ELF32_R_SYM(x) ((x) >> 8) #define ELF32_R_TYPE(x) ((x) & 0xff) +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((i) & 0xffffffff) + #define R_386_NONE 0 #define R_386_32 1 #define R_386_PC32 2 @@ -291,6 +294,7 @@ #define R_SPARC_PCPLT10 29 #define R_SPARC_10 30 #define R_SPARC_11 31 +#define R_SPARC_64 32 #define R_SPARC_WDISP16 40 #define R_SPARC_WDISP19 41 #define R_SPARC_7 43 @@ -364,6 +368,47 @@ #define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ #define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ #define R_ALPHA_RELATIVE 27 /* Adjust by program base */ + +/* PowerPC relocations defined by the ABIs */ +#define R_PPC_NONE 0 +#define R_PPC_ADDR32 1 /* 32bit absolute address */ +#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ +#define R_PPC_ADDR16 3 /* 16bit absolute address */ +#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ +#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ +#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ +#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ +#define R_PPC_ADDR14_BRTAKEN 8 +#define R_PPC_ADDR14_BRNTAKEN 9 +#define R_PPC_REL24 10 /* PC relative 26 bit */ +#define R_PPC_REL14 11 /* PC relative 16 bit */ +#define R_PPC_REL14_BRTAKEN 12 +#define R_PPC_REL14_BRNTAKEN 13 +#define R_PPC_GOT16 14 +#define R_PPC_GOT16_LO 15 +#define R_PPC_GOT16_HI 16 +#define R_PPC_GOT16_HA 17 +#define R_PPC_PLTREL24 18 +#define R_PPC_COPY 19 +#define R_PPC_GLOB_DAT 20 +#define R_PPC_JMP_SLOT 21 +#define R_PPC_RELATIVE 22 +#define R_PPC_LOCAL24PC 23 +#define R_PPC_UADDR32 24 +#define R_PPC_UADDR16 25 +#define R_PPC_REL32 26 +#define R_PPC_PLT32 27 +#define R_PPC_PLTREL32 28 +#define R_PPC_PLT16_LO 29 +#define R_PPC_PLT16_HI 30 +#define R_PPC_PLT16_HA 31 +#define R_PPC_SDAREL16 32 +#define R_PPC_SECTOFF 33 +#define R_PPC_SECTOFF_LO 34 +#define R_PPC_SECTOFF_HI 35 +#define R_PPC_SECTOFF_HA 36 +/* Keep this the last entry. */ +#define R_PPC_NUM 37 /* Legal values for e_flags field of Elf64_Ehdr. */ diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/include/linux/module.h tmp/include/linux/module.h --- linux-2.4.13-ac8/include/linux/module.h Thu Nov 15 21:47:39 2001 +++ tmp/include/linux/module.h Thu Nov 22 14:46:31 2001 @@ -1,416 +1,217 @@ +#ifndef _LINUX_MODULE_H +#define _LINUX_MODULE_H /* * Dynamic loading of modules into the kernel. * * Rewritten by Richard Henderson Dec 1996 + * Rewritten again by Rusty Russell, 2001 */ - -#ifndef _LINUX_MODULE_H -#define _LINUX_MODULE_H - #include +#include +#include +#include #include #include +#include +#include -#ifndef CONFIG_KBUILD_2_5 -#ifdef __GENKSYMS__ -# define _set_ver(sym) sym -# undef MODVERSIONS -# define MODVERSIONS -#else /* ! __GENKSYMS__ */ -# if !defined(MODVERSIONS) && defined(EXPORT_SYMTAB) -# define _set_ver(sym) sym -# include -# endif -#endif /* __GENKSYMS__ */ -#endif /* CONFIG_KBUILD_2_5 */ - -#include - -/* Don't need to bring in all of uaccess.h just for this decl. */ -struct exception_table_entry; +/* Not Yet Implemented */ +#define MODULE_LICENCE(name) +#define MODULE_AUTHOR(name) +#define MODULE_DESCRIPTION(desc) +#define MODULE_SUPPORTED_DEVICE(name) +#define MODULE_GENERIC_TABLE(gtype,name) +#define MODULE_DEVICE_TABLE(type,name) +#define MODULE_PARM_DESC(var,desc) +#define MODULE_LICENSE(license) -/* Used by get_kernel_syms, which is obsolete. */ -struct kernel_sym -{ - unsigned long value; - char name[60]; /* should have been 64-sizeof(long); oh well */ -}; +#ifdef MODULE +/* This is magically filled in by the linker, but THIS_MODULE must be + a constant so it works in initializers. */ +extern struct module __this_module; +#define THIS_MODULE (&__this_module) +#else +#define THIS_MODULE 0 +#endif -struct module_symbol +#ifdef CONFIG_MODULES +#define MODULE_NAME_LEN (64 - sizeof(unsigned long)) +struct kernel_symbol { unsigned long value; - const char *name; + char name[MODULE_NAME_LEN]; }; -struct module_ref -{ - struct module *dep; /* "parent" pointer */ - struct module *ref; /* "child" pointer */ - struct module_ref *next_ref; -}; +/* Get/put a kernel symbol (calls should be symmetric) */ +#define symbol_get(x) ((typeof(&x))(__get_symbol(#x))) -/* TBD */ -struct module_persist; +/* For every exported symbol, place a struct in the __ksymtab section */ +#define EXPORT_SYMBOL(sym) \ + const struct kernel_symbol __ksymtab_##sym \ + __attribute__((section("__ksymtab"))) \ + = { (unsigned long)&sym, #sym } -struct module -{ - unsigned long size_of_struct; /* == sizeof(module) */ - struct module *next; - const char *name; - unsigned long size; - - union - { - atomic_t usecount; - long pad; - } uc; /* Needs to keep its size - so says rth */ - - unsigned long flags; /* AUTOCLEAN et al */ - - unsigned nsyms; - unsigned ndeps; - - struct module_symbol *syms; - struct module_ref *deps; - struct module_ref *refs; - int (*init)(void); - void (*cleanup)(void); - const struct exception_table_entry *ex_table_start; - const struct exception_table_entry *ex_table_end; -#ifdef __alpha__ - unsigned long gp; -#endif - /* Members past this point are extensions to the basic - module support and are optional. Use mod_member_present() - to examine them. */ - const struct module_persist *persist_start; - const struct module_persist *persist_end; - int (*can_unload)(void); - int runsize; /* In modutils, not currently used */ - const char *kallsyms_start; /* All symbols for kernel debugging */ - const char *kallsyms_end; - const char *archdata_start; /* arch specific data for module */ - const char *archdata_end; - const char *kernel_data; /* Reserved for kernel internal use */ -}; +#define EXPORT_SYMBOL_NOVERS(sym) EXPORT_SYMBOL(sym) +#define EXPORT_SYMBOL_GPL(sym) EXPORT_SYMBOL(sym) -struct module_info +struct kernel_symbol_group { - unsigned long addr; - unsigned long size; - unsigned long flags; - long usecount; -}; - -/* Bits of module.flags. */ - -#define MOD_UNINITIALIZED 0 -#define MOD_RUNNING 1 -#define MOD_DELETED 2 -#define MOD_AUTOCLEAN 4 -#define MOD_VISITED 8 -#define MOD_USED_ONCE 16 -#define MOD_JUST_FREED 32 -#define MOD_INITIALIZING 64 - -/* Values for query_module's which. */ - -#define QM_MODULES 1 -#define QM_DEPS 2 -#define QM_REFS 3 -#define QM_SYMBOLS 4 -#define QM_INFO 5 - -/* Can the module be queried? */ -#define MOD_CAN_QUERY(mod) (((mod)->flags & (MOD_RUNNING | MOD_INITIALIZING)) && !((mod)->flags & MOD_DELETED)) - -/* When struct module is extended, we must test whether the new member - is present in the header received from insmod before we can use it. - This function returns true if the member is present. */ - -#define mod_member_present(mod,member) \ - ((unsigned long)(&((struct module *)0L)->member + 1) \ - <= (mod)->size_of_struct) - -/* - * Ditto for archdata. Assumes mod->archdata_start and mod->archdata_end - * are validated elsewhere. - */ -#define mod_archdata_member_present(mod, type, member) \ - (((unsigned long)(&((type *)0L)->member) + \ - sizeof(((type *)0L)->member)) <= \ - ((mod)->archdata_end - (mod)->archdata_start)) - - -/* Check if an address p with number of entries n is within the body of module m */ -#define mod_bound(p, n, m) ((unsigned long)(p) >= ((unsigned long)(m) + ((m)->size_of_struct)) && \ - (unsigned long)((p)+(n)) <= (unsigned long)(m) + (m)->size) - -/* Backwards compatibility definition. */ - -#define GET_USE_COUNT(module) (atomic_read(&(module)->uc.usecount)) - -/* Poke the use count of a module. */ - -#define __MOD_INC_USE_COUNT(mod) \ - (atomic_inc(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED|MOD_USED_ONCE) -#define __MOD_DEC_USE_COUNT(mod) \ - (atomic_dec(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED) -#define __MOD_IN_USE(mod) \ - (mod_member_present((mod), can_unload) && (mod)->can_unload \ - ? (mod)->can_unload() : atomic_read(&(mod)->uc.usecount)) - -/* Indirect stringification. */ - -#define __MODULE_STRING_1(x) #x -#define __MODULE_STRING(x) __MODULE_STRING_1(x) + /* Links us into the global symbol list */ + struct list_head list; -/* Generic inter module communication. - * - * NOTE: This interface is intended for small amounts of data that are - * passed between two objects and either or both of the objects - * might be compiled as modules. Do not over use this interface. - * - * If more than two objects need to communicate then you probably - * need a specific interface instead of abusing this generic - * interface. If both objects are *always* built into the kernel - * then a global extern variable is good enough, you do not need - * this interface. - * - * Keith Owens 28 Oct 2000. - */ + /* Module which owns it (if any) */ + struct module *owner; -#ifdef __KERNEL__ -#define HAVE_INTER_MODULE -extern void inter_module_register(const char *, struct module *, const void *); -extern void inter_module_unregister(const char *); -extern const void *inter_module_get(const char *); -extern const void *inter_module_get_request(const char *, const char *); -extern void inter_module_put(const char *); + unsigned int num_syms; + const struct kernel_symbol *syms; +}; -struct inter_module_entry { +struct exception_table +{ struct list_head list; - const char *im_name; - struct module *owner; - const void *userdata; + + unsigned int num_entries; + const struct exception_table_entry *entry; }; -extern int try_inc_mod_count(struct module *mod); -#endif /* __KERNEL__ */ +struct module +{ + struct list_head list; -#if defined(MODULE) && !defined(__GENKSYMS__) + /* Unique handle for this module */ + char name[MODULE_NAME_LEN]; -/* Embedded module documentation macros. */ + /* Exported symbols */ + struct kernel_symbol_group symbols; -/* For documentation purposes only. */ + /* Exception tables */ + struct exception_table extable; -#define MODULE_AUTHOR(name) \ -const char __module_author[] __attribute__((section(".modinfo"))) = \ -"author=" name - -#define MODULE_DESCRIPTION(desc) \ -const char __module_description[] __attribute__((section(".modinfo"))) = \ -"description=" desc - -/* Could potentially be used by kmod... */ - -#define MODULE_SUPPORTED_DEVICE(dev) \ -const char __module_device[] __attribute__((section(".modinfo"))) = \ -"device=" dev - -/* Used to verify parameters given to the module. The TYPE arg should - be a string in the following format: - [min[-max]]{b,h,i,l,s} - The MIN and MAX specifiers delimit the length of the array. If MAX - is omitted, it defaults to MIN; if both are omitted, the default is 1. - The final character is a type specifier: - b byte - h short - i int - l long - s string -*/ - -#define MODULE_PARM(var,type) \ -const char __module_parm_##var[] \ -__attribute__((section(".modinfo"))) = \ -"parm_" __MODULE_STRING(var) "=" type - -#define MODULE_PARM_DESC(var,desc) \ -const char __module_parm_desc_##var[] \ -__attribute__((section(".modinfo"))) = \ -"parm_desc_" __MODULE_STRING(var) "=" desc + /* Startup functions (both init and start stages encapulated here). */ + int (*init)(void); -/* - * MODULE_DEVICE_TABLE exports information about devices - * currently supported by this module. A device type, such as PCI, - * is a C-like identifier passed as the first arg to this macro. - * The second macro arg is the variable containing the device - * information being made public. - * - * The following is a list of known device types (arg 1), - * and the C types which are to be passed as arg 2. - * pci - struct pci_device_id - List of PCI ids supported by this module - * isapnp - struct isapnp_device_id - List of ISA PnP ids supported by this module - * usb - struct usb_device_id - List of USB ids supported by this module - */ -#define MODULE_GENERIC_TABLE(gtype,name) \ -static const unsigned long __module_##gtype##_size \ - __attribute__ ((unused)) = sizeof(struct gtype##_id); \ -static const struct gtype##_id * __module_##gtype##_table \ - __attribute__ ((unused)) = name + /* If this is non-NULL, vfree after init() returns */ + void *module_init; -/* - * The following license idents are currently accepted as indicating free - * software modules - * - * "GPL" [GNU Public License v2 or later] - * "GPL and additional rights" [GNU Public License v2 rights and more] - * "Dual BSD/GPL" [GNU Public License v2 or BSD license choice] - * "Dual MPL/GPL" [GNU Public License v2 or Mozilla license choice] - * - * The following other idents are available - * - * "Proprietary" [Non free products] - * - * There are dual licensed components, but when running with Linux it is the - * GPL that is relevant so this is a non issue. Similarly LGPL linked with GPL - * is a GPL combined work. - * - * This exists for several reasons - * 1. So modinfo can show license info for users wanting to vet their setup - * is free - * 2. So the community can ignore bug reports including proprietary modules - * 3. So vendors can do likewise based on their own policies - */ - -#define MODULE_LICENSE(license) \ -static const char __module_license[] __attribute__((section(".modinfo"))) = \ -"license=" license + /* Here is the actual code + data, vfree'd on unload. */ + void *module_core; -/* Define the module variable, and usage macros. */ -extern struct module __this_module; + /* Here are the sizes of the init and core sections */ + unsigned long init_size, core_size; -#define THIS_MODULE (&__this_module) -#define MOD_INC_USE_COUNT __MOD_INC_USE_COUNT(THIS_MODULE) -#define MOD_DEC_USE_COUNT __MOD_DEC_USE_COUNT(THIS_MODULE) -#define MOD_IN_USE __MOD_IN_USE(THIS_MODULE) - -#include -static const char __module_kernel_version[] __attribute__((section(".modinfo"))) = -"kernel_version=" UTS_RELEASE; -#ifdef MODVERSIONS -static const char __module_using_checksums[] __attribute__((section(".modinfo"))) = -"using_checksums=1"; + /* Arch-specific module values */ + struct mod_arch_specific arch; + +#ifdef CONFIG_MODULE_UNLOAD + /* What modules do I use? */ + struct list_head modules_used; + + /* Usage count: drop to zero and you get to clean up */ + atomic_t use; + + /* What usage is "0" to userspace */ + unsigned int base_usage; + + /* Who is waiting for us to be unloaded */ + struct task_struct *waiting; + + /* Two stage unload: stop unregisters all interfaces, so count + never increases (ie. no new users), exit deallocs + everything (ie. no users at all) */ + int (*stop)(void); + void (*exit)(void); #endif -#else /* MODULE */ - -#define MODULE_AUTHOR(name) -#define MODULE_LICENSE(license) -#define MODULE_DESCRIPTION(desc) -#define MODULE_SUPPORTED_DEVICE(name) -#define MODULE_PARM(var,type) -#define MODULE_PARM_DESC(var,desc) - -/* Create a dummy reference to the table to suppress gcc unused warnings. Put - * the reference in the .data.exit section which is discarded when code is built - * in, so the reference does not bloat the running kernel. Note: cannot be - * const, other exit data may be writable. - */ -#define MODULE_GENERIC_TABLE(gtype,name) \ -static struct gtype##_id * __module_##gtype##_table \ - __attribute__ ((unused, __section__(".data.exit"))) = name - -#ifndef __GENKSYMS__ - -#define THIS_MODULE NULL -#define MOD_INC_USE_COUNT do { } while (0) -#define MOD_DEC_USE_COUNT do { } while (0) -#define MOD_IN_USE 1 - -extern struct module *module_list; - -#endif /* !__GENKSYMS__ */ - -#endif /* MODULE */ - -#define MODULE_DEVICE_TABLE(type,name) \ - MODULE_GENERIC_TABLE(type##_device,name) - -/* Export a symbol either from the kernel or a module. - - In the kernel, the symbol is added to the kernel's global symbol table. - - In a module, it controls which variables are exported. If no - variables are explicitly exported, the action is controled by the - insmod -[xX] flags. Otherwise, only the variables listed are exported. - This obviates the need for the old register_symtab() function. */ - -#if defined(__GENKSYMS__) - -/* We want the EXPORT_SYMBOL tag left intact for recognition. */ - -#elif !defined(AUTOCONF_INCLUDED) - -#define __EXPORT_SYMBOL(sym,str) error config_must_be_included_before_module -#define EXPORT_SYMBOL(var) error config_must_be_included_before_module -#define EXPORT_SYMBOL_NOVERS(var) error config_must_be_included_before_module -#define EXPORT_SYMBOL_GPL(var) error config_must_be_included_before_module - -#elif !defined(CONFIG_MODULES) - -#define __EXPORT_SYMBOL(sym,str) -#define EXPORT_SYMBOL(var) -#define EXPORT_SYMBOL_NOVERS(var) -#define EXPORT_SYMBOL_GPL(var) - -#elif !defined(EXPORT_SYMTAB) - -#define __EXPORT_SYMBOL(sym,str) error this_object_must_be_defined_as_export_objs_in_the_Makefile -#define EXPORT_SYMBOL(var) error this_object_must_be_defined_as_export_objs_in_the_Makefile -#define EXPORT_SYMBOL_NOVERS(var) error this_object_must_be_defined_as_export_objs_in_the_Makefile -#define EXPORT_SYMBOL_GPL(var) error this_object_must_be_defined_as_export_objs_in_the_Makefile + /* The command line arguments (may be mangled). People like + keeping pointers to this stuff */ + char args[0]; +}; + +/* These module loading routines are defined by the specific arch: + return ERR_PTR on error. */ +void *module_core_alloc(const Elf_Ehdr *hdr, + const Elf_Shdr *sechdrs, + const char *secstrings, + struct module *module); + +/* You can return NULL here if init not supported */ +void *module_init_alloc(const Elf_Ehdr *hdr, + const Elf_Shdr *sechdrs, + const char *secstrings, + struct module *module); + +/* Free memory returned from module_core_alloc/module_init_alloc */ +void module_free(void *module_region); + +int apply_relocate(Elf_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *module); + +int apply_relocate_add(Elf_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *module); #else +#define EXPORT_SYMBOL(sym) 0 +#define EXPORT_SYMBOL_GPL(sym) 0 -#define __EXPORT_SYMBOL(sym, str) \ -const char __kstrtab_##sym[] \ -__attribute__((section(".kstrtab"))) = str; \ -const struct module_symbol __ksymtab_##sym \ -__attribute__((section("__ksymtab"))) = \ -{ (unsigned long)&sym, __kstrtab_##sym } - -#define __EXPORT_SYMBOL_GPL(sym, str) \ -const char __kstrtab_##sym[] \ -__attribute__((section(".kstrtab"))) = "GPLONLY_" str; \ -const struct module_symbol __ksymtab_##sym \ -__attribute__((section("__ksymtab"))) = \ -{ (unsigned long)&sym, __kstrtab_##sym } - -#if defined(MODVERSIONS) || !defined(CONFIG_MODVERSIONS) -#define EXPORT_SYMBOL(var) __EXPORT_SYMBOL(var, __MODULE_STRING(var)) -#define EXPORT_SYMBOL_GPL(var) __EXPORT_SYMBOL_GPL(var, __MODULE_STRING(var)) -#else -#define EXPORT_SYMBOL(var) __EXPORT_SYMBOL(var, __MODULE_STRING(__VERSIONED_SYMBOL(var))) -#define EXPORT_SYMBOL_GPL(var) __EXPORT_SYMBOL(var, __MODULE_STRING(__VERSIONED_SYMBOL(var))) -#endif +/* Get/put a kernel symbol (calls should be symmetric) */ +#define symbol_get(x) ((typeof(&x))(0)) -#define EXPORT_SYMBOL_NOVERS(var) __EXPORT_SYMBOL(var, __MODULE_STRING(var)) +#endif /* CONFIG_MODULES */ -#endif /* __GENKSYMS__ */ +#ifdef CONFIG_MODULE_UNLOAD +#define symbol_put(x) __put_symbol(#x) -#ifdef MODULE -/* Force a module to export no symbols. */ -#define EXPORT_NO_SYMBOLS __asm__(".section __ksymtab\n.previous") -#else -#define EXPORT_NO_SYMBOLS -#endif /* MODULE */ +void *__symbol_get(const char *symbol); +void __symbol_put(const char *symbol); -#ifdef CONFIG_MODULES -#define SET_MODULE_OWNER(some_struct) do { (some_struct)->owner = THIS_MODULE; } while (0) -#else -#define SET_MODULE_OWNER(some_struct) do { } while (0) -#endif +static inline void module_get(struct module *module) +{ + if (module) atomic_inc(&module->use); +} +static inline void module_put(struct module *module) +{ + if (module && atomic_dec_and_test(&module->use)) + wake_up_process(module->waiting); +} +#else /*!CONFIG_MODULE_UNLOAD*/ +static inline void module_get(struct module *module) { } +static inline void module_put(struct module *module) { } +#define symbol_put(x) do { } while(0) +#endif /* CONFIG_MODULE_UNLOAD */ + +/* For archs to search exception tables */ +extern struct list_head extables; +extern spinlock_t modlist_lock; + +/* THESE ARE OBSOLETE AND WILL VANISH */ +#define __MOD_DEC_USE_COUNT(mod) module_put(mod) +#define __MOD_INC_USE_COUNT(mod) module_get(mod) +#define SET_MODULE_OWNER(dev) ((dev)->owner = THIS_MODULE) +#define MOD_INC_USE_COUNT module_get(THIS_MODULE) +#define MOD_DEC_USE_COUNT module_put(THIS_MODULE) +#define try_inc_mod_count(mod) ({ module_get(mod); 1; }) +#define MODULE_PARM(parm,string) +#define EXPORT_NO_SYMBOLS +extern int module_dummy_usage; +#define GET_USE_COUNT(module) (module_dummy_usage) +#define MOD_IN_USE 0 +#define __mod_between(a_start, a_len, b_start, b_len) \ +(((a_start) >= (b_start) && (a_start) <= (b_start)+(b_len)) \ + || ((a_start)+(a_len) >= (b_start) \ + && (a_start)+(a_len) <= (b_start)+(b_len))) +#define mod_bound(p, n, m) \ +(((m)->module_init \ + && __mod_between((unsigned long)(p),(n),(m)->module_init,(m)->init_size)) \ + || __mod_between((unsigned long)(p),(n),(m)->module_core,(m)->core_size)) +#define inter_module_register(name, mod, p) +#define inter_module_get(name) (0) +#define inter_module_put(name) +#define inter_module_unregister(name) #endif /* _LINUX_MODULE_H */ diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/init/main.c tmp/init/main.c --- linux-2.4.13-ac8/init/main.c Thu Nov 15 21:47:41 2001 +++ tmp/init/main.c Thu Nov 22 13:08:31 2001 @@ -652,9 +652,6 @@ * this. But we do want output early, in case something goes wrong. */ console_init(); -#ifdef CONFIG_MODULES - init_modules(); -#endif if (prof_shift) { unsigned int size; /* only text is profiled */ diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/kernel/kmod.c tmp/kernel/kmod.c --- linux-2.4.13-ac8/kernel/kmod.c Wed Jul 18 11:23:50 2001 +++ tmp/kernel/kmod.c Thu Nov 22 13:08:31 2001 @@ -145,7 +145,7 @@ static int exec_modprobe(void * module_name) { static char * envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; - char *argv[] = { modprobe_path, "-s", "-k", "--", (char*)module_name, NULL }; + char *argv[] = { modprobe_path, "--", (char*)module_name, NULL }; int ret; ret = exec_usermodehelper(modprobe_path, argv, envp); diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/kernel/ksyms.c tmp/kernel/ksyms.c --- linux-2.4.13-ac8/kernel/ksyms.c Thu Nov 15 21:47:41 2001 +++ tmp/kernel/ksyms.c Thu Nov 22 13:08:31 2001 @@ -72,14 +72,6 @@ }; #endif - -EXPORT_SYMBOL(inter_module_register); -EXPORT_SYMBOL(inter_module_unregister); -EXPORT_SYMBOL(inter_module_get); -EXPORT_SYMBOL(inter_module_get_request); -EXPORT_SYMBOL(inter_module_put); -EXPORT_SYMBOL(try_inc_mod_count); - /* process memory management */ EXPORT_SYMBOL(do_mmap_pgoff); EXPORT_SYMBOL(do_munmap); diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/kernel/module.c tmp/kernel/module.c --- linux-2.4.13-ac8/kernel/module.c Thu Nov 15 21:47:41 2001 +++ tmp/kernel/module.c Thu Nov 22 13:11:12 2001 --- linux-2.4.13-ac8/kernel/module.c Thu Nov 15 21:47:41 2001 +++ tmp/kernel/module.c Thu Nov 22 14:46:14 2001 @@ -1,1283 +1,890 @@ +/* Rewritten by Rusty Russell, on the backs of many others... + Copyright (C) 2001 Rusty Russell. + + 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 -#include #include -#include -#include +#include +#include #include -#include +#include +#include +#include #include -#include -#include -#include -#include - -/* - * Originally by Anonymous (as far as I know...) - * Linux version by Bas Laarhoven - * 0.99.14 version by Jon Tombs , - * Heavily modified by Bjorn Ekwall May 1994 (C) - * Rewritten by Richard Henderson Dec 1996 - * Add MOD_INITIALIZING Keith Owens Nov 1999 - * Add kallsyms support, Keith Owens Apr 2000 - * Add asm/module support, IA64 has special requirements. Keith Owens Sep 2000 - * Fix assorted bugs in module verification. Keith Owens Sep 2000 - * Fix sys_init_module race, Andrew Morton Oct 2000 - * http://www.uwsg.iu.edu/hypermail/linux/kernel/0008.3/0379.html - * Replace xxx_module_symbol with inter_module_xxx. Keith Owens Oct 2000 - * Add a module list lock for kernel fault race fixing. Alan Cox - * - * 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[]; +#if 1 +#define DEBUGP printk +#else +#define DEBUGP(fmt , ...) +#endif extern const struct exception_table_entry __start___ex_table[]; extern const struct exception_table_entry __stop___ex_table[]; +extern const struct kernel_symbol __start___ksymtab[]; +extern const struct kernel_symbol __stop___ksymtab[]; -extern const char __start___kallsyms[] __attribute__ ((weak)); -extern const char __stop___kallsyms[] __attribute__ ((weak)); +/* The exception tables: start with kernel only, protected by extable_lock. */ +LIST_HEAD(extables); +spinlock_t modlist_lock = SPIN_LOCK_UNLOCKED; +static struct exception_table kernel_extable; -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, -}; +/* Symbol list: starts with kernel only */ +static LIST_HEAD(symbols); +static struct kernel_symbol_group kernel_symbols; -struct module *module_list = &kernel_module; +/* List of modules, protected by module_mutex */ +LIST_HEAD(modules); /* Accessed w/o lock on oops by some archs */ +static DECLARE_MUTEX(module_mutex); -#endif /* defined(CONFIG_MODULES) || defined(CONFIG_KALLSYMS) */ +/* This is harder when we don't assume a is power of 2 */ +#define ALIGN(n, a) (((a) && ((n) % (a))) ? (n) + ((a) - (n) % (a)) : (n)) -/* 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; +/* Convenient structure for holding init and core sizes */ +struct module_sizes +{ + unsigned long init; + unsigned long core; +}; -/** - * 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); +#ifdef CONFIG_MODULE_UNLOAD +/* Init the unload section of the module */ +static void module_unload_init(struct module *mod) +{ + INIT_LIST_HEAD(&mod->modules_used); + atomic_set(&mod->use, 1); } -/** - * 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(); - } -} +/* modules using other modules */ +struct module_use +{ + struct list_head list; + struct module *used_module; +}; -/** - * 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); -} +/* Does a already use b? */ +static int already_uses(struct module *a, struct module *b) +{ + struct list_head *i; -/** - * 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; + list_for_each(i, &a->modules_used) { + struct module_use *use + = list_entry(i, struct module_use, list); + if (use->used_module == b) { + DEBUGP("%s uses %s!\n", a->name, b->name); + return 1; } } - 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); + DEBUGP("%s does not use %s!\n", a->name, b->name); + return 0; } -/* - * Copy the name of a module from user space. - */ - -static inline long -get_mod_name(const char *user_name, char **buf) +/* Module a uses b */ +static int use_module(struct module *a, struct module *b) { - unsigned long page; - long retval; + struct module_use *use; + if (a == b || already_uses(a, b)) return 1; - 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; + DEBUGP("Allocating new usage for %s.\n", a->name); + use = kmalloc(sizeof(*use), GFP_KERNEL); + if (!use) { + printk("%s: out of memory loading\n", a->name); + return 0; + } - free_page(page); - return retval; + use->used_module = b; + list_add(&use->list, &a->modules_used); + module_get(b); + return 1; } -static inline void -put_mod_name(char *buf) +/* Clear the unload stuff of the module. */ +static void module_unload_free(struct module *mod) { - free_page((unsigned long)buf); -} + if (atomic_read(&mod->use)) + BUG(); -/* - * Allocate space for a module. - */ + while (!list_empty(&mod->modules_used)) { + struct module_use *use + = list_entry(&mod->modules_used, + struct module_use, list); + module_put(use->used_module); + list_del(&use->list); + kfree(use); + } +} -asmlinkage unsigned long -sys_create_module(const char *name_user, size_t size) +/* Search for module by name: must hold module_mutex. */ +static struct module *find_module(const char *name) { - char *name; - long namelen, error; + struct list_head *m; 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; + list_for_each(m, &modules) { + mod = list_entry(m, struct module, list); + if (strcmp(mod->name, name) == 0) + return mod; + } + return NULL; } -/* - * Initialize a module. - */ +/* This exists whether we can unload or not */ +static void free_module(struct module *mod); asmlinkage long -sys_init_module(const char *name_user, struct module *mod_user) +sys_delete_module(const char *name_user, unsigned int flags) { - 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; + struct module *mod; + char name[MODULE_NAME_LEN]; + int ret; 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 long -sys_delete_module(const char *name_user) -{ - struct module *mod, *next; - char *name; - long error; - int something_changed; + if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0) + return -EFAULT; + name[MODULE_NAME_LEN-1] = '\0'; - if (!capable(CAP_SYS_MODULE)) - return -EPERM; + if (down_interruptible(&module_mutex)) + return -EINTR; - 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); - } + mod = find_module(name); + if (!mod) { + ret = -ENOENT; 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) - && !__MOD_IN_USE(mod)) { - if ((mod->flags & MOD_VISITED) - && !(mod->flags & MOD_JUST_FREED)) { - spin_unlock(&unload_lock); - mod->flags &= ~MOD_VISITED; - } else { - mod->flags |= MOD_DELETED; - spin_unlock(&unload_lock); - free_module(mod, 1); - something_changed = 1; - } - } else { - spin_unlock(&unload_lock); - } + if (!mod->stop && !mod->exit) { + /* This module can't be removed */ + ret = -EBUSY; + goto out; } - - if (something_changed) - goto restart; - - for (mod = module_list; mod != &kernel_module; mod = mod->next) - mod->flags &= ~MOD_JUST_FREED; - - error = 0; -out: - unlock_kernel(); - return error; -} -/* Query various bits about modules. */ - -static int -qm_modules(char *buf, size_t bufsize, size_t *ret) -{ - struct module *mod; - size_t nmod, space, len; + if (mod->stop) { + ret = mod->stop(); + if (ret != 0) goto out; + } - nmod = space = 0; + /* Remove symbols so module count can't increase */ + spin_lock_irq(&modlist_lock); + list_del_init(&mod->symbols.list); + spin_unlock_irq(&modlist_lock); - 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; - } + /* This may or may not decrement to zero. We may be waiting + for someone else who is holding a reference. */ + set_current_state(TASK_UNINTERRUPTIBLE); + mod->waiting = current; + module_put(mod); + schedule(); - if (put_user(nmod, ret)) - return -EFAULT; - else - return 0; + /* Wait to ensure that noone is executing inside a module right now */ + synchronize_kernel(); -calc_space_needed: - space += len; - while ((mod = mod->next) != &kernel_module) - space += strlen(mod->name)+1; + /* Final destruction now noone is using it. */ + if (mod->exit) mod->exit(); + free_module(mod); + ret = 0; - if (put_user(space, ret)) - return -EFAULT; - else - return -ENOSPC; + out: + up(&module_mutex); + return ret; } -static int -qm_deps(struct module *mod, char *buf, size_t bufsize, size_t *ret) +static size_t print_unload_info(char *buf, + size_t remaining, + struct module *mod) { - size_t i, space, len; + struct list_head *i; + int done; - if (mod == &kernel_module) - return -EINVAL; - if (!MOD_CAN_QUERY(mod)) - if (put_user(0, ret)) - return -EFAULT; - else - return 0; + done = snprintf(buf, remaining, " %u", + atomic_read(&mod->use) - mod->base_usage); - 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; - } + list_for_each(i, &mod->modules_used) { + struct module_use *use + = list_entry(i, struct module_use, list); - 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; + done += snprintf(buf+done, remaining-done, + " %s", use->used_module->name); + } + if (done < remaining) { + buf[done] = '\n'; + done++; + } + return done; } -static int -qm_refs(struct module *mod, char *buf, size_t bufsize, size_t *ret) +/* Must be holding the list lock */ +static struct kernel_symbol_group *find_sym(const char *name, + unsigned int *symnum) { - size_t nrefs, space, len; - struct module_ref *ref; + struct kernel_symbol_group *ksg; + struct list_head *l; - if (mod == &kernel_module) - return -EINVAL; - if (!MOD_CAN_QUERY(mod)) - if (put_user(0, ret)) - return -EFAULT; - else - return 0; + list_for_each(l, &symbols) { + unsigned int i; + ksg = list_entry(l, struct kernel_symbol_group, list); - 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; + for (i = 0; i < ksg->num_syms; i++) { + if (strcmp(ksg->syms[i].name, name) == 0) { + *symnum = i; + return ksg; + } + } } + return NULL; +} - 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; +void __symbol_put(const char *symbol) +{ + struct kernel_symbol_group *ksg; + unsigned int sym_num; + unsigned long flags; - if (put_user(space, ret)) - return -EFAULT; - else - return -ENOSPC; + spin_lock_irqsave(&modlist_lock, flags); + ksg = find_sym(symbol, &sym_num); + if (!ksg) + BUG(); + module_put(ksg->owner); + spin_unlock_irqrestore(&modlist_lock, flags); } +EXPORT_SYMBOL(__symbol_put); -static int -qm_symbols(struct module *mod, char *buf, size_t bufsize, size_t *ret) +#else /* !CONFIG_MODULE_UNLOAD */ +static size_t print_unload_info(char *buf, + size_t remaining, + struct module *mod) { - size_t i, space, len; - struct module_symbol *s; - char *strings; - unsigned long *vals; - - if (!MOD_CAN_QUERY(mod)) - if (put_user(0, ret)) - return -EFAULT; - else - return 0; + if (remaining) { + buf[0] = '\n'; + remaining--; + } + return remaining; +} - space = mod->nsyms * 2*sizeof(void *); +static inline void module_unload_free(struct module *mod) +{ +} - i = len = 0; - s = mod->syms; +static inline int use_module(struct module *a, struct module *b) +{ + return 1; +} - if (space > bufsize) - goto calc_space_needed; +static inline void module_unload_init(struct module *mod) +{ +} +#endif /* CONFIG_MODULE_UNLOAD */ - if (!access_ok(VERIFY_WRITE, buf, space)) - return -EFAULT; +/* Free a module, remove from lists, etc (must hold module mutex). */ +static void free_module(struct module *mod) +{ + /* This may be NULL, but that's OK */ + module_free(mod->module_init); + module_free(mod->module_core); - 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; - } - if (put_user(i, ret)) - return -EFAULT; - else - return 0; + /* Delete from various lists */ + list_del(&mod->list); + spin_lock_irq(&modlist_lock); + list_del(&mod->symbols.list); + list_del(&mod->extable.list); + spin_unlock_irq(&modlist_lock); -calc_space_needed: - for (; i < mod->nsyms; ++i, ++s) - space += strlen(s->name)+1; + /* Module unload stuff */ + module_unload_free(mod); - if (put_user(space, ret)) - return -EFAULT; - else - return -ENOSPC; + /* Finally, free the module structure */ + kfree(mod); } -static int -qm_info(struct module *mod, char *buf, size_t bufsize, size_t *ret) +void *__symbol_get(const char *symbol) { - int error = 0; - - 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; - - /* usecount is one too high here - report appropriately to - compensate for locking */ - info.usecount = (mod_member_present(mod, can_unload) - && mod->can_unload ? -1 : atomic_read(&mod->uc.usecount)-1); - - if (copy_to_user(buf, &info, sizeof(struct module_info))) - return -EFAULT; - } else - error = -ENOSPC; + struct kernel_symbol_group *ksg; + unsigned int sym_num; + unsigned long flags; - if (put_user(sizeof(struct module_info), ret)) - return -EFAULT; + spin_lock_irqsave(&modlist_lock, flags); + ksg = find_sym(symbol, &sym_num); + if (ksg) module_get(ksg->owner); + spin_unlock_irqrestore(&modlist_lock, flags); - return error; + if (ksg) return (void *)ksg->syms[sym_num].value; + else return NULL; } +EXPORT_SYMBOL(__symbol_get); -asmlinkage long -sys_query_module(const char *name_user, int which, char *buf, size_t bufsize, - size_t *ret) -{ - struct module *mod; - int err; +/* Simple symbol lookup: use module if it's not already used. */ +static const struct kernel_symbol *attach_symbol(const char *name, + struct module *mod) +{ + struct kernel_symbol_group *ksg; + const struct kernel_symbol *ret; + unsigned int sym_num; + + ret = NULL; + spin_lock_irq(&modlist_lock); + ksg = find_sym(name, &sym_num); + if (ksg) { + /* If use_module fails OOM, we'll give a (bogus) + unfound symbol error, and load will fail. */ + if (ksg->owner == NULL || use_module(mod, ksg->owner)) + ret = &ksg->syms[sym_num]; + } + spin_unlock_irq(&modlist_lock); + return ret; +} + +/* Transfer one ELF section to the correct (init or core) area. */ +static void *copy_section(const char *name, + void *base, + Elf_Shdr *sechdr, + struct module *mod, + struct module_sizes *used) +{ + void *dest; + unsigned long *use; + + /* Only copy to init section if there is one */ + if (strstr(name, ".init") && mod->module_init) { + dest = mod->module_init; + use = &used->init; + } else { + dest = mod->module_core; + use = &used->core; + } - 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; - } - err = -ENOENT; - if ((mod = find_module(name)) == NULL) { - put_mod_name(name); - goto out; - } - put_mod_name(name); + /* Align up */ + *use = ALIGN(*use, sechdr->sh_addralign); + dest += *use; + *use += sechdr->sh_size; + + /* May not actually be in the file (eg. bss). */ + if (sechdr->sh_type != SHT_NOBITS) + memcpy(dest, base + sechdr->sh_offset, sechdr->sh_size); + + return dest; +} + +/* Look for the special symbols */ +static int grab_private_symbols(Elf_Shdr *sechdrs, + unsigned int symbolsec, + const char *strtab, + struct module *mod) +{ + Elf_Sym *sym = (void *)sechdrs[symbolsec].sh_offset; + unsigned int i; + + for (i = 1; i < sechdrs[symbolsec].sh_size/sizeof(*sym); i++) { + if (strcmp("__initfn", strtab + sym[i].st_name) == 0) + mod->init = (void *)sym[i].st_value; +#ifdef CONFIG_MODULE_UNLOAD + if (strcmp("__exitfn", strtab + sym[i].st_name) == 0) + mod->exit = (void *)sym[i].st_value; + if (strcmp("__stopfn", strtab + sym[i].st_name) == 0) + mod->stop = (void *)sym[i].st_value; +#endif } - /* __MOD_ touches the flags. We must avoid that */ - - atomic_inc(&mod->uc.usecount); - - 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); + return 0; +} + +/* Deal with the given section */ +static int handle_section(const char *name, + Elf_Shdr *sechdrs, + unsigned int strindex, + unsigned int symindex, + unsigned int i, + struct module *mod) +{ + int ret; + const char *strtab = (char *)sechdrs[strindex].sh_offset; + + switch (sechdrs[i].sh_type) { + case SHT_REL: + ret = apply_relocate(sechdrs, strtab, symindex, i, mod); break; - case QM_SYMBOLS: - err = qm_symbols(mod, buf, bufsize, ret); + case SHT_RELA: + ret = apply_relocate_add(sechdrs, strtab, symindex, i, mod); break; - case QM_INFO: - err = qm_info(mod, buf, bufsize, ret); + case SHT_SYMTAB: + ret = grab_private_symbols(sechdrs, i, strtab, mod); break; default: - err = -EINVAL; - break; + DEBUGP("Ignoring section %u: %s\n", i, + sechdrs[i].sh_type==SHT_NULL ? "NULL": + sechdrs[i].sh_type==SHT_PROGBITS ? "PROGBITS": + sechdrs[i].sh_type==SHT_SYMTAB ? "SYMTAB": + sechdrs[i].sh_type==SHT_STRTAB ? "STRTAB": + sechdrs[i].sh_type==SHT_RELA ? "RELA": + sechdrs[i].sh_type==SHT_HASH ? "HASH": + sechdrs[i].sh_type==SHT_DYNAMIC ? "DYNAMIC": + sechdrs[i].sh_type==SHT_NOTE ? "NOTE": + sechdrs[i].sh_type==SHT_NOBITS ? "NOBITS": + sechdrs[i].sh_type==SHT_REL ? "REL": + sechdrs[i].sh_type==SHT_SHLIB ? "SHLIB": + sechdrs[i].sh_type==SHT_DYNSYM ? "DYNSYM": + sechdrs[i].sh_type==SHT_NUM ? "NUM": + "UNKNOWN"); + ret = 0; } - atomic_dec(&mod->uc.usecount); - -out: - unlock_kernel(); - return err; + return ret; } -/* - * 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. - */ - -asmlinkage long -sys_get_kernel_syms(struct kernel_sym *table) +/* Figure out total size desired for the common vars */ +static unsigned long read_commons(void *start, Elf_Shdr *sechdr) { - struct module *mod; - int i; - struct kernel_sym ksym; - - 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; - - /* So that we don't give the user our stack content */ - memset (&ksym, 0, sizeof (ksym)); + unsigned long size, i, max_align; + Elf_Sym *sym; + + size = max_align = 0; - for (mod = module_list, i = 0; mod; mod = mod->next) { - struct module_symbol *msym; - unsigned int j; - - if (!MOD_CAN_QUERY(mod)) - 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; + for (sym = start + sechdr->sh_offset, i = 0; + i < sechdr->sh_size / sizeof(Elf_Sym); + i++) { + if (sym[i].st_shndx == SHN_COMMON) { + /* Value encodes alignment. */ + if (sym[i].st_value > max_align) + max_align = sym[i].st_value; + /* Pad to required alignment */ + size = ALIGN(size, sym[i].st_value) + sym[i].st_size; } } -out: - unlock_kernel(); - return i; -} -/* - * Look for a module by name, ignoring modules marked for deletion. - */ - -struct module * -find_module(const char *name) -{ - struct module *mod; + /* Now, add in max alignment requirement (with align + attribute, this could be large), so we know we have space + whatever the start alignment is */ + return size + max_align; +} + +/* Change all symbols so that sh_value encodes the pointer directly. */ +static void simplify_symbols(Elf_Shdr *sechdrs, + unsigned int symindex, + unsigned int strindex, + void *common, + struct module *mod) +{ + unsigned int i; + Elf_Sym *sym; + + for (sym = (void *)sechdrs[symindex].sh_offset, i = 0; + i < sechdrs[symindex].sh_size / sizeof(Elf_Sym); + i++) { + switch (sym[i].st_shndx) { + case SHN_COMMON: + /* Value encodes alignment. */ + common = (void *)ALIGN((unsigned long)common, + sym[i].st_value); + /* Change it to encode pointer */ + sym[i].st_value = (unsigned long)common; + common += sym[i].st_size; + break; - for (mod = module_list; mod ; mod = mod->next) { - if (mod->flags & MOD_DELETED) - continue; - if (!strcmp(mod->name, name)) + case SHN_UNDEF: { + /* Look for symbol */ + const struct kernel_symbol *extsym; + const char *name + = (char *)sechdrs[strindex].sh_offset + + sym[i].st_name; + + extsym = attach_symbol(name, mod); + if (extsym) { + sym[i].st_value = extsym->value; + DEBUGP("Found external symbol: 0x%08lx\n", + (unsigned long)extsym->value); + } else { + /* Unknown values are OK, unless they + are *used* */ + sym[i].st_value = 0; + /* We fake up "__this_module" */ + if (strcmp(name, "__this_module") == 0) + sym[i].st_value = (unsigned long)mod; + } break; + } + case SHN_ABS: + /* Don't need to do anything */ + DEBUGP("Absolute symbol: 0x%08lx\n", + (unsigned long)sym[i].st_value); + break; + + default: + sym[i].st_value + = (unsigned long) + (sechdrs[sym[i].st_shndx].sh_offset + + sym[i].st_value); + DEBUGP("Updated normal symbol: 0x%08lx\n", + (unsigned long)sym[i].st_value); + } } - - return mod; } -/* - * Free the given module. - */ - -void -free_module(struct module *mod, int tag_freed) +/* Get the total allocation size of the init and non-init sections */ +static void get_sizes(const Elf_Ehdr *hdr, + const Elf_Shdr *sechdrs, + const char *secstrings, + unsigned long *init_size, + unsigned long *core_size) { - struct module_ref *dep; unsigned i; - unsigned long flags; - - /* Let the module clean up. */ - if (mod->flags & MOD_RUNNING) - { - if(mod->cleanup) - mod->cleanup(); - mod->flags &= ~MOD_RUNNING; - } - - /* Remove the module from the dependency lists. */ - - 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; - if (tag_freed && dep->dep->refs == NULL) - dep->dep->flags |= MOD_JUST_FREED; - } - - /* And from the main module list. */ + /* Everything marked ALLOC (this includes the exported + symbols) */ + for (i = 1; i < hdr->e_shnum; i++) { + unsigned long *add; + + /* If it's called *.init*, it goes into the init section */ + if (strstr(secstrings + sechdrs[i].sh_name, ".init") != 0) + add = init_size; + else + add = core_size; - spin_lock_irqsave(&modlist_lock, flags); - 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; + if (sechdrs[i].sh_flags & SHF_ALLOC) { + /* Pad up to required alignment */ + *add = ALIGN(*add, sechdrs[i].sh_addralign ?: 1); + *add += sechdrs[i].sh_size; + } } - spin_unlock_irqrestore(&modlist_lock, flags); - - /* And free the memory. */ - - module_unmap(mod); } -/* - * Called by the /proc file system to return a current list of modules. - */ - -int get_module_list(char *p) +/* Allocate and load the module */ +static struct module *load_module(const char *uname, + void *umod, + unsigned long len, + const char *uargs) { - size_t left = PAGE_SIZE; + Elf_Ehdr *hdr; + Elf_Shdr *sechdrs; + char *secstrings; + unsigned int i, symindex, exportindex, strindex, setupindex, exindex; + long arglen; + unsigned long common_length; + struct module_sizes used; struct module *mod; - char tmpstr[64]; - struct module_ref *ref; - - 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; - } + void *ptr; - len = sprintf(tmpstr, "%8lu", mod->size); - safe_copy_str(tmpstr, len); - - if (mod->flags & MOD_RUNNING) { - len = sprintf(tmpstr, "%4ld", - (mod_member_present(mod, can_unload) - && mod->can_unload - ? -1L : (long)atomic_read(&mod->uc.usecount))); - safe_copy_str(tmpstr, len); - } - - 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 if (mod->flags & MOD_INITIALIZING) - safe_copy_cstr(" (initializing)"); - else - safe_copy_cstr(" (uninitialized)"); + DEBUGP("load_module: uname=%p, umod=%p, len=%lu, uargs=%p\n", + uname, umod, len, uargs); + if (len < sizeof(*hdr)) + return ERR_PTR(-ENOEXEC); + + /* Suck in entire file: we'll want most of it. */ + /* vmalloc barfs on "unusual" numbers. Check here */ + if (len > 64 * 1024 * 1024 || (hdr = vmalloc(len)) == NULL) + return ERR_PTR(-ENOMEM); + if (copy_from_user(hdr, umod, len) != 0) { + vfree(hdr); + return ERR_PTR(-EFAULT); + } + + /* Sanity checks against insmoding binaries or wrong arch, + weird elf version */ + if (memcmp(hdr->e_ident, ELFMAG, 4) != 0 + || hdr->e_type != ET_REL + || !elf_check_arch(hdr) + || hdr->e_shentsize != sizeof(*sechdrs)) { + vfree(hdr); + return ERR_PTR(-ENOEXEC); + } + + /* Convenience variables */ + sechdrs = (void *)hdr + hdr->e_shoff; + secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; + + /* May not export symbols, or have setup params, so these may + not exist */ + exportindex = setupindex = 0; + + /* And these should exist, but gcc whinges if we don't init them */ + symindex = strindex = exindex = 0; + + /* Find where important sections are */ + for (i = 1; i < hdr->e_shnum; i++) { + if (sechdrs[i].sh_type == SHT_SYMTAB) { + /* Internal symbols */ + DEBUGP("Symbol table in section %u\n", i); + symindex = i; + } else if (strcmp(secstrings+sechdrs[i].sh_name, "__ksymtab") + == 0) { + /* Exported symbols. */ + DEBUGP("EXPORT table in section %u\n", i); + exportindex = i; + } else if (strcmp(secstrings + sechdrs[i].sh_name, ".strtab") + == 0) { + /* Strings */ + DEBUGP("String table found in section %u\n", i); + strindex = i; + } else if (strcmp(secstrings+sechdrs[i].sh_name, ".setup.init") + == 0) { + /* Setup parameter info */ + DEBUGP("Setup table found in section %u\n", i); + setupindex = i; + } else if (strcmp(secstrings+sechdrs[i].sh_name, "__ex_table") + == 0) { + /* Exception table */ + DEBUGP("Exception table found in section %u\n", i); + exindex = i; + } +#ifndef CONFIG_MODULE_UNLOAD + /* Don't load .exit sections */ + if (strstr(secstrings+sechdrs[i].sh_name, ".exit")) + sechdrs[i].flags &= ~(Elf_Word)SHF_ALLOC; +#endif + } + + /* Now allocate space for the module proper, and copy name and args. */ + arglen = strlen_user(uargs); + if (arglen < 0) { + vfree(hdr); + return ERR_PTR(arglen); + } + mod = kmalloc(sizeof(*mod) + arglen+1, GFP_KERNEL); + if (!mod) { + vfree(hdr); + return ERR_PTR(-ENOMEM); + } + memset(mod, 0, sizeof(*mod) + arglen+1); - 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("]"); + if (strncpy_from_user(mod->name, uname, sizeof(mod->name)-1) < 0 + || copy_from_user(mod->args, uargs, arglen) != 0) { + kfree(mod); + vfree(hdr); + return ERR_PTR(-EFAULT); + } + + /* Initialize the rest */ + INIT_LIST_HEAD(&mod->extable.list); + INIT_LIST_HEAD(&mod->list); + INIT_LIST_HEAD(&mod->symbols.list); + module_unload_init(mod); + + /* How much space will we need? (Common area in core) */ + get_sizes(hdr, sechdrs, secstrings, &mod->init_size, &mod->core_size); + common_length = read_commons(hdr, &sechdrs[symindex]); + mod->core_size += common_length; + + /* Allocate (this is arch specific) */ + ptr = module_core_alloc(hdr, sechdrs, secstrings, mod); + if (IS_ERR(ptr)) { + kfree(mod); + vfree(hdr); + return ptr; + } + mod->module_core = ptr; + + ptr = module_init_alloc(hdr, sechdrs, secstrings, mod); + if (IS_ERR(ptr)) { + module_free(mod->module_core); + kfree(mod); + vfree(hdr); + return ptr; + } + mod->module_init = ptr; + + /* Transfer each section which requires ALLOC, and set sh_offset + fields to absolute addresses. */ + used.core = common_length; + used.init = 0; + for (i = 1; i < hdr->e_shnum; i++) { + if (sechdrs[i].sh_flags & SHF_ALLOC) { + ptr = copy_section(secstrings + sechdrs[i].sh_name, + hdr, &sechdrs[i], mod, &used); + if (IS_ERR(ptr)) goto cleanup; + sechdrs[i].sh_offset = (unsigned long)ptr; + } else { + sechdrs[i].sh_offset += (unsigned long)hdr; } - safe_copy_cstr("\n"); - -#undef safe_copy_str -#undef safe_copy_cstr } + /* Don't use more than we allocated! */ + if (used.init > mod->init_size || used.core > mod->core_size) + BUG(); -fini: - return PAGE_SIZE - left; -} - -/* - * Called by the /proc file system to return a current list of ksyms. - */ + /* Fix up syms, so that st_value is a pointer to location. */ + simplify_symbols(sechdrs, symindex, strindex, mod->module_core, mod); -struct mod_sym { - struct module *mod; - int index; -}; + /* Set up EXPORTed symbols */ + if (exportindex) { + mod->symbols.num_syms = (sechdrs[exportindex].sh_size + / sizeof(*mod->symbols.syms)); + mod->symbols.syms = (void *)sechdrs[exportindex].sh_offset; + } + + /* Set up exception table */ + if (exindex) { + mod->extable.num_entries = (sechdrs[exindex].sh_size + / sizeof(struct + exception_table_entry)); + mod->extable.entry = (void *)sechdrs[exindex].sh_offset; + } + + /* Now handle each section. */ + for (i = 1; i < hdr->e_shnum; i++) { + ptr = ERR_PTR(handle_section(secstrings + sechdrs[i].sh_name, + sechdrs, + strindex, + symindex, + i, + mod)); + if (IS_ERR(ptr)) goto cleanup; + } + + /* A useful module could simply export symbols, but they can + put a dummy function in, because otherwise this can be a + very confusing silent failure */ + if (!mod->init) { + printk("%s: No init function.\n", mod->name); + ptr = ERR_PTR(-ENOEXEC); + goto cleanup; + } + +#if 0 /* Needs param support */ + /* Size of section 0 is 0, so this works well */ + ptr = ERR_PTR(parse_args(mod->args, + (struct kernel_param *) + sechdrs[setupindex].sh_offset, + sechdrs[setupindex].sh_size + / sizeof(struct kernel_param), + NULL)); + if (IS_ERR(ptr)) goto cleanup; +#endif -/* iterator */ + /* Get rid of temporary copy */ + vfree(hdr); -static void *s_start(struct seq_file *m, loff_t *pos) -{ - struct mod_sym *p = kmalloc(sizeof(*p), GFP_KERNEL); - struct module *v; - loff_t n = *pos; + /* Done! */ + return mod; - if (!p) - return ERR_PTR(-ENOMEM); - lock_kernel(); - for (v = module_list, n = *pos; v; n -= v->nsyms, v = v->next) { - if (n < v->nsyms) { - p->mod = v; - p->index = n; - return p; - } - } - unlock_kernel(); - kfree(p); - return NULL; + cleanup: + module_free(mod->module_init); + module_free(mod->module_core); + kfree(mod); + vfree(hdr); + return ptr; } -static void *s_next(struct seq_file *m, void *p, loff_t *pos) +/* This is where the real work happens */ +asmlinkage long +sys_init_module(const char *uname, + void *umod, + unsigned long len, + const char *uargs) { - struct mod_sym *v = p; - (*pos)++; - if (++v->index >= v->mod->nsyms) { - do { - v->mod = v->mod->next; - if (!v->mod) { - unlock_kernel(); - kfree(p); - return NULL; - } - } while (!v->mod->nsyms); - v->index = 0; - } - return p; -} + struct module *mod; + int ret; -static void s_stop(struct seq_file *m, void *p) -{ - if (p && !IS_ERR(p)) { - unlock_kernel(); - kfree(p); - } -} + /* Must have permission */ + if (!capable(CAP_SYS_MODULE)) + return -EPERM; -static int s_show(struct seq_file *m, void *p) -{ - struct mod_sym *v = p; - struct module_symbol *sym; + /* Only one module load at a time, please */ + if (down_interruptible(&module_mutex) != 0) + return -EINTR; + + /* Do all the hard work */ + mod = load_module(uname, umod, len, uargs); + if (IS_ERR(mod)) { + up(&module_mutex); + return PTR_ERR(mod); + } + + /* Flush the instruction cache, since we've played with text */ + if (mod->module_init) + flush_icache_range((unsigned long)mod->module_init, + (unsigned long)mod->module_init + + mod->init_size); + flush_icache_range((unsigned long)mod->module_core, + (unsigned long)mod->module_core + mod->core_size); + + /* Now sew it into exception list (just in case...). */ + spin_lock_irq(&modlist_lock); + list_add(&mod->extable.list, &extables); + spin_unlock_irq(&modlist_lock); + + /* Initialize (and start) the module */ + if (mod->init) { + ret = mod->init(); + if (ret < 0) { + /* Init routine failed: abort */ + free_module(mod); + up(&module_mutex); + return ret; + } + } else + ret = 0; - if (!MOD_CAN_QUERY(v->mod)) - return 0; - sym = &v->mod->syms[v->index]; - if (*v->mod->name) - seq_printf(m, "%0*lx %s\t[%s]\n", (int)(2*sizeof(void*)), - sym->value, sym->name, v->mod->name); - else - seq_printf(m, "%0*lx %s\n", (int)(2*sizeof(void*)), - sym->value, sym->name); + /* Now it's a first class citizen! */ + spin_lock_irq(&modlist_lock); + list_add(&mod->symbols.list, &kernel_symbols.list); + spin_unlock_irq(&modlist_lock); + list_add(&mod->list, &modules); + +#ifdef CONFIG_MODULE_UNLOAD + /* Base for showing user when a module is unloadable */ + mod->base_usage = ret + 1; +#endif + + /* Free the init section (if any) */ + module_free(mod->module_init); + mod->module_init = NULL; + + /* All ok! */ + up(&module_mutex); return 0; } -struct seq_operations ksyms_op = { - start: s_start, - next: s_next, - stop: s_stop, - show: s_show -}; - -#else /* CONFIG_MODULES */ - -/* Dummy syscalls for people who don't want modules */ - -asmlinkage unsigned long -sys_create_module(const char *name_user, size_t size) +/* Oh, /proc interface, how I hate thee. Let me count the ways... */ +int get_module_list(char *buf) { - return -ENOSYS; -} + struct list_head *m; + size_t remaining = PAGE_SIZE - 1; /* 1 */ -asmlinkage long -sys_init_module(const char *name_user, struct module *mod_user) -{ - return -ENOSYS; -} + if (down_interruptible(&module_mutex) != 0) + return 0; /* 2 */ -asmlinkage long -sys_delete_module(const char *name_user) -{ - return -ENOSYS; + list_for_each(m, &modules) { + int done; + struct module *mod = list_entry(m, struct module, list); + + done = snprintf(buf, remaining, "%s", mod->name); + done += print_unload_info(buf+done, remaining-done, mod); + if (done == remaining) + break; + remaining -= done; + buf += done; + } + + up(&module_mutex); + return PAGE_SIZE - 1 - remaining; + /* 25 lines for someone almost every proc file does? 3 */ } -asmlinkage long -sys_query_module(const char *name_user, int which, char *buf, size_t bufsize, - size_t *ret) +static int __init init(void) { - /* Let the program know about the new interface. Not that - it'll do them much good. */ - if (which == 0) - return 0; + /* Add kernel symbols to symbol table */ + kernel_symbols.num_syms = (__stop___ksymtab - __start___ksymtab); + kernel_symbols.syms = __start___ksymtab; + list_add(&kernel_symbols.list, &symbols); - return -ENOSYS; -} + /* Add kernel exception table to exception tables */ + kernel_extable.num_entries = (__stop___ex_table -__start___ex_table); + kernel_extable.entry = __start___ex_table; + list_add(&kernel_extable.list, &extables); -asmlinkage long -sys_get_kernel_syms(struct kernel_sym *table) -{ - return -ENOSYS; + return 0; } -int try_inc_mod_count(struct module *mod) -{ - return 1; -} +/* Obsolete lvalue for broken code which asks about usage */ +int module_dummy_usage = 1; -#endif /* CONFIG_MODULES */ +/* Call this at boot */ +__initcall(init); diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.4.13-ac8/net/ipv6/af_inet6.c tmp/net/ipv6/af_inet6.c --- linux-2.4.13-ac8/net/ipv6/af_inet6.c Thu Oct 18 07:16:39 2001 +++ tmp/net/ipv6/af_inet6.c Thu Nov 22 13:08:31 2001 @@ -507,6 +507,7 @@ }; #ifdef MODULE +#if 0 /* FIXME --RR */ int ipv6_unload(void) { if (!unloadable) return 1; @@ -514,6 +515,7 @@ return atomic_read(&(__this_module.uc.usecount)) - 3; } #endif +#endif #if defined(MODULE) && defined(CONFIG_SYSCTL) extern void ipv6_sysctl_register(void); @@ -593,10 +595,12 @@ int err; #ifdef MODULE +#if 0 /* FIXME --RR */ if (!mod_member_present(&__this_module, can_unload)) return -EINVAL; __this_module.can_unload = &ipv6_unload; +#endif #endif printk(KERN_INFO "IPv6 v0.8 for NET4.0\n");