diff options
Diffstat (limited to 'arch/i386/kernel/alternative.c')
-rw-r--r-- | arch/i386/kernel/alternative.c | 64 |
1 files changed, 53 insertions, 11 deletions
diff --git a/arch/i386/kernel/alternative.c b/arch/i386/kernel/alternative.c index 583c238e17fb..9eca21b49f6b 100644 --- a/arch/i386/kernel/alternative.c +++ b/arch/i386/kernel/alternative.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #include <linux/module.h> | 1 | #include <linux/module.h> |
2 | #include <linux/sched.h> | ||
2 | #include <linux/spinlock.h> | 3 | #include <linux/spinlock.h> |
3 | #include <linux/list.h> | 4 | #include <linux/list.h> |
4 | #include <asm/alternative.h> | 5 | #include <asm/alternative.h> |
@@ -123,6 +124,20 @@ static unsigned char** find_nop_table(void) | |||
123 | 124 | ||
124 | #endif /* CONFIG_X86_64 */ | 125 | #endif /* CONFIG_X86_64 */ |
125 | 126 | ||
127 | static void nop_out(void *insns, unsigned int len) | ||
128 | { | ||
129 | unsigned char **noptable = find_nop_table(); | ||
130 | |||
131 | while (len > 0) { | ||
132 | unsigned int noplen = len; | ||
133 | if (noplen > ASM_NOP_MAX) | ||
134 | noplen = ASM_NOP_MAX; | ||
135 | memcpy(insns, noptable[noplen], noplen); | ||
136 | insns += noplen; | ||
137 | len -= noplen; | ||
138 | } | ||
139 | } | ||
140 | |||
126 | extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; | 141 | extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; |
127 | extern struct alt_instr __smp_alt_instructions[], __smp_alt_instructions_end[]; | 142 | extern struct alt_instr __smp_alt_instructions[], __smp_alt_instructions_end[]; |
128 | extern u8 *__smp_locks[], *__smp_locks_end[]; | 143 | extern u8 *__smp_locks[], *__smp_locks_end[]; |
@@ -137,10 +152,9 @@ extern u8 __smp_alt_begin[], __smp_alt_end[]; | |||
137 | 152 | ||
138 | void apply_alternatives(struct alt_instr *start, struct alt_instr *end) | 153 | void apply_alternatives(struct alt_instr *start, struct alt_instr *end) |
139 | { | 154 | { |
140 | unsigned char **noptable = find_nop_table(); | ||
141 | struct alt_instr *a; | 155 | struct alt_instr *a; |
142 | u8 *instr; | 156 | u8 *instr; |
143 | int diff, i, k; | 157 | int diff; |
144 | 158 | ||
145 | DPRINTK("%s: alt table %p -> %p\n", __FUNCTION__, start, end); | 159 | DPRINTK("%s: alt table %p -> %p\n", __FUNCTION__, start, end); |
146 | for (a = start; a < end; a++) { | 160 | for (a = start; a < end; a++) { |
@@ -158,13 +172,7 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end) | |||
158 | #endif | 172 | #endif |
159 | memcpy(instr, a->replacement, a->replacementlen); | 173 | memcpy(instr, a->replacement, a->replacementlen); |
160 | diff = a->instrlen - a->replacementlen; | 174 | diff = a->instrlen - a->replacementlen; |
161 | /* Pad the rest with nops */ | 175 | nop_out(instr + a->replacementlen, diff); |
162 | for (i = a->replacementlen; diff > 0; diff -= k, i += k) { | ||
163 | k = diff; | ||
164 | if (k > ASM_NOP_MAX) | ||
165 | k = ASM_NOP_MAX; | ||
166 | memcpy(a->instr + i, noptable[k], k); | ||
167 | } | ||
168 | } | 176 | } |
169 | } | 177 | } |
170 | 178 | ||
@@ -208,7 +216,6 @@ static void alternatives_smp_lock(u8 **start, u8 **end, u8 *text, u8 *text_end) | |||
208 | 216 | ||
209 | static void alternatives_smp_unlock(u8 **start, u8 **end, u8 *text, u8 *text_end) | 217 | static void alternatives_smp_unlock(u8 **start, u8 **end, u8 *text, u8 *text_end) |
210 | { | 218 | { |
211 | unsigned char **noptable = find_nop_table(); | ||
212 | u8 **ptr; | 219 | u8 **ptr; |
213 | 220 | ||
214 | for (ptr = start; ptr < end; ptr++) { | 221 | for (ptr = start; ptr < end; ptr++) { |
@@ -216,7 +223,7 @@ static void alternatives_smp_unlock(u8 **start, u8 **end, u8 *text, u8 *text_end | |||
216 | continue; | 223 | continue; |
217 | if (*ptr > text_end) | 224 | if (*ptr > text_end) |
218 | continue; | 225 | continue; |
219 | **ptr = noptable[1][0]; | 226 | nop_out(*ptr, 1); |
220 | }; | 227 | }; |
221 | } | 228 | } |
222 | 229 | ||
@@ -342,6 +349,40 @@ void alternatives_smp_switch(int smp) | |||
342 | 349 | ||
343 | #endif | 350 | #endif |
344 | 351 | ||
352 | #ifdef CONFIG_PARAVIRT | ||
353 | void apply_paravirt(struct paravirt_patch *start, struct paravirt_patch *end) | ||
354 | { | ||
355 | struct paravirt_patch *p; | ||
356 | |||
357 | for (p = start; p < end; p++) { | ||
358 | unsigned int used; | ||
359 | |||
360 | used = paravirt_ops.patch(p->instrtype, p->clobbers, p->instr, | ||
361 | p->len); | ||
362 | #ifdef CONFIG_DEBUG_PARAVIRT | ||
363 | { | ||
364 | int i; | ||
365 | /* Deliberately clobber regs using "not %reg" to find bugs. */ | ||
366 | for (i = 0; i < 3; i++) { | ||
367 | if (p->len - used >= 2 && (p->clobbers & (1 << i))) { | ||
368 | memcpy(p->instr + used, "\xf7\xd0", 2); | ||
369 | p->instr[used+1] |= i; | ||
370 | used += 2; | ||
371 | } | ||
372 | } | ||
373 | } | ||
374 | #endif | ||
375 | /* Pad the rest with nops */ | ||
376 | nop_out(p->instr + used, p->len - used); | ||
377 | } | ||
378 | |||
379 | /* Sync to be conservative, in case we patched following instructions */ | ||
380 | sync_core(); | ||
381 | } | ||
382 | extern struct paravirt_patch __start_parainstructions[], | ||
383 | __stop_parainstructions[]; | ||
384 | #endif /* CONFIG_PARAVIRT */ | ||
385 | |||
345 | void __init alternative_instructions(void) | 386 | void __init alternative_instructions(void) |
346 | { | 387 | { |
347 | unsigned long flags; | 388 | unsigned long flags; |
@@ -389,5 +430,6 @@ void __init alternative_instructions(void) | |||
389 | alternatives_smp_switch(0); | 430 | alternatives_smp_switch(0); |
390 | } | 431 | } |
391 | #endif | 432 | #endif |
433 | apply_paravirt(__start_parainstructions, __stop_parainstructions); | ||
392 | local_irq_restore(flags); | 434 | local_irq_restore(flags); |
393 | } | 435 | } |