1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
33
34
35
36
37
38
39#if 0
40#define DEBUGP printk
41#else
42#define DEBUGP(fmt , ...)
43#endif
44
45
46
47
48struct ppc64_stub_entry
49{
50
51 unsigned char jump[28];
52 unsigned char unused[4];
53
54 struct ppc64_opd_entry opd;
55};
56
57
58
59
60
61
62
63static struct ppc64_stub_entry ppc64_stub =
64{ .jump = {
65 0x3d, 0x82, 0x00, 0x00,
66 0x39, 0x8c, 0x00, 0x00,
67
68 0xf8, 0x41, 0x00, 0x28,
69 0xe9, 0x6c, 0x00, 0x20,
70 0xe8, 0x4c, 0x00, 0x28,
71 0x7d, 0x69, 0x03, 0xa6,
72 0x4e, 0x80, 0x04, 0x20
73} };
74
75
76
77static unsigned int count_relocs(const Elf64_Rela *rela, unsigned int num)
78{
79 unsigned int i, r_info, r_addend, _count_relocs;
80
81
82 _count_relocs = 0;
83 r_info = 0;
84 r_addend = 0;
85 for (i = 0; i < num; i++)
86
87 if (ELF64_R_TYPE(rela[i].r_info) == R_PPC_REL24 &&
88 (r_info != ELF64_R_SYM(rela[i].r_info) ||
89 r_addend != rela[i].r_addend)) {
90 _count_relocs++;
91 r_info = ELF64_R_SYM(rela[i].r_info);
92 r_addend = rela[i].r_addend;
93 }
94
95 return _count_relocs;
96}
97
98static int relacmp(const void *_x, const void *_y)
99{
100 const Elf64_Rela *x, *y;
101
102 y = (Elf64_Rela *)_x;
103 x = (Elf64_Rela *)_y;
104
105
106
107
108
109 if (x->r_info < y->r_info)
110 return -1;
111 else if (x->r_info > y->r_info)
112 return 1;
113 else if (x->r_addend < y->r_addend)
114 return -1;
115 else if (x->r_addend > y->r_addend)
116 return 1;
117 else
118 return 0;
119}
120
121static void relaswap(void *_x, void *_y, int size)
122{
123 uint64_t *x, *y, tmp;
124 int i;
125
126 y = (uint64_t *)_x;
127 x = (uint64_t *)_y;
128
129 for (i = 0; i < sizeof(Elf64_Rela) / sizeof(uint64_t); i++) {
130 tmp = x[i];
131 x[i] = y[i];
132 y[i] = tmp;
133 }
134}
135
136
137static unsigned long get_stubs_size(const Elf64_Ehdr *hdr,
138 const Elf64_Shdr *sechdrs)
139{
140
141 unsigned long relocs = 1;
142 unsigned i;
143
144
145 for (i = 1; i < hdr->e_shnum; i++) {
146 if (sechdrs[i].sh_type == SHT_RELA) {
147 DEBUGP("Found relocations in section %u\n", i);
148 DEBUGP("Ptr: %p. Number: %lu\n",
149 (void *)sechdrs[i].sh_addr,
150 sechdrs[i].sh_size / sizeof(Elf64_Rela));
151
152
153
154
155
156
157 sort((void *)sechdrs[i].sh_addr,
158 sechdrs[i].sh_size / sizeof(Elf64_Rela),
159 sizeof(Elf64_Rela), relacmp, relaswap);
160
161 relocs += count_relocs((void *)sechdrs[i].sh_addr,
162 sechdrs[i].sh_size
163 / sizeof(Elf64_Rela));
164 }
165 }
166
167#ifdef CONFIG_DYNAMIC_FTRACE
168
169 relocs++;
170#endif
171
172 DEBUGP("Looks like a total of %lu stubs, max\n", relocs);
173 return relocs * sizeof(struct ppc64_stub_entry);
174}
175
176static void dedotify_versions(struct modversion_info *vers,
177 unsigned long size)
178{
179 struct modversion_info *end;
180
181 for (end = (void *)vers + size; vers < end; vers++)
182 if (vers->name[0] == '.')
183 memmove(vers->name, vers->name+1, strlen(vers->name));
184}
185
186
187static void dedotify(Elf64_Sym *syms, unsigned int numsyms, char *strtab)
188{
189 unsigned int i;
190
191 for (i = 1; i < numsyms; i++) {
192 if (syms[i].st_shndx == SHN_UNDEF) {
193 char *name = strtab + syms[i].st_name;
194 if (name[0] == '.')
195 memmove(name, name+1, strlen(name));
196 }
197 }
198}
199
200int module_frob_arch_sections(Elf64_Ehdr *hdr,
201 Elf64_Shdr *sechdrs,
202 char *secstrings,
203 struct module *me)
204{
205 unsigned int i;
206
207
208 for (i = 1; i < hdr->e_shnum; i++) {
209 char *p;
210 if (strcmp(secstrings + sechdrs[i].sh_name, ".stubs") == 0)
211 me->arch.stubs_section = i;
212 else if (strcmp(secstrings + sechdrs[i].sh_name, ".toc") == 0)
213 me->arch.toc_section = i;
214 else if (strcmp(secstrings+sechdrs[i].sh_name,"__versions")==0)
215 dedotify_versions((void *)hdr + sechdrs[i].sh_offset,
216 sechdrs[i].sh_size);
217
218
219 while ((p = strstr(secstrings + sechdrs[i].sh_name, ".init")))
220 p[0] = '_';
221
222 if (sechdrs[i].sh_type == SHT_SYMTAB)
223 dedotify((void *)hdr + sechdrs[i].sh_offset,
224 sechdrs[i].sh_size / sizeof(Elf64_Sym),
225 (void *)hdr
226 + sechdrs[sechdrs[i].sh_link].sh_offset);
227 }
228
229 if (!me->arch.stubs_section) {
230 printk("%s: doesn't contain .stubs.\n", me->name);
231 return -ENOEXEC;
232 }
233
234
235
236
237
238 if (!me->arch.toc_section)
239 me->arch.toc_section = me->arch.stubs_section;
240
241
242 sechdrs[me->arch.stubs_section].sh_size = get_stubs_size(hdr, sechdrs);
243 return 0;
244}
245
246
247
248
249static inline unsigned long my_r2(Elf64_Shdr *sechdrs, struct module *me)
250{
251 return sechdrs[me->arch.toc_section].sh_addr + 0x8000;
252}
253
254
255
256
257#define PPC_LO(v) ((v) & 0xffff)
258#define PPC_HI(v) (((v) >> 16) & 0xffff)
259#define PPC_HA(v) PPC_HI ((v) + 0x8000)
260
261
262static inline int create_stub(Elf64_Shdr *sechdrs,
263 struct ppc64_stub_entry *entry,
264 struct ppc64_opd_entry *opd,
265 struct module *me)
266{
267 Elf64_Half *loc1, *loc2;
268 long reladdr;
269
270 *entry = ppc64_stub;
271
272 loc1 = (Elf64_Half *)&entry->jump[2];
273 loc2 = (Elf64_Half *)&entry->jump[6];
274
275
276 reladdr = (unsigned long)entry - my_r2(sechdrs, me);
277 if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) {
278 printk("%s: Address %p of stub out of range of %p.\n",
279 me->name, (void *)reladdr, (void *)my_r2);
280 return 0;
281 }
282 DEBUGP("Stub %p get data from reladdr %li\n", entry, reladdr);
283
284 *loc1 = PPC_HA(reladdr);
285 *loc2 = PPC_LO(reladdr);
286 entry->opd.funcaddr = opd->funcaddr;
287 entry->opd.r2 = opd->r2;
288 return 1;
289}
290
291
292
293static unsigned long stub_for_addr(Elf64_Shdr *sechdrs,
294 unsigned long opdaddr,
295 struct module *me)
296{
297 struct ppc64_stub_entry *stubs;
298 struct ppc64_opd_entry *opd = (void *)opdaddr;
299 unsigned int i, num_stubs;
300
301 num_stubs = sechdrs[me->arch.stubs_section].sh_size / sizeof(*stubs);
302
303
304 stubs = (void *)sechdrs[me->arch.stubs_section].sh_addr;
305 for (i = 0; stubs[i].opd.funcaddr; i++) {
306 BUG_ON(i >= num_stubs);
307
308 if (stubs[i].opd.funcaddr == opd->funcaddr)
309 return (unsigned long)&stubs[i];
310 }
311
312 if (!create_stub(sechdrs, &stubs[i], opd, me))
313 return 0;
314
315 return (unsigned long)&stubs[i];
316}
317
318
319
320static int restore_r2(u32 *instruction, struct module *me)
321{
322 if (*instruction != PPC_INST_NOP) {
323 printk("%s: Expect noop after relocate, got %08x\n",
324 me->name, *instruction);
325 return 0;
326 }
327 *instruction = 0xe8410028;
328 return 1;
329}
330
331int apply_relocate_add(Elf64_Shdr *sechdrs,
332 const char *strtab,
333 unsigned int symindex,
334 unsigned int relsec,
335 struct module *me)
336{
337 unsigned int i;
338 Elf64_Rela *rela = (void *)sechdrs[relsec].sh_addr;
339 Elf64_Sym *sym;
340 unsigned long *location;
341 unsigned long value;
342
343 DEBUGP("Applying ADD relocate section %u to %u\n", relsec,
344 sechdrs[relsec].sh_info);
345 for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) {
346
347 location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
348 + rela[i].r_offset;
349
350 sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
351 + ELF64_R_SYM(rela[i].r_info);
352
353 DEBUGP("RELOC at %p: %li-type as %s (%lu) + %li\n",
354 location, (long)ELF64_R_TYPE(rela[i].r_info),
355 strtab + sym->st_name, (unsigned long)sym->st_value,
356 (long)rela[i].r_addend);
357
358
359 value = sym->st_value + rela[i].r_addend;
360
361 switch (ELF64_R_TYPE(rela[i].r_info)) {
362 case R_PPC64_ADDR32:
363
364 *(u32 *)location = value;
365 break;
366
367 case R_PPC64_ADDR64:
368
369 *(unsigned long *)location = value;
370 break;
371
372 case R_PPC64_TOC:
373 *(unsigned long *)location = my_r2(sechdrs, me);
374 break;
375
376 case R_PPC64_TOC16:
377
378 value -= my_r2(sechdrs, me);
379 if (value + 0x8000 > 0xffff) {
380 printk("%s: bad TOC16 relocation (%lu)\n",
381 me->name, value);
382 return -ENOEXEC;
383 }
384 *((uint16_t *) location)
385 = (*((uint16_t *) location) & ~0xffff)
386 | (value & 0xffff);
387 break;
388
389 case R_PPC64_TOC16_LO:
390
391 value -= my_r2(sechdrs, me);
392 *((uint16_t *) location)
393 = (*((uint16_t *) location) & ~0xffff)
394 | (value & 0xffff);
395 break;
396
397 case R_PPC64_TOC16_DS:
398
399 value -= my_r2(sechdrs, me);
400 if ((value & 3) != 0 || value + 0x8000 > 0xffff) {
401 printk("%s: bad TOC16_DS relocation (%lu)\n",
402 me->name, value);
403 return -ENOEXEC;
404 }
405 *((uint16_t *) location)
406 = (*((uint16_t *) location) & ~0xfffc)
407 | (value & 0xfffc);
408 break;
409
410 case R_PPC64_TOC16_LO_DS:
411
412 value -= my_r2(sechdrs, me);
413 if ((value & 3) != 0) {
414 printk("%s: bad TOC16_LO_DS relocation (%lu)\n",
415 me->name, value);
416 return -ENOEXEC;
417 }
418 *((uint16_t *) location)
419 = (*((uint16_t *) location) & ~0xfffc)
420 | (value & 0xfffc);
421 break;
422
423 case R_PPC64_TOC16_HA:
424
425 value -= my_r2(sechdrs, me);
426 value = ((value + 0x8000) >> 16);
427 *((uint16_t *) location)
428 = (*((uint16_t *) location) & ~0xffff)
429 | (value & 0xffff);
430 break;
431
432 case R_PPC_REL24:
433
434 if (sym->st_shndx == SHN_UNDEF) {
435
436 value = stub_for_addr(sechdrs, value, me);
437 if (!value)
438 return -ENOENT;
439 if (!restore_r2((u32 *)location + 1, me))
440 return -ENOEXEC;
441 }
442
443
444 value -= (unsigned long)location;
445 if (value + 0x2000000 > 0x3ffffff || (value & 3) != 0){
446 printk("%s: REL24 %li out of range!\n",
447 me->name, (long int)value);
448 return -ENOEXEC;
449 }
450
451
452 *(uint32_t *)location
453 = (*(uint32_t *)location & ~0x03fffffc)
454 | (value & 0x03fffffc);
455 break;
456
457 case R_PPC64_REL64:
458
459 *location = value - (unsigned long)location;
460 break;
461
462 default:
463 printk("%s: Unknown ADD relocation: %lu\n",
464 me->name,
465 (unsigned long)ELF64_R_TYPE(rela[i].r_info));
466 return -ENOEXEC;
467 }
468 }
469
470#ifdef CONFIG_DYNAMIC_FTRACE
471 me->arch.toc = my_r2(sechdrs, me);
472 me->arch.tramp = stub_for_addr(sechdrs,
473 (unsigned long)ftrace_caller,
474 me);
475#endif
476
477 return 0;
478}
479