Commit fbe9ff9e authored by Borislav Petkov's avatar Borislav Petkov Committed by Ingo Molnar

x86/mce: Get rid of register_mce_write_callback()

Make the mcelog call a notifier which lands in the injector module and
does the injection. This allows for mce-inject to be a normal kernel
module now.
Tested-by: default avatarYazen Ghannam <yazen.ghannam@amd.com>
Signed-off-by: default avatarBorislav Petkov <bp@suse.de>
Acked-by: default avatarYazen Ghannam <yazen.ghannam@amd.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Tony Luck <tony.luck@intel.com>
Link: http://lkml.kernel.org/r/20170613162835.30750-5-bp@alien8.deSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent bc8e80d5
...@@ -285,10 +285,6 @@ int mce_notify_irq(void); ...@@ -285,10 +285,6 @@ int mce_notify_irq(void);
DECLARE_PER_CPU(struct mce, injectm); DECLARE_PER_CPU(struct mce, injectm);
extern void register_mce_write_callback(ssize_t (*)(struct file *filp,
const char __user *ubuf,
size_t usize, loff_t *off));
/* Disable CMCI/polling for MCA bank claimed by firmware */ /* Disable CMCI/polling for MCA bank claimed by firmware */
extern void mce_disable_bank(int bank); extern void mce_disable_bank(int bank);
......
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
#include "mce-internal.h" #include "mce-internal.h"
static BLOCKING_NOTIFIER_HEAD(mce_injector_chain);
static DEFINE_MUTEX(mce_chrdev_read_mutex); static DEFINE_MUTEX(mce_chrdev_read_mutex);
static char mce_helper[128]; static char mce_helper[128];
...@@ -345,24 +347,49 @@ static long mce_chrdev_ioctl(struct file *f, unsigned int cmd, ...@@ -345,24 +347,49 @@ static long mce_chrdev_ioctl(struct file *f, unsigned int cmd,
} }
} }
static ssize_t (*mce_write)(struct file *filp, const char __user *ubuf, void mce_register_injector_chain(struct notifier_block *nb)
size_t usize, loff_t *off); {
blocking_notifier_chain_register(&mce_injector_chain, nb);
}
EXPORT_SYMBOL_GPL(mce_register_injector_chain);
void register_mce_write_callback(ssize_t (*fn)(struct file *filp, void mce_unregister_injector_chain(struct notifier_block *nb)
const char __user *ubuf,
size_t usize, loff_t *off))
{ {
mce_write = fn; blocking_notifier_chain_unregister(&mce_injector_chain, nb);
} }
EXPORT_SYMBOL_GPL(register_mce_write_callback); EXPORT_SYMBOL_GPL(mce_unregister_injector_chain);
static ssize_t mce_chrdev_write(struct file *filp, const char __user *ubuf, static ssize_t mce_chrdev_write(struct file *filp, const char __user *ubuf,
size_t usize, loff_t *off) size_t usize, loff_t *off)
{ {
if (mce_write) struct mce m;
return mce_write(filp, ubuf, usize, off);
else if (!capable(CAP_SYS_ADMIN))
return -EPERM;
/*
* There are some cases where real MSR reads could slip
* through.
*/
if (!boot_cpu_has(X86_FEATURE_MCE) || !boot_cpu_has(X86_FEATURE_MCA))
return -EIO;
if ((unsigned long)usize > sizeof(struct mce))
usize = sizeof(struct mce);
if (copy_from_user(&m, ubuf, usize))
return -EFAULT;
if (m.extcpu >= num_possible_cpus() || !cpu_online(m.extcpu))
return -EINVAL; return -EINVAL;
/*
* Need to give user space some time to set everything up,
* so do it a jiffie or two later everywhere.
*/
schedule_timeout(2);
blocking_notifier_call_chain(&mce_injector_chain, 0, &m);
return usize;
} }
static const struct file_operations mce_chrdev_ops = { static const struct file_operations mce_chrdev_ops = {
......
...@@ -283,42 +283,24 @@ static void __maybe_unused raise_mce(struct mce *m) ...@@ -283,42 +283,24 @@ static void __maybe_unused raise_mce(struct mce *m)
} }
} }
#ifdef CONFIG_X86_MCELOG_LEGACY static int mce_inject_raise(struct notifier_block *nb, unsigned long val,
/* Error injection interface */ void *data)
static ssize_t mce_write(struct file *filp, const char __user *ubuf,
size_t usize, loff_t *off)
{ {
struct mce m; struct mce *m = (struct mce *)data;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
/*
* There are some cases where real MSR reads could slip
* through.
*/
if (!boot_cpu_has(X86_FEATURE_MCE) || !boot_cpu_has(X86_FEATURE_MCA))
return -EIO;
if ((unsigned long)usize > sizeof(struct mce))
usize = sizeof(struct mce);
if (copy_from_user(&m, ubuf, usize))
return -EFAULT;
if (m.extcpu >= num_possible_cpus() || !cpu_online(m.extcpu))
return -EINVAL;
/* if (!m)
* Need to give user space some time to set everything up, return NOTIFY_DONE;
* so do it a jiffie or two later everywhere.
*/
schedule_timeout(2);
mutex_lock(&mce_inject_mutex); mutex_lock(&mce_inject_mutex);
raise_mce(&m); raise_mce(m);
mutex_unlock(&mce_inject_mutex); mutex_unlock(&mce_inject_mutex);
return usize;
return NOTIFY_DONE;
} }
#endif /* CONFIG_X86_MCELOG_LEGACY */
static struct notifier_block inject_nb = {
.notifier_call = mce_inject_raise,
};
/* /*
* Caller needs to be make sure this cpu doesn't disappear * Caller needs to be make sure this cpu doesn't disappear
...@@ -719,44 +701,34 @@ static int __init inject_init(void) ...@@ -719,44 +701,34 @@ static int __init inject_init(void)
if (!alloc_cpumask_var(&mce_inject_cpumask, GFP_KERNEL)) if (!alloc_cpumask_var(&mce_inject_cpumask, GFP_KERNEL))
return -ENOMEM; return -ENOMEM;
#ifdef CONFIG_X86_MCELOG_LEGACY
register_mce_write_callback(mce_write);
#endif
register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify");
err = debugfs_init(); err = debugfs_init();
if (err) { if (err) {
free_cpumask_var(mce_inject_cpumask); free_cpumask_var(mce_inject_cpumask);
return err; return err;
} }
register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify");
mce_register_injector_chain(&inject_nb);
pr_info("Machine check injector initialized\n"); pr_info("Machine check injector initialized\n");
return 0; return 0;
} }
module_init(inject_init);
/*
* Cannot tolerate unloading currently because we cannot
* guarantee all openers of mce_chrdev will get a reference to us.
*/
#ifndef CONFIG_X86_MCELOG_LEGACY
static void __exit inject_exit(void) static void __exit inject_exit(void)
{ {
mce_unregister_injector_chain(&inject_nb);
unregister_nmi_handler(NMI_LOCAL, "mce_notify");
debugfs_remove_recursive(dfs_inj); debugfs_remove_recursive(dfs_inj);
dfs_inj = NULL; dfs_inj = NULL;
memset(&dfs_fls, 0, sizeof(dfs_fls)); memset(&dfs_fls, 0, sizeof(dfs_fls));
unregister_nmi_handler(NMI_LOCAL, "mce_notify");
free_cpumask_var(mce_inject_cpumask); free_cpumask_var(mce_inject_cpumask);
} }
module_init(inject_init);
module_exit(inject_exit); module_exit(inject_exit);
#endif
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -100,7 +100,11 @@ static inline bool mce_cmp(struct mce *m1, struct mce *m2) ...@@ -100,7 +100,11 @@ static inline bool mce_cmp(struct mce *m1, struct mce *m2)
extern struct device_attribute dev_attr_trigger; extern struct device_attribute dev_attr_trigger;
#ifdef CONFIG_X86_MCELOG_LEGACY #ifdef CONFIG_X86_MCELOG_LEGACY
extern void mce_work_trigger(void); void mce_work_trigger(void);
void mce_register_injector_chain(struct notifier_block *nb);
void mce_unregister_injector_chain(struct notifier_block *nb);
#else #else
static inline void mce_work_trigger(void) { } static inline void mce_work_trigger(void) { }
static inline void mce_register_injector_chain(struct notifier_block *nb) { }
static inline void mce_unregister_injector_chain(struct notifier_block *nb) { }
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment