diff options
Diffstat (limited to 'arch/powerpc/lib')
-rw-r--r-- | arch/powerpc/lib/feature-fixups.c | 79 |
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 | ||
29 | static void patch_feature_section(unsigned long value, struct fixup_entry *fcur) | 29 | static 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 | |||
39 | static 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 | |||
62 | static 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 | ||
49 | void do_feature_fixups(unsigned long value, void *fixup_start, void *fixup_end) | 91 | void 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 | } |