aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/lib
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/lib')
-rw-r--r--arch/powerpc/lib/feature-fixups.c79
1 files changed, 65 insertions, 14 deletions
diff --git a/arch/powerpc/lib/feature-fixups.c b/arch/powerpc/lib/feature-fixups.c
index 973d547ef01d..174c1eba9ac8 100644
--- a/arch/powerpc/lib/feature-fixups.c
+++ b/arch/powerpc/lib/feature-fixups.c
@@ -26,24 +26,66 @@ struct fixup_entry {
26 long alt_end_off; 26 long alt_end_off;
27}; 27};
28 28
29static void patch_feature_section(unsigned long value, struct fixup_entry *fcur) 29static unsigned int *calc_addr(struct fixup_entry *fcur, long offset)
30{ 30{
31 unsigned int *pstart, *pend, *p; 31 /*
32 * We store the offset to the code as a negative offset from
33 * the start of the alt_entry, to support the VDSO. This
34 * routine converts that back into an actual address.
35 */
36 return (unsigned int *)((unsigned long)fcur + offset);
37}
38
39static int patch_alt_instruction(unsigned int *src, unsigned int *dest,
40 unsigned int *alt_start, unsigned int *alt_end)
41{
42 unsigned int instr;
43
44 instr = *src;
45
46 if (instr_is_relative_branch(*src)) {
47 unsigned int *target = (unsigned int *)branch_target(src);
48
49 /* Branch within the section doesn't need translating */
50 if (target < alt_start || target >= alt_end) {
51 instr = translate_branch(dest, src);
52 if (!instr)
53 return 1;
54 }
55 }
56
57 patch_instruction(dest, instr);
58
59 return 0;
60}
61
62static int patch_feature_section(unsigned long value, struct fixup_entry *fcur)
63{
64 unsigned int *start, *end, *alt_start, *alt_end, *src, *dest;
65
66 start = calc_addr(fcur, fcur->start_off);
67 end = calc_addr(fcur, fcur->end_off);
68 alt_start = calc_addr(fcur, fcur->alt_start_off);
69 alt_end = calc_addr(fcur, fcur->alt_end_off);
70
71 if ((alt_end - alt_start) > (end - start))
72 return 1;
32 73
33 if ((value & fcur->mask) == fcur->value) 74 if ((value & fcur->mask) == fcur->value)
34 return; 75 return 0;
35 76
36 pstart = ((unsigned int *)fcur) + (fcur->start_off / 4); 77 src = alt_start;
37 pend = ((unsigned int *)fcur) + (fcur->end_off / 4); 78 dest = start;
38 79
39 for (p = pstart; p < pend; p++) { 80 for (; src < alt_end; src++, dest++) {
40 *p = PPC_NOP_INSTR; 81 if (patch_alt_instruction(src, dest, alt_start, alt_end))
41 asm volatile ("dcbst 0, %0" : : "r" (p)); 82 return 1;
42 } 83 }
43 asm volatile ("sync" : : : "memory"); 84
44 for (p = pstart; p < pend; p++) 85 for (; dest < end; dest++)
45 asm volatile ("icbi 0,%0" : : "r" (p)); 86 patch_instruction(dest, PPC_NOP_INSTR);
46 asm volatile ("sync; isync" : : : "memory"); 87
88 return 0;
47} 89}
48 90
49void do_feature_fixups(unsigned long value, void *fixup_start, void *fixup_end) 91void do_feature_fixups(unsigned long value, void *fixup_start, void *fixup_end)
@@ -53,6 +95,15 @@ void do_feature_fixups(unsigned long value, void *fixup_start, void *fixup_end)
53 fcur = fixup_start; 95 fcur = fixup_start;
54 fend = fixup_end; 96 fend = fixup_end;
55 97
56 for (; fcur < fend; fcur++) 98 for (; fcur < fend; fcur++) {
57 patch_feature_section(value, fcur); 99 if (patch_feature_section(value, fcur)) {
100 __WARN();
101 printk("Unable to patch feature section at %p - %p" \
102 " with %p - %p\n",
103 calc_addr(fcur, fcur->start_off),
104 calc_addr(fcur, fcur->end_off),
105 calc_addr(fcur, fcur->alt_start_off),
106 calc_addr(fcur, fcur->alt_end_off));
107 }
108 }
58} 109}