1ed981856SPaul Mackerras /* Kernel module help for PPC. 2ed981856SPaul Mackerras Copyright (C) 2001 Rusty Russell. 3ed981856SPaul Mackerras 4ed981856SPaul Mackerras This program is free software; you can redistribute it and/or modify 5ed981856SPaul Mackerras it under the terms of the GNU General Public License as published by 6ed981856SPaul Mackerras the Free Software Foundation; either version 2 of the License, or 7ed981856SPaul Mackerras (at your option) any later version. 8ed981856SPaul Mackerras 9ed981856SPaul Mackerras This program is distributed in the hope that it will be useful, 10ed981856SPaul Mackerras but WITHOUT ANY WARRANTY; without even the implied warranty of 11ed981856SPaul Mackerras MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12ed981856SPaul Mackerras GNU General Public License for more details. 13ed981856SPaul Mackerras 14ed981856SPaul Mackerras You should have received a copy of the GNU General Public License 15ed981856SPaul Mackerras along with this program; if not, write to the Free Software 16ed981856SPaul Mackerras Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17ed981856SPaul Mackerras */ 18ed981856SPaul Mackerras #include <linux/module.h> 19ed981856SPaul Mackerras #include <linux/moduleloader.h> 20ed981856SPaul Mackerras #include <linux/elf.h> 21ed981856SPaul Mackerras #include <linux/vmalloc.h> 22ed981856SPaul Mackerras #include <linux/fs.h> 23ed981856SPaul Mackerras #include <linux/string.h> 24ed981856SPaul Mackerras #include <linux/kernel.h> 25ed981856SPaul Mackerras #include <linux/cache.h> 2673c9ceabSJeremy Fitzhardinge #include <linux/bug.h> 27eda09fbdSEmil Medve #include <linux/sort.h> 28ed981856SPaul Mackerras 2921c4ff80SBenjamin Herrenschmidt #include "setup.h" 3021c4ff80SBenjamin Herrenschmidt 31ed981856SPaul Mackerras #if 0 32ed981856SPaul Mackerras #define DEBUGP printk 33ed981856SPaul Mackerras #else 34ed981856SPaul Mackerras #define DEBUGP(fmt , ...) 35ed981856SPaul Mackerras #endif 36ed981856SPaul Mackerras 37ed981856SPaul Mackerras LIST_HEAD(module_bug_list); 38ed981856SPaul Mackerras 39ed981856SPaul Mackerras void *module_alloc(unsigned long size) 40ed981856SPaul Mackerras { 41ed981856SPaul Mackerras if (size == 0) 42ed981856SPaul Mackerras return NULL; 43ed981856SPaul Mackerras return vmalloc(size); 44ed981856SPaul Mackerras } 45ed981856SPaul Mackerras 46ed981856SPaul Mackerras /* Free memory returned from module_alloc */ 47ed981856SPaul Mackerras void module_free(struct module *mod, void *module_region) 48ed981856SPaul Mackerras { 49ed981856SPaul Mackerras vfree(module_region); 50ed981856SPaul Mackerras /* FIXME: If module_region == mod->init_region, trim exception 51ed981856SPaul Mackerras table entries. */ 52ed981856SPaul Mackerras } 53ed981856SPaul Mackerras 54ed981856SPaul Mackerras /* Count how many different relocations (different symbol, different 55ed981856SPaul Mackerras addend) */ 56ed981856SPaul Mackerras static unsigned int count_relocs(const Elf32_Rela *rela, unsigned int num) 57ed981856SPaul Mackerras { 58eda09fbdSEmil Medve unsigned int i, r_info, r_addend, _count_relocs; 59ed981856SPaul Mackerras 60eda09fbdSEmil Medve _count_relocs = 0; 61eda09fbdSEmil Medve r_info = 0; 62eda09fbdSEmil Medve r_addend = 0; 63eda09fbdSEmil Medve for (i = 0; i < num; i++) 64eda09fbdSEmil Medve /* Only count 24-bit relocs, others don't need stubs */ 65eda09fbdSEmil Medve if (ELF32_R_TYPE(rela[i].r_info) == R_PPC_REL24 && 66eda09fbdSEmil Medve (r_info != ELF32_R_SYM(rela[i].r_info) || 67eda09fbdSEmil Medve r_addend != rela[i].r_addend)) { 68eda09fbdSEmil Medve _count_relocs++; 69eda09fbdSEmil Medve r_info = ELF32_R_SYM(rela[i].r_info); 70eda09fbdSEmil Medve r_addend = rela[i].r_addend; 71ed981856SPaul Mackerras } 72eda09fbdSEmil Medve 73eda09fbdSEmil Medve return _count_relocs; 74ed981856SPaul Mackerras } 75eda09fbdSEmil Medve 76eda09fbdSEmil Medve static int relacmp(const void *_x, const void *_y) 77eda09fbdSEmil Medve { 78eda09fbdSEmil Medve const Elf32_Rela *x, *y; 79eda09fbdSEmil Medve 80eda09fbdSEmil Medve y = (Elf32_Rela *)_x; 81eda09fbdSEmil Medve x = (Elf32_Rela *)_y; 82eda09fbdSEmil Medve 83eda09fbdSEmil Medve /* Compare the entire r_info (as opposed to ELF32_R_SYM(r_info) only) to 84eda09fbdSEmil Medve * make the comparison cheaper/faster. It won't affect the sorting or 85eda09fbdSEmil Medve * the counting algorithms' performance 86eda09fbdSEmil Medve */ 87eda09fbdSEmil Medve if (x->r_info < y->r_info) 88eda09fbdSEmil Medve return -1; 89eda09fbdSEmil Medve else if (x->r_info > y->r_info) 90eda09fbdSEmil Medve return 1; 91eda09fbdSEmil Medve else if (x->r_addend < y->r_addend) 92eda09fbdSEmil Medve return -1; 93eda09fbdSEmil Medve else if (x->r_addend > y->r_addend) 94eda09fbdSEmil Medve return 1; 95eda09fbdSEmil Medve else 96eda09fbdSEmil Medve return 0; 97eda09fbdSEmil Medve } 98eda09fbdSEmil Medve 99eda09fbdSEmil Medve static void relaswap(void *_x, void *_y, int size) 100eda09fbdSEmil Medve { 101eda09fbdSEmil Medve uint32_t *x, *y, tmp; 102eda09fbdSEmil Medve int i; 103eda09fbdSEmil Medve 104eda09fbdSEmil Medve y = (uint32_t *)_x; 105eda09fbdSEmil Medve x = (uint32_t *)_y; 106eda09fbdSEmil Medve 107eda09fbdSEmil Medve for (i = 0; i < sizeof(Elf32_Rela) / sizeof(uint32_t); i++) { 108eda09fbdSEmil Medve tmp = x[i]; 109eda09fbdSEmil Medve x[i] = y[i]; 110eda09fbdSEmil Medve y[i] = tmp; 111eda09fbdSEmil Medve } 112ed981856SPaul Mackerras } 113ed981856SPaul Mackerras 114ed981856SPaul Mackerras /* Get the potential trampolines size required of the init and 115ed981856SPaul Mackerras non-init sections */ 116ed981856SPaul Mackerras static unsigned long get_plt_size(const Elf32_Ehdr *hdr, 117ed981856SPaul Mackerras const Elf32_Shdr *sechdrs, 118ed981856SPaul Mackerras const char *secstrings, 119ed981856SPaul Mackerras int is_init) 120ed981856SPaul Mackerras { 121ed981856SPaul Mackerras unsigned long ret = 0; 122ed981856SPaul Mackerras unsigned i; 123ed981856SPaul Mackerras 124ed981856SPaul Mackerras /* Everything marked ALLOC (this includes the exported 125ed981856SPaul Mackerras symbols) */ 126ed981856SPaul Mackerras for (i = 1; i < hdr->e_shnum; i++) { 127ed981856SPaul Mackerras /* If it's called *.init*, and we're not init, we're 128ed981856SPaul Mackerras not interested */ 129ed981856SPaul Mackerras if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != 0) 130ed981856SPaul Mackerras != is_init) 131ed981856SPaul Mackerras continue; 132ed981856SPaul Mackerras 133ed981856SPaul Mackerras /* We don't want to look at debug sections. */ 134ed981856SPaul Mackerras if (strstr(secstrings + sechdrs[i].sh_name, ".debug") != 0) 135ed981856SPaul Mackerras continue; 136ed981856SPaul Mackerras 137ed981856SPaul Mackerras if (sechdrs[i].sh_type == SHT_RELA) { 138ed981856SPaul Mackerras DEBUGP("Found relocations in section %u\n", i); 139ed981856SPaul Mackerras DEBUGP("Ptr: %p. Number: %u\n", 140ed981856SPaul Mackerras (void *)hdr + sechdrs[i].sh_offset, 141ed981856SPaul Mackerras sechdrs[i].sh_size / sizeof(Elf32_Rela)); 142eda09fbdSEmil Medve 143eda09fbdSEmil Medve /* Sort the relocation information based on a symbol and 144eda09fbdSEmil Medve * addend key. This is a stable O(n*log n) complexity 145eda09fbdSEmil Medve * alogrithm but it will reduce the complexity of 146eda09fbdSEmil Medve * count_relocs() to linear complexity O(n) 147eda09fbdSEmil Medve */ 148eda09fbdSEmil Medve sort((void *)hdr + sechdrs[i].sh_offset, 149eda09fbdSEmil Medve sechdrs[i].sh_size / sizeof(Elf32_Rela), 150eda09fbdSEmil Medve sizeof(Elf32_Rela), relacmp, relaswap); 151eda09fbdSEmil Medve 152ed981856SPaul Mackerras ret += count_relocs((void *)hdr 153ed981856SPaul Mackerras + sechdrs[i].sh_offset, 154ed981856SPaul Mackerras sechdrs[i].sh_size 155ed981856SPaul Mackerras / sizeof(Elf32_Rela)) 156ed981856SPaul Mackerras * sizeof(struct ppc_plt_entry); 157ed981856SPaul Mackerras } 158ed981856SPaul Mackerras } 159ed981856SPaul Mackerras 160ed981856SPaul Mackerras return ret; 161ed981856SPaul Mackerras } 162ed981856SPaul Mackerras 163ed981856SPaul Mackerras int module_frob_arch_sections(Elf32_Ehdr *hdr, 164ed981856SPaul Mackerras Elf32_Shdr *sechdrs, 165ed981856SPaul Mackerras char *secstrings, 166ed981856SPaul Mackerras struct module *me) 167ed981856SPaul Mackerras { 168ed981856SPaul Mackerras unsigned int i; 169ed981856SPaul Mackerras 170ed981856SPaul Mackerras /* Find .plt and .init.plt sections */ 171ed981856SPaul Mackerras for (i = 0; i < hdr->e_shnum; i++) { 172ed981856SPaul Mackerras if (strcmp(secstrings + sechdrs[i].sh_name, ".init.plt") == 0) 173ed981856SPaul Mackerras me->arch.init_plt_section = i; 174ed981856SPaul Mackerras else if (strcmp(secstrings + sechdrs[i].sh_name, ".plt") == 0) 175ed981856SPaul Mackerras me->arch.core_plt_section = i; 176ed981856SPaul Mackerras } 177ed981856SPaul Mackerras if (!me->arch.core_plt_section || !me->arch.init_plt_section) { 178ed981856SPaul Mackerras printk("Module doesn't contain .plt or .init.plt sections.\n"); 179ed981856SPaul Mackerras return -ENOEXEC; 180ed981856SPaul Mackerras } 181ed981856SPaul Mackerras 182ed981856SPaul Mackerras /* Override their sizes */ 183ed981856SPaul Mackerras sechdrs[me->arch.core_plt_section].sh_size 184ed981856SPaul Mackerras = get_plt_size(hdr, sechdrs, secstrings, 0); 185ed981856SPaul Mackerras sechdrs[me->arch.init_plt_section].sh_size 186ed981856SPaul Mackerras = get_plt_size(hdr, sechdrs, secstrings, 1); 187ed981856SPaul Mackerras return 0; 188ed981856SPaul Mackerras } 189ed981856SPaul Mackerras 190ed981856SPaul Mackerras int apply_relocate(Elf32_Shdr *sechdrs, 191ed981856SPaul Mackerras const char *strtab, 192ed981856SPaul Mackerras unsigned int symindex, 193ed981856SPaul Mackerras unsigned int relsec, 194ed981856SPaul Mackerras struct module *module) 195ed981856SPaul Mackerras { 196ed981856SPaul Mackerras printk(KERN_ERR "%s: Non-ADD RELOCATION unsupported\n", 197ed981856SPaul Mackerras module->name); 198ed981856SPaul Mackerras return -ENOEXEC; 199ed981856SPaul Mackerras } 200ed981856SPaul Mackerras 201ed981856SPaul Mackerras static inline int entry_matches(struct ppc_plt_entry *entry, Elf32_Addr val) 202ed981856SPaul Mackerras { 203ed981856SPaul Mackerras if (entry->jump[0] == 0x3d600000 + ((val + 0x8000) >> 16) 204ed981856SPaul Mackerras && entry->jump[1] == 0x396b0000 + (val & 0xffff)) 205ed981856SPaul Mackerras return 1; 206ed981856SPaul Mackerras return 0; 207ed981856SPaul Mackerras } 208ed981856SPaul Mackerras 209ed981856SPaul Mackerras /* Set up a trampoline in the PLT to bounce us to the distant function */ 210ed981856SPaul Mackerras static uint32_t do_plt_call(void *location, 211ed981856SPaul Mackerras Elf32_Addr val, 212ed981856SPaul Mackerras Elf32_Shdr *sechdrs, 213ed981856SPaul Mackerras struct module *mod) 214ed981856SPaul Mackerras { 215ed981856SPaul Mackerras struct ppc_plt_entry *entry; 216ed981856SPaul Mackerras 217ed981856SPaul Mackerras DEBUGP("Doing plt for call to 0x%x at 0x%x\n", val, (unsigned int)location); 218ed981856SPaul Mackerras /* Init, or core PLT? */ 219ed981856SPaul Mackerras if (location >= mod->module_core 220ed981856SPaul Mackerras && location < mod->module_core + mod->core_size) 221ed981856SPaul Mackerras entry = (void *)sechdrs[mod->arch.core_plt_section].sh_addr; 222ed981856SPaul Mackerras else 223ed981856SPaul Mackerras entry = (void *)sechdrs[mod->arch.init_plt_section].sh_addr; 224ed981856SPaul Mackerras 225ed981856SPaul Mackerras /* Find this entry, or if that fails, the next avail. entry */ 226ed981856SPaul Mackerras while (entry->jump[0]) { 227ed981856SPaul Mackerras if (entry_matches(entry, val)) return (uint32_t)entry; 228ed981856SPaul Mackerras entry++; 229ed981856SPaul Mackerras } 230ed981856SPaul Mackerras 231ed981856SPaul Mackerras /* Stolen from Paul Mackerras as well... */ 232ed981856SPaul Mackerras entry->jump[0] = 0x3d600000+((val+0x8000)>>16); /* lis r11,sym@ha */ 233ed981856SPaul Mackerras entry->jump[1] = 0x396b0000 + (val&0xffff); /* addi r11,r11,sym@l*/ 234ed981856SPaul Mackerras entry->jump[2] = 0x7d6903a6; /* mtctr r11 */ 235ed981856SPaul Mackerras entry->jump[3] = 0x4e800420; /* bctr */ 236ed981856SPaul Mackerras 237ed981856SPaul Mackerras DEBUGP("Initialized plt for 0x%x at %p\n", val, entry); 238ed981856SPaul Mackerras return (uint32_t)entry; 239ed981856SPaul Mackerras } 240ed981856SPaul Mackerras 241ed981856SPaul Mackerras int apply_relocate_add(Elf32_Shdr *sechdrs, 242ed981856SPaul Mackerras const char *strtab, 243ed981856SPaul Mackerras unsigned int symindex, 244ed981856SPaul Mackerras unsigned int relsec, 245ed981856SPaul Mackerras struct module *module) 246ed981856SPaul Mackerras { 247ed981856SPaul Mackerras unsigned int i; 248ed981856SPaul Mackerras Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr; 249ed981856SPaul Mackerras Elf32_Sym *sym; 250ed981856SPaul Mackerras uint32_t *location; 251ed981856SPaul Mackerras uint32_t value; 252ed981856SPaul Mackerras 253ed981856SPaul Mackerras DEBUGP("Applying ADD relocate section %u to %u\n", relsec, 254ed981856SPaul Mackerras sechdrs[relsec].sh_info); 255ed981856SPaul Mackerras for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) { 256ed981856SPaul Mackerras /* This is where to make the change */ 257ed981856SPaul Mackerras location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr 258ed981856SPaul Mackerras + rela[i].r_offset; 259ed981856SPaul Mackerras /* This is the symbol it is referring to. Note that all 260ed981856SPaul Mackerras undefined symbols have been resolved. */ 261ed981856SPaul Mackerras sym = (Elf32_Sym *)sechdrs[symindex].sh_addr 262ed981856SPaul Mackerras + ELF32_R_SYM(rela[i].r_info); 263ed981856SPaul Mackerras /* `Everything is relative'. */ 264ed981856SPaul Mackerras value = sym->st_value + rela[i].r_addend; 265ed981856SPaul Mackerras 266ed981856SPaul Mackerras switch (ELF32_R_TYPE(rela[i].r_info)) { 267ed981856SPaul Mackerras case R_PPC_ADDR32: 268ed981856SPaul Mackerras /* Simply set it */ 269ed981856SPaul Mackerras *(uint32_t *)location = value; 270ed981856SPaul Mackerras break; 271ed981856SPaul Mackerras 272ed981856SPaul Mackerras case R_PPC_ADDR16_LO: 273ed981856SPaul Mackerras /* Low half of the symbol */ 274ed981856SPaul Mackerras *(uint16_t *)location = value; 275ed981856SPaul Mackerras break; 276ed981856SPaul Mackerras 2779a3d6458SSimon Vallet case R_PPC_ADDR16_HI: 2789a3d6458SSimon Vallet /* Higher half of the symbol */ 2799a3d6458SSimon Vallet *(uint16_t *)location = (value >> 16); 2809a3d6458SSimon Vallet break; 2819a3d6458SSimon Vallet 282ed981856SPaul Mackerras case R_PPC_ADDR16_HA: 283ed981856SPaul Mackerras /* Sign-adjusted lower 16 bits: PPC ELF ABI says: 284ed981856SPaul Mackerras (((x >> 16) + ((x & 0x8000) ? 1 : 0))) & 0xFFFF. 285ed981856SPaul Mackerras This is the same, only sane. 286ed981856SPaul Mackerras */ 287ed981856SPaul Mackerras *(uint16_t *)location = (value + 0x8000) >> 16; 288ed981856SPaul Mackerras break; 289ed981856SPaul Mackerras 290ed981856SPaul Mackerras case R_PPC_REL24: 291ed981856SPaul Mackerras if ((int)(value - (uint32_t)location) < -0x02000000 292ed981856SPaul Mackerras || (int)(value - (uint32_t)location) >= 0x02000000) 293ed981856SPaul Mackerras value = do_plt_call(location, value, 294ed981856SPaul Mackerras sechdrs, module); 295ed981856SPaul Mackerras 296ed981856SPaul Mackerras /* Only replace bits 2 through 26 */ 297ed981856SPaul Mackerras DEBUGP("REL24 value = %08X. location = %08X\n", 298ed981856SPaul Mackerras value, (uint32_t)location); 299ed981856SPaul Mackerras DEBUGP("Location before: %08X.\n", 300ed981856SPaul Mackerras *(uint32_t *)location); 301ed981856SPaul Mackerras *(uint32_t *)location 302ed981856SPaul Mackerras = (*(uint32_t *)location & ~0x03fffffc) 303ed981856SPaul Mackerras | ((value - (uint32_t)location) 304ed981856SPaul Mackerras & 0x03fffffc); 305ed981856SPaul Mackerras DEBUGP("Location after: %08X.\n", 306ed981856SPaul Mackerras *(uint32_t *)location); 307ed981856SPaul Mackerras DEBUGP("ie. jump to %08X+%08X = %08X\n", 308ed981856SPaul Mackerras *(uint32_t *)location & 0x03fffffc, 309ed981856SPaul Mackerras (uint32_t)location, 310ed981856SPaul Mackerras (*(uint32_t *)location & 0x03fffffc) 311ed981856SPaul Mackerras + (uint32_t)location); 312ed981856SPaul Mackerras break; 313ed981856SPaul Mackerras 314ed981856SPaul Mackerras case R_PPC_REL32: 315ed981856SPaul Mackerras /* 32-bit relative jump. */ 316ed981856SPaul Mackerras *(uint32_t *)location = value - (uint32_t)location; 317ed981856SPaul Mackerras break; 318ed981856SPaul Mackerras 319ed981856SPaul Mackerras default: 320ed981856SPaul Mackerras printk("%s: unknown ADD relocation: %u\n", 321ed981856SPaul Mackerras module->name, 322ed981856SPaul Mackerras ELF32_R_TYPE(rela[i].r_info)); 323ed981856SPaul Mackerras return -ENOEXEC; 324ed981856SPaul Mackerras } 325ed981856SPaul Mackerras } 326ed981856SPaul Mackerras return 0; 327ed981856SPaul Mackerras } 328ed981856SPaul Mackerras 32921c4ff80SBenjamin Herrenschmidt static const Elf_Shdr *find_section(const Elf_Ehdr *hdr, 33021c4ff80SBenjamin Herrenschmidt const Elf_Shdr *sechdrs, 33121c4ff80SBenjamin Herrenschmidt const char *name) 33221c4ff80SBenjamin Herrenschmidt { 33321c4ff80SBenjamin Herrenschmidt char *secstrings; 33421c4ff80SBenjamin Herrenschmidt unsigned int i; 33521c4ff80SBenjamin Herrenschmidt 33621c4ff80SBenjamin Herrenschmidt secstrings = (char *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; 33721c4ff80SBenjamin Herrenschmidt for (i = 1; i < hdr->e_shnum; i++) 33821c4ff80SBenjamin Herrenschmidt if (strcmp(secstrings+sechdrs[i].sh_name, name) == 0) 33921c4ff80SBenjamin Herrenschmidt return &sechdrs[i]; 34021c4ff80SBenjamin Herrenschmidt return NULL; 34121c4ff80SBenjamin Herrenschmidt } 34221c4ff80SBenjamin Herrenschmidt 343ed981856SPaul Mackerras int module_finalize(const Elf_Ehdr *hdr, 344ed981856SPaul Mackerras const Elf_Shdr *sechdrs, 345ed981856SPaul Mackerras struct module *me) 346ed981856SPaul Mackerras { 34721c4ff80SBenjamin Herrenschmidt const Elf_Shdr *sect; 34873c9ceabSJeremy Fitzhardinge int err; 349ed981856SPaul Mackerras 35073c9ceabSJeremy Fitzhardinge err = module_bug_finalize(hdr, sechdrs, me); 35173c9ceabSJeremy Fitzhardinge if (err) /* never true, currently */ 35273c9ceabSJeremy Fitzhardinge return err; 353ed981856SPaul Mackerras 35421c4ff80SBenjamin Herrenschmidt /* Apply feature fixups */ 35521c4ff80SBenjamin Herrenschmidt sect = find_section(hdr, sechdrs, "__ftr_fixup"); 35621c4ff80SBenjamin Herrenschmidt if (sect != NULL) 35721c4ff80SBenjamin Herrenschmidt do_feature_fixups(cur_cpu_spec->cpu_features, 35821c4ff80SBenjamin Herrenschmidt (void *)sect->sh_addr, 35921c4ff80SBenjamin Herrenschmidt (void *)sect->sh_addr + sect->sh_size); 36021c4ff80SBenjamin Herrenschmidt 361ed981856SPaul Mackerras return 0; 362ed981856SPaul Mackerras } 363ed981856SPaul Mackerras 364ed981856SPaul Mackerras void module_arch_cleanup(struct module *mod) 365ed981856SPaul Mackerras { 36673c9ceabSJeremy Fitzhardinge module_bug_cleanup(mod); 367ed981856SPaul Mackerras } 368ed981856SPaul Mackerras 369ed981856SPaul Mackerras struct bug_entry *module_find_bug(unsigned long bugaddr) 370ed981856SPaul Mackerras { 371ed981856SPaul Mackerras struct mod_arch_specific *mod; 372ed981856SPaul Mackerras unsigned int i; 373ed981856SPaul Mackerras struct bug_entry *bug; 374ed981856SPaul Mackerras 375ed981856SPaul Mackerras list_for_each_entry(mod, &module_bug_list, bug_list) { 376ed981856SPaul Mackerras bug = mod->bug_table; 377ed981856SPaul Mackerras for (i = 0; i < mod->num_bugs; ++i, ++bug) 378ed981856SPaul Mackerras if (bugaddr == bug->bug_addr) 379ed981856SPaul Mackerras return bug; 380ed981856SPaul Mackerras } 381ed981856SPaul Mackerras return NULL; 382ed981856SPaul Mackerras } 383