1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | #include "edac_module.h" |
3 | |
4 | static struct dentry *edac_debugfs; |
5 | |
6 | static ssize_t edac_fake_inject_write(struct file *file, |
7 | const char __user *data, |
8 | size_t count, loff_t *ppos) |
9 | { |
10 | struct device *dev = file->private_data; |
11 | struct mem_ctl_info *mci = to_mci(dev); |
12 | static enum hw_event_mc_err_type type; |
13 | u16 errcount = mci->fake_inject_count; |
14 | |
15 | if (!errcount) |
16 | errcount = 1; |
17 | |
18 | type = mci->fake_inject_ue ? HW_EVENT_ERR_UNCORRECTED |
19 | : HW_EVENT_ERR_CORRECTED; |
20 | |
21 | printk(KERN_DEBUG |
22 | "Generating %d %s fake error%s to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n" , |
23 | errcount, |
24 | (type == HW_EVENT_ERR_UNCORRECTED) ? "UE" : "CE" , |
25 | errcount > 1 ? "s" : "" , |
26 | mci->fake_inject_layer[0], |
27 | mci->fake_inject_layer[1], |
28 | mci->fake_inject_layer[2] |
29 | ); |
30 | edac_mc_handle_error(type, mci, error_count: errcount, page_frame_number: 0, offset_in_page: 0, syndrome: 0, |
31 | top_layer: mci->fake_inject_layer[0], |
32 | mid_layer: mci->fake_inject_layer[1], |
33 | low_layer: mci->fake_inject_layer[2], |
34 | msg: "FAKE ERROR" , other_detail: "for EDAC testing only" ); |
35 | |
36 | return count; |
37 | } |
38 | |
39 | static const struct file_operations debug_fake_inject_fops = { |
40 | .open = simple_open, |
41 | .write = edac_fake_inject_write, |
42 | .llseek = generic_file_llseek, |
43 | }; |
44 | |
45 | void __init edac_debugfs_init(void) |
46 | { |
47 | edac_debugfs = debugfs_create_dir(name: "edac" , NULL); |
48 | } |
49 | |
50 | void edac_debugfs_exit(void) |
51 | { |
52 | debugfs_remove_recursive(dentry: edac_debugfs); |
53 | } |
54 | |
55 | void edac_create_debugfs_nodes(struct mem_ctl_info *mci) |
56 | { |
57 | struct dentry *parent; |
58 | char name[80]; |
59 | int i; |
60 | |
61 | parent = debugfs_create_dir(name: mci->dev.kobj.name, parent: edac_debugfs); |
62 | |
63 | for (i = 0; i < mci->n_layers; i++) { |
64 | sprintf(buf: name, fmt: "fake_inject_%s" , |
65 | edac_layer_name[mci->layers[i].type]); |
66 | debugfs_create_u8(name, S_IRUGO | S_IWUSR, parent, |
67 | value: &mci->fake_inject_layer[i]); |
68 | } |
69 | |
70 | debugfs_create_bool(name: "fake_inject_ue" , S_IRUGO | S_IWUSR, parent, |
71 | value: &mci->fake_inject_ue); |
72 | |
73 | debugfs_create_u16(name: "fake_inject_count" , S_IRUGO | S_IWUSR, parent, |
74 | value: &mci->fake_inject_count); |
75 | |
76 | debugfs_create_file(name: "fake_inject" , S_IWUSR, parent, data: &mci->dev, |
77 | fops: &debug_fake_inject_fops); |
78 | |
79 | mci->debugfs = parent; |
80 | } |
81 | |
82 | /* Create a toplevel dir under EDAC's debugfs hierarchy */ |
83 | struct dentry *edac_debugfs_create_dir(const char *dirname) |
84 | { |
85 | if (!edac_debugfs) |
86 | return NULL; |
87 | |
88 | return debugfs_create_dir(name: dirname, parent: edac_debugfs); |
89 | } |
90 | EXPORT_SYMBOL_GPL(edac_debugfs_create_dir); |
91 | |
92 | /* Create a toplevel dir under EDAC's debugfs hierarchy with parent @parent */ |
93 | struct dentry * |
94 | edac_debugfs_create_dir_at(const char *dirname, struct dentry *parent) |
95 | { |
96 | return debugfs_create_dir(name: dirname, parent); |
97 | } |
98 | EXPORT_SYMBOL_GPL(edac_debugfs_create_dir_at); |
99 | |
100 | /* |
101 | * Create a file under EDAC's hierarchy or a sub-hierarchy: |
102 | * |
103 | * @name: file name |
104 | * @mode: file permissions |
105 | * @parent: parent dentry. If NULL, it becomes the toplevel EDAC dir |
106 | * @data: private data of caller |
107 | * @fops: file operations of this file |
108 | */ |
109 | struct dentry * |
110 | edac_debugfs_create_file(const char *name, umode_t mode, struct dentry *parent, |
111 | void *data, const struct file_operations *fops) |
112 | { |
113 | if (!parent) |
114 | parent = edac_debugfs; |
115 | |
116 | return debugfs_create_file(name, mode, parent, data, fops); |
117 | } |
118 | EXPORT_SYMBOL_GPL(edac_debugfs_create_file); |
119 | |
120 | /* Wrapper for debugfs_create_x8() */ |
121 | void edac_debugfs_create_x8(const char *name, umode_t mode, |
122 | struct dentry *parent, u8 *value) |
123 | { |
124 | if (!parent) |
125 | parent = edac_debugfs; |
126 | |
127 | debugfs_create_x8(name, mode, parent, value); |
128 | } |
129 | EXPORT_SYMBOL_GPL(edac_debugfs_create_x8); |
130 | |
131 | /* Wrapper for debugfs_create_x16() */ |
132 | void edac_debugfs_create_x16(const char *name, umode_t mode, |
133 | struct dentry *parent, u16 *value) |
134 | { |
135 | if (!parent) |
136 | parent = edac_debugfs; |
137 | |
138 | debugfs_create_x16(name, mode, parent, value); |
139 | } |
140 | EXPORT_SYMBOL_GPL(edac_debugfs_create_x16); |
141 | |
142 | /* Wrapper for debugfs_create_x32() */ |
143 | void edac_debugfs_create_x32(const char *name, umode_t mode, |
144 | struct dentry *parent, u32 *value) |
145 | { |
146 | if (!parent) |
147 | parent = edac_debugfs; |
148 | |
149 | debugfs_create_x32(name, mode, parent, value); |
150 | } |
151 | EXPORT_SYMBOL_GPL(edac_debugfs_create_x32); |
152 | |