EDAC, mce_amd_inj: Add an injector function
authorBorislav Petkov <bp@suse.de>
Sat, 22 Nov 2014 10:47:07 +0000 (11:47 +0100)
committerBorislav Petkov <bp@suse.de>
Tue, 25 Nov 2014 12:09:45 +0000 (13:09 +0100)
Selectively inject either a real MCE or a sw-only version which
exercises the decoding path only. The hardware-injected MCE triggers a
machine check exception (#MC) so that the MCE handler can be bothered to
do something too.

Signed-off-by: Borislav Petkov <bp@suse.de>
drivers/edac/mce_amd_inj.c

index 5a758d63a8ccf1f4427f9893a0f959c5bf9d1565..0bd91a802c67a9fdc2a4d7af45f079202ac2505c 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/debugfs.h>
 #include <linux/device.h>
 #include <linux/module.h>
+#include <linux/cpu.h>
 #include <asm/mce.h>
 
 #include "mce_amd.h"
@@ -116,6 +117,55 @@ static int inj_extcpu_set(void *data, u64 val)
 
 DEFINE_SIMPLE_ATTRIBUTE(extcpu_fops, inj_extcpu_get, inj_extcpu_set, "%llu\n");
 
+static void trigger_mce(void *info)
+{
+       asm volatile("int $18");
+}
+
+static void do_inject(void)
+{
+       u64 mcg_status = 0;
+       unsigned int cpu = i_mce.extcpu;
+       u8 b = i_mce.bank;
+
+       if (!(i_mce.inject_flags & MCJ_EXCEPTION)) {
+               amd_decode_mce(NULL, 0, &i_mce);
+               return;
+       }
+
+       get_online_cpus();
+       if (!cpu_online(cpu))
+               goto err;
+
+       /* prep MCE global settings for the injection */
+       mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV;
+
+       if (!(i_mce.status & MCI_STATUS_PCC))
+               mcg_status |= MCG_STATUS_RIPV;
+
+       toggle_hw_mce_inject(cpu, true);
+
+       wrmsr_on_cpu(cpu, MSR_IA32_MCG_STATUS,
+                    (u32)mcg_status, (u32)(mcg_status >> 32));
+
+       wrmsr_on_cpu(cpu, MSR_IA32_MCx_STATUS(b),
+                    (u32)i_mce.status, (u32)(i_mce.status >> 32));
+
+       wrmsr_on_cpu(cpu, MSR_IA32_MCx_ADDR(b),
+                    (u32)i_mce.addr, (u32)(i_mce.addr >> 32));
+
+       wrmsr_on_cpu(cpu, MSR_IA32_MCx_MISC(b),
+                    (u32)i_mce.misc, (u32)(i_mce.misc >> 32));
+
+       toggle_hw_mce_inject(cpu, false);
+
+       smp_call_function_single(cpu, trigger_mce, NULL, 0);
+
+err:
+       put_online_cpus();
+
+}
+
 /*
  * This denotes into which bank we're injecting and triggers
  * the injection, at the same time.
@@ -132,8 +182,7 @@ static int inj_bank_set(void *data, u64 val)
        }
 
        m->bank = val;
-
-       amd_decode_mce(NULL, 0, m);
+       do_inject();
 
        return 0;
 }