blob: 1b66d5c70eaf65fc88fa1146313071843a069993 [file] [log] [blame]
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -08001#include <linux/module.h>
Al Virof6a57032006-10-18 01:47:25 -04002#include <linux/sched.h>
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -08003#include <linux/spinlock.h>
4#include <linux/list.h>
Andi Kleen19d36cc2007-07-22 11:12:31 +02005#include <linux/kprobes.h>
6#include <linux/mm.h>
7#include <linux/vmalloc.h>
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -08008#include <asm/alternative.h>
9#include <asm/sections.h>
Andi Kleen19d36cc2007-07-22 11:12:31 +020010#include <asm/pgtable.h>
Andi Kleen8f4e9562007-07-22 11:12:32 +020011#include <asm/mce.h>
12#include <asm/nmi.h>
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -080013
Andi Kleenab144f52007-08-10 22:31:03 +020014#define MAX_PATCH_LEN (255-1)
15
Jan Beulich09488162007-07-21 17:10:25 +020016#ifdef CONFIG_HOTPLUG_CPU
17static int smp_alt_once;
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -080018
Gerd Hoffmannd167a512006-06-26 13:56:16 +020019static int __init bootonly(char *str)
20{
21 smp_alt_once = 1;
22 return 1;
23}
Jeremy Fitzhardingeb7fb4af2007-05-02 19:27:13 +020024__setup("smp-alt-boot", bootonly);
Jan Beulich09488162007-07-21 17:10:25 +020025#else
26#define smp_alt_once 1
27#endif
28
29static int debug_alternative;
Jeremy Fitzhardingeb7fb4af2007-05-02 19:27:13 +020030
Gerd Hoffmannd167a512006-06-26 13:56:16 +020031static int __init debug_alt(char *str)
32{
33 debug_alternative = 1;
34 return 1;
35}
Gerd Hoffmannd167a512006-06-26 13:56:16 +020036__setup("debug-alternative", debug_alt);
37
Jan Beulich09488162007-07-21 17:10:25 +020038static int noreplace_smp;
39
Jeremy Fitzhardingeb7fb4af2007-05-02 19:27:13 +020040static int __init setup_noreplace_smp(char *str)
41{
42 noreplace_smp = 1;
43 return 1;
44}
45__setup("noreplace-smp", setup_noreplace_smp);
46
Jeremy Fitzhardinge959b4fd2007-05-02 19:27:16 +020047#ifdef CONFIG_PARAVIRT
48static int noreplace_paravirt = 0;
49
50static int __init setup_noreplace_paravirt(char *str)
51{
52 noreplace_paravirt = 1;
53 return 1;
54}
55__setup("noreplace-paravirt", setup_noreplace_paravirt);
56#endif
Jeremy Fitzhardingeb7fb4af2007-05-02 19:27:13 +020057
Gerd Hoffmannd167a512006-06-26 13:56:16 +020058#define DPRINTK(fmt, args...) if (debug_alternative) \
59 printk(KERN_DEBUG fmt, args)
60
61#ifdef GENERIC_NOP1
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -080062/* Use inline assembly to define this because the nops are defined
63 as inline assembly strings in the include files and we cannot
64 get them easily into strings. */
65asm("\t.data\nintelnops: "
66 GENERIC_NOP1 GENERIC_NOP2 GENERIC_NOP3 GENERIC_NOP4 GENERIC_NOP5 GENERIC_NOP6
67 GENERIC_NOP7 GENERIC_NOP8);
Gerd Hoffmannd167a512006-06-26 13:56:16 +020068extern unsigned char intelnops[];
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -080069static unsigned char *intel_nops[ASM_NOP_MAX+1] = {
70 NULL,
71 intelnops,
72 intelnops + 1,
73 intelnops + 1 + 2,
74 intelnops + 1 + 2 + 3,
75 intelnops + 1 + 2 + 3 + 4,
76 intelnops + 1 + 2 + 3 + 4 + 5,
77 intelnops + 1 + 2 + 3 + 4 + 5 + 6,
78 intelnops + 1 + 2 + 3 + 4 + 5 + 6 + 7,
79};
Gerd Hoffmannd167a512006-06-26 13:56:16 +020080#endif
81
82#ifdef K8_NOP1
83asm("\t.data\nk8nops: "
84 K8_NOP1 K8_NOP2 K8_NOP3 K8_NOP4 K8_NOP5 K8_NOP6
85 K8_NOP7 K8_NOP8);
86extern unsigned char k8nops[];
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -080087static unsigned char *k8_nops[ASM_NOP_MAX+1] = {
88 NULL,
89 k8nops,
90 k8nops + 1,
91 k8nops + 1 + 2,
92 k8nops + 1 + 2 + 3,
93 k8nops + 1 + 2 + 3 + 4,
94 k8nops + 1 + 2 + 3 + 4 + 5,
95 k8nops + 1 + 2 + 3 + 4 + 5 + 6,
96 k8nops + 1 + 2 + 3 + 4 + 5 + 6 + 7,
97};
Gerd Hoffmannd167a512006-06-26 13:56:16 +020098#endif
99
100#ifdef K7_NOP1
101asm("\t.data\nk7nops: "
102 K7_NOP1 K7_NOP2 K7_NOP3 K7_NOP4 K7_NOP5 K7_NOP6
103 K7_NOP7 K7_NOP8);
104extern unsigned char k7nops[];
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800105static unsigned char *k7_nops[ASM_NOP_MAX+1] = {
106 NULL,
107 k7nops,
108 k7nops + 1,
109 k7nops + 1 + 2,
110 k7nops + 1 + 2 + 3,
111 k7nops + 1 + 2 + 3 + 4,
112 k7nops + 1 + 2 + 3 + 4 + 5,
113 k7nops + 1 + 2 + 3 + 4 + 5 + 6,
114 k7nops + 1 + 2 + 3 + 4 + 5 + 6 + 7,
115};
Gerd Hoffmannd167a512006-06-26 13:56:16 +0200116#endif
117
118#ifdef CONFIG_X86_64
119
120extern char __vsyscall_0;
121static inline unsigned char** find_nop_table(void)
122{
123 return k8_nops;
124}
125
126#else /* CONFIG_X86_64 */
127
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800128static struct nop {
129 int cpuid;
130 unsigned char **noptable;
131} noptypes[] = {
132 { X86_FEATURE_K8, k8_nops },
133 { X86_FEATURE_K7, k7_nops },
134 { -1, NULL }
135};
136
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800137static unsigned char** find_nop_table(void)
138{
139 unsigned char **noptable = intel_nops;
140 int i;
141
142 for (i = 0; noptypes[i].cpuid >= 0; i++) {
143 if (boot_cpu_has(noptypes[i].cpuid)) {
144 noptable = noptypes[i].noptable;
145 break;
146 }
147 }
148 return noptable;
149}
150
Gerd Hoffmannd167a512006-06-26 13:56:16 +0200151#endif /* CONFIG_X86_64 */
152
Andi Kleenab144f52007-08-10 22:31:03 +0200153/* Use this to add nops to a buffer, then text_poke the whole buffer. */
154static void add_nops(void *insns, unsigned int len)
Rusty Russell139ec7c2006-12-07 02:14:08 +0100155{
156 unsigned char **noptable = find_nop_table();
157
158 while (len > 0) {
159 unsigned int noplen = len;
160 if (noplen > ASM_NOP_MAX)
161 noplen = ASM_NOP_MAX;
Andi Kleenab144f52007-08-10 22:31:03 +0200162 memcpy(insns, noptable[noplen], noplen);
Rusty Russell139ec7c2006-12-07 02:14:08 +0100163 insns += noplen;
164 len -= noplen;
165 }
166}
167
Gerd Hoffmannd167a512006-06-26 13:56:16 +0200168extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
Gerd Hoffmannd167a512006-06-26 13:56:16 +0200169extern u8 *__smp_locks[], *__smp_locks_end[];
170
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800171/* Replace instructions with better alternatives for this CPU type.
172 This runs before SMP is initialized to avoid SMP problems with
173 self modifying code. This implies that assymetric systems where
174 APs have less capabilities than the boot processor are not handled.
175 Tough. Make sure you disable such features by hand. */
176
177void apply_alternatives(struct alt_instr *start, struct alt_instr *end)
178{
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800179 struct alt_instr *a;
Andi Kleenab144f52007-08-10 22:31:03 +0200180 char insnbuf[MAX_PATCH_LEN];
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800181
182 DPRINTK("%s: alt table %p -> %p\n", __FUNCTION__, start, end);
183 for (a = start; a < end; a++) {
Andi Kleenab144f52007-08-10 22:31:03 +0200184 u8 *instr = a->instr;
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800185 BUG_ON(a->replacementlen > a->instrlen);
Andi Kleenab144f52007-08-10 22:31:03 +0200186 BUG_ON(a->instrlen > sizeof(insnbuf));
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800187 if (!boot_cpu_has(a->cpuid))
188 continue;
Gerd Hoffmannd167a512006-06-26 13:56:16 +0200189#ifdef CONFIG_X86_64
190 /* vsyscall code is not mapped yet. resolve it manually. */
191 if (instr >= (u8 *)VSYSCALL_START && instr < (u8*)VSYSCALL_END) {
192 instr = __va(instr - (u8*)VSYSCALL_START + (u8*)__pa_symbol(&__vsyscall_0));
193 DPRINTK("%s: vsyscall fixup: %p => %p\n",
194 __FUNCTION__, a->instr, instr);
195 }
196#endif
Andi Kleenab144f52007-08-10 22:31:03 +0200197 memcpy(insnbuf, a->replacement, a->replacementlen);
198 add_nops(insnbuf + a->replacementlen,
199 a->instrlen - a->replacementlen);
200 text_poke(instr, insnbuf, a->instrlen);
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800201 }
202}
203
Gerd Hoffmann8ec4d412006-07-01 04:36:18 -0700204#ifdef CONFIG_SMP
205
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800206static void alternatives_smp_lock(u8 **start, u8 **end, u8 *text, u8 *text_end)
207{
208 u8 **ptr;
209
210 for (ptr = start; ptr < end; ptr++) {
211 if (*ptr < text)
212 continue;
213 if (*ptr > text_end)
214 continue;
Andi Kleen19d36cc2007-07-22 11:12:31 +0200215 text_poke(*ptr, ((unsigned char []){0xf0}), 1); /* add lock prefix */
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800216 };
217}
218
219static void alternatives_smp_unlock(u8 **start, u8 **end, u8 *text, u8 *text_end)
220{
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800221 u8 **ptr;
Andi Kleenab144f52007-08-10 22:31:03 +0200222 char insn[1];
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800223
Jeremy Fitzhardingeb7fb4af2007-05-02 19:27:13 +0200224 if (noreplace_smp)
225 return;
226
Andi Kleenab144f52007-08-10 22:31:03 +0200227 add_nops(insn, 1);
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800228 for (ptr = start; ptr < end; ptr++) {
229 if (*ptr < text)
230 continue;
231 if (*ptr > text_end)
232 continue;
Andi Kleenab144f52007-08-10 22:31:03 +0200233 text_poke(*ptr, insn, 1);
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800234 };
235}
236
237struct smp_alt_module {
238 /* what is this ??? */
239 struct module *mod;
240 char *name;
241
242 /* ptrs to lock prefixes */
243 u8 **locks;
244 u8 **locks_end;
245
246 /* .text segment, needed to avoid patching init code ;) */
247 u8 *text;
248 u8 *text_end;
249
250 struct list_head next;
251};
252static LIST_HEAD(smp_alt_modules);
253static DEFINE_SPINLOCK(smp_alt);
254
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800255void alternatives_smp_module_add(struct module *mod, char *name,
256 void *locks, void *locks_end,
257 void *text, void *text_end)
258{
259 struct smp_alt_module *smp;
260 unsigned long flags;
261
Jeremy Fitzhardingeb7fb4af2007-05-02 19:27:13 +0200262 if (noreplace_smp)
263 return;
264
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800265 if (smp_alt_once) {
266 if (boot_cpu_has(X86_FEATURE_UP))
267 alternatives_smp_unlock(locks, locks_end,
268 text, text_end);
269 return;
270 }
271
272 smp = kzalloc(sizeof(*smp), GFP_KERNEL);
273 if (NULL == smp)
274 return; /* we'll run the (safe but slow) SMP code then ... */
275
276 smp->mod = mod;
277 smp->name = name;
278 smp->locks = locks;
279 smp->locks_end = locks_end;
280 smp->text = text;
281 smp->text_end = text_end;
282 DPRINTK("%s: locks %p -> %p, text %p -> %p, name %s\n",
283 __FUNCTION__, smp->locks, smp->locks_end,
284 smp->text, smp->text_end, smp->name);
285
286 spin_lock_irqsave(&smp_alt, flags);
287 list_add_tail(&smp->next, &smp_alt_modules);
288 if (boot_cpu_has(X86_FEATURE_UP))
289 alternatives_smp_unlock(smp->locks, smp->locks_end,
290 smp->text, smp->text_end);
291 spin_unlock_irqrestore(&smp_alt, flags);
292}
293
294void alternatives_smp_module_del(struct module *mod)
295{
296 struct smp_alt_module *item;
297 unsigned long flags;
298
Jeremy Fitzhardingeb7fb4af2007-05-02 19:27:13 +0200299 if (smp_alt_once || noreplace_smp)
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800300 return;
301
302 spin_lock_irqsave(&smp_alt, flags);
303 list_for_each_entry(item, &smp_alt_modules, next) {
304 if (mod != item->mod)
305 continue;
306 list_del(&item->next);
307 spin_unlock_irqrestore(&smp_alt, flags);
308 DPRINTK("%s: %s\n", __FUNCTION__, item->name);
309 kfree(item);
310 return;
311 }
312 spin_unlock_irqrestore(&smp_alt, flags);
313}
314
315void alternatives_smp_switch(int smp)
316{
317 struct smp_alt_module *mod;
318 unsigned long flags;
319
Ingo Molnar3047e992006-07-03 00:24:57 -0700320#ifdef CONFIG_LOCKDEP
321 /*
322 * A not yet fixed binutils section handling bug prevents
323 * alternatives-replacement from working reliably, so turn
324 * it off:
325 */
326 printk("lockdep: not fixing up alternatives.\n");
327 return;
328#endif
329
Jeremy Fitzhardingeb7fb4af2007-05-02 19:27:13 +0200330 if (noreplace_smp || smp_alt_once)
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800331 return;
332 BUG_ON(!smp && (num_online_cpus() > 1));
333
334 spin_lock_irqsave(&smp_alt, flags);
335 if (smp) {
336 printk(KERN_INFO "SMP alternatives: switching to SMP code\n");
337 clear_bit(X86_FEATURE_UP, boot_cpu_data.x86_capability);
338 clear_bit(X86_FEATURE_UP, cpu_data[0].x86_capability);
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800339 list_for_each_entry(mod, &smp_alt_modules, next)
340 alternatives_smp_lock(mod->locks, mod->locks_end,
341 mod->text, mod->text_end);
342 } else {
343 printk(KERN_INFO "SMP alternatives: switching to UP code\n");
344 set_bit(X86_FEATURE_UP, boot_cpu_data.x86_capability);
345 set_bit(X86_FEATURE_UP, cpu_data[0].x86_capability);
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800346 list_for_each_entry(mod, &smp_alt_modules, next)
347 alternatives_smp_unlock(mod->locks, mod->locks_end,
348 mod->text, mod->text_end);
349 }
350 spin_unlock_irqrestore(&smp_alt, flags);
351}
352
Gerd Hoffmann8ec4d412006-07-01 04:36:18 -0700353#endif
354
Rusty Russell139ec7c2006-12-07 02:14:08 +0100355#ifdef CONFIG_PARAVIRT
Jeremy Fitzhardinge98de0322007-05-02 19:27:14 +0200356void apply_paravirt(struct paravirt_patch_site *start,
357 struct paravirt_patch_site *end)
Rusty Russell139ec7c2006-12-07 02:14:08 +0100358{
Jeremy Fitzhardinge98de0322007-05-02 19:27:14 +0200359 struct paravirt_patch_site *p;
Andi Kleenab144f52007-08-10 22:31:03 +0200360 char insnbuf[MAX_PATCH_LEN];
Rusty Russell139ec7c2006-12-07 02:14:08 +0100361
Jeremy Fitzhardinge959b4fd2007-05-02 19:27:16 +0200362 if (noreplace_paravirt)
363 return;
364
Rusty Russell139ec7c2006-12-07 02:14:08 +0100365 for (p = start; p < end; p++) {
366 unsigned int used;
367
Andi Kleenab144f52007-08-10 22:31:03 +0200368 BUG_ON(p->len > MAX_PATCH_LEN);
369 used = paravirt_ops.patch(p->instrtype, p->clobbers, insnbuf,
370 (unsigned long)p->instr, p->len);
Jeremy Fitzhardinge7f63c412007-05-02 19:27:13 +0200371
Jeremy Fitzhardinge63f70272007-05-02 19:27:14 +0200372 BUG_ON(used > p->len);
373
Rusty Russell139ec7c2006-12-07 02:14:08 +0100374 /* Pad the rest with nops */
Andi Kleenab144f52007-08-10 22:31:03 +0200375 add_nops(insnbuf + used, p->len - used);
376 text_poke(p->instr, insnbuf, p->len);
Rusty Russell139ec7c2006-12-07 02:14:08 +0100377 }
Rusty Russell139ec7c2006-12-07 02:14:08 +0100378}
Jeremy Fitzhardinge98de0322007-05-02 19:27:14 +0200379extern struct paravirt_patch_site __start_parainstructions[],
Rusty Russell139ec7c2006-12-07 02:14:08 +0100380 __stop_parainstructions[];
381#endif /* CONFIG_PARAVIRT */
382
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800383void __init alternative_instructions(void)
384{
Zachary Amsdene51959f2006-10-19 23:29:04 -0700385 unsigned long flags;
Zachary Amsdene51959f2006-10-19 23:29:04 -0700386
Andi Kleen8f4e9562007-07-22 11:12:32 +0200387 /* The patching is not fully atomic, so try to avoid local interruptions
388 that might execute the to be patched code.
389 Other CPUs are not running. */
390 stop_nmi();
Adrian Bunkd2d02512007-08-10 22:31:06 +0200391#ifdef CONFIG_X86_MCE
Andi Kleen8f4e9562007-07-22 11:12:32 +0200392 stop_mce();
393#endif
394
Zachary Amsdene51959f2006-10-19 23:29:04 -0700395 local_irq_save(flags);
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800396 apply_alternatives(__alt_instructions, __alt_instructions_end);
397
398 /* switch to patch-once-at-boottime-only mode and free the
399 * tables in case we know the number of CPUs will never ever
400 * change */
401#ifdef CONFIG_HOTPLUG_CPU
402 if (num_possible_cpus() < 2)
403 smp_alt_once = 1;
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800404#endif
405
Gerd Hoffmann8ec4d412006-07-01 04:36:18 -0700406#ifdef CONFIG_SMP
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800407 if (smp_alt_once) {
408 if (1 == num_possible_cpus()) {
409 printk(KERN_INFO "SMP alternatives: switching to UP code\n");
410 set_bit(X86_FEATURE_UP, boot_cpu_data.x86_capability);
411 set_bit(X86_FEATURE_UP, cpu_data[0].x86_capability);
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800412 alternatives_smp_unlock(__smp_locks, __smp_locks_end,
413 _text, _etext);
414 }
415 free_init_pages("SMP alternatives",
Linus Torvaldse3ebadd2007-05-07 08:44:24 -0700416 (unsigned long)__smp_locks,
417 (unsigned long)__smp_locks_end);
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800418 } else {
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800419 alternatives_smp_module_add(NULL, "core kernel",
420 __smp_locks, __smp_locks_end,
421 _text, _etext);
422 alternatives_smp_switch(0);
423 }
Gerd Hoffmann8ec4d412006-07-01 04:36:18 -0700424#endif
Jeremy Fitzhardinge441d40d2007-05-02 19:27:16 +0200425 apply_paravirt(__parainstructions, __parainstructions_end);
Zachary Amsdene51959f2006-10-19 23:29:04 -0700426 local_irq_restore(flags);
Andi Kleen8f4e9562007-07-22 11:12:32 +0200427
428 restart_nmi();
Adrian Bunkd2d02512007-08-10 22:31:06 +0200429#ifdef CONFIG_X86_MCE
Andi Kleen8f4e9562007-07-22 11:12:32 +0200430 restart_mce();
431#endif
Gerd Hoffmann9a0b5812006-03-23 02:59:32 -0800432}
Andi Kleen19d36cc2007-07-22 11:12:31 +0200433
434/*
435 * Warning:
436 * When you use this code to patch more than one byte of an instruction
437 * you need to make sure that other CPUs cannot execute this code in parallel.
438 * Also no thread must be currently preempted in the middle of these instructions.
439 * And on the local CPU you need to be protected again NMI or MCE handlers
440 * seeing an inconsistent instruction while you patch.
441 */
Linus Torvalds602033e2007-07-26 12:07:21 -0700442void __kprobes text_poke(void *addr, unsigned char *opcode, int len)
Andi Kleen19d36cc2007-07-22 11:12:31 +0200443{
Andi Kleen19d36cc2007-07-22 11:12:31 +0200444 memcpy(addr, opcode, len);
445 sync_core();
446 /* Not strictly needed, but can speed CPU recovery up. Ignore cross cacheline
447 case. */
448 if (cpu_has_clflush)
Linus Torvalds602033e2007-07-26 12:07:21 -0700449 asm("clflush (%0) " :: "r" (addr) : "memory");
Andi Kleen19d36cc2007-07-22 11:12:31 +0200450}