aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/module.c
diff options
context:
space:
mode:
authorJonathan Herman <hermanjl@cs.unc.edu>2013-01-17 16:15:55 -0500
committerJonathan Herman <hermanjl@cs.unc.edu>2013-01-17 16:15:55 -0500
commit8dea78da5cee153b8af9c07a2745f6c55057fe12 (patch)
treea8f4d49d63b1ecc92f2fddceba0655b2472c5bd9 /arch/mips/kernel/module.c
parent406089d01562f1e2bf9f089fd7637009ebaad589 (diff)
Patched in Tegra support.
Diffstat (limited to 'arch/mips/kernel/module.c')
-rw-r--r--arch/mips/kernel/module.c151
1 files changed, 123 insertions, 28 deletions
diff --git a/arch/mips/kernel/module.c b/arch/mips/kernel/module.c
index 07ff5812ffa..4b930ac4aff 100644
--- a/arch/mips/kernel/module.c
+++ b/arch/mips/kernel/module.c
@@ -28,6 +28,7 @@
28#include <linux/fs.h> 28#include <linux/fs.h>
29#include <linux/string.h> 29#include <linux/string.h>
30#include <linux/kernel.h> 30#include <linux/kernel.h>
31#include <linux/module.h>
31#include <linux/spinlock.h> 32#include <linux/spinlock.h>
32#include <linux/jump_label.h> 33#include <linux/jump_label.h>
33 34
@@ -39,6 +40,8 @@ struct mips_hi16 {
39 Elf_Addr value; 40 Elf_Addr value;
40}; 41};
41 42
43static struct mips_hi16 *mips_hi16_list;
44
42static LIST_HEAD(dbe_list); 45static LIST_HEAD(dbe_list);
43static DEFINE_SPINLOCK(dbe_lock); 46static DEFINE_SPINLOCK(dbe_lock);
44 47
@@ -51,7 +54,7 @@ void *module_alloc(unsigned long size)
51} 54}
52#endif 55#endif
53 56
54int apply_r_mips_none(struct module *me, u32 *location, Elf_Addr v) 57static int apply_r_mips_none(struct module *me, u32 *location, Elf_Addr v)
55{ 58{
56 return 0; 59 return 0;
57} 60}
@@ -63,6 +66,13 @@ static int apply_r_mips_32_rel(struct module *me, u32 *location, Elf_Addr v)
63 return 0; 66 return 0;
64} 67}
65 68
69static int apply_r_mips_32_rela(struct module *me, u32 *location, Elf_Addr v)
70{
71 *location = v;
72
73 return 0;
74}
75
66static int apply_r_mips_26_rel(struct module *me, u32 *location, Elf_Addr v) 76static int apply_r_mips_26_rel(struct module *me, u32 *location, Elf_Addr v)
67{ 77{
68 if (v % 4) { 78 if (v % 4) {
@@ -84,6 +94,26 @@ static int apply_r_mips_26_rel(struct module *me, u32 *location, Elf_Addr v)
84 return 0; 94 return 0;
85} 95}
86 96
97static int apply_r_mips_26_rela(struct module *me, u32 *location, Elf_Addr v)
98{
99 if (v % 4) {
100 pr_err("module %s: dangerous R_MIPS_26 RELArelocation\n",
101 me->name);
102 return -ENOEXEC;
103 }
104
105 if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) {
106 printk(KERN_ERR
107 "module %s: relocation overflow\n",
108 me->name);
109 return -ENOEXEC;
110 }
111
112 *location = (*location & ~0x03ffffff) | ((v >> 2) & 0x03ffffff);
113
114 return 0;
115}
116
87static int apply_r_mips_hi16_rel(struct module *me, u32 *location, Elf_Addr v) 117static int apply_r_mips_hi16_rel(struct module *me, u32 *location, Elf_Addr v)
88{ 118{
89 struct mips_hi16 *n; 119 struct mips_hi16 *n;
@@ -99,34 +129,32 @@ static int apply_r_mips_hi16_rel(struct module *me, u32 *location, Elf_Addr v)
99 129
100 n->addr = (Elf_Addr *)location; 130 n->addr = (Elf_Addr *)location;
101 n->value = v; 131 n->value = v;
102 n->next = me->arch.r_mips_hi16_list; 132 n->next = mips_hi16_list;
103 me->arch.r_mips_hi16_list = n; 133 mips_hi16_list = n;
104 134
105 return 0; 135 return 0;
106} 136}
107 137
108static void free_relocation_chain(struct mips_hi16 *l) 138static int apply_r_mips_hi16_rela(struct module *me, u32 *location, Elf_Addr v)
109{ 139{
110 struct mips_hi16 *next; 140 *location = (*location & 0xffff0000) |
141 ((((long long) v + 0x8000LL) >> 16) & 0xffff);
111 142
112 while (l) { 143 return 0;
113 next = l->next;
114 kfree(l);
115 l = next;
116 }
117} 144}
118 145
119static int apply_r_mips_lo16_rel(struct module *me, u32 *location, Elf_Addr v) 146static int apply_r_mips_lo16_rel(struct module *me, u32 *location, Elf_Addr v)
120{ 147{
121 unsigned long insnlo = *location; 148 unsigned long insnlo = *location;
122 struct mips_hi16 *l;
123 Elf_Addr val, vallo; 149 Elf_Addr val, vallo;
124 150
125 /* Sign extend the addend we extract from the lo insn. */ 151 /* Sign extend the addend we extract from the lo insn. */
126 vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000; 152 vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000;
127 153
128 if (me->arch.r_mips_hi16_list != NULL) { 154 if (mips_hi16_list != NULL) {
129 l = me->arch.r_mips_hi16_list; 155 struct mips_hi16 *l;
156
157 l = mips_hi16_list;
130 while (l != NULL) { 158 while (l != NULL) {
131 struct mips_hi16 *next; 159 struct mips_hi16 *next;
132 unsigned long insn; 160 unsigned long insn;
@@ -161,7 +189,7 @@ static int apply_r_mips_lo16_rel(struct module *me, u32 *location, Elf_Addr v)
161 l = next; 189 l = next;
162 } 190 }
163 191
164 me->arch.r_mips_hi16_list = NULL; 192 mips_hi16_list = NULL;
165 } 193 }
166 194
167 /* 195 /*
@@ -174,14 +202,43 @@ static int apply_r_mips_lo16_rel(struct module *me, u32 *location, Elf_Addr v)
174 return 0; 202 return 0;
175 203
176out_danger: 204out_danger:
177 free_relocation_chain(l);
178 me->arch.r_mips_hi16_list = NULL;
179
180 pr_err("module %s: dangerous R_MIPS_LO16 REL relocation\n", me->name); 205 pr_err("module %s: dangerous R_MIPS_LO16 REL relocation\n", me->name);
181 206
182 return -ENOEXEC; 207 return -ENOEXEC;
183} 208}
184 209
210static int apply_r_mips_lo16_rela(struct module *me, u32 *location, Elf_Addr v)
211{
212 *location = (*location & 0xffff0000) | (v & 0xffff);
213
214 return 0;
215}
216
217static int apply_r_mips_64_rela(struct module *me, u32 *location, Elf_Addr v)
218{
219 *(Elf_Addr *)location = v;
220
221 return 0;
222}
223
224static int apply_r_mips_higher_rela(struct module *me, u32 *location,
225 Elf_Addr v)
226{
227 *location = (*location & 0xffff0000) |
228 ((((long long) v + 0x80008000LL) >> 32) & 0xffff);
229
230 return 0;
231}
232
233static int apply_r_mips_highest_rela(struct module *me, u32 *location,
234 Elf_Addr v)
235{
236 *location = (*location & 0xffff0000) |
237 ((((long long) v + 0x800080008000LL) >> 48) & 0xffff);
238
239 return 0;
240}
241
185static int (*reloc_handlers_rel[]) (struct module *me, u32 *location, 242static int (*reloc_handlers_rel[]) (struct module *me, u32 *location,
186 Elf_Addr v) = { 243 Elf_Addr v) = {
187 [R_MIPS_NONE] = apply_r_mips_none, 244 [R_MIPS_NONE] = apply_r_mips_none,
@@ -191,6 +248,18 @@ static int (*reloc_handlers_rel[]) (struct module *me, u32 *location,
191 [R_MIPS_LO16] = apply_r_mips_lo16_rel 248 [R_MIPS_LO16] = apply_r_mips_lo16_rel
192}; 249};
193 250
251static int (*reloc_handlers_rela[]) (struct module *me, u32 *location,
252 Elf_Addr v) = {
253 [R_MIPS_NONE] = apply_r_mips_none,
254 [R_MIPS_32] = apply_r_mips_32_rela,
255 [R_MIPS_26] = apply_r_mips_26_rela,
256 [R_MIPS_HI16] = apply_r_mips_hi16_rela,
257 [R_MIPS_LO16] = apply_r_mips_lo16_rela,
258 [R_MIPS_64] = apply_r_mips_64_rela,
259 [R_MIPS_HIGHER] = apply_r_mips_higher_rela,
260 [R_MIPS_HIGHEST] = apply_r_mips_highest_rela
261};
262
194int apply_relocate(Elf_Shdr *sechdrs, const char *strtab, 263int apply_relocate(Elf_Shdr *sechdrs, const char *strtab,
195 unsigned int symindex, unsigned int relsec, 264 unsigned int symindex, unsigned int relsec,
196 struct module *me) 265 struct module *me)
@@ -205,7 +274,6 @@ int apply_relocate(Elf_Shdr *sechdrs, const char *strtab,
205 pr_debug("Applying relocate section %u to %u\n", relsec, 274 pr_debug("Applying relocate section %u to %u\n", relsec,
206 sechdrs[relsec].sh_info); 275 sechdrs[relsec].sh_info);
207 276
208 me->arch.r_mips_hi16_list = NULL;
209 for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { 277 for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
210 /* This is where to make the change */ 278 /* This is where to make the change */
211 location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr 279 location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
@@ -229,17 +297,44 @@ int apply_relocate(Elf_Shdr *sechdrs, const char *strtab,
229 return res; 297 return res;
230 } 298 }
231 299
232 /* 300 return 0;
233 * Normally the hi16 list should be deallocated at this point. A 301}
234 * malformed binary however could contain a series of R_MIPS_HI16
235 * relocations not followed by a R_MIPS_LO16 relocation. In that
236 * case, free up the list and return an error.
237 */
238 if (me->arch.r_mips_hi16_list) {
239 free_relocation_chain(me->arch.r_mips_hi16_list);
240 me->arch.r_mips_hi16_list = NULL;
241 302
242 return -ENOEXEC; 303int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
304 unsigned int symindex, unsigned int relsec,
305 struct module *me)
306{
307 Elf_Mips_Rela *rel = (void *) sechdrs[relsec].sh_addr;
308 Elf_Sym *sym;
309 u32 *location;
310 unsigned int i;
311 Elf_Addr v;
312 int res;
313
314 pr_debug("Applying relocate section %u to %u\n", relsec,
315 sechdrs[relsec].sh_info);
316
317 for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
318 /* This is where to make the change */
319 location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
320 + rel[i].r_offset;
321 /* This is the symbol it is referring to */
322 sym = (Elf_Sym *)sechdrs[symindex].sh_addr
323 + ELF_MIPS_R_SYM(rel[i]);
324 if (IS_ERR_VALUE(sym->st_value)) {
325 /* Ignore unresolved weak symbol */
326 if (ELF_ST_BIND(sym->st_info) == STB_WEAK)
327 continue;
328 printk(KERN_WARNING "%s: Unknown symbol %s\n",
329 me->name, strtab + sym->st_name);
330 return -ENOENT;
331 }
332
333 v = sym->st_value + rel[i].r_addend;
334
335 res = reloc_handlers_rela[ELF_MIPS_R_TYPE(rel[i])](me, location, v);
336 if (res)
337 return res;
243 } 338 }
244 339
245 return 0; 340 return 0;