]> www.infradead.org Git - users/sagi/nvme-cli.git/commitdiff
[NVMe-CLI] Add support for vs-fw-activate-history and clear-fw-activate-history
authorJeff Lien <jeff.lien@wdc.com>
Mon, 20 Jul 2020 15:45:58 +0000 (10:45 -0500)
committerKeith Busch <kbusch@kernel.org>
Fri, 31 Jul 2020 16:06:33 +0000 (10:06 -0600)
      in wdc plugins for SN345

plugins/wdc/wdc-nvme.c

index 8534cedb53a56edc8242075d968aa95d03d1cb27..8dc2752c4ab7374ed00a775f52e169b0799be392 100644 (file)
 #define WDC_DRIVE_CAP_TEMP_STATS            0x0000000000200000
 #define WDC_DRIVE_CAP_VUC_CLEAR_PCIE        0x0000000000400000
 #define WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE     0x0000000000800000
+#define WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY_C2        0x0000000001000000
+#define WDC_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY      0x0000000002000000
 
 #define WDC_DRIVE_CAP_DRIVE_ESSENTIALS      0x0000000100000000
 #define WDC_DRIVE_CAP_DUI_DATA                         0x0000000200000000
 #define WDC_NVME_CLEAR_FW_ACT_HIST_OPCODE       0xC6
 #define WDC_NVME_CLEAR_FW_ACT_HIST_CMD                 0x23
 #define WDC_NVME_CLEAR_FW_ACT_HIST_SUBCMD              0x05
+#define WDC_NVME_CLEAR_FW_ACT_HIST_VU_FID              0xC1
 
 /* Additional Smart Log */
 #define WDC_ADD_LOG_BUF_LEN                            0x4000
 #define WDC_NVME_GET_FW_ACT_HISTORY_LOG_ID      0xCB
 #define WDC_FW_ACT_HISTORY_LOG_BUF_LEN          0x3d0
 
+/* C2 - FW Activation History Log Page */
+#define WDC_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID   0xC2
+#define WDC_FW_ACT_HISTORY_C2_LOG_BUF_LEN       0x1000
+
 /* D0 Smart Log Page */
 #define WDC_NVME_GET_VU_SMART_LOG_OPCODE               0xD0
 #define WDC_NVME_VU_SMART_LOG_LEN                              0x200
@@ -843,6 +850,32 @@ struct wdc_fw_act_history_log_entry {
        __u8        reserved[12];
 };
 
+struct wdc_fw_act_history_log_entry_c2 {
+       __u8            entry_version_num;
+       __u8            entry_len;
+       __le16          reserved;
+       __le16          fw_act_hist_entries;
+       __le64          timestamp;
+       __u8            reserved2[8];
+       __le64          power_cycle_count;
+       __le64          previous_fw_version;
+       __le64          current_fw_version;
+       __u8            slot_number;
+       __u8            commit_action_type;
+       __le16          result;
+       __u8            reserved3[14];
+};
+
+struct wdc_fw_act_history_log_format_c2 {
+       __u8            log_identifier;
+       __u8            reserved[3];
+       __le32          num_entries;
+       struct          wdc_fw_act_history_log_entry_c2 entry[20];
+       __u8            reserved2[2790];
+       __le16          log_page_version;
+       __u8            log_page_guid[16];
+};
+
 #define WDC_REASON_INDEX_MAX                    16
 #define WDC_REASON_ID_ENTRY_LEN                128
 #define WDC_REASON_ID_PATH_NAME                "/usr/local/nvmecli"
@@ -1090,7 +1123,8 @@ static __u64 wdc_get_drive_capabilities(int fd) {
                case WDC_NVME_ZN345_DEV_ID:
                /* FALLTHRU */
                case WDC_NVME_ZN345_DEV_ID_1:
-                       capabilities = WDC_DRIVE_CAP_DUI_DATA | WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE | WDC_DRIVE_CAP_C0_LOG_PAGE;
+                       capabilities = WDC_DRIVE_CAP_DUI_DATA | WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE | WDC_DRIVE_CAP_C0_LOG_PAGE |
+                               WDC_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY | WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY_C2;
                        break;
                default:
                        capabilities = 0;
@@ -3661,8 +3695,7 @@ static void wdc_get_commit_action_bin(__u8 commit_action_type, char *action_bin)
 
 }
 
