Skip to contentSkip to conversation

x86, MCE, AMD: Hardware MCE injection

PATCH In review Borislav Petkov 2 participants 9 messages May 9, 2011 – May 10, 2011

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

cover x86, MCE, AMD: Hardware MCE injection

From: Borislav Petkov <[email protected]>

Hi,

this new set version moves the whole interface to debugfs since it belongs there in the first place. Remaining functionality unchanged.

Changelog:

v2: only small changes from the last submission
(http://lkml.org/lkml/2011/4/15/171) so that we can have valid MCG_STATUS values and don't choke on severity sanity checking.

v1: Initial submission.

Thanks.

#1 lore raw

PATCH 1/6 x86, MCE: Add a HW injection flag

From: Borislav Petkov <[email protected]>

Add an mce->inject_flag to denote that we're doing HW injection.

---
 arch/x86/include/asm/mce.h |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
arch/x86/include/asm/mce.h +1 -0
index eb16e94..a2e3dcf 100644
@@ -49,6 +49,7 @@
 #define MCJ_CTX_IRQ		2    /* inject context: IRQ */
 #define MCJ_NMI_BROADCAST	4    /* do NMI broadcasting */
 #define MCJ_EXCEPTION		8    /* raise as exception */
+#define MCJ_HW_INJECT		16   /* do a HW MCE inject, i.e. INT18 */
 
 /* Fields are zero when not available */
 struct mce {
Signature · 1 line
1.7.4.rc2
#2 lore raw

Re: [PATCH -v3 1/6] x86, MCE: Add a HW injection flag

Quoted 15 lines · Borislav Petkov

From: Borislav Petkov <[email protected]>

Add an mce->inject_flag to denote that we're doing HW injection.

---
 arch/x86/include/asm/mce.h |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
arch/x86/include/asm/mce.h +0 -0
index eb16e94..a2e3dcf 100644

arch/x86/include/asm/mce.h line 55

Quoted 7 lines
arch/x86/include/asm/mce.h +1 -0
@@ -49,6 +49,7 @@
 #define MCJ_CTX_IRQ		2    /* inject context: IRQ */
 #define MCJ_NMI_BROADCAST	4    /* do NMI broadcasting */
 #define MCJ_EXCEPTION		8    /* raise as exception */
+#define MCJ_HW_INJECT		16   /* do a HW MCE inject, i.e. INT18 */
 
 /* Fields are zero when not available */
 struct mce {

IMHO, we should have better name for this feature; using wrmsr + int$18 is not the only way to inject mce event to HW. I'd like to suggest MCJ_AMD_HW_INJECT, MCJ_USE_WRMSR or so instead. Then we can have MCJ_USE_APEI_EINJ and so on, for various way of MCE injection.

Thanks,
H.Seto

#3 lore raw

Re: [PATCH -v3 1/6] x86, MCE: Add a HW injection flag

Quoted 21 lines · Hidetoshi Seto
Quoted 20 lines · Borislav Petkov

From: Borislav Petkov <[email protected]>

Add an mce->inject_flag to denote that we're doing HW injection.

---
 arch/x86/include/asm/mce.h |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
arch/x86/include/asm/mce.h +1 -0
index eb16e94..a2e3dcf 100644
@@ -49,6 +49,7 @@
 #define MCJ_CTX_IRQ		2    /* inject context: IRQ */
 #define MCJ_NMI_BROADCAST	4    /* do NMI broadcasting */
 #define MCJ_EXCEPTION		8    /* raise as exception */
+#define MCJ_HW_INJECT		16   /* do a HW MCE inject, i.e. INT18 */
 
Quoted 7 lines

arch/x86/include/asm/mce.h line 55

Quoted 2 lines
arch/x86/include/asm/mce.h +0 -0
 /* Fields are zero when not available */
 struct mce {

IMHO, we should have better name for this feature; using wrmsr + int$18 is not the only way to inject mce event to HW. I'd like to suggest MCJ_AMD_HW_INJECT, MCJ_USE_WRMSR or so instead. Then we can have MCJ_USE_APEI_EINJ and so on, for various way of MCE injection.

Fair enough, the name might be too generic. Maybe I should change it to MCJ_HW_MSR_INJECT to denote injecting of hw errors through the MCA MSRs.

Signature · 8 lines
Regards/Gruss,
Boris.

Advanced Micro Devices GmbH
Einsteinring 24, 85609 Dornach
General Managers: Alberto Bozzo, Andrew Bowd
Registration: Dornach, Gemeinde Aschheim, Landkreis Muenchen
Registergericht Muenchen, HRB Nr. 43632
#4 lore raw

PATCH 2/6 x86, MCE: Export mce_get_debugfs_dir

In reply to Borislav Petkov

From: Borislav Petkov <[email protected]>

This is needed for external users of the same debugfs toplevel dir.

---
 arch/x86/include/asm/mce.h                |    1 +
 arch/x86/kernel/cpu/mcheck/mce-internal.h |    1 -
 arch/x86/kernel/cpu/mcheck/mce.c          |    1 +
 3 files changed, 2 insertions(+), 1 deletions(-)
arch/x86/include/asm/mce.h +1 -0
index a2e3dcf..1c9c7be 100644
@@ -241,5 +241,6 @@ struct cper_sec_mem_err;
 extern void apei_mce_report_mem_error(int corrected,
 				      struct cper_sec_mem_err *mem_err);
 
+struct dentry *mce_get_debugfs_dir(void);
 #endif /* __KERNEL__ */
 #endif /* _ASM_X86_MCE_H */
arch/x86/kernel/cpu/mcheck/mce-internal.h +0 -1
index fefcc69..660a9e1 100644
@@ -22,7 +22,6 @@ struct mce_bank {
 };
 
 int mce_severity(struct mce *a, int tolerant, char **msg);
-struct dentry *mce_get_debugfs_dir(void);
 
 extern int mce_ser;
 
arch/x86/kernel/cpu/mcheck/mce.c +1 -0
index 3385ea2..262f692 100644
@@ -2170,6 +2170,7 @@ struct dentry *mce_get_debugfs_dir(void)
 
 	return dmce;
 }
+EXPORT_SYMBOL_GPL(mce_get_debugfs_dir);
 
 static void mce_reset(void)
 {
Signature · 1 line
1.7.4.rc2
#5 lore raw

PATCH 3/6 EDAC, MCE, AMD: Add function to enable direct write to MCE MSRs

In reply to Borislav Petkov

From: Borislav Petkov <[email protected]>

Normally, writing to MCE MSRs causes a #GP. Add a function to enable direct access to those MSRs.

---
 drivers/edac/mce_amd_inj.c |   24 ++++++++++++++++++++++++
 1 files changed, 24 insertions(+), 0 deletions(-)
drivers/edac/mce_amd_inj.c +24 -0
index a4987e0..eb36e15 100644
@@ -30,6 +30,30 @@ static struct edac_mce_attr mce_attr_##_name = __ATTR(_name, _mode, _show, _stor
 static struct kobject *mce_kobj;
 
 /*
+ * Caller needs to be make sure this cpu doesn't disappear
+ * from under us, i.e.: get_cpu/put_cpu.
+ */
+static int toggle_hw_mce_inject(unsigned int cpu, bool enable)
+{
+	u32 l, h;
+	int err;
+
+	err = rdmsr_on_cpu(cpu, MSR_K7_HWCR, &l, &h);
+	if (err) {
+		printk(KERN_ERR "%s: error reading HWCR\n", __func__);
+		return err;
+	}
+
+	enable ? (l |= BIT(18)) : (l &= ~BIT(18));
+
+	err = wrmsr_on_cpu(cpu, MSR_K7_HWCR, l, h);
+	if (err)
+		printk(KERN_ERR "%s: error writing HWCR\n", __func__);
+
+	return err;
+}
+
+/*
  * Collect all the MCi_XXX settings
  */
 static struct mce i_mce;
Signature · 1 line
1.7.4.rc2
#6 lore raw

PATCH 4/6 EDAC, MCE, AMD: Convert to debugfs

In reply to Borislav Petkov

From: Borislav Petkov <[email protected]>

This is a module which is used for debugging MCE decoding paths so its userspace interface should go to debugfs, where it belongs conceptually.

While at it, add a warning to the Kconfig text that this interface is unstable and no userspace scripts should rely all too much on it.

Make it so.

---
 drivers/edac/Kconfig       |    8 +-
 drivers/edac/mce_amd_inj.c |  191 ++++++++++++++++++++------------------------
 2 files changed, 91 insertions(+), 108 deletions(-)
drivers/edac/Kconfig +5 -3
index af1a17d..19f85da 100644
@@ -52,15 +52,17 @@ config EDAC_DECODE_MCE
 	  has been initialized.
 
 config EDAC_MCE_INJ
-	tristate "Simple MCE injection interface over /sysfs"
-	depends on EDAC_DECODE_MCE
+	tristate "Simple debugfs MCE injection interface"
+	depends on EDAC_DECODE_MCE && DEBUG_FS
 	default n
 	help
-	  This is a simple interface to inject MCEs over /sysfs and test
+	  This is a simple interface to inject MCEs over debugfs and test
 	  the MCE decoding code in EDAC.
 
 	  This is currently AMD-only.
 
+	  WARNING: Do not even assume that this interface is staying stable!
+
 config EDAC_MM_EDAC
 	tristate "Main Memory EDAC (Error Detection And Correction) reporting"
 	help
drivers/edac/mce_amd_inj.c +86 -105

247 lines not on this page · raw message

Signature · 1 line
1.7.4.rc2
#7 lore raw

PATCH 5/6 EDAC, MCE, AMD: Add attributes needed for HW injection

In reply to Borislav Petkov

From: Borislav Petkov <[email protected]>

hw_inject denotes whether we want to do a hardware or a software injection and, in the case of hardware injection, we want to do that on a particular cpu, thus the second 'cpu' attribute.

---
 drivers/edac/mce_amd_inj.c |   57 ++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 57 insertions(+), 0 deletions(-)
drivers/edac/mce_amd_inj.c +57 -0
index 421c3b5..592871f 100644
@@ -77,6 +77,61 @@ static int toggle_hw_mce_inject(unsigned int cpu, bool enable)
 }
 
 /*
+ * HW or SW injection
+ */
+static int hw_inj_get(void *data, u64 *val)
+{
+	struct mce *m = (struct mce *)data;
+
+	*val = !!(m->inject_flags & MCJ_HW_INJECT);
+
+	return 0;
+}
+
+static int hw_inj_set(void *data, u64 val)
+{
+	struct mce *m = (struct mce *)data;
+
+	switch (val) {
+	case 0:
+		m->inject_flags &= (u8)~MCJ_HW_INJECT;
+		break;
+
+	case 1:
+		m->inject_flags |= MCJ_HW_INJECT;
+		break;
+
+	default:
+		printk(KERN_ERR "%s: Only 0 or 1 allowed!\n", __func__);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(hw_inj_fops, hw_inj_get, hw_inj_set, "%llu\n");
+
+/*
+ * On which CPU to inject?
+ */
+
+MCE_INJECT_GET(extcpu);
+
+
+static int inj_extcpu_set(void *data, u64 val)
+{
+	struct mce *m = (struct mce *)data;
+
+	if (val >= num_online_cpus()) {
+		printk(KERN_ERR "%s: Non-existent CPU: %llu\n", __func__, val);
+		return -EINVAL;
+	}
+	m->extcpu = val;
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(extcpu_fops, inj_extcpu_get, inj_extcpu_set, "%llu\n");
+
+/*
  * This denotes into which bank we're injecting and triggers
  * the injection, at the same time.
  */
@@ -116,6 +171,8 @@ struct dfs_node {
 	{ .name = "misc",	.fops = &misc_fops },
 	{ .name = "addr",	.fops = &addr_fops },
 	{ .name = "bank",	.fops = &bank_fops },
+	{ .name = "hw_inject",	.fops = &hw_inj_fops },
+	{ .name = "cpu",	.fops = &extcpu_fops },
 };
 
 static int __init edac_init_mce_inject(void)
Signature · 1 line
1.7.4.rc2
#8 lore raw

PATCH 6/6 EDAC, MCE, AMD: Add an injector function

In reply to Borislav Petkov

From: Borislav Petkov <[email protected]>

Selectively inject either a real MCE or a sw-only version which exercises the decoding code only. The hardware-injected MCE triggers a machine check exception (#MC) so that the MCE handler can be bothered to do something too.

---
 drivers/edac/mce_amd_inj.c |   50 ++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 48 insertions(+), 2 deletions(-)
drivers/edac/mce_amd_inj.c +48 -2
index 592871f..45e8122 100644
@@ -131,6 +131,53 @@ 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;
+	int this_cpu;
+	u8 b = i_mce.bank;
+
+	if (!(i_mce.inject_flags & MCJ_HW_INJECT)) {
+		amd_decode_mce(NULL, 0, &i_mce);
+		return;
+	}
+
+	/* 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;
+
+	this_cpu = get_cpu();
+
+	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);
+
+	put_cpu();
+
+}
+
 /*
  * This denotes into which bank we're injecting and triggers
  * the injection, at the same time.
@@ -146,8 +193,7 @@ static int inj_bank_set(void *data, u64 val)
 		}
 
 	m->bank = val;
-
-	amd_decode_mce(NULL, 0, m);
+	do_inject();
 
 	return 0;
 }
Signature · 1 line
1.7.4.rc2