]> www.infradead.org Git - users/hch/nvme-cli.git/commitdiff
[NVMe-CLI] Add new WDC plugin command vs-fw-activate-history.
authorJeff Lien <jeff.lien@wdc.com>
Fri, 13 Sep 2019 18:59:09 +0000 (13:59 -0500)
committerJeff Lien <jeff.lien@wdc.com>
Fri, 13 Sep 2019 18:59:09 +0000 (13:59 -0500)
plugins/wdc/wdc-nvme.c
plugins/wdc/wdc-nvme.h

index 78c4f7bf27bd77de349a9f727c69d89eb26a5ed4..dd5393cb2d3b78ff73dfeca541bb2e794e8c3ed3 100644 (file)
@@ -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
 #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;
index 85329ccfd5a83f36541d5cb163ab9c0c040194c3..5dce4b1df84f84dc9748f122b36f953d04b2a4b9 100644 (file)
@@ -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)
        )
 );