diff options
Diffstat (limited to 'arch/x86')
-rw-r--r-- | arch/x86/Kconfig | 8 | ||||
-rw-r--r-- | arch/x86/include/asm/mce.h | 3 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/mcheck/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/mcheck/mce-inject.c | 126 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/mcheck/mce.c | 39 |
5 files changed, 176 insertions, 1 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index a148e7ac0d83..e25b6358fbe3 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
@@ -835,6 +835,14 @@ config X86_MCE_THRESHOLD | |||
835 | bool | 835 | bool |
836 | default y | 836 | default y |
837 | 837 | ||
838 | config X86_MCE_INJECT | ||
839 | depends on X86_NEW_MCE | ||
840 | tristate "Machine check injector support" | ||
841 | ---help--- | ||
842 | Provide support for injecting machine checks for testing purposes. | ||
843 | If you don't know what a machine check is and you don't do kernel | ||
844 | QA it is safe to say n. | ||
845 | |||
838 | config X86_MCE_NONFATAL | 846 | config X86_MCE_NONFATAL |
839 | tristate "Check for non-fatal errors on AMD Athlon/Duron / Intel Pentium 4" | 847 | tristate "Check for non-fatal errors on AMD Athlon/Duron / Intel Pentium 4" |
840 | depends on X86_OLD_MCE | 848 | depends on X86_OLD_MCE |
diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h index c3c7ee701753..e7d2372301ef 100644 --- a/arch/x86/include/asm/mce.h +++ b/arch/x86/include/asm/mce.h | |||
@@ -141,6 +141,9 @@ extern void machine_check_poll(enum mcp_flags flags, mce_banks_t *b); | |||
141 | 141 | ||
142 | extern int mce_notify_user(void); | 142 | extern int mce_notify_user(void); |
143 | 143 | ||
144 | DECLARE_PER_CPU(struct mce, injectm); | ||
145 | extern struct file_operations mce_chrdev_ops; | ||
146 | |||
144 | #ifdef CONFIG_X86_MCE | 147 | #ifdef CONFIG_X86_MCE |
145 | extern void mcheck_init(struct cpuinfo_x86 *c); | 148 | extern void mcheck_init(struct cpuinfo_x86 *c); |
146 | #else | 149 | #else |
diff --git a/arch/x86/kernel/cpu/mcheck/Makefile b/arch/x86/kernel/cpu/mcheck/Makefile index 5f8b09425d39..60ee182c6c52 100644 --- a/arch/x86/kernel/cpu/mcheck/Makefile +++ b/arch/x86/kernel/cpu/mcheck/Makefile | |||
@@ -7,3 +7,4 @@ obj-$(CONFIG_X86_MCE_INTEL) += mce_intel_64.o mce_intel.o | |||
7 | obj-$(CONFIG_X86_MCE_AMD) += mce_amd_64.o | 7 | obj-$(CONFIG_X86_MCE_AMD) += mce_amd_64.o |
8 | obj-$(CONFIG_X86_MCE_NONFATAL) += non-fatal.o | 8 | obj-$(CONFIG_X86_MCE_NONFATAL) += non-fatal.o |
9 | obj-$(CONFIG_X86_MCE_THRESHOLD) += threshold.o | 9 | obj-$(CONFIG_X86_MCE_THRESHOLD) += threshold.o |
10 | obj-$(CONFIG_X86_MCE_INJECT) += mce-inject.o | ||
diff --git a/arch/x86/kernel/cpu/mcheck/mce-inject.c b/arch/x86/kernel/cpu/mcheck/mce-inject.c new file mode 100644 index 000000000000..58afac4b5df5 --- /dev/null +++ b/arch/x86/kernel/cpu/mcheck/mce-inject.c | |||
@@ -0,0 +1,126 @@ | |||
1 | /* | ||
2 | * Machine check injection support. | ||
3 | * Copyright 2008 Intel Corporation. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License | ||
7 | * as published by the Free Software Foundation; version 2 | ||
8 | * of the License. | ||
9 | * | ||
10 | * Authors: | ||
11 | * Andi Kleen | ||
12 | * Ying Huang | ||
13 | */ | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/timer.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/string.h> | ||
18 | #include <linux/fs.h> | ||
19 | #include <linux/smp.h> | ||
20 | #include <asm/uaccess.h> | ||
21 | #include <asm/mce.h> | ||
22 | |||
23 | /* Update fake mce registers on current CPU. */ | ||
24 | static void inject_mce(struct mce *m) | ||
25 | { | ||
26 | struct mce *i = &per_cpu(injectm, m->cpu); | ||
27 | |||
28 | /* Make sure noone reads partially written injectm */ | ||
29 | i->finished = 0; | ||
30 | mb(); | ||
31 | m->finished = 0; | ||
32 | /* First set the fields after finished */ | ||
33 | i->cpu = m->cpu; | ||
34 | mb(); | ||
35 | /* Now write record in order, finished last (except above) */ | ||
36 | memcpy(i, m, sizeof(struct mce)); | ||
37 | /* Finally activate it */ | ||
38 | mb(); | ||
39 | i->finished = 1; | ||
40 | } | ||
41 | |||
42 | struct delayed_mce { | ||
43 | struct timer_list timer; | ||
44 | struct mce m; | ||
45 | }; | ||
46 | |||
47 | /* Inject mce on current CPU */ | ||
48 | static void raise_mce(unsigned long data) | ||
49 | { | ||
50 | struct delayed_mce *dm = (struct delayed_mce *)data; | ||
51 | struct mce *m = &dm->m; | ||
52 | int cpu = m->cpu; | ||
53 | |||
54 | inject_mce(m); | ||
55 | if (m->status & MCI_STATUS_UC) { | ||
56 | struct pt_regs regs; | ||
57 | memset(®s, 0, sizeof(struct pt_regs)); | ||
58 | regs.ip = m->ip; | ||
59 | regs.cs = m->cs; | ||
60 | printk(KERN_INFO "Triggering MCE exception on CPU %d\n", cpu); | ||
61 | do_machine_check(®s, 0); | ||
62 | printk(KERN_INFO "MCE exception done on CPU %d\n", cpu); | ||
63 | } else { | ||
64 | mce_banks_t b; | ||
65 | memset(&b, 0xff, sizeof(mce_banks_t)); | ||
66 | printk(KERN_INFO "Starting machine check poll CPU %d\n", cpu); | ||
67 | machine_check_poll(0, &b); | ||
68 | mce_notify_user(); | ||
69 | printk(KERN_INFO "Finished machine check poll on CPU %d\n", | ||
70 | cpu); | ||
71 | } | ||
72 | kfree(dm); | ||
73 | } | ||
74 | |||
75 | /* Error injection interface */ | ||
76 | static ssize_t mce_write(struct file *filp, const char __user *ubuf, | ||
77 | size_t usize, loff_t *off) | ||
78 | { | ||
79 | struct delayed_mce *dm; | ||
80 | struct mce m; | ||
81 | |||
82 | if (!capable(CAP_SYS_ADMIN)) | ||
83 | return -EPERM; | ||
84 | /* | ||
85 | * There are some cases where real MSR reads could slip | ||
86 | * through. | ||
87 | */ | ||
88 | if (!boot_cpu_has(X86_FEATURE_MCE) || !boot_cpu_has(X86_FEATURE_MCA)) | ||
89 | return -EIO; | ||
90 | |||
91 | if ((unsigned long)usize > sizeof(struct mce)) | ||
92 | usize = sizeof(struct mce); | ||
93 | if (copy_from_user(&m, ubuf, usize)) | ||
94 | return -EFAULT; | ||
95 | |||
96 | if (m.cpu >= NR_CPUS || !cpu_online(m.cpu)) | ||
97 | return -EINVAL; | ||
98 | |||
99 | dm = kmalloc(sizeof(struct delayed_mce), GFP_KERNEL); | ||
100 | if (!dm) | ||
101 | return -ENOMEM; | ||
102 | |||
103 | /* | ||
104 | * Need to give user space some time to set everything up, | ||
105 | * so do it a jiffie or two later everywhere. | ||
106 | * Should we use a hrtimer here for better synchronization? | ||
107 | */ | ||
108 | memcpy(&dm->m, &m, sizeof(struct mce)); | ||
109 | setup_timer(&dm->timer, raise_mce, (unsigned long)dm); | ||
110 | dm->timer.expires = jiffies + 2; | ||
111 | add_timer_on(&dm->timer, m.cpu); | ||
112 | return usize; | ||
113 | } | ||
114 | |||
115 | static int inject_init(void) | ||
116 | { | ||
117 | printk(KERN_INFO "Machine check injector initialized\n"); | ||
118 | mce_chrdev_ops.write = mce_write; | ||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | module_init(inject_init); | ||
123 | /* Cannot tolerate unloading currently because we cannot | ||
124 | * guarantee all openers of mce_chrdev will get a reference to us. | ||
125 | */ | ||
126 | MODULE_LICENSE("GPL"); | ||
diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c index e755c95674dc..fe216bd10f43 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c | |||
@@ -98,6 +98,9 @@ void mce_setup(struct mce *m) | |||
98 | rdtscll(m->tsc); | 98 | rdtscll(m->tsc); |
99 | } | 99 | } |
100 | 100 | ||
101 | DEFINE_PER_CPU(struct mce, injectm); | ||
102 | EXPORT_PER_CPU_SYMBOL_GPL(injectm); | ||
103 | |||
101 | /* | 104 | /* |
102 | * Lockless MCE logging infrastructure. | 105 | * Lockless MCE logging infrastructure. |
103 | * This avoids deadlocks on printk locks without having to break locks. Also | 106 | * This avoids deadlocks on printk locks without having to break locks. Also |
@@ -194,16 +197,46 @@ static void mce_panic(char *msg, struct mce *backup, u64 start) | |||
194 | panic(msg); | 197 | panic(msg); |
195 | } | 198 | } |
196 | 199 | ||
200 | /* Support code for software error injection */ | ||
201 | |||
202 | static int msr_to_offset(u32 msr) | ||
203 | { | ||
204 | unsigned bank = __get_cpu_var(injectm.bank); | ||
205 | if (msr == rip_msr) | ||
206 | return offsetof(struct mce, ip); | ||
207 | if (msr == MSR_IA32_MC0_STATUS + bank*4) | ||
208 | return offsetof(struct mce, status); | ||
209 | if (msr == MSR_IA32_MC0_ADDR + bank*4) | ||
210 | return offsetof(struct mce, addr); | ||
211 | if (msr == MSR_IA32_MC0_MISC + bank*4) | ||
212 | return offsetof(struct mce, misc); | ||
213 | if (msr == MSR_IA32_MCG_STATUS) | ||
214 | return offsetof(struct mce, mcgstatus); | ||
215 | return -1; | ||
216 | } | ||
217 | |||
197 | /* MSR access wrappers used for error injection */ | 218 | /* MSR access wrappers used for error injection */ |
198 | static u64 mce_rdmsrl(u32 msr) | 219 | static u64 mce_rdmsrl(u32 msr) |
199 | { | 220 | { |
200 | u64 v; | 221 | u64 v; |
222 | if (__get_cpu_var(injectm).finished) { | ||
223 | int offset = msr_to_offset(msr); | ||
224 | if (offset < 0) | ||
225 | return 0; | ||
226 | return *(u64 *)((char *)&__get_cpu_var(injectm) + offset); | ||
227 | } | ||
201 | rdmsrl(msr, v); | 228 | rdmsrl(msr, v); |
202 | return v; | 229 | return v; |
203 | } | 230 | } |
204 | 231 | ||
205 | static void mce_wrmsrl(u32 msr, u64 v) | 232 | static void mce_wrmsrl(u32 msr, u64 v) |
206 | { | 233 | { |
234 | if (__get_cpu_var(injectm).finished) { | ||
235 | int offset = msr_to_offset(msr); | ||
236 | if (offset >= 0) | ||
237 | *(u64 *)((char *)&__get_cpu_var(injectm) + offset) = v; | ||
238 | return; | ||
239 | } | ||
207 | wrmsrl(msr, v); | 240 | wrmsrl(msr, v); |
208 | } | 241 | } |
209 | 242 | ||
@@ -296,6 +329,7 @@ void machine_check_poll(enum mcp_flags flags, mce_banks_t *b) | |||
296 | * exceptions. | 329 | * exceptions. |
297 | */ | 330 | */ |
298 | } | 331 | } |
332 | EXPORT_SYMBOL_GPL(machine_check_poll); | ||
299 | 333 | ||
300 | /* | 334 | /* |
301 | * The actual machine check handler. This only handles real | 335 | * The actual machine check handler. This only handles real |
@@ -468,6 +502,7 @@ void do_machine_check(struct pt_regs *regs, long error_code) | |||
468 | out2: | 502 | out2: |
469 | atomic_dec(&mce_entry); | 503 | atomic_dec(&mce_entry); |
470 | } | 504 | } |
505 | EXPORT_SYMBOL_GPL(do_machine_check); | ||
471 | 506 | ||
472 | #ifdef CONFIG_X86_MCE_INTEL | 507 | #ifdef CONFIG_X86_MCE_INTEL |
473 | /*** | 508 | /*** |
@@ -568,6 +603,7 @@ int mce_notify_user(void) | |||
568 | } | 603 | } |
569 | return 0; | 604 | return 0; |
570 | } | 605 | } |
606 | EXPORT_SYMBOL_GPL(mce_notify_user); | ||
571 | 607 | ||
572 | /* | 608 | /* |
573 | * Initialize Machine Checks for a CPU. | 609 | * Initialize Machine Checks for a CPU. |
@@ -904,13 +940,14 @@ static long mce_ioctl(struct file *f, unsigned int cmd, unsigned long arg) | |||
904 | } | 940 | } |
905 | } | 941 | } |
906 | 942 | ||
907 | static const struct file_operations mce_chrdev_ops = { | 943 | struct file_operations mce_chrdev_ops = { |
908 | .open = mce_open, | 944 | .open = mce_open, |
909 | .release = mce_release, | 945 | .release = mce_release, |
910 | .read = mce_read, | 946 | .read = mce_read, |
911 | .poll = mce_poll, | 947 | .poll = mce_poll, |
912 | .unlocked_ioctl = mce_ioctl, | 948 | .unlocked_ioctl = mce_ioctl, |
913 | }; | 949 | }; |
950 | EXPORT_SYMBOL_GPL(mce_chrdev_ops); | ||
914 | 951 | ||
915 | static struct miscdevice mce_log_device = { | 952 | static struct miscdevice mce_log_device = { |
916 | MISC_MCELOG_MINOR, | 953 | MISC_MCELOG_MINOR, |