aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/alternative.c
diff options
context:
space:
mode:
authorMathieu Desnoyers <mathieu.desnoyers@polymtl.ca>2008-03-06 08:48:49 -0500
committerIngo Molnar <mingo@elte.hu>2008-04-17 11:40:58 -0400
commite587cadd8f47e202a30712e2906a65a0606d5865 (patch)
tree9df9016b1cba513cc48472d335899ae5e216f8f1 /arch/x86/kernel/alternative.c
parent77bf90ed66116a1fc0e2f0554ecac75a54290cc0 (diff)
x86: enhance DEBUG_RODATA support - alternatives
Fix a memcpy that should be a text_poke (in apply_alternatives). Use kernel_wp_save/kernel_wp_restore in text_poke to support DEBUG_RODATA correctly and so the CPU HOTPLUG special case can be removed. Add text_poke_early, for alternatives and paravirt boot-time and module load time patching. Changelog: - Fix text_set and text_poke alignment check (mixed up bitwise and and or) - Remove text_set - Export add_nops, so it can be used by others. - Document text_poke_early. - Remove clflush, since it breaks some VIA architectures and is not strictly necessary. - Add kerneldoc to text_poke and text_poke_early. - Create a second vmap instead of using the WP bit to support Xen and VMI. - Move local_irq disable within text_poke and text_poke_early to be able to be sleepable in these functions. Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca> CC: Andi Kleen <andi@firstfloor.org> CC: pageexec@freemail.hu CC: H. Peter Anvin <hpa@zytor.com> CC: Jeremy Fitzhardinge <jeremy@goop.org> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/alternative.c')
-rw-r--r--arch/x86/kernel/alternative.c88
1 files changed, 65 insertions, 23 deletions
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index e2d30b8e08a2..0c92ad4d257a 100644
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -11,6 +11,8 @@
11#include <asm/mce.h> 11#include <asm/mce.h>
12#include <asm/nmi.h> 12#include <asm/nmi.h>
13#include <asm/vsyscall.h> 13#include <asm/vsyscall.h>
14#include <asm/cacheflush.h>
15#include <asm/io.h>
14 16
15#define MAX_PATCH_LEN (255-1) 17#define MAX_PATCH_LEN (255-1)
16 18
@@ -177,7 +179,7 @@ static const unsigned char*const * find_nop_table(void)
177#endif /* CONFIG_X86_64 */ 179#endif /* CONFIG_X86_64 */
178 180
179/* Use this to add nops to a buffer, then text_poke the whole buffer. */ 181/* Use this to add nops to a buffer, then text_poke the whole buffer. */
180static void add_nops(void *insns, unsigned int len) 182void add_nops(void *insns, unsigned int len)
181{ 183{
182 const unsigned char *const *noptable = find_nop_table(); 184 const unsigned char *const *noptable = find_nop_table();
183 185
@@ -190,6 +192,7 @@ static void add_nops(void *insns, unsigned int len)
190 len -= noplen; 192 len -= noplen;
191 } 193 }
192} 194}
195EXPORT_SYMBOL_GPL(add_nops);
193 196
194extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; 197extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
195extern u8 *__smp_locks[], *__smp_locks_end[]; 198extern u8 *__smp_locks[], *__smp_locks_end[];
@@ -223,7 +226,7 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end)
223 memcpy(insnbuf, a->replacement, a->replacementlen); 226 memcpy(insnbuf, a->replacement, a->replacementlen);
224 add_nops(insnbuf + a->replacementlen, 227 add_nops(insnbuf + a->replacementlen,
225 a->instrlen - a->replacementlen); 228 a->instrlen - a->replacementlen);
226 text_poke(instr, insnbuf, a->instrlen); 229 text_poke_early(instr, insnbuf, a->instrlen);
227 } 230 }
228} 231}
229 232
@@ -284,7 +287,6 @@ void alternatives_smp_module_add(struct module *mod, char *name,
284 void *text, void *text_end) 287 void *text, void *text_end)
285{ 288{
286 struct smp_alt_module *smp; 289 struct smp_alt_module *smp;
287 unsigned long flags;
288 290
289 if (noreplace_smp) 291 if (noreplace_smp)
290 return; 292 return;
@@ -310,39 +312,37 @@ void alternatives_smp_module_add(struct module *mod, char *name,
310 __func__, smp->locks, smp->locks_end, 312 __func__, smp->locks, smp->locks_end,
311 smp->text, smp->text_end, smp->name); 313 smp->text, smp->text_end, smp->name);
312 314
313 spin_lock_irqsave(&smp_alt, flags); 315 spin_lock(&smp_alt);
314 list_add_tail(&smp->next, &smp_alt_modules); 316 list_add_tail(&smp->next, &smp_alt_modules);
315 if (boot_cpu_has(X86_FEATURE_UP)) 317 if (boot_cpu_has(X86_FEATURE_UP))
316 alternatives_smp_unlock(smp->locks, smp->locks_end, 318 alternatives_smp_unlock(smp->locks, smp->locks_end,
317 smp->text, smp->text_end); 319 smp->text, smp->text_end);
318 spin_unlock_irqrestore(&smp_alt, flags); 320 spin_unlock(&smp_alt);
319} 321}
320 322
321void alternatives_smp_module_del(struct module *mod) 323void alternatives_smp_module_del(struct module *mod)
322{ 324{
323 struct smp_alt_module *item; 325 struct smp_alt_module *item;
324 unsigned long flags;
325 326
326 if (smp_alt_once || noreplace_smp) 327 if (smp_alt_once || noreplace_smp)
327 return; 328 return;
328 329
329 spin_lock_irqsave(&smp_alt, flags); 330 spin_lock(&smp_alt);
330 list_for_each_entry(item, &smp_alt_modules, next) { 331 list_for_each_entry(item, &smp_alt_modules, next) {
331 if (mod != item->mod) 332 if (mod != item->mod)
332 continue; 333 continue;
333 list_del(&item->next); 334 list_del(&item->next);
334 spin_unlock_irqrestore(&smp_alt, flags); 335 spin_unlock(&smp_alt);
335 DPRINTK("%s: %s\n", __func__, item->name); 336 DPRINTK("%s: %s\n", __func__, item->name);
336 kfree(item); 337 kfree(item);
337 return; 338 return;
338 } 339 }
339 spin_unlock_irqrestore(&smp_alt, flags); 340 spin_unlock(&smp_alt);
340} 341}
341 342
342void alternatives_smp_switch(int smp) 343void alternatives_smp_switch(int smp)
343{ 344{
344 struct smp_alt_module *mod; 345 struct smp_alt_module *mod;
345 unsigned long flags;
346 346
347#ifdef CONFIG_LOCKDEP 347#ifdef CONFIG_LOCKDEP
348 /* 348 /*
@@ -359,7 +359,7 @@ void alternatives_smp_switch(int smp)
359 return; 359 return;
360 BUG_ON(!smp && (num_online_cpus() > 1)); 360 BUG_ON(!smp && (num_online_cpus() > 1));
361 361
362 spin_lock_irqsave(&smp_alt, flags); 362 spin_lock(&smp_alt);
363 363
364 /* 364 /*
365 * Avoid unnecessary switches because it forces JIT based VMs to 365 * Avoid unnecessary switches because it forces JIT based VMs to
@@ -383,7 +383,7 @@ void alternatives_smp_switch(int smp)
383 mod->text, mod->text_end); 383 mod->text, mod->text_end);
384 } 384 }
385 smp_mode = smp; 385 smp_mode = smp;
386 spin_unlock_irqrestore(&smp_alt, flags); 386 spin_unlock(&smp_alt);
387} 387}
388 388
389#endif 389#endif
@@ -411,7 +411,7 @@ void apply_paravirt(struct paravirt_patch_site *start,
411 411
412 /* Pad the rest with nops */ 412 /* Pad the rest with nops */
413 add_nops(insnbuf + used, p->len - used); 413 add_nops(insnbuf + used, p->len - used);
414 text_poke(p->instr, insnbuf, p->len); 414 text_poke_early(p->instr, insnbuf, p->len);
415 } 415 }
416} 416}
417extern struct paravirt_patch_site __start_parainstructions[], 417extern struct paravirt_patch_site __start_parainstructions[],
@@ -420,8 +420,6 @@ extern struct paravirt_patch_site __start_parainstructions[],
420 420
421void __init alternative_instructions(void) 421void __init alternative_instructions(void)
422{ 422{
423 unsigned long flags;
424
425 /* The patching is not fully atomic, so try to avoid local interruptions 423 /* The patching is not fully atomic, so try to avoid local interruptions
426 that might execute the to be patched code. 424 that might execute the to be patched code.
427 Other CPUs are not running. */ 425 Other CPUs are not running. */
@@ -430,7 +428,6 @@ void __init alternative_instructions(void)
430 stop_mce(); 428 stop_mce();
431#endif 429#endif
432 430
433 local_irq_save(flags);
434 apply_alternatives(__alt_instructions, __alt_instructions_end); 431 apply_alternatives(__alt_instructions, __alt_instructions_end);
435 432
436 /* switch to patch-once-at-boottime-only mode and free the 433 /* switch to patch-once-at-boottime-only mode and free the
@@ -462,7 +459,6 @@ void __init alternative_instructions(void)
462 } 459 }
463#endif 460#endif
464 apply_paravirt(__parainstructions, __parainstructions_end); 461 apply_paravirt(__parainstructions, __parainstructions_end);
465 local_irq_restore(flags);
466 462
467 if (smp_alt_once) 463 if (smp_alt_once)
468 free_init_pages("SMP alternatives", 464 free_init_pages("SMP alternatives",
@@ -475,18 +471,64 @@ void __init alternative_instructions(void)
475#endif 471#endif
476} 472}
477 473
478/* 474/**
479 * Warning: 475 * text_poke_early - Update instructions on a live kernel at boot time
476 * @addr: address to modify
477 * @opcode: source of the copy
478 * @len: length to copy
479 *
480 * When you use this code to patch more than one byte of an instruction 480 * When you use this code to patch more than one byte of an instruction
481 * you need to make sure that other CPUs cannot execute this code in parallel. 481 * you need to make sure that other CPUs cannot execute this code in parallel.
482 * Also no thread must be currently preempted in the middle of these instructions. 482 * Also no thread must be currently preempted in the middle of these
483 * And on the local CPU you need to be protected again NMI or MCE handlers 483 * instructions. And on the local CPU you need to be protected again NMI or MCE
484 * seeing an inconsistent instruction while you patch. 484 * handlers seeing an inconsistent instruction while you patch.
485 */ 485 */
486void __kprobes text_poke(void *addr, unsigned char *opcode, int len) 486void *text_poke_early(void *addr, const void *opcode, size_t len)
487{ 487{
488 unsigned long flags;
489 local_irq_save(flags);
488 memcpy(addr, opcode, len); 490 memcpy(addr, opcode, len);
491 local_irq_restore(flags);
492 sync_core();
493 /* Could also do a CLFLUSH here to speed up CPU recovery; but
494 that causes hangs on some VIA CPUs. */
495 return addr;
496}
497
498/**
499 * text_poke - Update instructions on a live kernel
500 * @addr: address to modify
501 * @opcode: source of the copy
502 * @len: length to copy
503 *
504 * Only atomic text poke/set should be allowed when not doing early patching.
505 * It means the size must be writable atomically and the address must be aligned
506 * in a way that permits an atomic write. It also makes sure we fit on a single
507 * page.
508 */
509void *__kprobes text_poke(void *addr, const void *opcode, size_t len)
510{
511 unsigned long flags;
512 char *vaddr;
513 int nr_pages = 2;
514
515 BUG_ON(len > sizeof(long));
516 BUG_ON((((long)addr + len - 1) & ~(sizeof(long) - 1))
517 - ((long)addr & ~(sizeof(long) - 1)));
518 {
519 struct page *pages[2] = { virt_to_page(addr),
520 virt_to_page(addr + PAGE_SIZE) };
521 if (!pages[1])
522 nr_pages = 1;
523 vaddr = vmap(pages, nr_pages, VM_MAP, PAGE_KERNEL);
524 BUG_ON(!vaddr);
525 local_irq_save(flags);
526 memcpy(&vaddr[(unsigned long)addr & ~PAGE_MASK], opcode, len);
527 local_irq_restore(flags);
528 vunmap(vaddr);
529 }
489 sync_core(); 530 sync_core();
490 /* Could also do a CLFLUSH here to speed up CPU recovery; but 531 /* Could also do a CLFLUSH here to speed up CPU recovery; but
491 that causes hangs on some VIA CPUs. */ 532 that causes hangs on some VIA CPUs. */
533 return addr;
492} 534}