diff options
author | David S. Miller <davem@davemloft.net> | 2011-11-18 01:44:58 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2012-01-06 17:14:02 -0500 |
commit | cff6d2096e9a57c2497dd5ee4aed3c97149bfc9e (patch) | |
tree | a269f55f7d1043b3cfbbde550e080982be5b8fad /arch/sparc | |
parent | fde939495571ffd22458e94745b0c2e6af33478d (diff) |
sparc64: Patch sun4v code sequences properly on module load.
[ Upstream commit 0b64120cceb86e93cb1bda0dc055f13016646907 ]
Some of the sun4v code patching occurs in inline functions visible
to, and usable by, modules.
Therefore we have to patch them up during module load.
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'arch/sparc')
-rw-r--r-- | arch/sparc/kernel/entry.h | 7 | ||||
-rw-r--r-- | arch/sparc/kernel/module.c | 27 | ||||
-rw-r--r-- | arch/sparc/kernel/setup_64.c | 48 |
3 files changed, 63 insertions, 19 deletions
diff --git a/arch/sparc/kernel/entry.h b/arch/sparc/kernel/entry.h index e27f8ea8656..0c218e4c088 100644 --- a/arch/sparc/kernel/entry.h +++ b/arch/sparc/kernel/entry.h | |||
@@ -42,6 +42,9 @@ extern void fpsave(unsigned long *fpregs, unsigned long *fsr, | |||
42 | extern void fpload(unsigned long *fpregs, unsigned long *fsr); | 42 | extern void fpload(unsigned long *fpregs, unsigned long *fsr); |
43 | 43 | ||
44 | #else /* CONFIG_SPARC32 */ | 44 | #else /* CONFIG_SPARC32 */ |
45 | |||
46 | #include <asm/trap_block.h> | ||
47 | |||
45 | struct popc_3insn_patch_entry { | 48 | struct popc_3insn_patch_entry { |
46 | unsigned int addr; | 49 | unsigned int addr; |
47 | unsigned int insns[3]; | 50 | unsigned int insns[3]; |
@@ -57,6 +60,10 @@ extern struct popc_6insn_patch_entry __popc_6insn_patch, | |||
57 | __popc_6insn_patch_end; | 60 | __popc_6insn_patch_end; |
58 | 61 | ||
59 | extern void __init per_cpu_patch(void); | 62 | extern void __init per_cpu_patch(void); |
63 | extern void sun4v_patch_1insn_range(struct sun4v_1insn_patch_entry *, | ||
64 | struct sun4v_1insn_patch_entry *); | ||
65 | extern void sun4v_patch_2insn_range(struct sun4v_2insn_patch_entry *, | ||
66 | struct sun4v_2insn_patch_entry *); | ||
60 | extern void __init sun4v_patch(void); | 67 | extern void __init sun4v_patch(void); |
61 | extern void __init boot_cpu_id_too_large(int cpu); | 68 | extern void __init boot_cpu_id_too_large(int cpu); |
62 | extern unsigned int dcache_parity_tl1_occurred; | 69 | extern unsigned int dcache_parity_tl1_occurred; |
diff --git a/arch/sparc/kernel/module.c b/arch/sparc/kernel/module.c index 99ba5baa949..8172c18d844 100644 --- a/arch/sparc/kernel/module.c +++ b/arch/sparc/kernel/module.c | |||
@@ -17,6 +17,8 @@ | |||
17 | #include <asm/processor.h> | 17 | #include <asm/processor.h> |
18 | #include <asm/spitfire.h> | 18 | #include <asm/spitfire.h> |
19 | 19 | ||
20 | #include "entry.h" | ||
21 | |||
20 | #ifdef CONFIG_SPARC64 | 22 | #ifdef CONFIG_SPARC64 |
21 | 23 | ||
22 | #include <linux/jump_label.h> | 24 | #include <linux/jump_label.h> |
@@ -220,6 +222,29 @@ int apply_relocate_add(Elf_Shdr *sechdrs, | |||
220 | } | 222 | } |
221 | 223 | ||
222 | #ifdef CONFIG_SPARC64 | 224 | #ifdef CONFIG_SPARC64 |
225 | static void do_patch_sections(const Elf_Ehdr *hdr, | ||
226 | const Elf_Shdr *sechdrs) | ||
227 | { | ||
228 | const Elf_Shdr *s, *sun4v_1insn = NULL, *sun4v_2insn = NULL; | ||
229 | char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; | ||
230 | |||
231 | for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { | ||
232 | if (!strcmp(".sun4v_1insn_patch", secstrings + s->sh_name)) | ||
233 | sun4v_1insn = s; | ||
234 | if (!strcmp(".sun4v_2insn_patch", secstrings + s->sh_name)) | ||
235 | sun4v_2insn = s; | ||
236 | } | ||
237 | |||
238 | if (sun4v_1insn && tlb_type == hypervisor) { | ||
239 | void *p = (void *) sun4v_1insn->sh_addr; | ||
240 | sun4v_patch_1insn_range(p, p + sun4v_1insn->sh_size); | ||
241 | } | ||
242 | if (sun4v_2insn && tlb_type == hypervisor) { | ||
243 | void *p = (void *) sun4v_2insn->sh_addr; | ||
244 | sun4v_patch_2insn_range(p, p + sun4v_2insn->sh_size); | ||
245 | } | ||
246 | } | ||
247 | |||
223 | int module_finalize(const Elf_Ehdr *hdr, | 248 | int module_finalize(const Elf_Ehdr *hdr, |
224 | const Elf_Shdr *sechdrs, | 249 | const Elf_Shdr *sechdrs, |
225 | struct module *me) | 250 | struct module *me) |
@@ -227,6 +252,8 @@ int module_finalize(const Elf_Ehdr *hdr, | |||
227 | /* make jump label nops */ | 252 | /* make jump label nops */ |
228 | jump_label_apply_nops(me); | 253 | jump_label_apply_nops(me); |
229 | 254 | ||
255 | do_patch_sections(hdr, sechdrs); | ||
256 | |||
230 | /* Cheetah's I-cache is fully coherent. */ | 257 | /* Cheetah's I-cache is fully coherent. */ |
231 | if (tlb_type == spitfire) { | 258 | if (tlb_type == spitfire) { |
232 | unsigned long va; | 259 | unsigned long va; |
diff --git a/arch/sparc/kernel/setup_64.c b/arch/sparc/kernel/setup_64.c index 3c5bb784214..4e7d3ff0ccb 100644 --- a/arch/sparc/kernel/setup_64.c +++ b/arch/sparc/kernel/setup_64.c | |||
@@ -234,40 +234,50 @@ void __init per_cpu_patch(void) | |||
234 | } | 234 | } |
235 | } | 235 | } |
236 | 236 | ||
237 | void __init sun4v_patch(void) | 237 | void sun4v_patch_1insn_range(struct sun4v_1insn_patch_entry *start, |
238 | struct sun4v_1insn_patch_entry *end) | ||
238 | { | 239 | { |
239 | extern void sun4v_hvapi_init(void); | 240 | while (start < end) { |
240 | struct sun4v_1insn_patch_entry *p1; | 241 | unsigned long addr = start->addr; |
241 | struct sun4v_2insn_patch_entry *p2; | ||
242 | |||
243 | if (tlb_type != hypervisor) | ||
244 | return; | ||
245 | 242 | ||
246 | p1 = &__sun4v_1insn_patch; | 243 | *(unsigned int *) (addr + 0) = start->insn; |
247 | while (p1 < &__sun4v_1insn_patch_end) { | ||
248 | unsigned long addr = p1->addr; | ||
249 | |||
250 | *(unsigned int *) (addr + 0) = p1->insn; | ||
251 | wmb(); | 244 | wmb(); |
252 | __asm__ __volatile__("flush %0" : : "r" (addr + 0)); | 245 | __asm__ __volatile__("flush %0" : : "r" (addr + 0)); |
253 | 246 | ||
254 | p1++; | 247 | start++; |
255 | } | 248 | } |
249 | } | ||
256 | 250 | ||
257 | p2 = &__sun4v_2insn_patch; | 251 | void sun4v_patch_2insn_range(struct sun4v_2insn_patch_entry *start, |
258 | while (p2 < &__sun4v_2insn_patch_end) { | 252 | struct sun4v_2insn_patch_entry *end) |
259 | unsigned long addr = p2->addr; | 253 | { |
254 | while (start < end) { | ||
255 | unsigned long addr = start->addr; | ||
260 | 256 | ||
261 | *(unsigned int *) (addr + 0) = p2->insns[0]; | 257 | *(unsigned int *) (addr + 0) = start->insns[0]; |
262 | wmb(); | 258 | wmb(); |
263 | __asm__ __volatile__("flush %0" : : "r" (addr + 0)); | 259 | __asm__ __volatile__("flush %0" : : "r" (addr + 0)); |
264 | 260 | ||
265 | *(unsigned int *) (addr + 4) = p2->insns[1]; | 261 | *(unsigned int *) (addr + 4) = start->insns[1]; |
266 | wmb(); | 262 | wmb(); |
267 | __asm__ __volatile__("flush %0" : : "r" (addr + 4)); | 263 | __asm__ __volatile__("flush %0" : : "r" (addr + 4)); |
268 | 264 | ||
269 | p2++; | 265 | start++; |
270 | } | 266 | } |
267 | } | ||
268 | |||
269 | void __init sun4v_patch(void) | ||
270 | { | ||
271 | extern void sun4v_hvapi_init(void); | ||
272 | |||
273 | if (tlb_type != hypervisor) | ||
274 | return; | ||
275 | |||
276 | sun4v_patch_1insn_range(&__sun4v_1insn_patch, | ||
277 | &__sun4v_1insn_patch_end); | ||
278 | |||
279 | sun4v_patch_2insn_range(&__sun4v_2insn_patch, | ||
280 | &__sun4v_2insn_patch_end); | ||
271 | 281 | ||
272 | sun4v_hvapi_init(); | 282 | sun4v_hvapi_init(); |
273 | } | 283 | } |