Commit | Line | Data |
---|---|---|
1a59d1b8 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4 LT |
2 | /* Kernel module help for PPC. |
3 | Copyright (C) 2001 Rusty Russell. | |
4 | ||
1da177e4 | 5 | */ |
c7d1f6af AB |
6 | |
7 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
8 | ||
1da177e4 LT |
9 | #include <linux/module.h> |
10 | #include <linux/moduleloader.h> | |
11 | #include <linux/elf.h> | |
12 | #include <linux/vmalloc.h> | |
13 | #include <linux/fs.h> | |
14 | #include <linux/string.h> | |
15 | #include <linux/kernel.h> | |
7cc45e64 | 16 | #include <linux/ftrace.h> |
1da177e4 | 17 | #include <linux/cache.h> |
73c9ceab | 18 | #include <linux/bug.h> |
eda09fbd | 19 | #include <linux/sort.h> |
b88c4767 | 20 | #include <asm/setup.h> |
0c850965 | 21 | #include <asm/code-patching.h> |
21c4ff80 | 22 | |
1da177e4 LT |
23 | /* Count how many different relocations (different symbol, different |
24 | addend) */ | |
25 | static unsigned int count_relocs(const Elf32_Rela *rela, unsigned int num) | |
26 | { | |
eda09fbd EM |
27 | unsigned int i, r_info, r_addend, _count_relocs; |
28 | ||
29 | _count_relocs = 0; | |
30 | r_info = 0; | |
31 | r_addend = 0; | |
32 | for (i = 0; i < num; i++) | |
33 | /* Only count 24-bit relocs, others don't need stubs */ | |
34 | if (ELF32_R_TYPE(rela[i].r_info) == R_PPC_REL24 && | |
35 | (r_info != ELF32_R_SYM(rela[i].r_info) || | |
36 | r_addend != rela[i].r_addend)) { | |
37 | _count_relocs++; | |
38 | r_info = ELF32_R_SYM(rela[i].r_info); | |
39 | r_addend = rela[i].r_addend; | |
1da177e4 | 40 | } |
eda09fbd | 41 | |
7cc45e64 SR |
42 | #ifdef CONFIG_DYNAMIC_FTRACE |
43 | _count_relocs++; /* add one for ftrace_caller */ | |
44 | #endif | |
eda09fbd EM |
45 | return _count_relocs; |
46 | } | |
47 | ||
48 | static int relacmp(const void *_x, const void *_y) | |
49 | { | |
50 | const Elf32_Rela *x, *y; | |
51 | ||
52 | y = (Elf32_Rela *)_x; | |
53 | x = (Elf32_Rela *)_y; | |
54 | ||
55 | /* Compare the entire r_info (as opposed to ELF32_R_SYM(r_info) only) to | |
56 | * make the comparison cheaper/faster. It won't affect the sorting or | |
57 | * the counting algorithms' performance | |
58 | */ | |
59 | if (x->r_info < y->r_info) | |
60 | return -1; | |
61 | else if (x->r_info > y->r_info) | |
62 | return 1; | |
63 | else if (x->r_addend < y->r_addend) | |
64 | return -1; | |
65 | else if (x->r_addend > y->r_addend) | |
66 | return 1; | |
67 | else | |
68 | return 0; | |
69 | } | |
70 | ||
1da177e4 LT |
71 | /* Get the potential trampolines size required of the init and |
72 | non-init sections */ | |
73 | static unsigned long get_plt_size(const Elf32_Ehdr *hdr, | |
74 | const Elf32_Shdr *sechdrs, | |
75 | const char *secstrings, | |
76 | int is_init) | |
77 | { | |
78 | unsigned long ret = 0; | |
79 | unsigned i; | |
80 | ||
81 | /* Everything marked ALLOC (this includes the exported | |
82 | symbols) */ | |
83 | for (i = 1; i < hdr->e_shnum; i++) { | |
84 | /* If it's called *.init*, and we're not init, we're | |
85 | not interested */ | |
d8731527 | 86 | if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != NULL) |
1da177e4 LT |
87 | != is_init) |
88 | continue; | |
89 | ||
90 | /* We don't want to look at debug sections. */ | |
d8731527 | 91 | if (strstr(secstrings + sechdrs[i].sh_name, ".debug")) |
1da177e4 LT |
92 | continue; |
93 | ||
94 | if (sechdrs[i].sh_type == SHT_RELA) { | |
c7d1f6af AB |
95 | pr_debug("Found relocations in section %u\n", i); |
96 | pr_debug("Ptr: %p. Number: %u\n", | |
1da177e4 LT |
97 | (void *)hdr + sechdrs[i].sh_offset, |
98 | sechdrs[i].sh_size / sizeof(Elf32_Rela)); | |
eda09fbd EM |
99 | |
100 | /* Sort the relocation information based on a symbol and | |
101 | * addend key. This is a stable O(n*log n) complexity | |
1fd02f66 | 102 | * algorithm but it will reduce the complexity of |
eda09fbd EM |
103 | * count_relocs() to linear complexity O(n) |
104 | */ | |
105 | sort((void *)hdr + sechdrs[i].sh_offset, | |
106 | sechdrs[i].sh_size / sizeof(Elf32_Rela), | |
bac7ca7b | 107 | sizeof(Elf32_Rela), relacmp, NULL); |
eda09fbd | 108 | |
1da177e4 LT |
109 | ret += count_relocs((void *)hdr |
110 | + sechdrs[i].sh_offset, | |
111 | sechdrs[i].sh_size | |
112 | / sizeof(Elf32_Rela)) | |
113 | * sizeof(struct ppc_plt_entry); | |
114 | } | |
115 | } | |
116 | ||
117 | return ret; | |
118 | } | |
119 | ||
120 | int module_frob_arch_sections(Elf32_Ehdr *hdr, | |
121 | Elf32_Shdr *sechdrs, | |
122 | char *secstrings, | |
123 | struct module *me) | |
124 | { | |
125 | unsigned int i; | |
126 | ||
127 | /* Find .plt and .init.plt sections */ | |
128 | for (i = 0; i < hdr->e_shnum; i++) { | |
129 | if (strcmp(secstrings + sechdrs[i].sh_name, ".init.plt") == 0) | |
130 | me->arch.init_plt_section = i; | |
131 | else if (strcmp(secstrings + sechdrs[i].sh_name, ".plt") == 0) | |
132 | me->arch.core_plt_section = i; | |
133 | } | |
134 | if (!me->arch.core_plt_section || !me->arch.init_plt_section) { | |
c7d1f6af | 135 | pr_err("Module doesn't contain .plt or .init.plt sections.\n"); |
1da177e4 LT |
136 | return -ENOEXEC; |
137 | } | |
138 | ||
139 | /* Override their sizes */ | |
140 | sechdrs[me->arch.core_plt_section].sh_size | |
141 | = get_plt_size(hdr, sechdrs, secstrings, 0); | |
142 | sechdrs[me->arch.init_plt_section].sh_size | |
143 | = get_plt_size(hdr, sechdrs, secstrings, 1); | |
144 | return 0; | |
145 | } | |
146 | ||
1da177e4 LT |
147 | static inline int entry_matches(struct ppc_plt_entry *entry, Elf32_Addr val) |
148 | { | |
47b04699 | 149 | if (entry->jump[0] != PPC_RAW_LIS(_R12, PPC_HA(val))) |
4eb4516e | 150 | return 0; |
47b04699 | 151 | if (entry->jump[1] != PPC_RAW_ADDI(_R12, _R12, PPC_LO(val))) |
4eb4516e CL |
152 | return 0; |
153 | return 1; | |
1da177e4 LT |
154 | } |
155 | ||
156 | /* Set up a trampoline in the PLT to bounce us to the distant function */ | |
157 | static uint32_t do_plt_call(void *location, | |
158 | Elf32_Addr val, | |
136cd345 | 159 | const Elf32_Shdr *sechdrs, |
1da177e4 LT |
160 | struct module *mod) |
161 | { | |
162 | struct ppc_plt_entry *entry; | |
163 | ||
c7d1f6af | 164 | pr_debug("Doing plt for call to 0x%x at 0x%x\n", val, (unsigned int)location); |
1da177e4 | 165 | /* Init, or core PLT? */ |
7523e4dc RR |
166 | if (location >= mod->core_layout.base |
167 | && location < mod->core_layout.base + mod->core_layout.size) | |
1da177e4 LT |
168 | entry = (void *)sechdrs[mod->arch.core_plt_section].sh_addr; |
169 | else | |
170 | entry = (void *)sechdrs[mod->arch.init_plt_section].sh_addr; | |
171 | ||
172 | /* Find this entry, or if that fails, the next avail. entry */ | |
173 | while (entry->jump[0]) { | |
174 | if (entry_matches(entry, val)) return (uint32_t)entry; | |
175 | entry++; | |
176 | } | |
177 | ||
0c850965 CL |
178 | if (patch_instruction(&entry->jump[0], ppc_inst(PPC_RAW_LIS(_R12, PPC_HA(val))))) |
179 | return 0; | |
180 | if (patch_instruction(&entry->jump[1], ppc_inst(PPC_RAW_ADDI(_R12, _R12, PPC_LO(val))))) | |
181 | return 0; | |
182 | if (patch_instruction(&entry->jump[2], ppc_inst(PPC_RAW_MTCTR(_R12)))) | |
183 | return 0; | |
184 | if (patch_instruction(&entry->jump[3], ppc_inst(PPC_RAW_BCTR()))) | |
185 | return 0; | |
1da177e4 | 186 | |
c7d1f6af | 187 | pr_debug("Initialized plt for 0x%x at %p\n", val, entry); |
1da177e4 LT |
188 | return (uint32_t)entry; |
189 | } | |
190 | ||
0c850965 CL |
191 | static int patch_location_16(uint32_t *loc, u16 value) |
192 | { | |
193 | loc = PTR_ALIGN_DOWN(loc, sizeof(u32)); | |
194 | return patch_instruction(loc, ppc_inst((*loc & 0xffff0000) | value)); | |
195 | } | |
196 | ||
1da177e4 LT |
197 | int apply_relocate_add(Elf32_Shdr *sechdrs, |
198 | const char *strtab, | |
199 | unsigned int symindex, | |
200 | unsigned int relsec, | |
201 | struct module *module) | |
202 | { | |
203 | unsigned int i; | |
204 | Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr; | |
205 | Elf32_Sym *sym; | |
206 | uint32_t *location; | |
207 | uint32_t value; | |
208 | ||
c7d1f6af | 209 | pr_debug("Applying ADD relocate section %u to %u\n", relsec, |
1da177e4 LT |
210 | sechdrs[relsec].sh_info); |
211 | for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) { | |
212 | /* This is where to make the change */ | |
213 | location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr | |
214 | + rela[i].r_offset; | |
215 | /* This is the symbol it is referring to. Note that all | |
216 | undefined symbols have been resolved. */ | |
217 | sym = (Elf32_Sym *)sechdrs[symindex].sh_addr | |
218 | + ELF32_R_SYM(rela[i].r_info); | |
219 | /* `Everything is relative'. */ | |
220 | value = sym->st_value + rela[i].r_addend; | |
221 | ||
222 | switch (ELF32_R_TYPE(rela[i].r_info)) { | |
223 | case R_PPC_ADDR32: | |
224 | /* Simply set it */ | |
225 | *(uint32_t *)location = value; | |
226 | break; | |
227 | ||
228 | case R_PPC_ADDR16_LO: | |
229 | /* Low half of the symbol */ | |
0c850965 CL |
230 | if (patch_location_16(location, PPC_LO(value))) |
231 | return -EFAULT; | |
1da177e4 | 232 | break; |
9a3d6458 SV |
233 | |
234 | case R_PPC_ADDR16_HI: | |
235 | /* Higher half of the symbol */ | |
0c850965 CL |
236 | if (patch_location_16(location, PPC_HI(value))) |
237 | return -EFAULT; | |
9a3d6458 SV |
238 | break; |
239 | ||
1da177e4 | 240 | case R_PPC_ADDR16_HA: |
0c850965 CL |
241 | if (patch_location_16(location, PPC_HA(value))) |
242 | return -EFAULT; | |
1da177e4 LT |
243 | break; |
244 | ||
245 | case R_PPC_REL24: | |
246 | if ((int)(value - (uint32_t)location) < -0x02000000 | |
0c850965 | 247 | || (int)(value - (uint32_t)location) >= 0x02000000) { |
1da177e4 LT |
248 | value = do_plt_call(location, value, |
249 | sechdrs, module); | |
0c850965 CL |
250 | if (!value) |
251 | return -EFAULT; | |
252 | } | |
1da177e4 LT |
253 | |
254 | /* Only replace bits 2 through 26 */ | |
c7d1f6af | 255 | pr_debug("REL24 value = %08X. location = %08X\n", |
1da177e4 | 256 | value, (uint32_t)location); |
c7d1f6af | 257 | pr_debug("Location before: %08X.\n", |
1da177e4 | 258 | *(uint32_t *)location); |
0c850965 | 259 | value = (*(uint32_t *)location & ~0x03fffffc) |
1da177e4 LT |
260 | | ((value - (uint32_t)location) |
261 | & 0x03fffffc); | |
0c850965 CL |
262 | |
263 | if (patch_instruction(location, ppc_inst(value))) | |
264 | return -EFAULT; | |
265 | ||
c7d1f6af | 266 | pr_debug("Location after: %08X.\n", |
1da177e4 | 267 | *(uint32_t *)location); |
c7d1f6af | 268 | pr_debug("ie. jump to %08X+%08X = %08X\n", |
1da177e4 LT |
269 | *(uint32_t *)location & 0x03fffffc, |
270 | (uint32_t)location, | |
271 | (*(uint32_t *)location & 0x03fffffc) | |
272 | + (uint32_t)location); | |
273 | break; | |
274 | ||
275 | case R_PPC_REL32: | |
276 | /* 32-bit relative jump. */ | |
277 | *(uint32_t *)location = value - (uint32_t)location; | |
278 | break; | |
279 | ||
280 | default: | |
c7d1f6af | 281 | pr_err("%s: unknown ADD relocation: %u\n", |
1da177e4 LT |
282 | module->name, |
283 | ELF32_R_TYPE(rela[i].r_info)); | |
284 | return -ENOEXEC; | |
285 | } | |
286 | } | |
136cd345 ME |
287 | |
288 | return 0; | |
289 | } | |
290 | ||
7cc45e64 | 291 | #ifdef CONFIG_DYNAMIC_FTRACE |
8052d043 CL |
292 | notrace int module_trampoline_target(struct module *mod, unsigned long addr, |
293 | unsigned long *target) | |
c93d4f6e | 294 | { |
8052d043 | 295 | ppc_inst_t jmp[4]; |
c93d4f6e CL |
296 | |
297 | /* Find where the trampoline jumps to */ | |
8052d043 CL |
298 | if (copy_inst_from_kernel_nofault(jmp, (void *)addr)) |
299 | return -EFAULT; | |
300 | if (__copy_inst_from_kernel_nofault(jmp + 1, (void *)addr + 4)) | |
301 | return -EFAULT; | |
302 | if (__copy_inst_from_kernel_nofault(jmp + 2, (void *)addr + 8)) | |
303 | return -EFAULT; | |
304 | if (__copy_inst_from_kernel_nofault(jmp + 3, (void *)addr + 12)) | |
c93d4f6e CL |
305 | return -EFAULT; |
306 | ||
307 | /* verify that this is what we expect it to be */ | |
8052d043 CL |
308 | if ((ppc_inst_val(jmp[0]) & 0xffff0000) != PPC_RAW_LIS(_R12, 0)) |
309 | return -EINVAL; | |
310 | if ((ppc_inst_val(jmp[1]) & 0xffff0000) != PPC_RAW_ADDI(_R12, _R12, 0)) | |
311 | return -EINVAL; | |
312 | if (ppc_inst_val(jmp[2]) != PPC_RAW_MTCTR(_R12)) | |
313 | return -EINVAL; | |
314 | if (ppc_inst_val(jmp[3]) != PPC_RAW_BCTR()) | |
c93d4f6e CL |
315 | return -EINVAL; |
316 | ||
8052d043 | 317 | addr = (ppc_inst_val(jmp[1]) & 0xffff) | ((ppc_inst_val(jmp[0]) & 0xffff) << 16); |
c93d4f6e CL |
318 | if (addr & 0x8000) |
319 | addr -= 0x10000; | |
320 | ||
321 | *target = addr; | |
322 | ||
323 | return 0; | |
324 | } | |
325 | ||
136cd345 ME |
326 | int module_finalize_ftrace(struct module *module, const Elf_Shdr *sechdrs) |
327 | { | |
328 | module->arch.tramp = do_plt_call(module->core_layout.base, | |
329 | (unsigned long)ftrace_caller, | |
330 | sechdrs, module); | |
331 | if (!module->arch.tramp) | |
332 | return -ENOENT; | |
333 | ||
7dfbfb87 CL |
334 | #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS |
335 | module->arch.tramp_regs = do_plt_call(module->core_layout.base, | |
336 | (unsigned long)ftrace_regs_caller, | |
337 | sechdrs, module); | |
338 | if (!module->arch.tramp_regs) | |
339 | return -ENOENT; | |
340 | #endif | |
341 | ||
1da177e4 LT |
342 | return 0; |
343 | } | |
136cd345 | 344 | #endif |