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