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