From 86524bd4ef8a289c1e99661368d55e0ec48d41df Mon Sep 17 00:00:00 2001 From: Jeff Lien Date: Fri, 13 Sep 2019 13:59:09 -0500 Subject: [PATCH] [NVMe-CLI] Add new WDC plugin command vs-fw-activate-history. --- plugins/wdc/wdc-nvme.c | 297 ++++++++++++++++++++++++++++++++++++++++- plugins/wdc/wdc-nvme.h | 1 + 2 files changed, 295 insertions(+), 3 deletions(-) diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index 78c4f7b..dd5393c 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -89,7 +89,7 @@ #define WDC_DRIVE_CAP_DRIVE_LOG 0x0000000000000400 #define WDC_DRIVE_CAP_CRASH_DUMP 0x0000000000000800 #define WDC_DRIVE_CAP_PFAIL_DUMP 0x0000000000001000 - +#define WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY 0x0000000000002000 #define WDC_DRIVE_CAP_DRIVE_ESSENTIALS 0x0000000100000000 #define WDC_DRIVE_CAP_DUI_DATA 0x0000000200000000 @@ -222,6 +222,10 @@ #define WDC_NVME_GET_EOL_STATUS_LOG_OPCODE 0xC0 #define WDC_NVME_EOL_STATUS_LOG_LEN 0x200 +/* CB - FW Activate History Log Page */ +#define WDC_NVME_GET_FW_ACT_HISTORY_LOG_ID 0xCB +#define WDC_FW_ACT_HISTORY_LOG_BUF_LEN 0x3d0 + /* D0 Smart Log Page */ #define WDC_NVME_GET_VU_SMART_LOG_OPCODE 0xD0 #define WDC_NVME_VU_SMART_LOG_LEN 0x200 @@ -607,6 +611,28 @@ struct __attribute__((__packed__)) wdc_nand_stats { __u8 rsvd[460]; }; +struct wdc_fw_act_history_log_hdr { + __le32 eye_catcher; + __u8 version; + __u8 reserved1; + __u8 num_entries; + __u8 reserved2; + __le32 entry_size; + __le32 reserved3; +}; + +struct wdc_fw_act_history_log_entry { + __le32 entry_num; + __le32 power_cycle_count; + __le64 power_on_seconds; + __le64 current_fw_version; + __le64 new_fw_version; + __u8 slot_number; + __u8 commit_action_type; + __le16 result; + __u8 reserved[12]; +}; + static double safe_div_fp(double numerator, double denominator) { return denominator ? numerator / denominator : 0; @@ -778,7 +804,8 @@ static __u64 wdc_get_drive_capabilities(int fd) { case WDC_NVME_SN840_DEV_ID_1: capabilities = (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG | WDC_DRIVE_CAP_DRIVE_STATUS | WDC_DRIVE_CAP_CLEAR_ASSERT | - WDC_DRIVE_CAP_RESIZE | WDC_DRIVE_CAP_CLEAR_PCIE); + WDC_DRIVE_CAP_RESIZE | WDC_DRIVE_CAP_CLEAR_PCIE | + WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY); /* verify the 0xCA log page is supported */ if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE) == true) @@ -1884,7 +1911,7 @@ static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command OPT_FILE("output-file", 'o', &cfg.file, file), OPT_UINT("transfer-size", 's', &cfg.xfer_size, size), OPT_UINT("data-area", 'd', &cfg.data_area, data_area), - OPT_UINT("file-size", 'f', &cfg.file-size, file-size), + OPT_UINT("file-size", 'f', &cfg.file_size, file_size), OPT_LONG("offset", 't', &cfg.offset, offset), OPT_FLAG("verbose", 'v', &cfg.verbose, verbose), OPT_END() @@ -2710,6 +2737,155 @@ static void wdc_print_d0_log_json(struct wdc_ssd_d0_smart_log *perf) json_free_object(root); } +static void wdc_get_commit_action_bin(__u8 commit_action_type, char *action_bin) +{ + + switch (commit_action_type) + { + case(0): + strcpy(action_bin, "000b"); + break; + case(1): + strcpy(action_bin, "001b"); + break; + case(2): + strcpy(action_bin, "010b"); + break; + case(3): + strcpy(action_bin, "011b"); + break; + case(4): + strcpy(action_bin, "100b"); + break; + case(5): + strcpy(action_bin, "101b"); + break; + case(6): + strcpy(action_bin, "110b"); + break; + case(7): + strcpy(action_bin, "111b"); + break; + default: + strcpy(action_bin, "INVALID"); + } + +} + +static void wdc_print_fw_act_history_log_normal(struct wdc_fw_act_history_log_entry *fw_act_history_entry, + int num_entries) +{ + int i; + char current_fw[9]; + char new_fw[9]; + char commit_action_bin[8]; + memset((void *)current_fw, 0, 9); + memset((void *)new_fw, 0, 9); + memset((void *)commit_action_bin, 0, 8); + char *null_fw = "--------"; + + + printf(" Firmware Activate History Log \n"); + printf(" Power on Hour Power Cycle Current New \n"); + printf(" Entry hh:mm:ss Count Firmware Firmware Slot Action Result \n"); + printf(" ----- -------------- ------------ ---------- ---------- ----- ------ -------\n"); + + for (i = 0; i < num_entries; i++) { + memcpy(current_fw, (char *)&(fw_act_history_entry->current_fw_version), 8); + if (strlen((char *)&(fw_act_history_entry->new_fw_version)) > 1) + memcpy(new_fw, (char *)&(fw_act_history_entry->new_fw_version), 8); + else + memcpy(new_fw, null_fw, 8); + + printf("%5"PRIu32"", (uint32_t)le32_to_cpu(fw_act_history_entry->entry_num)); + printf(" "); + printf("%02d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)/3600), + (int)((le64_to_cpu(fw_act_history_entry->power_on_seconds)%3600)/60), + (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)%60)); + printf(" "); + printf("%8"PRIu32"", (uint32_t)le32_to_cpu(fw_act_history_entry->power_cycle_count)); + printf(" "); + printf("%s", (char *)current_fw); + printf(" "); + printf("%s", (char *)new_fw); + printf(" "); + printf("%2"PRIu8"", (uint8_t)fw_act_history_entry->slot_number); + printf(" "); + wdc_get_commit_action_bin(fw_act_history_entry->commit_action_type,(char *)&commit_action_bin); + printf(" %s", (char *)commit_action_bin); + printf(" "); + if (le16_to_cpu(fw_act_history_entry->result) == 0) + printf("pass"); + else + printf("fail #%d", (uint16_t)le16_to_cpu(fw_act_history_entry->result)); + + printf("\n"); + + fw_act_history_entry++; + } +} + +static void wdc_print_fw_act_history_log_json(struct wdc_fw_act_history_log_entry *fw_act_history_entry, + int num_entries) +{ + struct json_object *root; + int i; + char current_fw[9]; + char new_fw[9]; + char commit_action_bin[8]; + char fail_str[32]; + char time_str[9]; + memset((void *)current_fw, 0, 9); + memset((void *)new_fw, 0, 9); + memset((void *)commit_action_bin, 0, 8); + memset((void *)time_str, 0, 9); + memset((void *)fail_str, 0, 11); + char *null_fw = "--------"; + + root = json_create_object(); + + for (i = 0; i < num_entries; i++) { + memcpy(current_fw, (char *)&(fw_act_history_entry->current_fw_version), 8); + if (strlen((char *)&(fw_act_history_entry->new_fw_version)) > 1) + memcpy(new_fw, (char *)&(fw_act_history_entry->new_fw_version), 8); + else + memcpy(new_fw, null_fw, 8); + + json_object_add_value_int(root, "Entry", + le32_to_cpu(fw_act_history_entry->entry_num)); + + sprintf((char *)time_str, "%02d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)/3600), + (int)((le64_to_cpu(fw_act_history_entry->power_on_seconds)%3600)/60), + (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)%60)); + json_object_add_value_string(root, "Power on Hour", time_str); + + json_object_add_value_int(root, "Power Cycle Count", + le32_to_cpu(fw_act_history_entry->power_cycle_count)); + json_object_add_value_string(root, "Current Firmware", + current_fw); + json_object_add_value_string(root, "New Firmware", + new_fw); + json_object_add_value_int(root, "Slot", + fw_act_history_entry->slot_number); + + wdc_get_commit_action_bin(fw_act_history_entry->commit_action_type,(char *)&commit_action_bin); + json_object_add_value_string(root, "Action", commit_action_bin); + + if (le16_to_cpu(fw_act_history_entry->result) == 0) + json_object_add_value_string(root, "Result", "pass"); + else { + sprintf((char *)fail_str, "fail #%d", (int)(le16_to_cpu(fw_act_history_entry->result))); + json_object_add_value_string(root, "Result", fail_str); + } + + fw_act_history_entry++; + } + + json_print_object(root, NULL); + printf("\n"); + + json_free_object(root); +} static int wdc_print_ca_log(struct wdc_ssd_ca_perf_stats *perf, int fmt) { @@ -2745,6 +2921,26 @@ static int wdc_print_d0_log(struct wdc_ssd_d0_smart_log *perf, int fmt) return 0; } +static int wdc_print_fw_act_history_log(struct wdc_fw_act_history_log_entry *fw_act_history_entries, + int num_entries, + int fmt) +{ + if (!fw_act_history_entries) { + fprintf(stderr, "ERROR : WDC : Invalid buffer to read fw activate history entries\n"); + return -1; + } + + switch (fmt) { + case NORMAL: + wdc_print_fw_act_history_log_normal(fw_act_history_entries, num_entries); + break; + case JSON: + wdc_print_fw_act_history_log_json(fw_act_history_entries, num_entries); + break; + } + return 0; +} + static int wdc_get_ca_log_page(int fd, char *format) { int ret = 0; @@ -3152,6 +3348,101 @@ out: return ret; } +static int wdc_get_fw_act_history(int fd, char *format) +{ + int ret = 0; + int fmt = -1; + __u8 *data; + struct wdc_fw_act_history_log_hdr *fw_act_history_hdr; + struct wdc_fw_act_history_log_entry *fw_act_history_entry; + + if (!wdc_check_device(fd)) + return -1; + + fmt = validate_output_format(format); + if (fmt < 0) { + fprintf(stderr, "ERROR : WDC : invalid output format\n"); + return fmt; + } + + /* verify the FW Activate History log page is supported */ + if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_FW_ACT_HISTORY_LOG_ID) == false) { + fprintf(stderr, "ERROR : WDC : %d Log Page not supported\n", WDC_NVME_GET_FW_ACT_HISTORY_LOG_ID); + return -1; + } + + if ((data = (__u8*) malloc(sizeof (__u8) * WDC_FW_ACT_HISTORY_LOG_BUF_LEN)) == NULL) { + fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno)); + return -1; + } + + memset(data, 0, sizeof (__u8) * WDC_FW_ACT_HISTORY_LOG_BUF_LEN); + + ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_FW_ACT_HISTORY_LOG_ID, + false, WDC_FW_ACT_HISTORY_LOG_BUF_LEN, data); + + if (strcmp(format, "json")) + fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret); + + if (ret == 0) { + /* parse the data */ + fw_act_history_hdr = (struct wdc_fw_act_history_log_hdr *)(data); + fw_act_history_entry = (struct wdc_fw_act_history_log_entry *)(data + sizeof(struct wdc_fw_act_history_log_hdr)); + + if (fw_act_history_hdr->num_entries > 0) + ret = wdc_print_fw_act_history_log(fw_act_history_entry, fw_act_history_hdr->num_entries, fmt); + else + fprintf(stderr, "INFO : WDC : No entries found in FW Activate History Log Page\n"); + } else { + fprintf(stderr, "ERROR : WDC : Unable to read FW Activate History Log Page data\n"); + ret = -1; + } + + free(data); + return ret; +} + +static int wdc_vs_fw_activate_history(int argc, char **argv, struct command *command, + struct plugin *plugin) +{ + int fd; + int ret = 0; + __u64 capabilities = 0; + const char *desc = "Retrieve FW activate history table."; + + + struct config { + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts, NULL, 0); + + if (fd < 0) + return fd; + + capabilities = wdc_get_drive_capabilities(fd); + + if ((capabilities & WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY) == WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY) { + ret = wdc_get_fw_act_history(fd, cfg.output_format); + if (ret) + fprintf(stderr, "ERROR : WDC : Failure reading the FW Activate History, ret = %d\n", ret); + } else { + fprintf(stderr, "ERROR : WDC: unsupported device for this command\n"); + ret = -1; + } + + return ret; +} + static int wdc_get_serial_and_fw_rev(int fd, char *sn, char *fw_rev) { int i; diff --git a/plugins/wdc/wdc-nvme.h b/plugins/wdc/wdc-nvme.h index 85329cc..5dce4b1 100644 --- a/plugins/wdc/wdc-nvme.h +++ b/plugins/wdc/wdc-nvme.h @@ -23,6 +23,7 @@ PLUGIN(NAME("wdc", "Western Digital vendor specific extensions"), ENTRY("get-drive-status", "WDC Get Drive Status", wdc_drive_status) ENTRY("clear-assert-dump", "WDC Clear Assert Dump", wdc_clear_assert_dump) ENTRY("drive-resize", "WDC Drive Resize", wdc_drive_resize) + ENTRY("vs-fw-activate-history", "WDC Get FW Activate History", wdc_vs_fw_activate_history) ) ); -- 2.49.0