cbecf110dc30dbd584672b680dad3920448138fa
[linux-2.6.git] / arch / microblaze / kernel / module.c
1 /*
2  * Copyright (C) 2007-2009 Michal Simek <monstr@monstr.eu>
3  * Copyright (C) 2007-2009 PetaLogix
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  */
9
10 #include <linux/module.h>
11 #include <linux/moduleloader.h>
12 #include <linux/kernel.h>
13 #include <linux/elf.h>
14 #include <linux/vmalloc.h>
15 #include <linux/fs.h>
16 #include <linux/string.h>
17
18 #include <asm/pgtable.h>
19
20 void *module_alloc(unsigned long size)
21 {
22         void *ret;
23         ret = (size == 0) ? NULL : vmalloc(size);
24         pr_debug("module_alloc (%08lx@%08lx)\n", size, (unsigned long int)ret);
25         return ret;
26 }
27
28 void module_free(struct module *module, void *region)
29 {
30         pr_debug("module_free(%s,%08lx)\n", module->name,
31                                         (unsigned long)region);
32         vfree(region);
33 }
34
35 int module_frob_arch_sections(Elf_Ehdr *hdr,
36                                 Elf_Shdr *sechdrs,
37                                 char *secstrings,
38                                 struct module *mod)
39 {
40         return 0;
41 }
42
43 int apply_relocate(Elf32_Shdr *sechdrs, const char *strtab,
44         unsigned int symindex, unsigned int relsec, struct module *module)
45 {
46         printk(KERN_ERR "module %s: ADD RELOCATION unsupported\n",
47                 module->name);
48         return -ENOEXEC;
49 }
50
51 int apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab,
52         unsigned int symindex, unsigned int relsec, struct module *module)
53 {
54
55         unsigned int i;
56         Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr;
57         Elf32_Sym *sym;
58         unsigned long int *location;
59         unsigned long int value;
60 #if __GNUC__ < 4
61         unsigned long int old_value;
62 #endif
63
64         pr_debug("Applying add relocation section %u to %u\n",
65                 relsec, sechdrs[relsec].sh_info);
66
67         for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) {
68
69                 location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr +
70                                 rela[i].r_offset;
71                 sym = (Elf32_Sym *)sechdrs[symindex].sh_addr +
72                         ELF32_R_SYM(rela[i].r_info);
73                 value = sym->st_value + rela[i].r_addend;
74
75                 switch (ELF32_R_TYPE(rela[i].r_info)) {
76
77                 /*
78                  * Be careful! mb-gcc / mb-ld splits the relocs between the
79                  * text and the reloc table. In general this means we must
80                  * read the current contents of (*location), add any offset
81                  * then store the result back in
82                  */
83
84                 case R_MICROBLAZE_32:
85 #if __GNUC__ < 4
86                         old_value = *location;
87                         *location = value + old_value;
88
89                         pr_debug("R_MICROBLAZE_32 (%08lx->%08lx)\n",
90                                 old_value, value);
91 #else
92                         *location = value;
93 #endif
94                         break;
95
96                 case R_MICROBLAZE_64:
97 #if __GNUC__ < 4
98                         /* Split relocs only required/used pre gcc4.1.1 */
99                         old_value = ((location[0] & 0x0000FFFF) << 16) |
100                                         (location[1] & 0x0000FFFF);
101                         value += old_value;
102 #endif
103                         location[0] = (location[0] & 0xFFFF0000) |
104                                         (value >> 16);
105                         location[1] = (location[1] & 0xFFFF0000) |
106                                         (value & 0xFFFF);
107 #if __GNUC__ < 4
108                         pr_debug("R_MICROBLAZE_64 (%08lx->%08lx)\n",
109                                 old_value, value);
110 #endif
111                         break;
112
113                 case R_MICROBLAZE_64_PCREL:
114 #if __GNUC__ < 4
115                         old_value = (location[0] & 0xFFFF) << 16 |
116                                 (location[1] & 0xFFFF);
117                         value -= old_value;
118 #endif
119                         value -= (unsigned long int)(location) + 4;
120                         location[0] = (location[0] & 0xFFFF0000) |
121                                         (value >> 16);
122                         location[1] = (location[1] & 0xFFFF0000) |
123                                         (value & 0xFFFF);
124                         pr_debug("R_MICROBLAZE_64_PCREL (%08lx)\n",
125                                 value);
126                         break;
127
128                 case R_MICROBLAZE_32_PCREL_LO:
129                         pr_debug("R_MICROBLAZE_32_PCREL_LO\n");
130                         break;
131
132                 case R_MICROBLAZE_64_NONE:
133                         pr_debug("R_MICROBLAZE_NONE\n");
134                         break;
135
136                 case R_MICROBLAZE_NONE:
137                         pr_debug("R_MICROBLAZE_NONE\n");
138                         break;
139
140                 default:
141                         printk(KERN_ERR "module %s: "
142                                 "Unknown relocation: %u\n",
143                                 module->name,
144                                 ELF32_R_TYPE(rela[i].r_info));
145                         return -ENOEXEC;
146                 }
147         }
148         return 0;
149 }
150
151 int module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs,
152                 struct module *module)
153 {
154         return 0;
155 }
156
157 void module_arch_cleanup(struct module *mod)
158 {
159 }