X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=arch%2Fpowerpc%2Fkernel%2Fmodule_64.c;h=3a82b02b784b0c8a4d8da7467b8c7d4410cf9ea4;hb=990d0f2ced23052abc7efa09bd05bff34e00cf73;hp=8dd1f0aae5d6265809748dcc0e0001b2a55e440e;hpb=8c8a0eab988c94294adbbb4fe955bba7d8ebddcf;p=linux-2.6-omap-h63xx.git diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c index 8dd1f0aae5d..1af2377e499 100644 --- a/arch/powerpc/kernel/module_64.c +++ b/arch/powerpc/kernel/module_64.c @@ -20,9 +20,11 @@ #include #include #include +#include #include -#include #include +#include +#include #include "setup.h" @@ -39,13 +41,6 @@ #define DEBUGP(fmt , ...) #endif -/* There's actually a third entry here, but it's unused */ -struct ppc64_opd_entry -{ - unsigned long funcaddr; - unsigned long r2; -}; - /* Like PPC32, we need little trampolines to do > 24-bit jumps (into the kernel itself). But on PPC64, these need to be used for every jump, actually, to reset r2 (TOC+0x8000). */ @@ -80,41 +75,61 @@ static struct ppc64_stub_entry ppc64_stub = different addend) */ static unsigned int count_relocs(const Elf64_Rela *rela, unsigned int num) { - unsigned int i, j, ret = 0; + unsigned int i, r_info, r_addend, _count_relocs; /* FIXME: Only count external ones --RR */ - /* Sure, this is order(n^2), but it's usually short, and not - time critical */ - for (i = 0; i < num; i++) { + _count_relocs = 0; + r_info = 0; + r_addend = 0; + for (i = 0; i < num; i++) /* Only count 24-bit relocs, others don't need stubs */ - if (ELF64_R_TYPE(rela[i].r_info) != R_PPC_REL24) - continue; - for (j = 0; j < i; j++) { - /* If this addend appeared before, it's - already been counted */ - if (rela[i].r_info == rela[j].r_info - && rela[i].r_addend == rela[j].r_addend) - break; + if (ELF64_R_TYPE(rela[i].r_info) == R_PPC_REL24 && + (r_info != ELF64_R_SYM(rela[i].r_info) || + r_addend != rela[i].r_addend)) { + _count_relocs++; + r_info = ELF64_R_SYM(rela[i].r_info); + r_addend = rela[i].r_addend; } - if (j == i) ret++; - } - return ret; + + return _count_relocs; } -void *module_alloc(unsigned long size) +static int relacmp(const void *_x, const void *_y) { - if (size == 0) - return NULL; + const Elf64_Rela *x, *y; + + y = (Elf64_Rela *)_x; + x = (Elf64_Rela *)_y; - return vmalloc_exec(size); + /* Compare the entire r_info (as opposed to ELF64_R_SYM(r_info) only) to + * make the comparison cheaper/faster. It won't affect the sorting or + * the counting algorithms' performance + */ + if (x->r_info < y->r_info) + return -1; + else if (x->r_info > y->r_info) + return 1; + else if (x->r_addend < y->r_addend) + return -1; + else if (x->r_addend > y->r_addend) + return 1; + else + return 0; } -/* Free memory returned from module_alloc */ -void module_free(struct module *mod, void *module_region) +static void relaswap(void *_x, void *_y, int size) { - vfree(module_region); - /* FIXME: If module_region == mod->init_region, trim exception - table entries. */ + uint64_t *x, *y, tmp; + int i; + + y = (uint64_t *)_x; + x = (uint64_t *)_y; + + for (i = 0; i < sizeof(Elf64_Rela) / sizeof(uint64_t); i++) { + tmp = x[i]; + x[i] = y[i]; + y[i] = tmp; + } } /* Get size of potential trampolines required. */ @@ -132,6 +147,16 @@ static unsigned long get_stubs_size(const Elf64_Ehdr *hdr, DEBUGP("Ptr: %p. Number: %lu\n", (void *)sechdrs[i].sh_addr, sechdrs[i].sh_size / sizeof(Elf64_Rela)); + + /* Sort the relocation information based on a symbol and + * addend key. This is a stable O(n*log n) complexity + * alogrithm but it will reduce the complexity of + * count_relocs() to linear complexity O(n) + */ + sort((void *)sechdrs[i].sh_addr, + sechdrs[i].sh_size / sizeof(Elf64_Rela), + sizeof(Elf64_Rela), relacmp, relaswap); + relocs += count_relocs((void *)sechdrs[i].sh_addr, sechdrs[i].sh_size / sizeof(Elf64_Rela)); @@ -298,7 +323,7 @@ static unsigned long stub_for_addr(Elf64_Shdr *sechdrs, restore r2. */ static int restore_r2(u32 *instruction, struct module *me) { - if (*instruction != 0x60000000) { + if (*instruction != PPC_NOP_INSTR) { printk("%s: Expect noop after relocate, got %08x\n", me->name, *instruction); return 0; @@ -342,7 +367,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, /* Simply set it */ *(u32 *)location = value; break; - + case R_PPC64_ADDR64: /* Simply set it */ *(unsigned long *)location = value; @@ -398,7 +423,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, } /* Only replace bits 2 through 26 */ - *(uint32_t *)location + *(uint32_t *)location = (*(uint32_t *)location & ~0x03fffffc) | (value & 0x03fffffc); break; @@ -418,77 +443,3 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, return 0; } - -LIST_HEAD(module_bug_list); - -static const Elf_Shdr *find_section(const Elf_Ehdr *hdr, - const Elf_Shdr *sechdrs, - const char *name) -{ - char *secstrings; - unsigned int i; - - secstrings = (char *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; - for (i = 1; i < hdr->e_shnum; i++) - if (strcmp(secstrings+sechdrs[i].sh_name, name) == 0) - return &sechdrs[i]; - return NULL; -} - -int module_finalize(const Elf_Ehdr *hdr, - const Elf_Shdr *sechdrs, struct module *me) -{ - const Elf_Shdr *sect; - - me->arch.bug_table = NULL; - me->arch.num_bugs = 0; - - /* Find the __bug_table section, if present */ - sect = find_section(hdr, sechdrs, "__bug_table"); - if (sect != NULL) { - me->arch.bug_table = (void *) sect->sh_addr; - me->arch.num_bugs = sect->sh_size / sizeof(struct bug_entry); - } - - /* - * Strictly speaking this should have a spinlock to protect against - * traversals, but since we only traverse on BUG()s, a spinlock - * could potentially lead to deadlock and thus be counter-productive. - */ - list_add(&me->arch.bug_list, &module_bug_list); - - /* Apply feature fixups */ - sect = find_section(hdr, sechdrs, "__ftr_fixup"); - if (sect != NULL) - do_feature_fixups(cur_cpu_spec->cpu_features, - (void *)sect->sh_addr, - (void *)sect->sh_addr + sect->sh_size); - - sect = find_section(hdr, sechdrs, "__fw_ftr_fixup"); - if (sect != NULL) - do_feature_fixups(powerpc_firmware_features, - (void *)sect->sh_addr, - (void *)sect->sh_addr + sect->sh_size); - - return 0; -} - -void module_arch_cleanup(struct module *mod) -{ - list_del(&mod->arch.bug_list); -} - -struct bug_entry *module_find_bug(unsigned long bugaddr) -{ - struct mod_arch_specific *mod; - unsigned int i; - struct bug_entry *bug; - - list_for_each_entry(mod, &module_bug_list, bug_list) { - bug = mod->bug_table; - for (i = 0; i < mod->num_bugs; ++i, ++bug) - if (bugaddr == bug->bug_addr) - return bug; - } - return NULL; -}