aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386/kernel/alternative.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/i386/kernel/alternative.c')
-rw-r--r--arch/i386/kernel/alternative.c68
1 files changed, 57 insertions, 11 deletions
diff --git a/arch/i386/kernel/alternative.c b/arch/i386/kernel/alternative.c
index 28ab80649764..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
127static 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
126extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; 141extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
127extern struct alt_instr __smp_alt_instructions[], __smp_alt_instructions_end[]; 142extern struct alt_instr __smp_alt_instructions[], __smp_alt_instructions_end[];
128extern u8 *__smp_locks[], *__smp_locks_end[]; 143extern u8 *__smp_locks[], *__smp_locks_end[];
@@ -137,10 +152,9 @@ extern u8 __smp_alt_begin[], __smp_alt_end[];
137 152
138void apply_alternatives(struct alt_instr *start, struct alt_instr *end) 153void 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
209static void alternatives_smp_unlock(u8 **start, u8 **end, u8 *text, u8 *text_end) 217static 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,8 +349,43 @@ void alternatives_smp_switch(int smp)
342 349
343#endif 350#endif
344 351
352#ifdef CONFIG_PARAVIRT
353void 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}
382extern struct paravirt_patch __start_parainstructions[],
383 __stop_parainstructions[];
384#endif /* CONFIG_PARAVIRT */
385
345void __init alternative_instructions(void) 386void __init alternative_instructions(void)
346{ 387{
388 unsigned long flags;
347 if (no_replacement) { 389 if (no_replacement) {
348 printk(KERN_INFO "(SMP-)alternatives turned off\n"); 390 printk(KERN_INFO "(SMP-)alternatives turned off\n");
349 free_init_pages("SMP alternatives", 391 free_init_pages("SMP alternatives",
@@ -351,6 +393,8 @@ void __init alternative_instructions(void)
351 (unsigned long)__smp_alt_end); 393 (unsigned long)__smp_alt_end);
352 return; 394 return;
353 } 395 }
396
397 local_irq_save(flags);
354 apply_alternatives(__alt_instructions, __alt_instructions_end); 398 apply_alternatives(__alt_instructions, __alt_instructions_end);
355 399
356 /* switch to patch-once-at-boottime-only mode and free the 400 /* switch to patch-once-at-boottime-only mode and free the
@@ -386,4 +430,6 @@ void __init alternative_instructions(void)
386 alternatives_smp_switch(0); 430 alternatives_smp_switch(0);
387 } 431 }
388#endif 432#endif
433 apply_paravirt(__start_parainstructions, __stop_parainstructions);
434 local_irq_restore(flags);
389} 435}