1 /*
2 * Copyright (c) 2020-2022 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stdlib.h>
8 #include <zephyr/device.h>
9 #include <zephyr/shell/shell.h>
10
11 #include <zephyr/drivers/edac.h>
12 #include "ibecc.h"
13
14 /**
15 * EDAC Error Injection interface
16 *
17 * edac inject addr [value] Physical memory address base
18 * edac inject mask [value] Physical memory address mask
19 * edac inject error_type Show / Set EDAC error type
20 * edac inject trigger Trigger injection
21 *
22 * edac inject test_default Set default injection parameters
23 *
24 * edac disable_nmi Experimental disable NMI
25 * edac enable_nmi Experimental enable NMI
26 *
27 * EDAC Report interface
28 *
29 * edac info Show EDAC ECC / Parity error info
30 * edac info ecc_error [show|clear] Show ECC Errors
31 * edac info parity_error [show|clear] Show Parity Errors
32 *
33 * Physical memory access interface using devmem shell module
34 *
35 * devmem [width [value]] Physical memory read / write
36 */
37
decode_ecc_error(const struct shell * sh,uint64_t ecc_error)38 static void decode_ecc_error(const struct shell *sh, uint64_t ecc_error)
39 {
40 uint64_t erradd = ECC_ERROR_ERRADD(ecc_error);
41 unsigned long errsynd = ECC_ERROR_ERRSYND(ecc_error);
42
43 shell_fprintf(sh, SHELL_NORMAL,
44 "CMI Error address: 0x%llx\n", erradd);
45 shell_fprintf(sh, SHELL_NORMAL,
46 "Error Syndrome: 0x%lx\n", errsynd);
47
48 if (ecc_error & ECC_ERROR_MERRSTS) {
49 shell_fprintf(sh, SHELL_NORMAL,
50 "Uncorrectable Error (UE)\n");
51 }
52
53 if (ecc_error & ECC_ERROR_CERRSTS) {
54 shell_fprintf(sh, SHELL_NORMAL,
55 "Correctable Error (CE)\n");
56 }
57 }
58
ecc_error_show(const struct shell * sh,const struct device * dev)59 static int ecc_error_show(const struct shell *sh, const struct device *dev)
60 {
61 uint64_t error;
62 int err;
63
64 err = edac_ecc_error_log_get(dev, &error);
65 if (err != 0 && err != -ENODATA) {
66 shell_error(sh, "Error getting error log (err %d)", err);
67 return err;
68 }
69
70 shell_fprintf(sh, SHELL_NORMAL, "ECC Error: 0x%llx\n", error);
71
72 if (error != 0) {
73 decode_ecc_error(sh, error);
74 }
75
76 return 0;
77 }
78
parity_error_show(const struct shell * sh,const struct device * dev)79 static int parity_error_show(const struct shell *sh, const struct device *dev)
80 {
81 uint64_t error;
82 int err;
83
84 err = edac_parity_error_log_get(dev, &error);
85 if (err != 0 && err != -ENODATA) {
86 shell_error(sh, "Error getting parity error log (err %d)", err);
87 return err;
88 }
89
90 shell_fprintf(sh, SHELL_NORMAL, "Parity Error: 0x%llx\n", error);
91
92 return 0;
93 }
94
cmd_edac_info(const struct shell * sh,size_t argc,char ** argv)95 static int cmd_edac_info(const struct shell *sh, size_t argc, char **argv)
96 {
97 const struct device *dev;
98 int err;
99
100 dev = DEVICE_DT_GET(DT_NODELABEL(ibecc));
101 if (!device_is_ready(dev)) {
102 shell_error(sh, "IBECC device not ready");
103 return -ENODEV;
104 }
105
106 shell_fprintf(sh, SHELL_NORMAL, "Show EDAC status\n");
107
108 err = ecc_error_show(sh, dev);
109 if (err != 0) {
110 return err;
111 }
112
113 err = parity_error_show(sh, dev);
114 if (err != 0) {
115 return err;
116 }
117
118 shell_fprintf(sh, SHELL_NORMAL,
119 "Errors correctable: %d Errors uncorrectable %d\n",
120 edac_errors_cor_get(dev), edac_errors_uc_get(dev));
121
122 return err;
123 }
124
125 #if defined(CONFIG_EDAC_ERROR_INJECT)
cmd_inject_addr(const struct shell * sh,size_t argc,char ** argv)126 static int cmd_inject_addr(const struct shell *sh, size_t argc, char **argv)
127 {
128 const struct device *dev;
129 int err;
130
131 dev = DEVICE_DT_GET(DT_NODELABEL(ibecc));
132 if (!device_is_ready(dev)) {
133 shell_error(sh, "IBECC device not ready");
134 return -ENODEV;
135 }
136
137 if (argc > 2) {
138 /* Usage */
139 shell_fprintf(sh, SHELL_NORMAL,
140 "Usage: edac inject %s [addr]\n", argv[0]);
141 return -ENOTSUP;
142 }
143
144 if (argc == 1) {
145 uint64_t addr;
146
147 err = edac_inject_get_param1(dev, &addr);
148 if (err != 0) {
149 shell_error(sh, "Error getting address (err %d)",
150 err);
151 return err;
152 }
153
154 shell_fprintf(sh, SHELL_NORMAL,
155 "Injection address base: 0x%llx\n", addr);
156 } else {
157 unsigned long value = strtoul(argv[1], NULL, 16);
158
159 shell_fprintf(sh, SHELL_NORMAL,
160 "Set injection address base to: %s\n", argv[1]);
161
162 err = edac_inject_set_param1(dev, value);
163 if (err != 0) {
164 shell_error(sh, "Error setting address (err %d)",
165 err);
166 return err;
167 }
168 }
169
170 return err;
171 }
172
cmd_inject_mask(const struct shell * sh,size_t argc,char ** argv)173 static int cmd_inject_mask(const struct shell *sh, size_t argc, char **argv)
174 {
175 const struct device *dev;
176 int err;
177
178 dev = DEVICE_DT_GET(DT_NODELABEL(ibecc));
179 if (!device_is_ready(dev)) {
180 shell_error(sh, "IBECC device not ready");
181 return -ENODEV;
182 }
183
184 if (argc > 2) {
185 /* Usage */
186 shell_fprintf(sh, SHELL_NORMAL,
187 "Usage: edac inject %s [mask]\n", argv[0]);
188 return -ENOTSUP;
189 }
190
191 if (argc == 1) {
192 uint64_t mask;
193
194 err = edac_inject_get_param2(dev, &mask);
195 if (err != 0) {
196 shell_error(sh, "Error getting mask (err %d)", err);
197 return err;
198 }
199
200 shell_fprintf(sh, SHELL_NORMAL,
201 "Injection address mask: 0x%llx\n", mask);
202 } else {
203 uint64_t value = strtoul(argv[1], NULL, 16);
204
205 shell_fprintf(sh, SHELL_NORMAL,
206 "Set injection address mask to %llx\n", value);
207
208 err = edac_inject_set_param2(dev, value);
209 if (err != 0) {
210 shell_error(sh, "Error setting mask (err %d)", err);
211 return err;
212 }
213 }
214
215 return err;
216 }
217
cmd_inject_trigger(const struct shell * sh,size_t argc,char ** argv)218 static int cmd_inject_trigger(const struct shell *sh, size_t argc,
219 char **argv)
220 {
221 const struct device *dev;
222
223 dev = DEVICE_DT_GET(DT_NODELABEL(ibecc));
224 if (!device_is_ready(dev)) {
225 shell_error(sh, "IBECC device not ready");
226 return -ENODEV;
227 }
228
229 shell_fprintf(sh, SHELL_NORMAL, "Triggering injection\n");
230
231 edac_inject_error_trigger(dev);
232
233 return 0;
234 }
235
cmd_inject_disable_nmi(const struct shell * sh,size_t argc,char ** argv)236 static int cmd_inject_disable_nmi(const struct shell *sh, size_t argc,
237 char **argv)
238 {
239 sys_out8((sys_in8(0x70) | 0x80), 0x70);
240
241 return 0;
242 }
243
cmd_inject_enable_nmi(const struct shell * sh,size_t argc,char ** argv)244 static int cmd_inject_enable_nmi(const struct shell *sh, size_t argc,
245 char **argv)
246 {
247 sys_out8((sys_in8(0x70) & 0x7F), 0x70);
248
249 return 0;
250 }
251
get_error_type(uint32_t type)252 static const char *get_error_type(uint32_t type)
253 {
254 switch (type) {
255 case EDAC_ERROR_TYPE_DRAM_COR:
256 return "correctable";
257 case EDAC_ERROR_TYPE_DRAM_UC:
258 return "uncorrectable";
259 default:
260 return "unknown";
261 }
262 }
263
cmd_inject_error_type_show(const struct shell * sh,size_t argc,char ** argv)264 static int cmd_inject_error_type_show(const struct shell *sh, size_t argc,
265 char **argv)
266 {
267 const struct device *dev;
268 uint32_t error_type;
269 int err;
270
271 dev = DEVICE_DT_GET(DT_NODELABEL(ibecc));
272 if (!device_is_ready(dev)) {
273 shell_error(sh, "IBECC device not ready");
274 return -ENODEV;
275 }
276
277 err = edac_inject_get_error_type(dev, &error_type);
278 if (err != 0) {
279 shell_error(sh, "Error getting error type (err %d)", err);
280 return err;
281 }
282
283 shell_fprintf(sh, SHELL_NORMAL, "Injection error type: %s\n",
284 get_error_type(error_type));
285
286 return err;
287 }
288
set_error_type(const struct shell * sh,uint32_t error_type)289 static int set_error_type(const struct shell *sh, uint32_t error_type)
290 {
291 const struct device *dev;
292
293 dev = DEVICE_DT_GET(DT_NODELABEL(ibecc));
294 if (!device_is_ready(dev)) {
295 shell_error(sh, "IBECC device not ready");
296 return -ENODEV;
297 }
298
299 shell_fprintf(sh, SHELL_NORMAL, "Set injection error type: %s\n",
300 get_error_type(error_type));
301
302 return edac_inject_set_error_type(dev, error_type);
303 }
304
cmd_inject_error_type_cor(const struct shell * sh,size_t argc,char ** argv)305 static int cmd_inject_error_type_cor(const struct shell *sh, size_t argc,
306 char **argv)
307 {
308 return set_error_type(sh, EDAC_ERROR_TYPE_DRAM_COR);
309 }
310
cmd_inject_error_type_uc(const struct shell * sh,size_t argc,char ** argv)311 static int cmd_inject_error_type_uc(const struct shell *sh, size_t argc,
312 char **argv)
313 {
314 return set_error_type(sh, EDAC_ERROR_TYPE_DRAM_UC);
315 }
316
cmd_inject_test(const struct shell * sh,size_t argc,char ** argv)317 static int cmd_inject_test(const struct shell *sh, size_t argc, char **argv)
318 {
319 const struct device *dev;
320
321 dev = DEVICE_DT_GET(DT_NODELABEL(ibecc));
322 if (!device_is_ready(dev)) {
323 shell_error(sh, "IBECC device not ready");
324 return -ENODEV;
325 }
326
327 edac_inject_set_param1(dev, 0x1000);
328 edac_inject_set_param2(dev, INJ_ADDR_BASE_MASK_MASK);
329 edac_inject_set_error_type(dev, EDAC_ERROR_TYPE_DRAM_COR);
330 edac_inject_error_trigger(dev);
331
332 return 0;
333 }
334
335 SHELL_STATIC_SUBCMD_SET_CREATE(sub_inject_error_type_cmds,
336 SHELL_CMD(correctable, NULL, "Set correctable error type",
337 cmd_inject_error_type_cor),
338 SHELL_CMD(uncorrectable, NULL, "Set uncorrectable error type",
339 cmd_inject_error_type_uc),
340 SHELL_SUBCMD_SET_END /* Array terminated */
341 );
342
343 /* EDAC Error Injection shell commands */
344 SHELL_STATIC_SUBCMD_SET_CREATE(sub_inject_cmds,
345 SHELL_CMD(addr, NULL, "Get / Set physical address", cmd_inject_addr),
346 SHELL_CMD(mask, NULL, "Get / Set address mask", cmd_inject_mask),
347 SHELL_CMD_ARG(trigger, NULL, "Trigger injection", cmd_inject_trigger,
348 1, 0),
349 SHELL_CMD(error_type, &sub_inject_error_type_cmds,
350 "Get / Set injection error type",
351 cmd_inject_error_type_show),
352 SHELL_CMD(disable_nmi, NULL, "Disable NMI", cmd_inject_disable_nmi),
353 SHELL_CMD(enable_nmi, NULL, "Enable NMI", cmd_inject_enable_nmi),
354 SHELL_CMD_ARG(test_default, NULL, "Test default injection parameters",
355 cmd_inject_test, 1, 0),
356 SHELL_SUBCMD_SET_END /* Array terminated */
357 );
358 #endif /* CONFIG_EDAC_ERROR_INJECT */
359
cmd_ecc_error_show(const struct shell * sh,size_t argc,char ** argv)360 static int cmd_ecc_error_show(const struct shell *sh, size_t argc,
361 char **argv)
362 {
363 const struct device *dev;
364
365 dev = DEVICE_DT_GET(DT_NODELABEL(ibecc));
366 if (!device_is_ready(dev)) {
367 shell_error(sh, "IBECC device not ready");
368 return -ENODEV;
369 }
370
371 return ecc_error_show(sh, dev);
372 }
373
cmd_ecc_error_clear(const struct shell * sh,size_t argc,char ** argv)374 static int cmd_ecc_error_clear(const struct shell *sh, size_t argc,
375 char **argv)
376 {
377 const struct device *dev;
378 int err;
379
380 dev = DEVICE_DT_GET(DT_NODELABEL(ibecc));
381 if (!device_is_ready(dev)) {
382 shell_error(sh, "IBECC device not ready");
383 return -ENODEV;
384 }
385
386 err = edac_ecc_error_log_clear(dev);
387 if (err != 0) {
388 shell_error(sh, "Error clear ecc error log (err %d)",
389 err);
390 return err;
391 }
392
393 shell_fprintf(sh, SHELL_NORMAL, "ECC Error Log cleared\n");
394
395 return 0;
396 }
397
398 SHELL_STATIC_SUBCMD_SET_CREATE(sub_ecc_error_cmds,
399 SHELL_CMD(show, NULL, "Show ECC errors", cmd_ecc_error_show),
400 SHELL_CMD(clear, NULL, "Clear ECC errors", cmd_ecc_error_clear),
401 SHELL_SUBCMD_SET_END /* Array terminated */
402 );
403
cmd_parity_error_show(const struct shell * sh,size_t argc,char ** argv)404 static int cmd_parity_error_show(const struct shell *sh, size_t argc,
405 char **argv)
406 {
407 const struct device *dev;
408
409 dev = DEVICE_DT_GET(DT_NODELABEL(ibecc));
410 if (!device_is_ready(dev)) {
411 shell_error(sh, "IBECC device not ready");
412 return -ENODEV;
413 }
414
415 return parity_error_show(sh, dev);
416 }
417
cmd_parity_error_clear(const struct shell * sh,size_t argc,char ** argv)418 static int cmd_parity_error_clear(const struct shell *sh, size_t argc,
419 char **argv)
420 {
421 const struct device *dev;
422 int err;
423
424 dev = DEVICE_DT_GET(DT_NODELABEL(ibecc));
425 if (!device_is_ready(dev)) {
426 shell_error(sh, "IBECC device not ready");
427 return -ENODEV;
428 }
429
430 err = edac_parity_error_log_clear(dev);
431 if (err != 0) {
432 shell_error(sh, "Error clear parity error log (err %d)",
433 err);
434 return err;
435 }
436
437 shell_fprintf(sh, SHELL_NORMAL, "Parity Error Log cleared\n");
438
439 return 0;
440 }
441
442 SHELL_STATIC_SUBCMD_SET_CREATE(sub_parity_error_cmds,
443 SHELL_CMD(show, NULL, "Show Parity errors", cmd_parity_error_show),
444 SHELL_CMD(clear, NULL, "Clear Parity errors", cmd_parity_error_clear),
445 SHELL_SUBCMD_SET_END /* Array terminated */
446 );
447
448 /* EDAC Info shell commands */
449 SHELL_STATIC_SUBCMD_SET_CREATE(sub_info_cmds,
450 SHELL_CMD(ecc_error, &sub_ecc_error_cmds,
451 "ECC Error Show / Clear commands", cmd_ecc_error_show),
452 SHELL_CMD(parity_error, &sub_parity_error_cmds,
453 "Parity Error Show / Clear commands", cmd_parity_error_show),
454 SHELL_SUBCMD_SET_END /* Array terminated */
455 );
456
457 SHELL_STATIC_SUBCMD_SET_CREATE(sub_edac_cmds,
458 SHELL_CMD(info, &sub_info_cmds,
459 "Show EDAC information\n"
460 "edac info <subcommands>", cmd_edac_info),
461 #if defined(CONFIG_EDAC_ERROR_INJECT)
462 /* This does not work with SHELL_COND_CMD */
463 SHELL_CMD(inject, &sub_inject_cmds,
464 "Inject ECC error commands\n"
465 "edac inject <subcommands>", NULL),
466 #endif
467 SHELL_SUBCMD_SET_END /* Array terminated. */
468 );
469
470 SHELL_CMD_REGISTER(edac, &sub_edac_cmds, "EDAC information", cmd_edac_info);
471