|
@@ -31,14 +31,6 @@
|
|
#include <asm/nmi.h>
|
|
#include <asm/nmi.h>
|
|
#include <asm/x86_init.h>
|
|
#include <asm/x86_init.h>
|
|
|
|
|
|
-#define NMI_MAX_NAMELEN 16
|
|
|
|
-struct nmiaction {
|
|
|
|
- struct list_head list;
|
|
|
|
- nmi_handler_t handler;
|
|
|
|
- unsigned int flags;
|
|
|
|
- char *name;
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
struct nmi_desc {
|
|
struct nmi_desc {
|
|
spinlock_t lock;
|
|
spinlock_t lock;
|
|
struct list_head head;
|
|
struct list_head head;
|
|
@@ -54,6 +46,14 @@ static struct nmi_desc nmi_desc[NMI_MAX] =
|
|
.lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[1].lock),
|
|
.lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[1].lock),
|
|
.head = LIST_HEAD_INIT(nmi_desc[1].head),
|
|
.head = LIST_HEAD_INIT(nmi_desc[1].head),
|
|
},
|
|
},
|
|
|
|
+ {
|
|
|
|
+ .lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[2].lock),
|
|
|
|
+ .head = LIST_HEAD_INIT(nmi_desc[2].head),
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ .lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[3].lock),
|
|
|
|
+ .head = LIST_HEAD_INIT(nmi_desc[3].head),
|
|
|
|
+ },
|
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
@@ -107,11 +107,14 @@ static int notrace __kprobes nmi_handle(unsigned int type, struct pt_regs *regs,
|
|
return handled;
|
|
return handled;
|
|
}
|
|
}
|
|
|
|
|
|
-static int __setup_nmi(unsigned int type, struct nmiaction *action)
|
|
|
|
|
|
+int __register_nmi_handler(unsigned int type, struct nmiaction *action)
|
|
{
|
|
{
|
|
struct nmi_desc *desc = nmi_to_desc(type);
|
|
struct nmi_desc *desc = nmi_to_desc(type);
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
+ if (!action->handler)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
spin_lock_irqsave(&desc->lock, flags);
|
|
spin_lock_irqsave(&desc->lock, flags);
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -120,6 +123,8 @@ static int __setup_nmi(unsigned int type, struct nmiaction *action)
|
|
* to manage expectations
|
|
* to manage expectations
|
|
*/
|
|
*/
|
|
WARN_ON_ONCE(type == NMI_UNKNOWN && !list_empty(&desc->head));
|
|
WARN_ON_ONCE(type == NMI_UNKNOWN && !list_empty(&desc->head));
|
|
|
|
+ WARN_ON_ONCE(type == NMI_SERR && !list_empty(&desc->head));
|
|
|
|
+ WARN_ON_ONCE(type == NMI_IO_CHECK && !list_empty(&desc->head));
|
|
|
|
|
|
/*
|
|
/*
|
|
* some handlers need to be executed first otherwise a fake
|
|
* some handlers need to be executed first otherwise a fake
|
|
@@ -133,8 +138,9 @@ static int __setup_nmi(unsigned int type, struct nmiaction *action)
|
|
spin_unlock_irqrestore(&desc->lock, flags);
|
|
spin_unlock_irqrestore(&desc->lock, flags);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
+EXPORT_SYMBOL(__register_nmi_handler);
|
|
|
|
|
|
-static struct nmiaction *__free_nmi(unsigned int type, const char *name)
|
|
|
|
|
|
+void unregister_nmi_handler(unsigned int type, const char *name)
|
|
{
|
|
{
|
|
struct nmi_desc *desc = nmi_to_desc(type);
|
|
struct nmi_desc *desc = nmi_to_desc(type);
|
|
struct nmiaction *n;
|
|
struct nmiaction *n;
|
|
@@ -157,61 +163,16 @@ static struct nmiaction *__free_nmi(unsigned int type, const char *name)
|
|
|
|
|
|
spin_unlock_irqrestore(&desc->lock, flags);
|
|
spin_unlock_irqrestore(&desc->lock, flags);
|
|
synchronize_rcu();
|
|
synchronize_rcu();
|
|
- return (n);
|
|
|
|
}
|
|
}
|
|
-
|
|
|
|
-int register_nmi_handler(unsigned int type, nmi_handler_t handler,
|
|
|
|
- unsigned long nmiflags, const char *devname)
|
|
|
|
-{
|
|
|
|
- struct nmiaction *action;
|
|
|
|
- int retval = -ENOMEM;
|
|
|
|
-
|
|
|
|
- if (!handler)
|
|
|
|
- return -EINVAL;
|
|
|
|
-
|
|
|
|
- action = kzalloc(sizeof(struct nmiaction), GFP_KERNEL);
|
|
|
|
- if (!action)
|
|
|
|
- goto fail_action;
|
|
|
|
-
|
|
|
|
- action->handler = handler;
|
|
|
|
- action->flags = nmiflags;
|
|
|
|
- action->name = kstrndup(devname, NMI_MAX_NAMELEN, GFP_KERNEL);
|
|
|
|
- if (!action->name)
|
|
|
|
- goto fail_action_name;
|
|
|
|
-
|
|
|
|
- retval = __setup_nmi(type, action);
|
|
|
|
-
|
|
|
|
- if (retval)
|
|
|
|
- goto fail_setup_nmi;
|
|
|
|
-
|
|
|
|
- return retval;
|
|
|
|
-
|
|
|
|
-fail_setup_nmi:
|
|
|
|
- kfree(action->name);
|
|
|
|
-fail_action_name:
|
|
|
|
- kfree(action);
|
|
|
|
-fail_action:
|
|
|
|
-
|
|
|
|
- return retval;
|
|
|
|
-}
|
|
|
|
-EXPORT_SYMBOL_GPL(register_nmi_handler);
|
|
|
|
-
|
|
|
|
-void unregister_nmi_handler(unsigned int type, const char *name)
|
|
|
|
-{
|
|
|
|
- struct nmiaction *a;
|
|
|
|
-
|
|
|
|
- a = __free_nmi(type, name);
|
|
|
|
- if (a) {
|
|
|
|
- kfree(a->name);
|
|
|
|
- kfree(a);
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
EXPORT_SYMBOL_GPL(unregister_nmi_handler);
|
|
EXPORT_SYMBOL_GPL(unregister_nmi_handler);
|
|
|
|
|
|
static notrace __kprobes void
|
|
static notrace __kprobes void
|
|
pci_serr_error(unsigned char reason, struct pt_regs *regs)
|
|
pci_serr_error(unsigned char reason, struct pt_regs *regs)
|
|
{
|
|
{
|
|
|
|
+ /* check to see if anyone registered against these types of errors */
|
|
|
|
+ if (nmi_handle(NMI_SERR, regs, false))
|
|
|
|
+ return;
|
|
|
|
+
|
|
pr_emerg("NMI: PCI system error (SERR) for reason %02x on CPU %d.\n",
|
|
pr_emerg("NMI: PCI system error (SERR) for reason %02x on CPU %d.\n",
|
|
reason, smp_processor_id());
|
|
reason, smp_processor_id());
|
|
|
|
|
|
@@ -241,6 +202,10 @@ io_check_error(unsigned char reason, struct pt_regs *regs)
|
|
{
|
|
{
|
|
unsigned long i;
|
|
unsigned long i;
|
|
|
|
|
|
|
|
+ /* check to see if anyone registered against these types of errors */
|
|
|
|
+ if (nmi_handle(NMI_IO_CHECK, regs, false))
|
|
|
|
+ return;
|
|
|
|
+
|
|
pr_emerg(
|
|
pr_emerg(
|
|
"NMI: IOCK error (debug interrupt?) for reason %02x on CPU %d.\n",
|
|
"NMI: IOCK error (debug interrupt?) for reason %02x on CPU %d.\n",
|
|
reason, smp_processor_id());
|
|
reason, smp_processor_id());
|