1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Dummy EDAC driver 4 * 5 * A minimal EDAC device driver that registers its own platform device 6 * unconditionally, so it probes successfully on any system -- including 7 * VMs and guests that have no real ECC-capable cache/memory hardware 8 * exposed to them. Useful for exercising the EDAC core, sysfs interface, 9 * and userspace tooling (edac-utils, mcelog, etc.) without needing the 10 * physical hardware the "real" drivers depend on. 11 * 12 * Modeled after drivers/edac/a72_edac.c (Cortex A72 EDAC L1/L2 cache 13 * error detection), but with all of the hardware-specific bits (system 14 * register reads, SMP cross-calls, devicetree compatible matching) 15 * replaced by a software-only error source that can be poked from 16 * debugfs or left to generate nothing at all. 17 */ 18 19 #include <linux/cpu.h> 20 #include <linux/debugfs.h> 21 #include <linux/module.h> 22 #include <linux/platform_device.h> 23 #include <linux/slab.h> 24 25 #include "edac_module.h" 26 27 #define DRVNAME "dummy-edac" 28 #define MESSAGE_SIZE 64 29 30 /* 31 * Simulated "error syndrome". In real hardware drivers this would be 32 * read out of a per-CPU system/MMIO register. Here userspace (via 33 * debugfs) or nothing at all drives it, which is exactly the point: 34 * the driver has no dependency on any physical error source. 35 */ 36 struct dummy_edac_priv { 37 struct dentry *debugfs_dir; 38 atomic_t inject_ce; 39 atomic_t inject_ue; 40 }; 41 42 static void dummy_edac_check(struct edac_device_ctl_info *edac_ctl) 43 { 44 struct dummy_edac_priv *priv = edac_ctl->pvt_info; 45 int cpu = raw_smp_processor_id(); 46 char msg[MESSAGE_SIZE]; 47 48 if (atomic_xchg(&priv->inject_ce, 0)) { 49 snprintf(msg, MESSAGE_SIZE, 50 "simulated correctable error on CPU %d", cpu); 51 edac_device_handle_ce(edac_ctl, cpu, 0, msg); 52 } 53 54 if (atomic_xchg(&priv->inject_ue, 0)) { 55 snprintf(msg, MESSAGE_SIZE, 56 "simulated uncorrectable error on CPU %d", cpu); 57 edac_device_handle_ue(edac_ctl, cpu, 0, msg); 58 } 59 } 60 61 /* 62 * debugfs knobs so you can drive the driver from userspace inside the 63 * guest, e.g.: 64 * echo 1 > /sys/kernel/debug/dummy-edac/inject_ce 65 * echo 1 > /sys/kernel/debug/dummy-edac/inject_ue 66 */ 67 static int inject_ce_set(void *data, u64 val) 68 { 69 struct dummy_edac_priv *priv = data; 70 71 if (val) 72 atomic_set(&priv->inject_ce, 1); 73 return 0; 74 } 75 DEFINE_DEBUGFS_ATTRIBUTE(inject_ce_fops, NULL, inject_ce_set, "%llu\n"); 76 77 static int inject_ue_set(void *data, u64 val) 78 { 79 struct dummy_edac_priv *priv = data; 80 81 if (val) 82 atomic_set(&priv->inject_ue, 1); 83 return 0; 84 } 85 DEFINE_DEBUGFS_ATTRIBUTE(inject_ue_fops, NULL, inject_ue_set, "%llu\n"); 86 87 static int dummy_edac_probe(struct platform_device *pdev) 88 { 89 struct edac_device_ctl_info *edac_ctl; 90 struct dummy_edac_priv *priv; 91 struct device *dev = &pdev->dev; 92 int rc; 93 94 edac_ctl = edac_device_alloc_ctl_info(sizeof(*priv), "cpu", 95 num_possible_cpus(), "L", 1, 1, 96 edac_device_alloc_index()); 97 if (!edac_ctl) 98 return -ENOMEM; 99 100 priv = edac_ctl->pvt_info; 101 atomic_set(&priv->inject_ce, 0); 102 atomic_set(&priv->inject_ue, 0); 103 104 edac_ctl->edac_check = dummy_edac_check; 105 edac_ctl->dev = dev; 106 edac_ctl->mod_name = dev_name(dev); 107 edac_ctl->dev_name = dev_name(dev); 108 edac_ctl->ctl_name = DRVNAME; 109 /* Poll fairly slowly; this is a software source, not real hardware. */ 110 edac_ctl->poll_msec = 1000; 111 dev_set_drvdata(dev, edac_ctl); 112 113 rc = edac_device_add_device(edac_ctl); 114 if (rc) 115 goto out_dev; 116 117 priv->debugfs_dir = debugfs_create_dir(DRVNAME, NULL); 118 debugfs_create_file_unsafe("inject_ce", 0200, priv->debugfs_dir, 119 priv, &inject_ce_fops); 120 debugfs_create_file_unsafe("inject_ue", 0200, priv->debugfs_dir, 121 priv, &inject_ue_fops); 122 123 return 0; 124 125 out_dev: 126 edac_device_free_ctl_info(edac_ctl); 127 128 return rc; 129 } 130 131 static void dummy_edac_remove(struct platform_device *pdev) 132 { 133 struct edac_device_ctl_info *edac_ctl = dev_get_drvdata(&pdev->dev); 134 struct dummy_edac_priv *priv = edac_ctl->pvt_info; 135 136 debugfs_remove_recursive(priv->debugfs_dir); 137 edac_device_del_device(edac_ctl->dev); 138 edac_device_free_ctl_info(edac_ctl); 139 } 140 141 static struct platform_driver dummy_edac_driver = { 142 .probe = dummy_edac_probe, 143 .remove = dummy_edac_remove, 144 .driver = { 145 .name = DRVNAME, 146 }, 147 }; 148 149 /* 150 * No devicetree/ACPI match table, and no scan of CPU nodes for a "compatible" 151 * + enable property: simply register a platform device unconditionally so 152 * probe() always runs. There is nothing here that depends on the underlying 153 * platform actually exposing the corresponding hardware, so it loads fine 154 * under QEMU/KVM, containers-with-a-kernel, or any other guest environment. 155 */ 156 static struct platform_device *dummy_pdev; 157 158 static int __init dummy_edac_driver_init(void) 159 { 160 int rc; 161 162 dummy_pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0); 163 if (IS_ERR(dummy_pdev)) { 164 pr_err(DRVNAME ": failed to register dummy platform device\n"); 165 return PTR_ERR(dummy_pdev); 166 } 167 168 rc = platform_driver_register(&dummy_edac_driver); 169 if (rc) { 170 platform_device_unregister(dummy_pdev); 171 return rc; 172 } 173 174 pr_info("Loading %s\n", DRVNAME); 175 176 return 0; 177 } 178 179 static void __exit dummy_edac_driver_exit(void) 180 { 181 platform_driver_unregister(&dummy_edac_driver); 182 platform_device_unregister(dummy_pdev); 183 184 pr_info("Removing %s\n", DRVNAME); 185 } 186 187 module_init(dummy_edac_driver_init); 188 module_exit(dummy_edac_driver_exit); 189 190 MODULE_LICENSE("GPL"); 191 MODULE_DESCRIPTION("Dummy EDAC driver for testing without real ECC hardware"); 192