]> www.infradead.org Git - users/sagi/nvme-cli.git/commitdiff
plugins/ocp: Add firmware activation history log to OCP plugin
authorkdedow <karl.dedow@solidigmtechnology.com>
Thu, 30 Mar 2023 16:27:26 +0000 (09:27 -0700)
committerDaniel Wagner <dwagner@suse.de>
Wed, 12 Apr 2023 12:37:17 +0000 (14:37 +0200)
plugins/ocp/meson.build
plugins/ocp/ocp-fw-activation-history.c [new file with mode: 0644]
plugins/ocp/ocp-fw-activation-history.h [new file with mode: 0644]
plugins/ocp/ocp-nvme.c
plugins/ocp/ocp-nvme.h

index 641239a251fc915558cdf4983d9683af60a1da0f..405ee512f31c5bbd3106f13a5e7f41bf99e88762 100644 (file)
@@ -3,5 +3,6 @@ sources += [
   'plugins/ocp/ocp-nvme.c',
   'plugins/ocp/ocp-clear-fw-update-history.c',
   'plugins/ocp/ocp-smart-extended-log.c',
+  'plugins/ocp/ocp-fw-activation-history.c',
 ]
 
diff --git a/plugins/ocp/ocp-fw-activation-history.c b/plugins/ocp/ocp-fw-activation-history.c
new file mode 100644 (file)
index 0000000..c2d0692
--- /dev/null
@@ -0,0 +1,223 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: karl.dedow@solidigm.com
+ */
+
+#include "ocp-fw-activation-history.h"
+
+#include <errno.h>
+#include <stdio.h>
+
+#include "common.h"
+#include "nvme-print.h"
+
+#include "ocp-utils.h"
+
+static const unsigned char ocp_fw_activation_history_guid[16] = {
+       0x6D, 0x79, 0x9a, 0x76,
+       0xb4, 0xda, 0xf6, 0xa3,
+       0xe2, 0x4d, 0xb2, 0x8a,
+       0xac, 0xf3, 0x1c, 0xd1
+};
+
+struct __attribute__ ((packed)) fw_activation_history_entry {
+       __u8 ver_num;
+       __u8 entry_length;
+       __u16 reserved1;
+       __u16 activation_count;
+       __u64 timestamp;
+       __u64 reserved2;
+       __u64 power_cycle_count;
+       char previous_fw[8];
+       char new_fw[8];
+       __u8 slot_number;
+       __u8 commit_action;
+       __u16 result;
+       __u8 reserved3[14];
+};
+
+struct __attribute__ ((packed)) fw_activation_history {
+       __u8 log_id;
+       __u8 reserved1[3];
+       __u32 valid_entries;
+       struct fw_activation_history_entry entries[20];
+       __u8 reserved2[2790];
+       __u16 log_page_version;
+       __u64 log_page_guid[2];
+};
+
+static void ocp_fw_activation_history_normal(const struct fw_activation_history *fw_history)
+{
+       printf("Firmware History Log:\n");
+
+       printf("  %-26s%d\n", "log identifier:", fw_history->log_id);
+       printf("  %-26s%d\n", "valid entries:", le32_to_cpu(fw_history->valid_entries));
+
+       printf("  entries:\n");
+
+       for (int index = 0; index < fw_history->valid_entries; index++) {
+               const struct fw_activation_history_entry *entry = &fw_history->entries[index];
+
+               printf("    entry[%d]:\n", le32_to_cpu(index));
+               printf("      %-22s%d\n", "version number:", entry->ver_num);
+               printf("      %-22s%d\n", "entry length:", entry->entry_length);
+               printf("      %-22s%d\n", "activation count:",
+                      le16_to_cpu(entry->activation_count));
+               printf("      %-22s%"PRIu64"\n", "timestamp:",
+                      le64_to_cpu(entry->timestamp));
+               printf("      %-22s%"PRIu64"\n", "power cycle count:",
+                      le64_to_cpu(entry->power_cycle_count));
+               printf("      %-22s%.*s\n", "previous firmware:", (int)sizeof(entry->previous_fw),
+                      entry->previous_fw);
+               printf("      %-22s%.*s\n", "new firmware:", (int)sizeof(entry->new_fw),
+                      entry->new_fw);
+               printf("      %-22s%d\n", "slot number:", entry->slot_number);
+               printf("      %-22s%d\n", "commit action type:", entry->commit_action);
+               printf("      %-22s%d\n", "result:",  le16_to_cpu(entry->result));
+       }
+
+       printf("  %-26s%d\n", "log page version:",
+              le16_to_cpu(fw_history->log_page_version));
+
+       printf("  %-26s0x%"PRIx64"%"PRIx64"\n", "log page guid:",
+              le64_to_cpu(fw_history->log_page_guid[1]),
+              le64_to_cpu(fw_history->log_page_guid[0]));
+
+       printf("\n");
+}
+
+static void ocp_fw_activation_history_json(const struct fw_activation_history *fw_history)
+{
+       struct json_object *root = json_create_object();
+
+       json_object_add_value_uint(root, "log identifier", fw_history->log_id);
+       json_object_add_value_uint(root, "valid entries", le32_to_cpu(fw_history->valid_entries));
+
+       struct json_object *entries = json_create_array();
+
+       for (int index = 0; index < fw_history->valid_entries; index++) {
+               const struct fw_activation_history_entry *entry = &fw_history->entries[index];
+               struct json_object *entry_obj = json_create_object();
+
+               json_object_add_value_uint(entry_obj, "version number", entry->ver_num);
+               json_object_add_value_uint(entry_obj, "entry length", entry->entry_length);
+               json_object_add_value_uint(entry_obj, "activation count",
+                                          le16_to_cpu(entry->activation_count));
+               json_object_add_value_uint64(entry_obj, "timestamp",
+                                            le64_to_cpu(entry->timestamp));
+               json_object_add_value_uint(entry_obj, "power cycle count",
+                                          le64_to_cpu(entry->power_cycle_count));
+
+               struct json_object *fw = json_object_new_string_len(entry->previous_fw,
+                                                                   sizeof(entry->previous_fw));
+
+               json_object_add_value_object(entry_obj, "previous firmware", fw);
+
+               fw = json_object_new_string_len(entry->new_fw, sizeof(entry->new_fw));
+
+               json_object_add_value_object(entry_obj, "new firmware", fw);
+               json_object_add_value_uint(entry_obj, "slot number", entry->slot_number);
+               json_object_add_value_uint(entry_obj, "commit action type", entry->commit_action);
+               json_object_add_value_uint(entry_obj, "result", le16_to_cpu(entry->result));
+
+               json_array_add_value_object(entries, entry_obj);
+       }
+
+       json_object_add_value_array(root, "entries", entries);
+
+       json_object_add_value_uint(root, "log page version",
+                                  le16_to_cpu(fw_history->log_page_version));
+
+       char guid[2 * sizeof(fw_history->log_page_guid) + 3] = { 0 };
+
+       sprintf(guid, "0x%"PRIx64"%"PRIx64"",
+               le64_to_cpu(fw_history->log_page_guid[1]),
+               le64_to_cpu(fw_history->log_page_guid[0]));
+       json_object_add_value_string(root, "log page guid", guid);
+
+       json_print_object(root, NULL);
+       json_free_object(root);
+
+       printf("\n");
+}
+
+int ocp_fw_activation_history_log(int argc, char **argv, struct command *cmd,
+                                 struct plugin *plugin)
+{
+       const __u8 log_id = 0xC2;
+       const char *description = "Retrieves the OCP firmware activation history log.";
+
+       char *format = "normal";
+
+       OPT_ARGS(options) = {
+               OPT_FMT("output-format", 'o', &format, "output format : normal | json"),
+               OPT_END()
+       };
+
+       struct nvme_dev *dev = NULL;
+       int err = parse_and_open(&dev, argc, argv, description, options);
+
+       if (err)
+               return err;
+
+       int uuid_index = 0;
+
+       /*
+        * Best effort attempt at uuid. Otherwise, assume no index (i.e. 0)
+        * Log GUID check will ensure correctness of returned data
+        */
+       ocp_get_uuid_index(dev, &uuid_index);
+
+       struct fw_activation_history fw_history = { 0 };
+
+       struct nvme_get_log_args args = {
+               .lpo = 0,
+               .result = NULL,
+               .log = &fw_history,
+               .args_size = sizeof(args),
+               .fd = dev_fd(dev),
+               .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+               .lid = log_id,
+               .len = sizeof(fw_history),
+               .nsid = NVME_NSID_ALL,
+               .csi = NVME_CSI_NVM,
+               .lsi = NVME_LOG_LSI_NONE,
+               .lsp = 0,
+               .uuidx = uuid_index,
+               .rae = false,
+               .ot = false,
+       };
+
+       err = nvme_get_log(&args);
+
+       if (err)
+               nvme_show_status(err);
+
+       dev_close(dev);
+
+       int guid_cmp_res = memcmp(fw_history.log_page_guid, ocp_fw_activation_history_guid,
+                                 sizeof(ocp_fw_activation_history_guid));
+
+       if (!err && guid_cmp_res) {
+               fprintf(stderr,
+                       "Error: Unexpected data. Log page guid does not match with expected.\n");
+               err = -EINVAL;
+       }
+
+       if (!err) {
+               const enum nvme_print_flags print_flag = validate_output_format(format);
+
+               if (print_flag == JSON)
+                       ocp_fw_activation_history_json(&fw_history);
+               else if (print_flag == NORMAL)
+                       ocp_fw_activation_history_normal(&fw_history);
+               else {
+                       fprintf(stderr, "Error: Invalid output format.\n");
+                       err = -EINVAL;
+               }
+       }
+
+       return err;
+}
diff --git a/plugins/ocp/ocp-fw-activation-history.h b/plugins/ocp/ocp-fw-activation-history.h
new file mode 100644 (file)
index 0000000..a7f9058
--- /dev/null
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Authors: karl.dedow@solidigm.com
+ */
+
+#ifndef OCP_FIRMWARE_ACTIVATION_HISTORY_H
+#define OCP_FIRMWARE_ACTIVATION_HISTORY_H
+
+struct command;
+struct plugin;
+
+int ocp_fw_activation_history_log(int argc, char **argv,
+       struct command *cmd, struct plugin *plugin);
+
+#endif
index a864363b32417f9a8047e5e4ffb05841eea9ce0e..fac99bfeec72d8fec853a0b2e2a4e9b2a398d453 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "ocp-smart-extended-log.h"
 #include "ocp-clear-fw-update-history.h"
