aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/alternative.c
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2010-04-29 19:53:17 -0400
committerH. Peter Anvin <hpa@zytor.com>2010-04-29 19:53:17 -0400
commitd9c5841e22231e4e49fd0a1004164e6fce59b7a6 (patch)
treee1f589c46b3ff79bbe7b1b2469f6362f94576da6 /arch/x86/kernel/alternative.c
parentb701a47ba48b698976fb2fe05fb285b0edc1d26a (diff)
parent5967ed87ade85a421ef814296c3c7f182b08c225 (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.c122
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
194extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; 196extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
195extern u8 *__smp_locks[], *__smp_locks_end[]; 197extern s32 __smp_locks[], __smp_locks_end[];
196static void *text_poke_early(void *addr, const void *opcode, size_t len); 198static 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
236static void alternatives_smp_lock(u8 **start, u8 **end, u8 *text, u8 *text_end) 238static 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
253static void alternatives_smp_unlock(u8 **start, u8 **end, u8 *text, u8 *text_end) 256static 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 */
402int 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 */
589static atomic_t stop_machine_first;
590static int wrote_text;
591
592struct text_poke_params {
593 void *addr;
594 const void *opcode;
595 size_t len;
596};
597
598static 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 */
630void *__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