// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2024 */ #include #include #include "common.h" #include "util/types.h" #include "util/logging.h" #include "nvme-print.h" #include "ocp-hardware-component-log.h" #include "ocp-print.h" #include "ocp-utils.h" //#define HWCOMP_DUMMY #define print_info_array(...) \ do { \ if (log_level >= LOG_INFO) \ print_array(__VA_ARGS__); \ } while (false) #define print_info_error(...) \ do { \ if (log_level >= LOG_INFO) \ fprintf(stderr, __VA_ARGS__); \ } while (false) #ifdef HWCOMP_DUMMY static __u8 hwcomp_dummy[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x57, 0x0f, 0x9f, 0xb9, 0x31, 0x6b, 0xb7, 0xd0, 0x4e, 0xcd, 0x30, 0x1f, 0x82, 0xb6, 0xbc, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }; #endif /* HWCOMP_DUMMY */ const char *hwcomp_id_to_string(__u32 id) { switch (id) { case HWCOMP_ID_ASIC: return "Controller ASIC component"; case HWCOMP_ID_NAND: return "NAND Component"; case HWCOMP_ID_DRAM: return "DRAM Component"; case HWCOMP_ID_PMIC: return "PMIC Component"; case HWCOMP_ID_PCB: return "PCB Component"; case HWCOMP_ID_CAP: return "capacitor component"; case HWCOMP_ID_REG: return "registor component"; case HWCOMP_ID_CASE: return "case component"; case HWCOMP_ID_SN: return "Device Serial Number"; case HWCOMP_ID_COUNTRY: return "Country of Origin"; case HWCOMP_ID_HW_REV: return "Global Device Hardware Revision"; case HWCOMP_ID_BORN_ON_DATE: return "Born on Date"; case HWCOMP_ID_VENDOR ... HWCOMP_ID_MAX: return "Vendor Unique Component"; case HWCOMP_ID_RSVD: default: break; } return "Reserved"; } static int get_hwcomp_log_data(struct nvme_dev *dev, struct hwcomp_log *log) { int ret = 0; size_t desc_offset = offsetof(struct hwcomp_log, desc); long double log_bytes; nvme_uint128_t log_size; struct nvme_get_log_args args = { .args_size = sizeof(args), .fd = dev_fd(dev), .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, .lid = (enum nvme_cmd_get_log_lid)OCP_LID_HWCOMP, .nsid = NVME_NSID_ALL, .log = log, .len = desc_offset, }; ocp_get_uuid_index(dev, &args.uuidx); #ifdef HWCOMP_DUMMY memcpy(log, hwcomp_dummy, desc_offset); #else /* HWCOMP_DUMMY */ ret = nvme_get_log_page(dev_fd(dev), NVME_LOG_PAGE_PDU_SIZE, &args); if (ret) { print_info_error("error: ocp: failed to get hwcomp log size (ret: %d)\n", ret); return ret; } #endif /* HWCOMP_DUMMY */ log_size = le128_to_cpu(log->size); print_info("id: %02Xh\n", OCP_LID_HWCOMP); print_info("version: %04Xh\n", log->ver); print_info_array("guid", log->guid, ARRAY_SIZE(log->guid)); print_info("size: %s\n", uint128_t_to_string(log_size)); log_bytes = uint128_t_to_double(log_size); if (log->ver == 1) log_bytes *= sizeof(__le32); if (log_bytes <= desc_offset) { print_info_error("error: ocp: invalid hwcomp log size bytes: %.0Lf\n", log_bytes); return -EINVAL; } args.len = log_bytes - desc_offset; print_info("args.len: %u\n", args.len); log->desc = calloc(1, args.len); if (!log->desc) { fprintf(stderr, "error: ocp: calloc: %s\n", strerror(errno)); return -errno; } args.log = log->desc, args.lpo = desc_offset, #ifdef HWCOMP_DUMMY memcpy(log->desc, &hwcomp_dummy[desc_offset], args.len); #else /* HWCOMP_DUMMY */ ret = nvme_get_log_page(dev_fd(dev), NVME_LOG_PAGE_PDU_SIZE, &args); if (ret) { print_info_error("error: ocp: failed to get log page (hwcomp: %02X, ret: %d)\n", OCP_LID_HWCOMP, ret); free(log->desc); return ret; } #endif /* HWCOMP_DUMMY */ return ret; } static int get_hwcomp_log(struct nvme_dev *dev, __u32 id, bool list) { int ret; nvme_print_flags_t fmt; struct hwcomp_log log = { .desc = NULL, }; ret = validate_output_format(nvme_cfg.output_format, &fmt); if (ret < 0) { fprintf(stderr, "error: ocp: invalid output format\n"); return ret; } ret = get_hwcomp_log_data(dev, &log); if (ret) { print_info_error("error: ocp: failed get hwcomp log: %02X data, ret: %d\n", OCP_LID_HWCOMP, ret); return ret; } ocp_show_hwcomp_log(&log, id, list, fmt); free(log.desc); return 0; } int ocp_hwcomp_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) { _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; int ret = 0; const char *desc = "retrieve hardware component log"; struct config { __u64 id; bool list; } cfg = { 0 }; const char *id_desc = "component identifier"; const char *list_desc = "list component descriptions"; OPT_VALS(id) = { VAL_LONG("asic", HWCOMP_ID_ASIC), VAL_LONG("nand", HWCOMP_ID_NAND), VAL_LONG("dram", HWCOMP_ID_DRAM), VAL_LONG("pmic", HWCOMP_ID_PMIC), VAL_LONG("pcb", HWCOMP_ID_PCB), VAL_LONG("cap", HWCOMP_ID_CAP), VAL_LONG("reg", HWCOMP_ID_REG), VAL_LONG("case", HWCOMP_ID_CASE), VAL_LONG("sn", HWCOMP_ID_SN), VAL_LONG("country", HWCOMP_ID_COUNTRY), VAL_LONG("hw-rev", HWCOMP_ID_HW_REV), VAL_LONG("born-on-date", HWCOMP_ID_BORN_ON_DATE), VAL_LONG("vendor", HWCOMP_ID_VENDOR), VAL_END() }; NVME_ARGS(opts, OPT_LONG("comp-id", 'i', &cfg.id, id_desc, id), OPT_FLAG("list", 'l', &cfg.list, list_desc)); ret = parse_and_open(&dev, argc, argv, desc, opts); if (ret) return ret; ret = get_hwcomp_log(dev, cfg.id, cfg.list); if (ret) fprintf(stderr, "error: ocp: failed to get hwcomp log: %02X, ret: %d\n", OCP_LID_HWCOMP, ret); return ret; }