diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
commit | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (patch) | |
tree | a8f4d49d63b1ecc92f2fddceba0655b2472c5bd9 /arch/mips/kernel/module.c | |
parent | 406089d01562f1e2bf9f089fd7637009ebaad589 (diff) |
Patched in Tegra support.
Diffstat (limited to 'arch/mips/kernel/module.c')
-rw-r--r-- | arch/mips/kernel/module.c | 151 |
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 | ||
43 | static struct mips_hi16 *mips_hi16_list; | ||
44 | |||
42 | static LIST_HEAD(dbe_list); | 45 | static LIST_HEAD(dbe_list); |
43 | static DEFINE_SPINLOCK(dbe_lock); | 46 | static DEFINE_SPINLOCK(dbe_lock); |
44 | 47 | ||
@@ -51,7 +54,7 @@ void *module_alloc(unsigned long size) | |||
51 | } | 54 | } |
52 | #endif | 55 | #endif |
53 | 56 | ||
54 | int apply_r_mips_none(struct module *me, u32 *location, Elf_Addr v) | 57 | static 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 | ||
69 | static int apply_r_mips_32_rela(struct module *me, u32 *location, Elf_Addr v) | ||
70 | { | ||
71 | *location = v; | ||
72 | |||
73 | return 0; | ||
74 | } | ||
75 | |||
66 | static int apply_r_mips_26_rel(struct module *me, u32 *location, Elf_Addr v) | 76 | static 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 | ||
97 | static 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 | |||
87 | static int apply_r_mips_hi16_rel(struct module *me, u32 *location, Elf_Addr v) | 117 | static 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 | ||
108 | static void free_relocation_chain(struct mips_hi16 *l) | 138 | static 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 | ||
119 | static int apply_r_mips_lo16_rel(struct module *me, u32 *location, Elf_Addr v) | 146 | static 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 | ||
176 | out_danger: | 204 | out_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 | ||
210 | static 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 | |||
217 | static 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 | |||
224 | static 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 | |||
233 | static 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 | |||
185 | static int (*reloc_handlers_rel[]) (struct module *me, u32 *location, | 242 | static 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 | ||
251 | static 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 | |||
194 | int apply_relocate(Elf_Shdr *sechdrs, const char *strtab, | 263 | int 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; | 303 | int 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; |