xref: /openbmc/linux/arch/powerpc/kernel/module_32.c (revision eda09fbd)
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