diff options
author | H. Peter Anvin <hpa@zytor.com> | 2010-04-29 19:53:17 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2010-04-29 19:53:17 -0400 |
commit | d9c5841e22231e4e49fd0a1004164e6fce59b7a6 (patch) | |
tree | e1f589c46b3ff79bbe7b1b2469f6362f94576da6 /arch/x86/kernel/alternative.c | |
parent | b701a47ba48b698976fb2fe05fb285b0edc1d26a (diff) | |
parent | 5967ed87ade85a421ef814296c3c7f182b08c225 (diff) |
Merge branch 'x86/asm' into x86/atomic
Merge reason:
Conflict between LOCK_PREFIX_HERE and relative alternatives
pointers
Resolved Conflicts:
arch/x86/include/asm/alternative.h
arch/x86/kernel/alternative.c
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'arch/x86/kernel/alternative.c')
-rw-r--r-- | arch/x86/kernel/alternative.c | 122 |
1 files changed, 103 insertions, 19 deletions
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c index 80b222ea4cf6..70237732a6c7 100644 --- a/arch/x86/kernel/alternative.c +++ b/arch/x86/kernel/alternative.c | |||
@@ -7,6 +7,8 @@ | |||
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> | ||
11 | #include <linux/slab.h> | ||
10 | #include <asm/alternative.h> | 12 | #include <asm/alternative.h> |
11 | #include <asm/sections.h> | 13 | #include <asm/sections.h> |
12 | #include <asm/pgtable.h> | 14 | #include <asm/pgtable.h> |
@@ -192,7 +194,7 @@ static void __init_or_module add_nops(void *insns, unsigned int len) | |||
192 | } | 194 | } |
193 | 195 | ||
194 | extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; | 196 | extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; |
195 | extern u8 *__smp_locks[], *__smp_locks_end[]; | 197 | extern s32 __smp_locks[], __smp_locks_end[]; |
196 | static void *text_poke_early(void *addr, const void *opcode, size_t len); | 198 | static void *text_poke_early(void *addr, const void *opcode, size_t len); |
197 | 199 | ||
198 | /* Replace instructions with better alternatives for this CPU type. | 200 | /* Replace instructions with better alternatives for this CPU type. |
@@ -233,39 +235,41 @@ void __init_or_module apply_alternatives(struct alt_instr *start, | |||
233 | 235 | ||
234 | #ifdef CONFIG_SMP | 236 | #ifdef CONFIG_SMP |
235 | 237 | ||
236 | static void alternatives_smp_lock(u8 **start, u8 **end, u8 *text, u8 *text_end) | 238 | static void alternatives_smp_lock(const s32 *start, const s32 *end, |
239 | u8 *text, u8 *text_end) | ||
237 | { | 240 | { |
238 | u8 **ptr; | 241 | const s32 *poff; |
239 | 242 | ||
240 | mutex_lock(&text_mutex); | 243 | mutex_lock(&text_mutex); |
241 | for (ptr = start; ptr < end; ptr++) { | 244 | for (poff = start; poff < end; poff++) { |
242 | if (*ptr < text) | 245 | u8 *ptr = (u8 *)poff + *poff; |
243 | continue; | 246 | |
244 | if (*ptr > text_end) | 247 | if (!*poff || ptr < text || ptr >= text_end) |
245 | continue; | 248 | continue; |
246 | /* turn DS segment override prefix into lock prefix */ | 249 | /* turn DS segment override prefix into lock prefix */ |
247 | if (**ptr == 0x3e) | 250 | if (*ptr == 0x3e) |
248 | text_poke(*ptr, ((unsigned char []){0xf0}), 1); | 251 | text_poke(ptr, ((unsigned char []){0xf0}), 1); |
249 | }; | 252 | }; |
250 | mutex_unlock(&text_mutex); | 253 | mutex_unlock(&text_mutex); |
251 | } | 254 | } |
252 | 255 | ||
253 | static void alternatives_smp_unlock(u8 **start, u8 **end, u8 *text, u8 *text_end) | 256 | static void alternatives_smp_unlock(const s32 *start, const s32 *end, |
257 | u8 *text, u8 *text_end) | ||
254 | { | 258 | { |
255 | u8 **ptr; | 259 | const s32 *poff; |
256 | 260 | ||
257 | if (noreplace_smp) | 261 | if (noreplace_smp) |
258 | return; | 262 | return; |
259 | 263 | ||
260 | mutex_lock(&text_mutex); | 264 | mutex_lock(&text_mutex); |
261 | for (ptr = start; ptr < end; ptr++) { | 265 | for (poff = start; poff < end; poff++) { |
262 | if (*ptr < text) | 266 | u8 *ptr = (u8 *)poff + *poff; |
263 | continue; | 267 | |
264 | if (*ptr > text_end) | 268 | if (!*poff || ptr < text || ptr >= text_end) |
265 | continue; | 269 | continue; |
266 | /* turn lock prefix into DS segment override prefix */ | 270 | /* turn lock prefix into DS segment override prefix */ |
267 | if (**ptr == 0xf0) | 271 | if (*ptr == 0xf0) |
268 | text_poke(*ptr, ((unsigned char []){0x3E}), 1); | 272 | text_poke(ptr, ((unsigned char []){0x3E}), 1); |
269 | }; | 273 | }; |
270 | mutex_unlock(&text_mutex); | 274 | mutex_unlock(&text_mutex); |
271 | } | 275 | } |
@@ -276,8 +280,8 @@ struct smp_alt_module { | |||
276 | char *name; | 280 | char *name; |
277 | 281 | ||
278 | /* ptrs to lock prefixes */ | 282 | /* ptrs to lock prefixes */ |
279 | u8 **locks; | 283 | const s32 *locks; |
280 | u8 **locks_end; | 284 | const s32 *locks_end; |
281 | 285 | ||
282 | /* .text segment, needed to avoid patching init code ;) */ | 286 | /* .text segment, needed to avoid patching init code ;) */ |
283 | u8 *text; | 287 | u8 *text; |
@@ -394,6 +398,27 @@ void alternatives_smp_switch(int smp) | |||
394 | mutex_unlock(&smp_alt); | 398 | mutex_unlock(&smp_alt); |
395 | } | 399 | } |
396 | 400 | ||
401 | /* Return 1 if the address range is reserved for smp-alternatives */ | ||
402 | int alternatives_text_reserved(void *start, void *end) | ||
403 | { | ||
404 | struct smp_alt_module *mod; | ||
405 | const s32 *poff; | ||
406 | u8 *text_start = start; | ||
407 | u8 *text_end = end; | ||
408 | |||
409 | list_for_each_entry(mod, &smp_alt_modules, next) { | ||
410 | if (mod->text > text_end || mod->text_end < text_start) | ||
411 | continue; | ||
412 | for (poff = mod->locks; poff < mod->locks_end; poff++) { | ||
413 | const u8 *ptr = (const u8 *)poff + *poff; | ||
414 | |||
415 | if (text_start <= ptr && text_end > ptr) | ||
416 | return 1; | ||
417 | } | ||
418 | } | ||
419 | |||
420 | return 0; | ||
421 | } | ||
397 | #endif | 422 | #endif |
398 | 423 | ||
399 | #ifdef CONFIG_PARAVIRT | 424 | #ifdef CONFIG_PARAVIRT |
@@ -556,3 +581,62 @@ void *__kprobes text_poke(void *addr, const void *opcode, size_t len) | |||
556 | local_irq_restore(flags); | 581 | local_irq_restore(flags); |
557 | return addr; | 582 | return addr; |
558 | } | 583 | } |
584 | |||
585 | /* | ||
586 | * Cross-modifying kernel text with stop_machine(). | ||
587 | * This code originally comes from immediate value. | ||
588 | */ | ||
589 | static atomic_t stop_machine_first; | ||
590 | static int wrote_text; | ||
591 | |||
592 | struct text_poke_params { | ||
593 | void *addr; | ||
594 | const void *opcode; | ||
595 | size_t len; | ||
596 | }; | ||
597 | |||
598 | static int __kprobes stop_machine_text_poke(void *data) | ||
599 | { | ||
600 | struct text_poke_params *tpp = data; | ||
601 | |||
602 | if (atomic_dec_and_test(&stop_machine_first)) { | ||
603 | text_poke(tpp->addr, tpp->opcode, tpp->len); | ||
604 | smp_wmb(); /* Make sure other cpus see that this has run */ | ||
605 | wrote_text = 1; | ||
606 | } else { | ||
607 | while (!wrote_text) | ||
608 | cpu_relax(); | ||
609 | smp_mb(); /* Load wrote_text before following execution */ | ||
610 | } | ||
611 | |||
612 | flush_icache_range((unsigned long)tpp->addr, | ||
613 | (unsigned long)tpp->addr + tpp->len); | ||
614 | return 0; | ||
615 | } | ||
616 | |||
617 | /** | ||
618 | * text_poke_smp - Update instructions on a live kernel on SMP | ||
619 | * @addr: address to modify | ||
620 | * @opcode: source of the copy | ||
621 | * @len: length to copy | ||
622 | * | ||
623 | * Modify multi-byte instruction by using stop_machine() on SMP. This allows | ||
624 | * user to poke/set multi-byte text on SMP. Only non-NMI/MCE code modifying | ||
625 | * should be allowed, since stop_machine() does _not_ protect code against | ||
626 | * NMI and MCE. | ||
627 | * | ||
628 | * Note: Must be called under get_online_cpus() and text_mutex. | ||
629 | */ | ||
630 | void *__kprobes text_poke_smp(void *addr, const void *opcode, size_t len) | ||
631 | { | ||
632 | struct text_poke_params tpp; | ||
633 | |||
634 | tpp.addr = addr; | ||
635 | tpp.opcode = opcode; | ||
636 | tpp.len = len; | ||
637 | atomic_set(&stop_machine_first, 1); | ||
638 | wrote_text = 0; | ||
639 | stop_machine(stop_machine_text_poke, (void *)&tpp, NULL); | ||
640 | return addr; | ||
641 | } | ||
642 | |||