aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/include/asm/mce.h4
-rw-r--r--arch/x86/kernel/cpu/mcheck/dev-mcelog.c47
-rw-r--r--arch/x86/kernel/cpu/mcheck/mce-inject.c66
-rw-r--r--arch/x86/kernel/cpu/mcheck/mce-internal.h6
4 files changed, 61 insertions, 62 deletions
diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h
index 3f9a3d2a5209..181264989db5 100644
--- a/arch/x86/include/asm/mce.h
+++ b/arch/x86/include/asm/mce.h
@@ -285,10 +285,6 @@ int mce_notify_irq(void);
285 285
286DECLARE_PER_CPU(struct mce, injectm); 286DECLARE_PER_CPU(struct mce, injectm);
287 287
288extern void register_mce_write_callback(ssize_t (*)(struct file *filp,
289 const char __user *ubuf,
290 size_t usize, loff_t *off));
291
292/* Disable CMCI/polling for MCA bank claimed by firmware */ 288/* Disable CMCI/polling for MCA bank claimed by firmware */
293extern void mce_disable_bank(int bank); 289extern void mce_disable_bank(int bank);
294 290
diff --git a/arch/x86/kernel/cpu/mcheck/dev-mcelog.c b/arch/x86/kernel/cpu/mcheck/dev-mcelog.c
index 9c632cb88546..a80427c30c93 100644
--- a/arch/x86/kernel/cpu/mcheck/dev-mcelog.c
+++ b/arch/x86/kernel/cpu/mcheck/dev-mcelog.c
@@ -17,6 +17,8 @@
17 17
18#include "mce-internal.h" 18#include "mce-internal.h"
19 19
20static BLOCKING_NOTIFIER_HEAD(mce_injector_chain);
21
20static DEFINE_MUTEX(mce_chrdev_read_mutex); 22static DEFINE_MUTEX(mce_chrdev_read_mutex);
21 23
22static char mce_helper[128]; 24static char mce_helper[128];
@@ -345,24 +347,49 @@ static long mce_chrdev_ioctl(struct file *f, unsigned int cmd,
345 } 347 }
346} 348}
347 349
348static ssize_t (*mce_write)(struct file *filp, const char __user *ubuf, 350void mce_register_injector_chain(struct notifier_block *nb)
349 size_t usize, loff_t *off); 351{
352 blocking_notifier_chain_register(&mce_injector_chain, nb);
353}
354EXPORT_SYMBOL_GPL(mce_register_injector_chain);
350 355
351void register_mce_write_callback(ssize_t (*fn)(struct file *filp, 356void mce_unregister_injector_chain(struct notifier_block *nb)
352 const char __user *ubuf,
353 size_t usize, loff_t *off))
354{ 357{
355 mce_write = fn; 358 blocking_notifier_chain_unregister(&mce_injector_chain, nb);
356} 359}
357EXPORT_SYMBOL_GPL(register_mce_write_callback); 360EXPORT_SYMBOL_GPL(mce_unregister_injector_chain);
358 361
359static ssize_t mce_chrdev_write(struct file *filp, const char __user *ubuf, 362static ssize_t mce_chrdev_write(struct file *filp, const char __user *ubuf,
360 size_t usize, loff_t *off) 363 size_t usize, loff_t *off)
361{ 364{
362 if (mce_write) 365 struct mce m;
363 return mce_write(filp, ubuf, usize, off); 366
364 else 367 if (!capable(CAP_SYS_ADMIN))
368 return -EPERM;
369 /*
370 * There are some cases where real MSR reads could slip
371 * through.
372 */
373 if (!boot_cpu_has(X86_FEATURE_MCE) || !boot_cpu_has(X86_FEATURE_MCA))
374 return -EIO;
375
376 if ((unsigned long)usize > sizeof(struct mce))
377 usize = sizeof(struct mce);
378 if (copy_from_user(&m, ubuf, usize))
379 return -EFAULT;
380
381 if (m.extcpu >= num_possible_cpus() || !cpu_online(m.extcpu))
365 return -EINVAL; 382 return -EINVAL;
383
384 /*
385 * Need to give user space some time to set everything up,
386 * so do it a jiffie or two later everywhere.
387 */
388 schedule_timeout(2);
389
390 blocking_notifier_call_chain(&mce_injector_chain, 0, &m);
391
392 return usize;
366} 393}
367 394
368static const struct file_operations mce_chrdev_ops = { 395static const struct file_operations mce_chrdev_ops = {
diff --git a/arch/x86/kernel/cpu/mcheck/mce-inject.c b/arch/x86/kernel/cpu/mcheck/mce-inject.c
index 7170186938e5..c21c1a73712a 100644
--- a/arch/x86/kernel/cpu/mcheck/mce-inject.c
+++ b/arch/x86/kernel/cpu/mcheck/mce-inject.c
@@ -283,42 +283,24 @@ static void __maybe_unused raise_mce(struct mce *m)
283 } 283 }
284} 284}
285 285
286#ifdef CONFIG_X86_MCELOG_LEGACY 286static int mce_inject_raise(struct notifier_block *nb, unsigned long val,
287/* Error injection interface */ 287 void *data)
288static ssize_t mce_write(struct file *filp, const char __user *ubuf,
289 size_t usize, loff_t *off)
290{ 288{
291 struct mce m; 289 struct mce *m = (struct mce *)data;
292
293 if (!capable(CAP_SYS_ADMIN))
294 return -EPERM;
295 /*
296 * There are some cases where real MSR reads could slip
297 * through.
298 */
299 if (!boot_cpu_has(X86_FEATURE_MCE) || !boot_cpu_has(X86_FEATURE_MCA))
300 return -EIO;
301
302 if ((unsigned long)usize > sizeof(struct mce))
303 usize = sizeof(struct mce);
304 if (copy_from_user(&m, ubuf, usize))
305 return -EFAULT;
306
307 if (m.extcpu >= num_possible_cpus() || !cpu_online(m.extcpu))
308 return -EINVAL;
309 290
310 /* 291 if (!m)
311 * Need to give user space some time to set everything up, 292 return NOTIFY_DONE;
312 * so do it a jiffie or two later everywhere.
313 */
314 schedule_timeout(2);
315 293
316 mutex_lock(&mce_inject_mutex); 294 mutex_lock(&mce_inject_mutex);
317 raise_mce(&m); 295 raise_mce(m);
318 mutex_unlock(&mce_inject_mutex); 296 mutex_unlock(&mce_inject_mutex);
319 return usize; 297
298 return NOTIFY_DONE;
320} 299}
321#endif /* CONFIG_X86_MCELOG_LEGACY */ 300
301static struct notifier_block inject_nb = {
302 .notifier_call = mce_inject_raise,
303};
322 304
323/* 305/*
324 * Caller needs to be make sure this cpu doesn't disappear 306 * Caller needs to be make sure this cpu doesn't disappear
@@ -719,44 +701,34 @@ static int __init inject_init(void)
719 if (!alloc_cpumask_var(&mce_inject_cpumask, GFP_KERNEL)) 701 if (!alloc_cpumask_var(&mce_inject_cpumask, GFP_KERNEL))
720 return -ENOMEM; 702 return -ENOMEM;
721 703
722#ifdef CONFIG_X86_MCELOG_LEGACY
723 register_mce_write_callback(mce_write);
724#endif
725
726 register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify");
727
728 err = debugfs_init(); 704 err = debugfs_init();
729 if (err) { 705 if (err) {
730 free_cpumask_var(mce_inject_cpumask); 706 free_cpumask_var(mce_inject_cpumask);
731 return err; 707 return err;
732 } 708 }
733 709
710 register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify");
711 mce_register_injector_chain(&inject_nb);
712
734 pr_info("Machine check injector initialized\n"); 713 pr_info("Machine check injector initialized\n");
735 714
736 return 0; 715 return 0;
737} 716}
738 717
739module_init(inject_init);
740
741/*
742 * Cannot tolerate unloading currently because we cannot
743 * guarantee all openers of mce_chrdev will get a reference to us.
744 */
745#ifndef CONFIG_X86_MCELOG_LEGACY
746static void __exit inject_exit(void) 718static void __exit inject_exit(void)
747{ 719{
748 720
721 mce_unregister_injector_chain(&inject_nb);
722 unregister_nmi_handler(NMI_LOCAL, "mce_notify");
723
749 debugfs_remove_recursive(dfs_inj); 724 debugfs_remove_recursive(dfs_inj);
750 dfs_inj = NULL; 725 dfs_inj = NULL;
751 726
752 memset(&dfs_fls, 0, sizeof(dfs_fls)); 727 memset(&dfs_fls, 0, sizeof(dfs_fls));
753 728
754 unregister_nmi_handler(NMI_LOCAL, "mce_notify");
755
756 free_cpumask_var(mce_inject_cpumask); 729 free_cpumask_var(mce_inject_cpumask);
757} 730}
758 731
732module_init(inject_init);
759module_exit(inject_exit); 733module_exit(inject_exit);
760#endif
761
762MODULE_LICENSE("GPL"); 734MODULE_LICENSE("GPL");
diff --git a/arch/x86/kernel/cpu/mcheck/mce-internal.h b/arch/x86/kernel/cpu/mcheck/mce-internal.h
index 654ad0668d72..098530a93bb7 100644
--- a/arch/x86/kernel/cpu/mcheck/mce-internal.h
+++ b/arch/x86/kernel/cpu/mcheck/mce-internal.h
@@ -100,7 +100,11 @@ static inline bool mce_cmp(struct mce *m1, struct mce *m2)
100extern struct device_attribute dev_attr_trigger; 100extern struct device_attribute dev_attr_trigger;
101 101
102#ifdef CONFIG_X86_MCELOG_LEGACY 102#ifdef CONFIG_X86_MCELOG_LEGACY
103extern void mce_work_trigger(void); 103void mce_work_trigger(void);
104void mce_register_injector_chain(struct notifier_block *nb);
105void mce_unregister_injector_chain(struct notifier_block *nb);
104#else 106#else
105static inline void mce_work_trigger(void) { } 107static inline void mce_work_trigger(void) { }
108static inline void mce_register_injector_chain(struct notifier_block *nb) { }
109static inline void mce_unregister_injector_chain(struct notifier_block *nb) { }
106#endif 110#endif