EDAC/igen6: Add debugfs interface for Intel client SoC EDAC driver
authorQiuxu Zhuo <qiuxu.zhuo@intel.com>
Thu, 5 Nov 2020 07:49:34 +0000 (15:49 +0800)
committerTony Luck <tony.luck@intel.com>
Thu, 19 Nov 2020 20:52:47 +0000 (12:52 -0800)
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>
Signed-off-by: Tony Luck <tony.luck@intel.com>
drivers/edac/igen6_edac.c

index 318b9b67080f8f339a775dfff6e9fecd6cfa4e10..6c0039e1171fd35ac96198442c37124eba194bfc 100644 (file)
@@ -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);