-static void wdc_print_fw_act_history_log_normal(struct wdc_fw_act_history_log_entry *fw_act_history_entry,
-               int num_entries)
+static void wdc_print_fw_act_history_log_normal(__u8 *data, int num_entries)
 {
        int i;
        char previous_fw[9];
@@ -3673,49 +3706,87 @@ static void wdc_print_fw_act_history_log_normal(struct wdc_fw_act_history_log_en
        memset((void *)commit_action_bin, 0, 8);
        char *null_fw = "--------";
 
-
        printf("  Firmware Activate History Log \n");
-       printf("         Power on Hour   Power Cycle   Previous    New                               \n");
-       printf("  Entry    hh:mm:ss      Count         Firmware    Firmware    Slot   Action  Result \n");
-       printf("  -----  --------------  ------------  ----------  ----------  -----  ------  -------\n");
-
-       for (i = 0; i < num_entries; i++) {
-               memcpy(previous_fw, (char *)&(fw_act_history_entry->previous_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 *)previous_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++;
+       printf("         Power on Hour   Power Cycle           Previous    New                               \n");
+       printf("  Entry    hh:mm:ss      Count                 Firmware    Firmware    Slot   Action  Result \n");
+       printf("  -----  --------------  --------------------  ----------  ----------  -----  ------  -------\n");
+
+       if(data[0] == WDC_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID) {
+               struct wdc_fw_act_history_log_format_c2 *fw_act_history_entry = (struct wdc_fw_act_history_log_format_c2 *)(data);
+
+               for (i = 0; i < num_entries; i++) {
+                       memcpy(previous_fw, (char *)&(fw_act_history_entry->entry[i].previous_fw_version), 8);
+                       if (strlen((char *)&(fw_act_history_entry->entry[i].current_fw_version)) > 1)
+                               memcpy(new_fw, (char *)&(fw_act_history_entry[i].entry->current_fw_version), 8);
+                       else
+                               memcpy(new_fw, null_fw, 8);
+
+                       printf("%5"PRIu16"", (uint16_t)le16_to_cpu(fw_act_history_entry->entry[i].fw_act_hist_entries));
+                       printf("       ");
+                       printf("%02d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry->entry[i].timestamp)/(1000*3600)),
+                                       (int)(((le64_to_cpu(fw_act_history_entry->entry[i].timestamp)/1000)%3600)/60),
+                                       (int)((le64_to_cpu(fw_act_history_entry->entry[i].timestamp)/1000)%60));
+                       printf("       ");
+                       printf("%16"PRIu64"", (uint64_t)le64_to_cpu(fw_act_history_entry->entry[i].power_cycle_count));
+                       printf("     ");
+                       printf("%s", (char *)previous_fw);
+                       printf("    ");
+                       printf("%s", (char *)new_fw);
+                       printf("     ");
+                       printf("%2"PRIu8"", (uint8_t)fw_act_history_entry->entry[i].slot_number);
+                       printf("  ");
+                       wdc_get_commit_action_bin(fw_act_history_entry->entry[i].commit_action_type,(char *)&commit_action_bin);
+                       printf("  %s", (char *)commit_action_bin);
+                       printf("   ");
+                       if (le16_to_cpu(fw_act_history_entry->entry[i].result) == 0)
+                               printf("pass");
+                       else
+                               printf("fail #%d", (uint16_t)le16_to_cpu(fw_act_history_entry->entry[i].result));
+
+                       printf("\n");
+               }
+       }
+       else
+       {
+               struct wdc_fw_act_history_log_entry *fw_act_history_entry = (struct wdc_fw_act_history_log_entry *)(data + sizeof(struct wdc_fw_act_history_log_hdr));
+
+               for (i = 0; i < num_entries; i++) {
+                       memcpy(previous_fw, (char *)&(fw_act_history_entry->previous_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("%16"PRIu32"", (uint32_t)le32_to_cpu(fw_act_history_entry->power_cycle_count));
+                       printf("     ");
+                       printf("%s", (char *)previous_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)
+static void wdc_print_fw_act_history_log_json(__u8 *data, int num_entries)
 {
        struct json_object *root;
        int i;
@@ -3733,41 +3804,83 @@ static void wdc_print_fw_act_history_log_json(struct wdc_fw_act_history_log_entr
 
        root = json_create_object();
 
-       for (i = 0; i < num_entries; i++) {
-               memcpy(previous_fw, (char *)&(fw_act_history_entry->previous_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, "Previous Firmware",
-                               previous_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);
+       if(data[0] == WDC_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID) {
+               struct wdc_fw_act_history_log_format_c2 *fw_act_history_entry = (struct wdc_fw_act_history_log_format_c2 *)(data);
+
+               for (i = 0; i < num_entries; i++) {
+                       memcpy(previous_fw, (char *)&(fw_act_history_entry->entry[i].previous_fw_version), 8);
+                       if (strlen((char *)&(fw_act_history_entry->entry[i].current_fw_version)) > 1)
+                           memcpy(new_fw, (char *)&(fw_act_history_entry->entry[i].current_fw_version), 8);
+                       else
+                               memcpy(new_fw, null_fw, 8);
+
+                       json_object_add_value_int(root, "Entry",
+                               le16_to_cpu(fw_act_history_entry->entry[i].fw_act_hist_entries));
+
+                       sprintf((char *)time_str, "%02d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry->entry[i].timestamp)/(1000*3600)),
+                                       (int)(((le64_to_cpu(fw_act_history_entry->entry[i].timestamp)/1000)%3600)/60),
+                                       (int)((le64_to_cpu(fw_act_history_entry->entry[i].timestamp)/1000)%60));
+                       json_object_add_value_string(root, "Power on Hour", time_str);
+
+                       json_object_add_value_int(root, "Power Cycle Count",
+                               le64_to_cpu(fw_act_history_entry->entry[i].power_cycle_count));
+                       json_object_add_value_string(root, "Previous Firmware",
+                                       previous_fw);
+                       json_object_add_value_string(root, "New Firmware",
+                                       new_fw);
+                       json_object_add_value_int(root, "Slot",
+                               fw_act_history_entry->entry[i].slot_number);
+
+                       wdc_get_commit_action_bin(fw_act_history_entry->entry[i].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->entry[i].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->entry[i].result)));
+                               json_object_add_value_string(root, "Result", fail_str);
+                       }
                }
+       }
+       else {
+               struct wdc_fw_act_history_log_entry *fw_act_history_entry = (struct wdc_fw_act_history_log_entry *)(data + sizeof(struct wdc_fw_act_history_log_hdr));
+
+               for (i = 0; i < num_entries; i++) {
+                       memcpy(previous_fw, (char *)&(fw_act_history_entry->previous_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, "Previous Firmware",
+                                       previous_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++;
+                   fw_act_history_entry++;
+               }
        }
 
        json_print_object(root, NULL);
@@ -4216,21 +4329,19 @@ 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)
+static int wdc_print_fw_act_history_log(__u8 *data, int num_entries, int fmt)
 {
-       if (!fw_act_history_entries) {
+       if (!data) {
                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);
+               wdc_print_fw_act_history_log_normal(data, num_entries);
                break;
        case JSON:
-               wdc_print_fw_act_history_log_json(fw_act_history_entries, num_entries);
+               wdc_print_fw_act_history_log_json(data, num_entries);
                break;
        }
        return 0;
@@ -4805,7 +4916,6 @@ static int wdc_get_fw_act_history(int fd, char *format)
        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;
@@ -4838,10 +4948,57 @@ static int wdc_get_fw_act_history(int fd, char *format)
        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);
+                       ret = wdc_print_fw_act_history_log(data, 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_get_fw_act_history_C2(int fd, char *format)
+{
+       int ret = 0;
+       int fmt = -1;
+       __u8 *data;
+       struct wdc_fw_act_history_log_format_c2 *fw_act_history_log;
+       __u32 num_entries = 0;
+
+       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;
+       }
+
+       if ((data = (__u8*) malloc(sizeof (__u8) * WDC_FW_ACT_HISTORY_C2_LOG_BUF_LEN)) == NULL) {
+               fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+               return -1;
+       }
+
+       memset(data, 0, sizeof (__u8) * WDC_FW_ACT_HISTORY_C2_LOG_BUF_LEN);
+
+       ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID,
+                          false, WDC_FW_ACT_HISTORY_C2_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_log = (struct wdc_fw_act_history_log_format_c2*)(data);
+               num_entries = le32_to_cpu(fw_act_history_log->num_entries);
+
+               if (num_entries > 0)
+                       ret = wdc_print_fw_act_history_log(data, num_entries, fmt);
                else
                        fprintf(stderr, "INFO : WDC : No entries found in FW Activate History Log Page\n");
        } else {
@@ -4881,16 +5038,50 @@ static int wdc_vs_fw_activate_history(int argc, char **argv, struct command *com
                return fd;
 
        capabilities = wdc_get_drive_capabilities(fd);
+       if ((capabilities & (WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY | WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY_C2)) == 0) {
+               fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+               ret = -1;
+               goto out;
+       }
 
-       if ((capabilities & WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY) == WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY) {
+       if (capabilities & 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;
+               ret = wdc_get_fw_act_history_C2(fd, cfg.output_format);
        }
 
+       if (ret)
+               fprintf(stderr, "ERROR : WDC : Failure reading the FW Activate History, ret = %d\n", ret);
+out:
+       return ret;
+}
+
+static int wdc_do_clear_fw_activate_history_vuc(int fd)
+{
+       int ret = -1;
+       struct nvme_passthru_cmd admin_cmd;
+
+       memset(&admin_cmd, 0, sizeof (admin_cmd));
+       admin_cmd.opcode = WDC_NVME_CLEAR_FW_ACT_HIST_OPCODE;
+       admin_cmd.cdw12 = ((WDC_NVME_CLEAR_FW_ACT_HIST_SUBCMD << WDC_NVME_SUBCMD_SHIFT) |
+                       WDC_NVME_CLEAR_FW_ACT_HIST_CMD);
+
+       ret = nvme_submit_admin_passthru(fd, &admin_cmd);
+       fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
+
+       return ret;
+}
+
+static int wdc_do_clear_fw_activate_history_fid(int fd)
+{
+       int ret = -1;
+       __u32 result;
+       __u32 value = 1 << 31; /* Bit 31 - Clear Firmware Update History Log */
+
+       ret = nvme_set_feature(fd, 0, WDC_NVME_CLEAR_FW_ACT_HIST_VU_FID, value,
+                               0, 0, 0, NULL, &result);
+
+       fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
        return ret;
 }
 
@@ -4901,7 +5092,6 @@ static int wdc_clear_fw_activate_history(int argc, char **argv, struct command *
        int fd;
        int ret = -1;
        __u64 capabilities = 0;
-       struct nvme_passthru_cmd admin_cmd;
 
        OPT_ARGS(opts) = {
                OPT_END()
@@ -4912,19 +5102,18 @@ static int wdc_clear_fw_activate_history(int argc, char **argv, struct command *
                return fd;
 
        capabilities = wdc_get_drive_capabilities(fd);
-       if ((capabilities & WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY) != WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY) {
+       if ((capabilities & (WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY | WDC_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY)) == 0) {
                fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
                ret = -1;
                goto out;
        }
 
-       memset(&admin_cmd, 0, sizeof (admin_cmd));
-       admin_cmd.opcode = WDC_NVME_CLEAR_FW_ACT_HIST_OPCODE;
-       admin_cmd.cdw12 = ((WDC_NVME_CLEAR_FW_ACT_HIST_SUBCMD << WDC_NVME_SUBCMD_SHIFT) |
-                       WDC_NVME_CLEAR_FW_ACT_HIST_CMD);
-
-       ret = nvme_submit_admin_passthru(fd, &admin_cmd);
-       fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
+       if (capabilities & WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY) {
+               ret = wdc_do_clear_fw_activate_history_vuc(fd);
+       }
+       else {
+               ret = wdc_do_clear_fw_activate_history_fid(fd);
+       }
 
 out:
        return ret;