aboutsummaryrefslogtreecommitdiffstats
path: root/arch/tile/kernel/module.c
diff options
context:
space:
mode:
authorChris Metcalf <cmetcalf@tilera.com>2010-05-28 23:09:12 -0400
committerChris Metcalf <cmetcalf@tilera.com>2010-06-04 17:11:18 -0400
commit867e359b97c970a60626d5d76bbe2a8fadbf38fb (patch)
treec5ccbb7f5172e8555977119608ecb1eee3cc37e3 /arch/tile/kernel/module.c
parent5360bd776f73d0a7da571d72a09a03f237e99900 (diff)
arch/tile: core support for Tilera 32-bit chips.
This change is the core kernel support for TILEPro and TILE64 chips. No driver support (except the console driver) is included yet. This includes the relevant Linux headers in asm/; the low-level low-level "Tile architecture" headers in arch/, which are shared with the hypervisor, etc., and are build-system agnostic; and the relevant hypervisor headers in hv/. Signed-off-by: Chris Metcalf <cmetcalf@tilera.com> Acked-by: Arnd Bergmann <arnd@arndb.de> Acked-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> Reviewed-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/tile/kernel/module.c')
-rw-r--r--arch/tile/kernel/module.c257
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..ed3e91161f88
--- /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 */
46void *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
77error:
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 */
86void 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. */
96int module_frob_arch_sections(Elf_Ehdr *hdr,
97 Elf_Shdr *sechdrs,
98 char *secstrings,
99 struct module *mod)
100{
101 return 0;
102}
103
104int apply_relocate(Elf_Shdr *sechdrs,
105 const char *strtab,
106 unsigned int symindex,
107 unsigned int relsec,
108 struct module *me)
109{
110 printk(KERN_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 */
119static int validate_hw2_last(long value, struct module *me)
120{
121 if (((value << 16) >> 16) != value) {
122 printk("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 */
132static 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
145int 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 printk("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 printk(KERN_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
247int 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
255void module_arch_cleanup(struct module *mod)
256{
257}