aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/alternative.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86/kernel/alternative.c')
-rw-r--r--arch/x86/kernel/alternative.c125
1 files changed, 107 insertions, 18 deletions
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index de7353c0ce9c..f65ab8b014c4 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.
@@ -205,13 +207,14 @@ void __init_or_module apply_alternatives(struct alt_instr *start,
205 struct alt_instr *end) 207 struct alt_instr *end)
206{ 208{
207 struct alt_instr *a; 209 struct alt_instr *a;
208 char insnbuf[MAX_PATCH_LEN]; 210 u8 insnbuf[MAX_PATCH_LEN];
209 211
210 DPRINTK("%s: alt table %p -> %p\n", __func__, start, end); 212 DPRINTK("%s: alt table %p -> %p\n", __func__, start, end);
211 for (a = start; a < end; a++) { 213 for (a = start; a < end; a++) {
212 u8 *instr = a->instr; 214 u8 *instr = a->instr;
213 BUG_ON(a->replacementlen > a->instrlen); 215 BUG_ON(a->replacementlen > a->instrlen);
214 BUG_ON(a->instrlen > sizeof(insnbuf)); 216 BUG_ON(a->instrlen > sizeof(insnbuf));
217 BUG_ON(a->cpuid >= NCAPINTS*32);
215 if (!boot_cpu_has(a->cpuid)) 218 if (!boot_cpu_has(a->cpuid))
216 continue; 219 continue;
217#ifdef CONFIG_X86_64 220#ifdef CONFIG_X86_64
@@ -223,6 +226,8 @@ void __init_or_module apply_alternatives(struct alt_instr *start,
223 } 226 }
224#endif 227#endif
225 memcpy(insnbuf, a->replacement, a->replacementlen); 228 memcpy(insnbuf, a->replacement, a->replacementlen);
229 if (*insnbuf == 0xe8 && a->replacementlen == 5)
230 *(s32 *)(insnbuf + 1) += a->replacement - a->instr;
226 add_nops(insnbuf + a->replacementlen, 231 add_nops(insnbuf + a->replacementlen,
227 a->instrlen - a->replacementlen); 232 a->instrlen - a->replacementlen);
228 text_poke_early(instr, insnbuf, a->instrlen); 233 text_poke_early(instr, insnbuf, a->instrlen);
@@ -231,37 +236,41 @@ void __init_or_module apply_alternatives(struct alt_instr *start,
231 236
232#ifdef CONFIG_SMP 237#ifdef CONFIG_SMP
233 238
234static void alternatives_smp_lock(u8 **start, u8 **end, u8 *text, u8 *text_end) 239static void alternatives_smp_lock(const s32 *start, const s32 *end,
240 u8 *text, u8 *text_end)
235{ 241{
236 u8 **ptr; 242 const s32 *poff;
237 243
238 mutex_lock(&text_mutex); 244 mutex_lock(&text_mutex);
239 for (ptr = start; ptr < end; ptr++) { 245 for (poff = start; poff < end; poff++) {
240 if (*ptr < text) 246 u8 *ptr = (u8 *)poff + *poff;
241 continue; 247
242 if (*ptr > text_end) 248 if (!*poff || ptr < text || ptr >= text_end)
243 continue; 249 continue;
244 /* turn DS segment override prefix into lock prefix */ 250 /* turn DS segment override prefix into lock prefix */
245 text_poke(*ptr, ((unsigned char []){0xf0}), 1); 251 if (*ptr == 0x3e)
252 text_poke(ptr, ((unsigned char []){0xf0}), 1);
246 }; 253 };
247 mutex_unlock(&text_mutex); 254 mutex_unlock(&text_mutex);
248} 255}
249 256
250static void alternatives_smp_unlock(u8 **start, u8 **end, u8 *text, u8 *text_end) 257static void alternatives_smp_unlock(const s32 *start, const s32 *end,
258 u8 *text, u8 *text_end)
251{ 259{
252 u8 **ptr; 260 const s32 *poff;
253 261
254 if (noreplace_smp) 262 if (noreplace_smp)
255 return; 263 return;
256 264
257 mutex_lock(&text_mutex); 265 mutex_lock(&text_mutex);
258 for (ptr = start; ptr < end; ptr++) { 266 for (poff = start; poff < end; poff++) {
259 if (*ptr < text) 267 u8 *ptr = (u8 *)poff + *poff;
260 continue; 268
261 if (*ptr > text_end) 269 if (!*poff || ptr < text || ptr >= text_end)
262 continue; 270 continue;
263 /* turn lock prefix into DS segment override prefix */ 271 /* turn lock prefix into DS segment override prefix */
264 text_poke(*ptr, ((unsigned char []){0x3E}), 1); 272 if (*ptr == 0xf0)
273 text_poke(ptr, ((unsigned char []){0x3E}), 1);
265 }; 274 };
266 mutex_unlock(&text_mutex); 275 mutex_unlock(&text_mutex);
267} 276}
@@ -272,8 +281,8 @@ struct smp_alt_module {
272 char *name; 281 char *name;
273 282
274 /* ptrs to lock prefixes */ 283 /* ptrs to lock prefixes */
275 u8 **locks; 284 const s32 *locks;
276 u8 **locks_end; 285 const s32 *locks_end;
277 286
278 /* .text segment, needed to avoid patching init code ;) */ 287 /* .text segment, needed to avoid patching init code ;) */
279 u8 *text; 288 u8 *text;
@@ -390,6 +399,27 @@ void alternatives_smp_switch(int smp)
390 mutex_unlock(&smp_alt); 399 mutex_unlock(&smp_alt);
391} 400}
392 401
402/* Return 1 if the address range is reserved for smp-alternatives */
403int alternatives_text_reserved(void *start, void *end)
404{
405 struct smp_alt_module *mod;
406 const s32 *poff;
407 u8 *text_start = start;
408 u8 *text_end = end;
409
410 list_for_each_entry(mod, &smp_alt_modules, next) {
411 if (mod->text > text_end || mod->text_end < text_start)
412 continue;
413 for (poff = mod->locks; poff < mod->locks_end; poff++) {
414 const u8 *ptr = (const u8 *)poff + *poff;
415
416 if (text_start <= ptr && text_end > ptr)
417 return 1;
418 }
419 }
420
421 return 0;
422}
393#endif 423#endif
394 424
395#ifdef CONFIG_PARAVIRT 425#ifdef CONFIG_PARAVIRT
@@ -552,3 +582,62 @@ void *__kprobes text_poke(void *addr, const void *opcode, size_t len)
552 local_irq_restore(flags); 582 local_irq_restore(flags);
553 return addr; 583 return addr;
554} 584}
585
586/*
587 * Cross-modifying kernel text with stop_machine().
588 * This code originally comes from immediate value.
589 */
590static atomic_t stop_machine_first;
591static int wrote_text;
592
593struct text_poke_params {
594 void *addr;
595 const void *opcode;
596 size_t len;
597};
598
599static int __kprobes stop_machine_text_poke(void *data)
600{
601 struct text_poke_params *tpp = data;
602
603 if (atomic_dec_and_test(&stop_machine_first)) {
604 text_poke(tpp->addr, tpp->opcode, tpp->len);
605 smp_wmb(); /* Make sure other cpus see that this has run */
606 wrote_text = 1;
607 } else {
608 while (!wrote_text)
609 cpu_relax();
610 smp_mb(); /* Load wrote_text before following execution */
611 }
612
613 flush_icache_range((unsigned long)tpp->addr,
614 (unsigned long)tpp->addr + tpp->len);
615 return 0;
616}
617
618/**
619 * text_poke_smp - Update instructions on a live kernel on SMP
620 * @addr: address to modify
621 * @opcode: source of the copy
622 * @len: length to copy
623 *
624 * Modify multi-byte instruction by using stop_machine() on SMP. This allows
625 * user to poke/set multi-byte text on SMP. Only non-NMI/MCE code modifying
626 * should be allowed, since stop_machine() does _not_ protect code against
627 * NMI and MCE.
628 *
629 * Note: Must be called under get_online_cpus() and text_mutex.
630 */
631void *__kprobes text_poke_smp(void *addr, const void *opcode, size_t len)
632{
633 struct text_poke_params tpp;
634
635 tpp.addr = addr;
636 tpp.opcode = opcode;
637 tpp.len = len;
638 atomic_set(&stop_machine_first, 1);
639 wrote_text = 0;
640 stop_machine(stop_machine_text_poke, (void *)&tpp, NULL);
641 return addr;
642}
643