diff options
Diffstat (limited to 'arch/tile/kernel/module.c')
-rw-r--r-- | arch/tile/kernel/module.c | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/arch/tile/kernel/module.c b/arch/tile/kernel/module.c new file mode 100644 index 000000000000..e2ab82b7c7e7 --- /dev/null +++ b/arch/tile/kernel/module.c | |||
@@ -0,0 +1,257 @@ | |||
1 | /* | ||
2 | * Copyright 2010 Tilera Corporation. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation, version 2. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
11 | * NON INFRINGEMENT. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * Based on i386 version, copyright (C) 2001 Rusty Russell. | ||
15 | */ | ||
16 | |||
17 | #include <linux/moduleloader.h> | ||
18 | #include <linux/elf.h> | ||
19 | #include <linux/vmalloc.h> | ||
20 | #include <linux/fs.h> | ||
21 | #include <linux/string.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <asm/opcode-tile.h> | ||
24 | #include <asm/pgtable.h> | ||
25 | |||
26 | #ifdef __tilegx__ | ||
27 | # define Elf_Rela Elf64_Rela | ||
28 | # define ELF_R_SYM ELF64_R_SYM | ||
29 | # define ELF_R_TYPE ELF64_R_TYPE | ||
30 | #else | ||
31 | # define Elf_Rela Elf32_Rela | ||
32 | # define ELF_R_SYM ELF32_R_SYM | ||
33 | # define ELF_R_TYPE ELF32_R_TYPE | ||
34 | #endif | ||
35 | |||
36 | #ifdef MODULE_DEBUG | ||
37 | #define DEBUGP printk | ||
38 | #else | ||
39 | #define DEBUGP(fmt...) | ||
40 | #endif | ||
41 | |||
42 | /* | ||
43 | * Allocate some address space in the range MEM_MODULE_START to | ||
44 | * MEM_MODULE_END and populate it with memory. | ||
45 | */ | ||
46 | void *module_alloc(unsigned long size) | ||
47 | { | ||
48 | struct page **pages; | ||
49 | pgprot_t prot_rwx = __pgprot(_PAGE_KERNEL | _PAGE_KERNEL_EXEC); | ||
50 | struct vm_struct *area; | ||
51 | int i = 0; | ||
52 | int npages; | ||
53 | |||
54 | if (size == 0) | ||
55 | return NULL; | ||
56 | npages = (size + PAGE_SIZE - 1) / PAGE_SIZE; | ||
57 | pages = kmalloc(npages * sizeof(struct page *), GFP_KERNEL); | ||
58 | if (pages == NULL) | ||
59 | return NULL; | ||
60 | for (; i < npages; ++i) { | ||
61 | pages[i] = alloc_page(GFP_KERNEL | __GFP_HIGHMEM); | ||
62 | if (!pages[i]) | ||
63 | goto error; | ||
64 | } | ||
65 | |||
66 | area = __get_vm_area(size, VM_ALLOC, MEM_MODULE_START, MEM_MODULE_END); | ||
67 | if (!area) | ||
68 | goto error; | ||
69 | |||
70 | if (map_vm_area(area, prot_rwx, &pages)) { | ||
71 | vunmap(area->addr); | ||
72 | goto error; | ||
73 | } | ||
74 | |||
75 | return area->addr; | ||
76 | |||
77 | error: | ||
78 | while (--i >= 0) | ||
79 | __free_page(pages[i]); | ||
80 | kfree(pages); | ||
81 | return NULL; | ||
82 | } | ||
83 | |||
84 | |||
85 | /* Free memory returned from module_alloc */ | ||
86 | void module_free(struct module *mod, void *module_region) | ||
87 | { | ||
88 | vfree(module_region); | ||
89 | /* | ||
90 | * FIXME: If module_region == mod->init_region, trim exception | ||
91 | * table entries. | ||
92 | */ | ||
93 | } | ||
94 | |||
95 | /* We don't need anything special. */ | ||
96 | int module_frob_arch_sections(Elf_Ehdr *hdr, | ||
97 | Elf_Shdr *sechdrs, | ||
98 | char *secstrings, | ||
99 | struct module *mod) | ||
100 | { | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | int apply_relocate(Elf_Shdr *sechdrs, | ||
105 | const char *strtab, | ||
106 | unsigned int symindex, | ||
107 | unsigned int relsec, | ||
108 | struct module *me) | ||
109 | { | ||
110 | pr_err("module %s: .rel relocation unsupported\n", me->name); | ||
111 | return -ENOEXEC; | ||
112 | } | ||
113 | |||
114 | #ifdef __tilegx__ | ||
115 | /* | ||
116 | * Validate that the high 16 bits of "value" is just the sign-extension of | ||
117 | * the low 48 bits. | ||
118 | */ | ||
119 | static int validate_hw2_last(long value, struct module *me) | ||
120 | { | ||
121 | if (((value << 16) >> 16) != value) { | ||
122 | pr_warning("module %s: Out of range HW2_LAST value %#lx\n", | ||
123 | me->name, value); | ||
124 | return 0; | ||
125 | } | ||
126 | return 1; | ||
127 | } | ||
128 | |||
129 | /* | ||
130 | * Validate that "value" isn't too big to hold in a JumpOff relocation. | ||
131 | */ | ||
132 | static int validate_jumpoff(long value) | ||
133 | { | ||
134 | /* Determine size of jump offset. */ | ||
135 | int shift = __builtin_clzl(get_JumpOff_X1(create_JumpOff_X1(-1))); | ||
136 | |||
137 | /* Check to see if it fits into the relocation slot. */ | ||
138 | long f = get_JumpOff_X1(create_JumpOff_X1(value)); | ||
139 | f = (f << shift) >> shift; | ||
140 | |||
141 | return f == value; | ||
142 | } | ||
143 | #endif | ||
144 | |||
145 | int apply_relocate_add(Elf_Shdr *sechdrs, | ||
146 | const char *strtab, | ||
147 | unsigned int symindex, | ||
148 | unsigned int relsec, | ||
149 | struct module *me) | ||
150 | { | ||
151 | unsigned int i; | ||
152 | Elf_Rela *rel = (void *)sechdrs[relsec].sh_addr; | ||
153 | Elf_Sym *sym; | ||
154 | u64 *location; | ||
155 | unsigned long value; | ||
156 | |||
157 | DEBUGP("Applying relocate section %u to %u\n", relsec, | ||
158 | sechdrs[relsec].sh_info); | ||
159 | for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { | ||
160 | /* This is where to make the change */ | ||
161 | location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr | ||
162 | + rel[i].r_offset; | ||
163 | /* | ||
164 | * This is the symbol it is referring to. | ||
165 | * Note that all undefined symbols have been resolved. | ||
166 | */ | ||
167 | sym = (Elf_Sym *)sechdrs[symindex].sh_addr | ||
168 | + ELF_R_SYM(rel[i].r_info); | ||
169 | value = sym->st_value + rel[i].r_addend; | ||
170 | |||
171 | switch (ELF_R_TYPE(rel[i].r_info)) { | ||
172 | |||
173 | #define MUNGE(func) (*location = ((*location & ~func(-1)) | func(value))) | ||
174 | |||
175 | #ifndef __tilegx__ | ||
176 | case R_TILE_32: | ||
177 | *(uint32_t *)location = value; | ||
178 | break; | ||
179 | case R_TILE_IMM16_X0_HA: | ||
180 | value = (value + 0x8000) >> 16; | ||
181 | /*FALLTHROUGH*/ | ||
182 | case R_TILE_IMM16_X0_LO: | ||
183 | MUNGE(create_Imm16_X0); | ||
184 | break; | ||
185 | case R_TILE_IMM16_X1_HA: | ||
186 | value = (value + 0x8000) >> 16; | ||
187 | /*FALLTHROUGH*/ | ||
188 | case R_TILE_IMM16_X1_LO: | ||
189 | MUNGE(create_Imm16_X1); | ||
190 | break; | ||
191 | case R_TILE_JOFFLONG_X1: | ||
192 | value -= (unsigned long) location; /* pc-relative */ | ||
193 | value = (long) value >> 3; /* count by instrs */ | ||
194 | MUNGE(create_JOffLong_X1); | ||
195 | break; | ||
196 | #else | ||
197 | case R_TILEGX_64: | ||
198 | *location = value; | ||
199 | break; | ||
200 | case R_TILEGX_IMM16_X0_HW2_LAST: | ||
201 | if (!validate_hw2_last(value, me)) | ||
202 | return -ENOEXEC; | ||
203 | value >>= 16; | ||
204 | /*FALLTHROUGH*/ | ||
205 | case R_TILEGX_IMM16_X0_HW1: | ||
206 | value >>= 16; | ||
207 | /*FALLTHROUGH*/ | ||
208 | case R_TILEGX_IMM16_X0_HW0: | ||
209 | MUNGE(create_Imm16_X0); | ||
210 | break; | ||
211 | case R_TILEGX_IMM16_X1_HW2_LAST: | ||
212 | if (!validate_hw2_last(value, me)) | ||
213 | return -ENOEXEC; | ||
214 | value >>= 16; | ||
215 | /*FALLTHROUGH*/ | ||
216 | case R_TILEGX_IMM16_X1_HW1: | ||
217 | value >>= 16; | ||
218 | /*FALLTHROUGH*/ | ||
219 | case R_TILEGX_IMM16_X1_HW0: | ||
220 | MUNGE(create_Imm16_X1); | ||
221 | break; | ||
222 | case R_TILEGX_JUMPOFF_X1: | ||
223 | value -= (unsigned long) location; /* pc-relative */ | ||
224 | value = (long) value >> 3; /* count by instrs */ | ||
225 | if (!validate_jumpoff(value)) { | ||
226 | pr_warning("module %s: Out of range jump to" | ||
227 | " %#llx at %#llx (%p)\n", me->name, | ||
228 | sym->st_value + rel[i].r_addend, | ||
229 | rel[i].r_offset, location); | ||
230 | return -ENOEXEC; | ||
231 | } | ||
232 | MUNGE(create_JumpOff_X1); | ||
233 | break; | ||
234 | #endif | ||
235 | |||
236 | #undef MUNGE | ||
237 | |||
238 | default: | ||
239 | pr_err("module %s: Unknown relocation: %d\n", | ||
240 | me->name, (int) ELF_R_TYPE(rel[i].r_info)); | ||
241 | return -ENOEXEC; | ||
242 | } | ||
243 | } | ||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | int module_finalize(const Elf_Ehdr *hdr, | ||
248 | const Elf_Shdr *sechdrs, | ||
249 | struct module *me) | ||
250 | { | ||
251 | /* FIXME: perhaps remove the "writable" bit from the TLB? */ | ||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | void module_arch_cleanup(struct module *mod) | ||
256 | { | ||
257 | } | ||