@@ -612,6 +612,10 @@ static int igen6_get_dimm_config(struct mem_ctl_info *mci)
}
#ifdef CONFIG_EDAC_DEBUG
+/* Top of upper usable DRAM */
+static u64 igen6_touud;
+#define TOUUD_OFFSET 0xa8
+
static void igen6_reg_dump(struct igen6_imc *imc)
{
int i;
@@ -632,10 +636,54 @@ static void igen6_reg_dump(struct igen6_imc *imc)
readl(imc->window + MAD_DIMM_CH0_OFFSET + i * 4));
}
edac_dbg(2, "TOLUD : 0x%x", igen6_tolud);
+ edac_dbg(2, "TOUUD : 0x%llx", igen6_touud);
edac_dbg(2, "TOM : 0x%llx", igen6_tom);
}
+
+static struct dentry *igen6_test;
+
+static int debugfs_u64_set(void *data, u64 val)
+{
+ u64 ecclog;
+
+ if ((val >= igen6_tolud && val < _4GB) || val >= igen6_touud) {
+ edac_dbg(0, "Address 0x%llx out of range\n", val);
+ return 0;
+ }
+
+ pr_warn_once("Fake error to 0x%llx injected via debugfs\n", val);
+
+ val >>= ECC_ERROR_LOG_ADDR_SHIFT;
+ ecclog = (val << ECC_ERROR_LOG_ADDR_SHIFT) | ECC_ERROR_LOG_CE;
+
+ if (!ecclog_gen_pool_add(0, ecclog))
+ irq_work_queue(&ecclog_irq_work);
+
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%llu\n");
+
+static void igen6_debug_setup(void)
+{
+ igen6_test = edac_debugfs_create_dir("igen6_test");
+ if (!igen6_test)
+ return;
+
+ if (!edac_debugfs_create_file("addr", 0200, igen6_test,
+ NULL, &fops_u64_wo)) {
+ debugfs_remove(igen6_test);
+ igen6_test = NULL;
+ }
+}
+
+static void igen6_debug_teardown(void)
+{
+ debugfs_remove_recursive(igen6_test);
+}
#else
static void igen6_reg_dump(struct igen6_imc *imc) {}
+static void igen6_debug_setup(void) {}
+static void igen6_debug_teardown(void) {}
#endif
static int igen6_pci_setup(struct pci_dev *pdev, u64 *mchbar)
@@ -691,6 +739,15 @@ static int igen6_pci_setup(struct pci_dev *pdev, u64 *mchbar)
*mchbar = MCHBAR_BASE(u.v);
+#ifdef CONFIG_EDAC_DEBUG
+ if (pci_read_config_dword(pdev, TOUUD_OFFSET, &u.v_lo))
+ edac_dbg(2, "Failed to read lower TOUUD\n");
+ else if (pci_read_config_dword(pdev, TOUUD_OFFSET + 4, &u.v_hi))
+ edac_dbg(2, "Failed to read upper TOUUD\n");
+ else
+ igen6_touud = u.v & GENMASK_ULL(38, 20);
+#endif
+
return 0;
fail:
return -ENODEV;
@@ -849,6 +906,7 @@ static int igen6_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto fail4;
}
+ igen6_debug_setup();
return 0;
fail4:
unregister_nmi_handler(NMI_SERR, IGEN6_NMI_NAME);
@@ -865,6 +923,7 @@ static void igen6_remove(struct pci_dev *pdev)
{
edac_dbg(2, "\n");
+ igen6_debug_teardown();
errcmd_enable_error_reporting(false);
unregister_nmi_handler(NMI_SERR, IGEN6_NMI_NAME);
irq_work_sync(&ecclog_irq_work);
Add debugfs support to fake memory correctable errors to test the error reporting path and the error address decoding logic in the igen6_edac driver. Please note that the fake errors are also reported to EDAC core and then the CE counter in EDAC sysfs is also increased. Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com> --- drivers/edac/igen6_edac.c | 59 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+)