• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/arch/powerpc/kernel/
1/*  Kernel module help for PPC64.
2    Copyright (C) 2001, 2003 Rusty Russell IBM Corporation.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17*/
18#include <linux/module.h>
19#include <linux/elf.h>
20#include <linux/moduleloader.h>
21#include <linux/err.h>
22#include <linux/vmalloc.h>
23#include <linux/ftrace.h>
24#include <linux/bug.h>
25#include <asm/module.h>
26#include <asm/firmware.h>
27#include <asm/code-patching.h>
28#include <linux/sort.h>
29
30#include "setup.h"
31
32#define DEBUGP(fmt , ...)
33
34/* Like PPC32, we need little trampolines to do > 24-bit jumps (into
35   the kernel itself).  But on PPC64, these need to be used for every
36   jump, actually, to reset r2 (TOC+0x8000). */
37struct ppc64_stub_entry
38{
39	/* 28 byte jump instruction sequence (7 instructions) */
40	unsigned char jump[28];
41	unsigned char unused[4];
42	/* Data for the above code */
43	struct ppc64_opd_entry opd;
44};
45
46/* We use a stub to fix up r2 (TOC ptr) and to jump to the (external)
47   function which may be more than 24-bits away.  We could simply
48   patch the new r2 value and function pointer into the stub, but it's
49   significantly shorter to put these values at the end of the stub
50   code, and patch the stub address (32-bits relative to the TOC ptr,
51   r2) into the stub. */
52static struct ppc64_stub_entry ppc64_stub =
53{ .jump = {
54	0x3d, 0x82, 0x00, 0x00, /* addis   r12,r2, <high> */
55	0x39, 0x8c, 0x00, 0x00, /* addi    r12,r12, <low> */
56	/* Save current r2 value in magic place on the stack. */
57	0xf8, 0x41, 0x00, 0x28, /* std     r2,40(r1) */
58	0xe9, 0x6c, 0x00, 0x20, /* ld      r11,32(r12) */
59	0xe8, 0x4c, 0x00, 0x28, /* ld      r2,40(r12) */
60	0x7d, 0x69, 0x03, 0xa6, /* mtctr   r11 */
61	0x4e, 0x80, 0x04, 0x20  /* bctr */
62} };
63
64/* Count how many different 24-bit relocations (different symbol,
65   different addend) */
66static unsigned int count_relocs(const Elf64_Rela *rela, unsigned int num)
67{
68	unsigned int i, r_info, r_addend, _count_relocs;
69
70	_count_relocs = 0;
71	r_info = 0;
72	r_addend = 0;
73	for (i = 0; i < num; i++)
74		/* Only count 24-bit relocs, others don't need stubs */
75		if (ELF64_R_TYPE(rela[i].r_info) == R_PPC_REL24 &&
76		    (r_info != ELF64_R_SYM(rela[i].r_info) ||
77		     r_addend != rela[i].r_addend)) {
78			_count_relocs++;
79			r_info = ELF64_R_SYM(rela[i].r_info);
80			r_addend = rela[i].r_addend;
81		}
82
83	return _count_relocs;
84}
85
86static int relacmp(const void *_x, const void *_y)
87{
88	const Elf64_Rela *x, *y;
89
90	y = (Elf64_Rela *)_x;
91	x = (Elf64_Rela *)_y;
92
93	/* Compare the entire r_info (as opposed to ELF64_R_SYM(r_info) only) to
94	 * make the comparison cheaper/faster. It won't affect the sorting or
95	 * the counting algorithms' performance
96	 */
97	if (x->r_info < y->r_info)
98		return -1;
99	else if (x->r_info > y->r_info)
100		return 1;
101	else if (x->r_addend < y->r_addend)
102		return -1;
103	else if (x->r_addend > y->r_addend)
104		return 1;
105	else
106		return 0;
107}
108
109static void relaswap(void *_x, void *_y, int size)
110{
111	uint64_t *x, *y, tmp;
112	int i;
113
114	y = (uint64_t *)_x;
115	x = (uint64_t *)_y;
116
117	for (i = 0; i < sizeof(Elf64_Rela) / sizeof(uint64_t); i++) {
118		tmp = x[i];
119		x[i] = y[i];
120		y[i] = tmp;
121	}
122}
123
124/* Get size of potential trampolines required. */
125static unsigned long get_stubs_size(const Elf64_Ehdr *hdr,
126				    const Elf64_Shdr *sechdrs)
127{
128	/* One extra reloc so it's always 0-funcaddr terminated */
129	unsigned long relocs = 1;
130	unsigned i;
131
132	/* Every relocated section... */
133	for (i = 1; i < hdr->e_shnum; i++) {
134		if (sechdrs[i].sh_type == SHT_RELA) {
135			DEBUGP("Found relocations in section %u\n", i);
136			DEBUGP("Ptr: %p.  Number: %lu\n",
137			       (void *)sechdrs[i].sh_addr,
138			       sechdrs[i].sh_size / sizeof(Elf64_Rela));
139
140			/* Sort the relocation information based on a symbol and
141			 * addend key. This is a stable O(n*log n) complexity
142			 * alogrithm but it will reduce the complexity of
143			 * count_relocs() to linear complexity O(n)
144			 */
145			sort((void *)sechdrs[i].sh_addr,
146			     sechdrs[i].sh_size / sizeof(Elf64_Rela),
147			     sizeof(Elf64_Rela), relacmp, relaswap);
148
149			relocs += count_relocs((void *)sechdrs[i].sh_addr,
150					       sechdrs[i].sh_size
151					       / sizeof(Elf64_Rela));
152		}
153	}
154
155#ifdef CONFIG_DYNAMIC_FTRACE
156	/* make the trampoline to the ftrace_caller */
157	relocs++;
158#endif
159
160	DEBUGP("Looks like a total of %lu stubs, max\n", relocs);
161	return relocs * sizeof(struct ppc64_stub_entry);
162}
163
164static void dedotify_versions(struct modversion_info *vers,
165			      unsigned long size)
166{
167	struct modversion_info *end;
168
169	for (end = (void *)vers + size; vers < end; vers++)
170		if (vers->name[0] == '.')
171			memmove(vers->name, vers->name+1, strlen(vers->name));
172}
173
174/* Undefined symbols which refer to .funcname, hack to funcname */
175static void dedotify(Elf64_Sym *syms, unsigned int numsyms, char *strtab)
176{
177	unsigned int i;
178
179	for (i = 1; i < numsyms; i++) {
180		if (syms[i].st_shndx == SHN_UNDEF) {
181			char *name = strtab + syms[i].st_name;
182			if (name[0] == '.')
183				memmove(name, name+1, strlen(name));
184		}
185	}
186}
187
188int module_frob_arch_sections(Elf64_Ehdr *hdr,
189			      Elf64_Shdr *sechdrs,
190			      char *secstrings,
191			      struct module *me)
192{
193	unsigned int i;
194
195	/* Find .toc and .stubs sections, symtab and strtab */
196	for (i = 1; i < hdr->e_shnum; i++) {
197		char *p;
198		if (strcmp(secstrings + sechdrs[i].sh_name, ".stubs") == 0)
199			me->arch.stubs_section = i;
200		else if (strcmp(secstrings + sechdrs[i].sh_name, ".toc") == 0)
201			me->arch.toc_section = i;
202		else if (strcmp(secstrings+sechdrs[i].sh_name,"__versions")==0)
203			dedotify_versions((void *)hdr + sechdrs[i].sh_offset,
204					  sechdrs[i].sh_size);
205
206		/* We don't handle .init for the moment: rename to _init */
207		while ((p = strstr(secstrings + sechdrs[i].sh_name, ".init")))
208			p[0] = '_';
209
210		if (sechdrs[i].sh_type == SHT_SYMTAB)
211			dedotify((void *)hdr + sechdrs[i].sh_offset,
212				 sechdrs[i].sh_size / sizeof(Elf64_Sym),
213				 (void *)hdr
214				 + sechdrs[sechdrs[i].sh_link].sh_offset);
215	}
216
217	if (!me->arch.stubs_section) {
218		printk("%s: doesn't contain .stubs.\n", me->name);
219		return -ENOEXEC;
220	}
221
222	/* If we don't have a .toc, just use .stubs.  We need to set r2
223	   to some reasonable value in case the module calls out to
224	   other functions via a stub, or if a function pointer escapes
225	   the module by some means.  */
226	if (!me->arch.toc_section)
227		me->arch.toc_section = me->arch.stubs_section;
228
229	/* Override the stubs size */
230	sechdrs[me->arch.stubs_section].sh_size = get_stubs_size(hdr, sechdrs);
231	return 0;
232}
233
234int apply_relocate(Elf64_Shdr *sechdrs,
235		   const char *strtab,
236		   unsigned int symindex,
237		   unsigned int relsec,
238		   struct module *me)
239{
240	printk(KERN_ERR "%s: Non-ADD RELOCATION unsupported\n", me->name);
241	return -ENOEXEC;
242}
243
244/* r2 is the TOC pointer: it actually points 0x8000 into the TOC (this
245   gives the value maximum span in an instruction which uses a signed
246   offset) */
247static inline unsigned long my_r2(Elf64_Shdr *sechdrs, struct module *me)
248{
249	return sechdrs[me->arch.toc_section].sh_addr + 0x8000;
250}
251
252/* Both low and high 16 bits are added as SIGNED additions, so if low
253   16 bits has high bit set, high 16 bits must be adjusted.  These
254   macros do that (stolen from binutils). */
255#define PPC_LO(v) ((v) & 0xffff)
256#define PPC_HI(v) (((v) >> 16) & 0xffff)
257#define PPC_HA(v) PPC_HI ((v) + 0x8000)
258
259/* Patch stub to reference function and correct r2 value. */
260static inline int create_stub(Elf64_Shdr *sechdrs,
261			      struct ppc64_stub_entry *entry,
262			      struct ppc64_opd_entry *opd,
263			      struct module *me)
264{
265	Elf64_Half *loc1, *loc2;
266	long reladdr;
267
268	*entry = ppc64_stub;
269
270	loc1 = (Elf64_Half *)&entry->jump[2];
271	loc2 = (Elf64_Half *)&entry->jump[6];
272
273	/* Stub uses address relative to r2. */
274	reladdr = (unsigned long)entry - my_r2(sechdrs, me);
275	if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) {
276		printk("%s: Address %p of stub out of range of %p.\n",
277		       me->name, (void *)reladdr, (void *)my_r2);
278		return 0;
279	}
280	DEBUGP("Stub %p get data from reladdr %li\n", entry, reladdr);
281
282	*loc1 = PPC_HA(reladdr);
283	*loc2 = PPC_LO(reladdr);
284	entry->opd.funcaddr = opd->funcaddr;
285	entry->opd.r2 = opd->r2;
286	return 1;
287}
288
289/* Create stub to jump to function described in this OPD: we need the
290   stub to set up the TOC ptr (r2) for the function. */
291static unsigned long stub_for_addr(Elf64_Shdr *sechdrs,
292				   unsigned long opdaddr,
293				   struct module *me)
294{
295	struct ppc64_stub_entry *stubs;
296	struct ppc64_opd_entry *opd = (void *)opdaddr;
297	unsigned int i, num_stubs;
298
299	num_stubs = sechdrs[me->arch.stubs_section].sh_size / sizeof(*stubs);
300
301	/* Find this stub, or if that fails, the next avail. entry */
302	stubs = (void *)sechdrs[me->arch.stubs_section].sh_addr;
303	for (i = 0; stubs[i].opd.funcaddr; i++) {
304		BUG_ON(i >= num_stubs);
305
306		if (stubs[i].opd.funcaddr == opd->funcaddr)
307			return (unsigned long)&stubs[i];
308	}
309
310	if (!create_stub(sechdrs, &stubs[i], opd, me))
311		return 0;
312
313	return (unsigned long)&stubs[i];
314}
315
316/* We expect a noop next: if it is, replace it with instruction to
317   restore r2. */
318static int restore_r2(u32 *instruction, struct module *me)
319{
320	if (*instruction != PPC_INST_NOP) {
321		printk("%s: Expect noop after relocate, got %08x\n",
322		       me->name, *instruction);
323		return 0;
324	}
325	*instruction = 0xe8410028;	/* ld r2,40(r1) */
326	return 1;
327}
328
329int apply_relocate_add(Elf64_Shdr *sechdrs,
330		       const char *strtab,
331		       unsigned int symindex,
332		       unsigned int relsec,
333		       struct module *me)
334{
335	unsigned int i;
336	Elf64_Rela *rela = (void *)sechdrs[relsec].sh_addr;
337	Elf64_Sym *sym;
338	unsigned long *location;
339	unsigned long value;
340
341	DEBUGP("Applying ADD relocate section %u to %u\n", relsec,
342	       sechdrs[relsec].sh_info);
343	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) {
344		/* This is where to make the change */
345		location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
346			+ rela[i].r_offset;
347		/* This is the symbol it is referring to */
348		sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
349			+ ELF64_R_SYM(rela[i].r_info);
350
351		DEBUGP("RELOC at %p: %li-type as %s (%lu) + %li\n",
352		       location, (long)ELF64_R_TYPE(rela[i].r_info),
353		       strtab + sym->st_name, (unsigned long)sym->st_value,
354		       (long)rela[i].r_addend);
355
356		/* `Everything is relative'. */
357		value = sym->st_value + rela[i].r_addend;
358
359		switch (ELF64_R_TYPE(rela[i].r_info)) {
360		case R_PPC64_ADDR32:
361			/* Simply set it */
362			*(u32 *)location = value;
363			break;
364
365		case R_PPC64_ADDR64:
366			/* Simply set it */
367			*(unsigned long *)location = value;
368			break;
369
370		case R_PPC64_TOC:
371			*(unsigned long *)location = my_r2(sechdrs, me);
372			break;
373
374		case R_PPC64_TOC16:
375			/* Subtract TOC pointer */
376			value -= my_r2(sechdrs, me);
377			if (value + 0x8000 > 0xffff) {
378				printk("%s: bad TOC16 relocation (%lu)\n",
379				       me->name, value);
380				return -ENOEXEC;
381			}
382			*((uint16_t *) location)
383				= (*((uint16_t *) location) & ~0xffff)
384				| (value & 0xffff);
385			break;
386
387		case R_PPC64_TOC16_DS:
388			/* Subtract TOC pointer */
389			value -= my_r2(sechdrs, me);
390			if ((value & 3) != 0 || value + 0x8000 > 0xffff) {
391				printk("%s: bad TOC16_DS relocation (%lu)\n",
392				       me->name, value);
393				return -ENOEXEC;
394			}
395			*((uint16_t *) location)
396				= (*((uint16_t *) location) & ~0xfffc)
397				| (value & 0xfffc);
398			break;
399
400		case R_PPC_REL24:
401			if (sym->st_shndx == SHN_UNDEF) {
402				/* External: go via stub */
403				value = stub_for_addr(sechdrs, value, me);
404				if (!value)
405					return -ENOENT;
406				if (!restore_r2((u32 *)location + 1, me))
407					return -ENOEXEC;
408			}
409
410			/* Convert value to relative */
411			value -= (unsigned long)location;
412			if (value + 0x2000000 > 0x3ffffff || (value & 3) != 0){
413				printk("%s: REL24 %li out of range!\n",
414				       me->name, (long int)value);
415				return -ENOEXEC;
416			}
417
418			/* Only replace bits 2 through 26 */
419			*(uint32_t *)location
420				= (*(uint32_t *)location & ~0x03fffffc)
421				| (value & 0x03fffffc);
422			break;
423
424		case R_PPC64_REL64:
425			/* 64 bits relative (used by features fixups) */
426			*location = value - (unsigned long)location;
427			break;
428
429		default:
430			printk("%s: Unknown ADD relocation: %lu\n",
431			       me->name,
432			       (unsigned long)ELF64_R_TYPE(rela[i].r_info));
433			return -ENOEXEC;
434		}
435	}
436
437#ifdef CONFIG_DYNAMIC_FTRACE
438	me->arch.toc = my_r2(sechdrs, me);
439	me->arch.tramp = stub_for_addr(sechdrs,
440				       (unsigned long)ftrace_caller,
441				       me);
442#endif
443
444	return 0;
445}
446