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