/* * Copyright (c) 2020-2022 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include "ibecc.h" /** * EDAC Error Injection interface * * edac inject addr [value] Physical memory address base * edac inject mask [value] Physical memory address mask * edac inject error_type Show / Set EDAC error type * edac inject trigger Trigger injection * * edac inject test_default Set default injection parameters * * edac disable_nmi Experimental disable NMI * edac enable_nmi Experimental enable NMI * * EDAC Report interface * * edac info Show EDAC ECC / Parity error info * edac info ecc_error [show|clear] Show ECC Errors * edac info parity_error [show|clear] Show Parity Errors * * Physical memory access interface using devmem shell module * * devmem [width [value]] Physical memory read / write */ static void decode_ecc_error(const struct shell *sh, uint64_t ecc_error) { uint64_t erradd = ECC_ERROR_ERRADD(ecc_error); unsigned long errsynd = ECC_ERROR_ERRSYND(ecc_error); shell_fprintf(sh, SHELL_NORMAL, "CMI Error address: 0x%llx\n", erradd); shell_fprintf(sh, SHELL_NORMAL, "Error Syndrome: 0x%lx\n", errsynd); if (ecc_error & ECC_ERROR_MERRSTS) { shell_fprintf(sh, SHELL_NORMAL, "Uncorrectable Error (UE)\n"); } if (ecc_error & ECC_ERROR_CERRSTS) { shell_fprintf(sh, SHELL_NORMAL, "Correctable Error (CE)\n"); } } static int ecc_error_show(const struct shell *sh, const struct device *dev) { uint64_t error; int err; err = edac_ecc_error_log_get(dev, &error); if (err != 0 && err != -ENODATA) { shell_error(sh, "Error getting error log (err %d)", err); return err; } shell_fprintf(sh, SHELL_NORMAL, "ECC Error: 0x%llx\n", error); if (error != 0) { decode_ecc_error(sh, error); } return 0; } static int parity_error_show(const struct shell *sh, const struct device *dev) { uint64_t error; int err; err = edac_parity_error_log_get(dev, &error); if (err != 0 && err != -ENODATA) { shell_error(sh, "Error getting parity error log (err %d)", err); return err; } shell_fprintf(sh, SHELL_NORMAL, "Parity Error: 0x%llx\n", error); return 0; } static int cmd_edac_info(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; int err; dev = DEVICE_DT_GET(DT_NODELABEL(ibecc)); if (!device_is_ready(dev)) { shell_error(sh, "IBECC device not ready"); return -ENODEV; } shell_fprintf(sh, SHELL_NORMAL, "Show EDAC status\n"); err = ecc_error_show(sh, dev); if (err != 0) { return err; } err = parity_error_show(sh, dev); if (err != 0) { return err; } shell_fprintf(sh, SHELL_NORMAL, "Errors correctable: %d Errors uncorrectable %d\n", edac_errors_cor_get(dev), edac_errors_uc_get(dev)); return err; } #if defined(CONFIG_EDAC_ERROR_INJECT) static int cmd_inject_addr(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; int err; dev = DEVICE_DT_GET(DT_NODELABEL(ibecc)); if (!device_is_ready(dev)) { shell_error(sh, "IBECC device not ready"); return -ENODEV; } if (argc > 2) { /* Usage */ shell_fprintf(sh, SHELL_NORMAL, "Usage: edac inject %s [addr]\n", argv[0]); return -ENOTSUP; } if (argc == 1) { uint64_t addr; err = edac_inject_get_param1(dev, &addr); if (err != 0) { shell_error(sh, "Error getting address (err %d)", err); return err; } shell_fprintf(sh, SHELL_NORMAL, "Injection address base: 0x%llx\n", addr); } else { unsigned long value = strtoul(argv[1], NULL, 16); shell_fprintf(sh, SHELL_NORMAL, "Set injection address base to: %s\n", argv[1]); err = edac_inject_set_param1(dev, value); if (err != 0) { shell_error(sh, "Error setting address (err %d)", err); return err; } } return err; } static int cmd_inject_mask(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; int err; dev = DEVICE_DT_GET(DT_NODELABEL(ibecc)); if (!device_is_ready(dev)) { shell_error(sh, "IBECC device not ready"); return -ENODEV; } if (argc > 2) { /* Usage */ shell_fprintf(sh, SHELL_NORMAL, "Usage: edac inject %s [mask]\n", argv[0]); return -ENOTSUP; } if (argc == 1) { uint64_t mask; err = edac_inject_get_param2(dev, &mask); if (err != 0) { shell_error(sh, "Error getting mask (err %d)", err); return err; } shell_fprintf(sh, SHELL_NORMAL, "Injection address mask: 0x%llx\n", mask); } else { uint64_t value = strtoul(argv[1], NULL, 16); shell_fprintf(sh, SHELL_NORMAL, "Set injection address mask to %llx\n", value); err = edac_inject_set_param2(dev, value); if (err != 0) { shell_error(sh, "Error setting mask (err %d)", err); return err; } } return err; } static int cmd_inject_trigger(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; dev = DEVICE_DT_GET(DT_NODELABEL(ibecc)); if (!device_is_ready(dev)) { shell_error(sh, "IBECC device not ready"); return -ENODEV; } shell_fprintf(sh, SHELL_NORMAL, "Triggering injection\n"); edac_inject_error_trigger(dev); return 0; } static int cmd_inject_disable_nmi(const struct shell *sh, size_t argc, char **argv) { sys_out8((sys_in8(0x70) | 0x80), 0x70); return 0; } static int cmd_inject_enable_nmi(const struct shell *sh, size_t argc, char **argv) { sys_out8((sys_in8(0x70) & 0x7F), 0x70); return 0; } static const char *get_error_type(uint32_t type) { switch (type) { case EDAC_ERROR_TYPE_DRAM_COR: return "correctable"; case EDAC_ERROR_TYPE_DRAM_UC: return "uncorrectable"; default: return "unknown"; } } static int cmd_inject_error_type_show(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; uint32_t error_type; int err; dev = DEVICE_DT_GET(DT_NODELABEL(ibecc)); if (!device_is_ready(dev)) { shell_error(sh, "IBECC device not ready"); return -ENODEV; } err = edac_inject_get_error_type(dev, &error_type); if (err != 0) { shell_error(sh, "Error getting error type (err %d)", err); return err; } shell_fprintf(sh, SHELL_NORMAL, "Injection error type: %s\n", get_error_type(error_type)); return err; } static int set_error_type(const struct shell *sh, uint32_t error_type) { const struct device *dev; dev = DEVICE_DT_GET(DT_NODELABEL(ibecc)); if (!device_is_ready(dev)) { shell_error(sh, "IBECC device not ready"); return -ENODEV; } shell_fprintf(sh, SHELL_NORMAL, "Set injection error type: %s\n", get_error_type(error_type)); return edac_inject_set_error_type(dev, error_type); } static int cmd_inject_error_type_cor(const struct shell *sh, size_t argc, char **argv) { return set_error_type(sh, EDAC_ERROR_TYPE_DRAM_COR); } static int cmd_inject_error_type_uc(const struct shell *sh, size_t argc, char **argv) { return set_error_type(sh, EDAC_ERROR_TYPE_DRAM_UC); } static int cmd_inject_test(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; dev = DEVICE_DT_GET(DT_NODELABEL(ibecc)); if (!device_is_ready(dev)) { shell_error(sh, "IBECC device not ready"); return -ENODEV; } edac_inject_set_param1(dev, 0x1000); edac_inject_set_param2(dev, INJ_ADDR_BASE_MASK_MASK); edac_inject_set_error_type(dev, EDAC_ERROR_TYPE_DRAM_COR); edac_inject_error_trigger(dev); return 0; } SHELL_STATIC_SUBCMD_SET_CREATE(sub_inject_error_type_cmds, SHELL_CMD(correctable, NULL, "Set correctable error type", cmd_inject_error_type_cor), SHELL_CMD(uncorrectable, NULL, "Set uncorrectable error type", cmd_inject_error_type_uc), SHELL_SUBCMD_SET_END /* Array terminated */ ); /* EDAC Error Injection shell commands */ SHELL_STATIC_SUBCMD_SET_CREATE(sub_inject_cmds, SHELL_CMD(addr, NULL, "Get / Set physical address", cmd_inject_addr), SHELL_CMD(mask, NULL, "Get / Set address mask", cmd_inject_mask), SHELL_CMD_ARG(trigger, NULL, "Trigger injection", cmd_inject_trigger, 1, 0), SHELL_CMD(error_type, &sub_inject_error_type_cmds, "Get / Set injection error type", cmd_inject_error_type_show), SHELL_CMD(disable_nmi, NULL, "Disable NMI", cmd_inject_disable_nmi), SHELL_CMD(enable_nmi, NULL, "Enable NMI", cmd_inject_enable_nmi), SHELL_CMD_ARG(test_default, NULL, "Test default injection parameters", cmd_inject_test, 1, 0), SHELL_SUBCMD_SET_END /* Array terminated */ ); #endif /* CONFIG_EDAC_ERROR_INJECT */ static int cmd_ecc_error_show(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; dev = DEVICE_DT_GET(DT_NODELABEL(ibecc)); if (!device_is_ready(dev)) { shell_error(sh, "IBECC device not ready"); return -ENODEV; } return ecc_error_show(sh, dev); } static int cmd_ecc_error_clear(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; int err; dev = DEVICE_DT_GET(DT_NODELABEL(ibecc)); if (!device_is_ready(dev)) { shell_error(sh, "IBECC device not ready"); return -ENODEV; } err = edac_ecc_error_log_clear(dev); if (err != 0) { shell_error(sh, "Error clear ecc error log (err %d)", err); return err; } shell_fprintf(sh, SHELL_NORMAL, "ECC Error Log cleared\n"); return 0; } SHELL_STATIC_SUBCMD_SET_CREATE(sub_ecc_error_cmds, SHELL_CMD(show, NULL, "Show ECC errors", cmd_ecc_error_show), SHELL_CMD(clear, NULL, "Clear ECC errors", cmd_ecc_error_clear), SHELL_SUBCMD_SET_END /* Array terminated */ ); static int cmd_parity_error_show(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; dev = DEVICE_DT_GET(DT_NODELABEL(ibecc)); if (!device_is_ready(dev)) { shell_error(sh, "IBECC device not ready"); return -ENODEV; } return parity_error_show(sh, dev); } static int cmd_parity_error_clear(const struct shell *sh, size_t argc, char **argv) { const struct device *dev; int err; dev = DEVICE_DT_GET(DT_NODELABEL(ibecc)); if (!device_is_ready(dev)) { shell_error(sh, "IBECC device not ready"); return -ENODEV; } err = edac_parity_error_log_clear(dev); if (err != 0) { shell_error(sh, "Error clear parity error log (err %d)", err); return err; } shell_fprintf(sh, SHELL_NORMAL, "Parity Error Log cleared\n"); return 0; } SHELL_STATIC_SUBCMD_SET_CREATE(sub_parity_error_cmds, SHELL_CMD(show, NULL, "Show Parity errors", cmd_parity_error_show), SHELL_CMD(clear, NULL, "Clear Parity errors", cmd_parity_error_clear), SHELL_SUBCMD_SET_END /* Array terminated */ ); /* EDAC Info shell commands */ SHELL_STATIC_SUBCMD_SET_CREATE(sub_info_cmds, SHELL_CMD(ecc_error, &sub_ecc_error_cmds, "ECC Error Show / Clear commands", cmd_ecc_error_show), SHELL_CMD(parity_error, &sub_parity_error_cmds, "Parity Error Show / Clear commands", cmd_parity_error_show), SHELL_SUBCMD_SET_END /* Array terminated */ ); SHELL_STATIC_SUBCMD_SET_CREATE(sub_edac_cmds, SHELL_CMD(info, &sub_info_cmds, "Show EDAC information\n" "edac info ", cmd_edac_info), #if defined(CONFIG_EDAC_ERROR_INJECT) /* This does not work with SHELL_COND_CMD */ SHELL_CMD(inject, &sub_inject_cmds, "Inject ECC error commands\n" "edac inject ", NULL), #endif SHELL_SUBCMD_SET_END /* Array terminated. */ ); SHELL_CMD_REGISTER(edac, &sub_edac_cmds, "EDAC information", cmd_edac_info);