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