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
246int apply_relocate(Elf64_Shdr *sechdrs,
247 const char *strtab,
248 unsigned int symindex,
249 unsigned int relsec,
250 struct module *me)
251{
252 printk(KERN_ERR "%s: Non-ADD RELOCATION unsupported\n", me->name);
253 return -ENOEXEC;
254}
255
256
257
258
259static inline unsigned long my_r2(Elf64_Shdr *sechdrs, struct module *me)
260{
261 return sechdrs[me->arch.toc_section].sh_addr + 0x8000;
262}
263
264
265
266
267#define PPC_LO(v) ((v) & 0xffff)
268#define PPC_HI(v) (((v) >> 16) & 0xffff)
269#define PPC_HA(v) PPC_HI ((v) + 0x8000)
270
271
272static inline int create_stub(Elf64_Shdr *sechdrs,
273 struct ppc64_stub_entry *entry,
274 struct ppc64_opd_entry *opd,
275 struct module *me)
276{
277 Elf64_Half *loc1, *loc2;
278 long reladdr;
279
280 *entry = ppc64_stub;
281
282 loc1 = (Elf64_Half *)&entry->jump[2];
283 loc2 = (Elf64_Half *)&entry->jump[6];
284
285
286 reladdr = (unsigned long)entry - my_r2(sechdrs, me);
287 if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) {
288 printk("%s: Address %p of stub out of range of %p.\n",
289 me->name, (void *)reladdr, (void *)my_r2);
290 return 0;
291 }
292 DEBUGP("Stub %p get data from reladdr %li\n", entry, reladdr);
293
294 *loc1 = PPC_HA(reladdr);
295 *loc2 = PPC_LO(reladdr);
296 entry->opd.funcaddr = opd->funcaddr;
297 entry->opd.r2 = opd->r2;
298 return 1;
299}
300
301
302
303static unsigned long stub_for_addr(Elf64_Shdr *sechdrs,
304 unsigned long opdaddr,
305 struct module *me)
306{
307 struct ppc64_stub_entry *stubs;
308 struct ppc64_opd_entry *opd = (void *)opdaddr;
309 unsigned int i, num_stubs;
310
311 num_stubs = sechdrs[me->arch.stubs_section].sh_size / sizeof(*stubs);
312
313
314 stubs = (void *)sechdrs[me->arch.stubs_section].sh_addr;
315 for (i = 0; stubs[i].opd.funcaddr; i++) {
316 BUG_ON(i >= num_stubs);
317
318 if (stubs[i].opd.funcaddr == opd->funcaddr)
319 return (unsigned long)&stubs[i];
320 }
321
322 if (!create_stub(sechdrs, &stubs[i], opd, me))
323 return 0;
324
325 return (unsigned long)&stubs[i];
326}
327
328
329
330static int restore_r2(u32 *instruction, struct module *me)
331{
332 if (*instruction != PPC_INST_NOP) {
333 printk("%s: Expect noop after relocate, got %08x\n",
334 me->name, *instruction);
335 return 0;
336 }
337 *instruction = 0xe8410028;
338 return 1;
339}
340
341int apply_relocate_add(Elf64_Shdr *sechdrs,
342 const char *strtab,
343 unsigned int symindex,
344 unsigned int relsec,
345 struct module *me)
346{
347 unsigned int i;
348 Elf64_Rela *rela = (void *)sechdrs[relsec].sh_addr;
349 Elf64_Sym *sym;
350 unsigned long *location;
351 unsigned long value;
352
353 DEBUGP("Applying ADD relocate section %u to %u\n", relsec,
354 sechdrs[relsec].sh_info);
355 for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) {
356
357 location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
358 + rela[i].r_offset;
359
360 sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
361 + ELF64_R_SYM(rela[i].r_info);
362
363 DEBUGP("RELOC at %p: %li-type as %s (%lu) + %li\n",
364 location, (long)ELF64_R_TYPE(rela[i].r_info),
365 strtab + sym->st_name, (unsigned long)sym->st_value,
366 (long)rela[i].r_addend);
367
368
369 value = sym->st_value + rela[i].r_addend;
370
371 switch (ELF64_R_TYPE(rela[i].r_info)) {
372 case R_PPC64_ADDR32:
373
374 *(u32 *)location = value;
375 break;
376
377 case R_PPC64_ADDR64:
378
379 *(unsigned long *)location = value;
380 break;
381
382 case R_PPC64_TOC:
383 *(unsigned long *)location = my_r2(sechdrs, me);
384 break;
385
386 case R_PPC64_TOC16:
387
388 value -= my_r2(sechdrs, me);
389 if (value + 0x8000 > 0xffff) {
390 printk("%s: bad TOC16 relocation (%lu)\n",
391 me->name, value);
392 return -ENOEXEC;
393 }
394 *((uint16_t *) location)
395 = (*((uint16_t *) location) & ~0xffff)
396 | (value & 0xffff);
397 break;
398
399 case R_PPC64_TOC16_DS:
400
401 value -= my_r2(sechdrs, me);
402 if ((value & 3) != 0 || value + 0x8000 > 0xffff) {
403 printk("%s: bad TOC16_DS relocation (%lu)\n",
404 me->name, value);
405 return -ENOEXEC;
406 }
407 *((uint16_t *) location)
408 = (*((uint16_t *) location) & ~0xfffc)
409 | (value & 0xfffc);
410 break;
411
412 case R_PPC_REL24:
413
414 if (sym->st_shndx == SHN_UNDEF) {
415
416 value = stub_for_addr(sechdrs, value, me);
417 if (!value)
418 return -ENOENT;
419 if (!restore_r2((u32 *)location + 1, me))
420 return -ENOEXEC;
421 }
422
423
424 value -= (unsigned long)location;
425 if (value + 0x2000000 > 0x3ffffff || (value & 3) != 0){
426 printk("%s: REL24 %li out of range!\n",
427 me->name, (long int)value);
428 return -ENOEXEC;
429 }
430
431
432 *(uint32_t *)location
433 = (*(uint32_t *)location & ~0x03fffffc)
434 | (value & 0x03fffffc);
435 break;
436
437 case R_PPC64_REL64:
438
439 *location = value - (unsigned long)location;
440 break;
441
442 default:
443 printk("%s: Unknown ADD relocation: %lu\n",
444 me->name,
445 (unsigned long)ELF64_R_TYPE(rela[i].r_info));
446 return -ENOEXEC;
447 }
448 }
449
450#ifdef CONFIG_DYNAMIC_FTRACE
451 me->arch.toc = my_r2(sechdrs, me);
452 me->arch.tramp = stub_for_addr(sechdrs,
453 (unsigned long)ftrace_caller,
454 me);
455#endif
456
457 return 0;
458}
459