diff options
| author | Masami Hiramatsu <mhiramat@redhat.com> | 2010-02-25 08:34:38 -0500 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2010-02-25 11:49:26 -0500 |
| commit | 3d55cc8a058ee96291d6d45b1e35121b9920eca3 (patch) | |
| tree | 716a79a900c013082f02de127e1fae784997ce49 | |
| parent | f007ea2685692bafb386820144cf73a14016fc7c (diff) | |
x86: Add text_poke_smp for SMP cross modifying code
Add generic text_poke_smp for SMP which uses stop_machine()
to synchronize modifying code.
This stop_machine() method is officially described at "7.1.3
Handling Self- and Cross-Modifying Code" on the intel's
software developer's manual 3A.
Since stop_machine() can't protect code against NMI/MCE, this
function can not modify those handlers. And also, this function
is basically for modifying multibyte-single-instruction. For
modifying multibyte-multi-instructions, we need another special
trap & detour code.
This code originaly comes from immediate values with
stop_machine() version. Thanks Jason and Mathieu!
Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: systemtap <systemtap@sources.redhat.com>
Cc: DLE <dle-develop@lists.sourceforge.net>
Cc: Mathieu Desnoyers <compudj@krystal.dyndns.org>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Jim Keniston <jkenisto@us.ibm.com>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Anders Kaseorg <andersk@ksplice.com>
Cc: Tim Abbott <tabbott@ksplice.com>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Jason Baron <jbaron@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
LKML-Reference: <20100225133438.6725.80273.stgit@localhost6.localdomain6>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
| -rw-r--r-- | arch/x86/include/asm/alternative.h | 4 | ||||
| -rw-r--r-- | arch/x86/kernel/alternative.c | 60 |
2 files changed, 63 insertions, 1 deletions
diff --git a/arch/x86/include/asm/alternative.h b/arch/x86/include/asm/alternative.h index ac80b7d70014..643d6ab3588b 100644 --- a/arch/x86/include/asm/alternative.h +++ b/arch/x86/include/asm/alternative.h | |||
| @@ -160,10 +160,12 @@ static inline void apply_paravirt(struct paravirt_patch_site *start, | |||
| 160 | * invalid instruction possible) or if the instructions are changed from a | 160 | * invalid instruction possible) or if the instructions are changed from a |
| 161 | * consistent state to another consistent state atomically. | 161 | * consistent state to another consistent state atomically. |
| 162 | * More care must be taken when modifying code in the SMP case because of | 162 | * More care must be taken when modifying code in the SMP case because of |
| 163 | * Intel's errata. | 163 | * Intel's errata. text_poke_smp() takes care that errata, but still |
| 164 | * doesn't support NMI/MCE handler code modifying. | ||
| 164 | * On the local CPU you need to be protected again NMI or MCE handlers seeing an | 165 | * On the local CPU you need to be protected again NMI or MCE handlers seeing an |
| 165 | * inconsistent instruction while you patch. | 166 | * inconsistent instruction while you patch. |
| 166 | */ | 167 | */ |
| 167 | extern void *text_poke(void *addr, const void *opcode, size_t len); | 168 | extern void *text_poke(void *addr, const void *opcode, size_t len); |
| 169 | extern void *text_poke_smp(void *addr, const void *opcode, size_t len); | ||
| 168 | 170 | ||
| 169 | #endif /* _ASM_X86_ALTERNATIVE_H */ | 171 | #endif /* _ASM_X86_ALTERNATIVE_H */ |
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c index e63b80e5861c..c41f13c15e8f 100644 --- a/arch/x86/kernel/alternative.c +++ b/arch/x86/kernel/alternative.c | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <linux/mm.h> | 7 | #include <linux/mm.h> |
| 8 | #include <linux/vmalloc.h> | 8 | #include <linux/vmalloc.h> |
| 9 | #include <linux/memory.h> | 9 | #include <linux/memory.h> |
| 10 | #include <linux/stop_machine.h> | ||
| 10 | #include <asm/alternative.h> | 11 | #include <asm/alternative.h> |
| 11 | #include <asm/sections.h> | 12 | #include <asm/sections.h> |
| 12 | #include <asm/pgtable.h> | 13 | #include <asm/pgtable.h> |
| @@ -570,3 +571,62 @@ void *__kprobes text_poke(void *addr, const void *opcode, size_t len) | |||
| 570 | local_irq_restore(flags); | 571 | local_irq_restore(flags); |
| 571 | return addr; | 572 | return addr; |
| 572 | } | 573 | } |
| 574 | |||
| 575 | /* | ||
| 576 | * Cross-modifying kernel text with stop_machine(). | ||
| 577 | * This code originally comes from immediate value. | ||
| 578 | */ | ||
| 579 | static atomic_t stop_machine_first; | ||
| 580 | static int wrote_text; | ||
| 581 | |||
| 582 | struct text_poke_params { | ||
| 583 | void *addr; | ||
| 584 | const void *opcode; | ||
| 585 | size_t len; | ||
| 586 | }; | ||
| 587 | |||
| 588 | static int __kprobes stop_machine_text_poke(void *data) | ||
| 589 | { | ||
| 590 | struct text_poke_params *tpp = data; | ||
| 591 | |||
| 592 | if (atomic_dec_and_test(&stop_machine_first)) { | ||
| 593 | text_poke(tpp->addr, tpp->opcode, tpp->len); | ||
| 594 | smp_wmb(); /* Make sure other cpus see that this has run */ | ||
| 595 | wrote_text = 1; | ||
| 596 | } else { | ||
| 597 | while (!wrote_text) | ||
| 598 | smp_rmb(); | ||
| 599 | sync_core(); | ||
| 600 | } | ||
| 601 | |||
| 602 | flush_icache_range((unsigned long)tpp->addr, | ||
| 603 | (unsigned long)tpp->addr + tpp->len); | ||
| 604 | return 0; | ||
| 605 | } | ||
| 606 | |||
| 607 | /** | ||
| 608 | * text_poke_smp - Update instructions on a live kernel on SMP | ||
| 609 | * @addr: address to modify | ||
| 610 | * @opcode: source of the copy | ||
| 611 | * @len: length to copy | ||
| 612 | * | ||
| 613 | * Modify multi-byte instruction by using stop_machine() on SMP. This allows | ||
| 614 | * user to poke/set multi-byte text on SMP. Only non-NMI/MCE code modifying | ||
| 615 | * should be allowed, since stop_machine() does _not_ protect code against | ||
| 616 | * NMI and MCE. | ||
| 617 | * | ||
| 618 | * Note: Must be called under get_online_cpus() and text_mutex. | ||
| 619 | */ | ||
| 620 | void *__kprobes text_poke_smp(void *addr, const void *opcode, size_t len) | ||
| 621 | { | ||
| 622 | struct text_poke_params tpp; | ||
| 623 | |||
| 624 | tpp.addr = addr; | ||
| 625 | tpp.opcode = opcode; | ||
| 626 | tpp.len = len; | ||
| 627 | atomic_set(&stop_machine_first, 1); | ||
| 628 | wrote_text = 0; | ||
| 629 | stop_machine(stop_machine_text_poke, (void *)&tpp, NULL); | ||
| 630 | return addr; | ||
| 631 | } | ||
| 632 | |||