+#include "ocp-fw-activation-history.h"
 
 #define CREATE_CMD
 #include "ocp-nvme.h"
@@ -646,3 +647,9 @@ static int clear_pcie_corectable_error_counters(int argc, char **argv,
        return ocp_clear_feature(argc, argv, desc,
                                 OCP_FID_CLEAR_PCIE_CORRECTABLE_ERROR_COUNTERS);
 }
+
+static int fw_activation_history_log(int argc, char **argv, struct command *cmd,
+                                    struct plugin *plugin)
+{
+       return ocp_fw_activation_history_log(argc, argv, cmd, plugin);
+}
index dc9e154f45f849dae29870550e66a842d4ac23fe..d2abf19cfc4608fa4fa981319e4ead509ca75d2d 100644 (file)
@@ -20,6 +20,7 @@ PLUGIN(NAME("ocp", "OCP cloud SSD extensions", NVME_VERSION),
                ENTRY("clear-fw-activate-history", "Clear firmware update history log", clear_fw_update_history)
                ENTRY("eol-plp-failure-mode", "Define EOL or PLP circuitry failure mode.", eol_plp_failure_mode)
                ENTRY("clear-pcie-correctable-error-counters", "Clear PCIe correctable error counters", clear_pcie_corectable_error_counters)
+               ENTRY("vs-fw-activate-history", "Get firmware activation history log", fw_activation_history_log)
        )
 );