summaryrefslogtreecommitdiffstats
path: root/plugins/ocp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 19:41:32 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 19:41:32 +0000
commitf26f66d866ba1a9f3204e6fdfe2b07e67b5492ad (patch)
treec953c007cbe4f60a147ab62f97937d58abb2e9ca /plugins/ocp
parentInitial commit. (diff)
downloadnvme-cli-upstream/2.8.tar.xz
nvme-cli-upstream/2.8.zip
Adding upstream version 2.8.upstream/2.8
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--plugins/ocp/meson.build8
-rw-r--r--plugins/ocp/ocp-clear-features.c93
-rw-r--r--plugins/ocp/ocp-clear-features.h12
-rw-r--r--plugins/ocp/ocp-fw-activation-history.c225
-rw-r--r--plugins/ocp/ocp-fw-activation-history.h17
-rw-r--r--plugins/ocp/ocp-nvme.c2971
-rw-r--r--plugins/ocp/ocp-nvme.h38
-rw-r--r--plugins/ocp/ocp-smart-extended-log.c352
-rw-r--r--plugins/ocp/ocp-smart-extended-log.h18
-rw-r--r--plugins/ocp/ocp-utils.c32
-rw-r--r--plugins/ocp/ocp-utils.h18
11 files changed, 3784 insertions, 0 deletions
diff --git a/plugins/ocp/meson.build b/plugins/ocp/meson.build
new file mode 100644
index 0000000..64447ff
--- /dev/null
+++ b/plugins/ocp/meson.build
@@ -0,0 +1,8 @@
+sources += [
+ 'plugins/ocp/ocp-utils.c',
+ 'plugins/ocp/ocp-nvme.c',
+ 'plugins/ocp/ocp-clear-features.c',
+ 'plugins/ocp/ocp-smart-extended-log.c',
+ 'plugins/ocp/ocp-fw-activation-history.c',
+]
+
diff --git a/plugins/ocp/ocp-clear-features.c b/plugins/ocp/ocp-clear-features.c
new file mode 100644
index 0000000..0f49584
--- /dev/null
+++ b/plugins/ocp/ocp-clear-features.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Authors: haro.panosyan@solidigm.com
+ * leonardo.da.cunha@solidigm.com
+ */
+
+#include <unistd.h>
+#include "ocp-utils.h"
+#include "nvme-print.h"
+
+static const __u8 OCP_FID_CLEAR_FW_ACTIVATION_HISTORY = 0xC1;
+static const __u8 OCP_FID_CLEAR_PCIE_CORRECTABLE_ERROR_COUNTERS = 0xC3;
+
+static int ocp_clear_feature(int argc, char **argv, const char *desc, const __u8 fid)
+{
+ __u32 result = 0;
+ __u32 clear = 1 << 31;
+ struct nvme_dev *dev;
+ int uuid_index = 0;
+ bool uuid = true;
+ int err;
+
+ OPT_ARGS(opts) = {
+ OPT_FLAG("no-uuid", 'n', NULL,
+ "Skip UUID index search (UUID index not required for OCP 1.0)"),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ if (opts[0].seen)
+ uuid = false;
+
+ if (uuid) {
+ /* OCP 2.0 requires UUID index support */
+ err = ocp_get_uuid_index(dev, &uuid_index);
+ if (err || !uuid_index) {
+ fprintf(stderr, "ERROR: No OCP UUID index found\n");
+ goto close_dev;
+ }
+ }
+
+ struct nvme_set_features_args args = {
+ .result = &result,
+ .data = NULL,
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .nsid = 0,
+ .cdw11 = clear,
+ .cdw12 = 0,
+ .cdw13 = 0,
+ .cdw15 = 0,
+ .data_len = 0,
+ .save = 0,
+ .uuidx = uuid_index,
+ .fid = fid,
+ };
+
+ err = nvme_set_features(&args);
+
+ if (err == 0)
+ printf("Success : %s\n", desc);
+ else if (err > 0)
+ nvme_show_status(err);
+ else
+ printf("Fail : %s\n", desc);
+close_dev:
+ /* Redundant close() to make static code analysis happy */
+ close(dev->direct.fd);
+ dev_close(dev);
+ return err;
+}
+
+int ocp_clear_fw_update_history(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "OCP Clear Firmware Update History";
+
+ return ocp_clear_feature(argc, argv, desc, OCP_FID_CLEAR_FW_ACTIVATION_HISTORY);
+}
+
+int ocp_clear_pcie_correctable_errors(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ const char *desc = "OCP Clear PCIe Correctable Error Counters";
+
+ return ocp_clear_feature(argc, argv, desc,
+ OCP_FID_CLEAR_PCIE_CORRECTABLE_ERROR_COUNTERS);
+}
diff --git a/plugins/ocp/ocp-clear-features.h b/plugins/ocp/ocp-clear-features.h
new file mode 100644
index 0000000..99766dd
--- /dev/null
+++ b/plugins/ocp/ocp-clear-features.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Authors: haro.panosyan@solidigm.com
+ * leonardo.da.cunha@solidigm.com
+ */
+
+int ocp_clear_fw_update_history(int argc, char **argv, struct command *cmd, struct plugin *plugin);
+
+int ocp_clear_pcie_correctable_errors(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin);
diff --git a/plugins/ocp/ocp-fw-activation-history.c b/plugins/ocp/ocp-fw-activation-history.c
new file mode 100644
index 0000000..ad96c6b
--- /dev/null
+++ b/plugins/ocp/ocp-fw-activation-history.c
@@ -0,0 +1,225 @@
+// 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 __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 __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) {
+ enum nvme_print_flags print_flag;
+
+ err = validate_output_format(format, &print_flag);
+ if (err < 0) {
+ fprintf(stderr, "Error: Invalid output format.\n");
+ return err;
+ }
+
+ if (print_flag == JSON)
+ ocp_fw_activation_history_json(&fw_history);
+ else if (print_flag == NORMAL)
+ ocp_fw_activation_history_normal(&fw_history);
+ }
+
+ return err;
+}
diff --git a/plugins/ocp/ocp-fw-activation-history.h b/plugins/ocp/ocp-fw-activation-history.h
new file mode 100644
index 0000000..a7f9058
--- /dev/null
+++ b/plugins/ocp/ocp-fw-activation-history.h
@@ -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
diff --git a/plugins/ocp/ocp-nvme.c b/plugins/ocp/ocp-nvme.c
new file mode 100644
index 0000000..53ae0f4
--- /dev/null
+++ b/plugins/ocp/ocp-nvme.c
@@ -0,0 +1,2971 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2023 Meta Platforms, Inc.
+ *
+ * Authors: Arthur Shau <arthurshau@meta.com>,
+ * Wei Zhang <wzhang@meta.com>,
+ * Venkat Ramesh <venkatraghavan@meta.com>
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "common.h"
+#include "nvme.h"
+#include "libnvme.h"
+#include "plugin.h"
+#include "linux/types.h"
+#include "util/types.h"
+#include "nvme-print.h"
+
+#include "ocp-smart-extended-log.h"
+#include "ocp-clear-features.h"
+#include "ocp-fw-activation-history.h"
+
+#define CREATE_CMD
+#include "ocp-nvme.h"
+#include "ocp-utils.h"
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+/// Latency Monitor Log
+
+#define C3_LATENCY_MON_LOG_BUF_LEN 0x200
+#define C3_LATENCY_MON_OPCODE 0xC3
+#define C3_LATENCY_MON_VERSION 0x0001
+#define C3_GUID_LENGTH 16
+#define NVME_FEAT_OCP_LATENCY_MONITOR 0xC5
+
+#define C3_ACTIVE_BUCKET_TIMER_INCREMENT 5
+#define C3_ACTIVE_THRESHOLD_INCREMENT 5
+#define C3_MINIMUM_WINDOW_INCREMENT 100
+#define C3_BUCKET_NUM 4
+
+static __u8 lat_mon_guid[C3_GUID_LENGTH] = {
+ 0x92, 0x7a, 0xc0, 0x8c,
+ 0xd0, 0x84, 0x6c, 0x9c,
+ 0x70, 0x43, 0xe6, 0xd4,
+ 0x58, 0x5e, 0xd4, 0x85
+};
+
+#define READ 3
+#define WRITE 2
+#define TRIM 1
+#define RESERVED 0
+
+struct __packed ssd_latency_monitor_log {
+ __u8 feature_status; /* 0x00 */
+ __u8 rsvd1; /* 0x01 */
+ __le16 active_bucket_timer; /* 0x02 */
+ __le16 active_bucket_timer_threshold; /* 0x04 */
+ __u8 active_threshold_a; /* 0x06 */
+ __u8 active_threshold_b; /* 0x07 */
+ __u8 active_threshold_c; /* 0x08 */
+ __u8 active_threshold_d; /* 0x09 */
+ __le16 active_latency_config; /* 0x0A */
+ __u8 active_latency_min_window; /* 0x0C */
+ __u8 rsvd2[0x13]; /* 0x0D */
+
+ __le32 active_bucket_counter[4][4]; /* 0x20 - 0x5F */
+ __le64 active_latency_timestamp[4][3]; /* 0x60 - 0xBF */
+ __le16 active_measured_latency[4][3]; /* 0xC0 - 0xD7 */
+ __le16 active_latency_stamp_units; /* 0xD8 */
+ __u8 rsvd3[0x16]; /* 0xDA */
+
+ __le32 static_bucket_counter[4][4]; /* 0x0F0 - 0x12F */
+ __le64 static_latency_timestamp[4][3]; /* 0x130 - 0x18F */
+ __le16 static_measured_latency[4][3]; /* 0x190 - 0x1A7 */
+ __le16 static_latency_stamp_units; /* 0x1A8 */
+ __u8 rsvd4[0x16]; /* 0x1AA */
+
+ __le16 debug_log_trigger_enable; /* 0x1C0 */
+ __le16 debug_log_measured_latency; /* 0x1C2 */
+ __le64 debug_log_latency_stamp; /* 0x1C4 */
+ __le16 debug_log_ptr; /* 0x1CC */
+ __le16 debug_log_counter_trigger; /* 0x1CE */
+ __u8 debug_log_stamp_units; /* 0x1D0 */
+ __u8 rsvd5[0x1D]; /* 0x1D1 */
+
+ __le16 log_page_version; /* 0x1EE */
+ __u8 log_page_guid[0x10]; /* 0x1F0 */
+};
+
+struct __packed feature_latency_monitor {
+ __u16 active_bucket_timer_threshold;
+ __u8 active_threshold_a;
+ __u8 active_threshold_b;
+ __u8 active_threshold_c;
+ __u8 active_threshold_d;
+ __u16 active_latency_config;
+ __u8 active_latency_minimum_window;
+ __u16 debug_log_trigger_enable;
+ __u8 discard_debug_log;
+ __u8 latency_monitor_feature_enable;
+ __u8 reserved[4083];
+};
+
+static int ocp_print_C3_log_normal(struct nvme_dev *dev,
+ struct ssd_latency_monitor_log *log_data)
+{
+ char ts_buf[128];
+ int i, j;
+
+ printf("-Latency Monitor/C3 Log Page Data-\n");
+ printf(" Controller : %s\n", dev->name);
+ printf(" Feature Status 0x%x\n",
+ log_data->feature_status);
+ printf(" Active Bucket Timer %d min\n",
+ C3_ACTIVE_BUCKET_TIMER_INCREMENT *
+ le16_to_cpu(log_data->active_bucket_timer));
+ printf(" Active Bucket Timer Threshold %d min\n",
+ C3_ACTIVE_BUCKET_TIMER_INCREMENT *
+ le16_to_cpu(log_data->active_bucket_timer_threshold));
+ printf(" Active Threshold A %d ms\n",
+ C3_ACTIVE_THRESHOLD_INCREMENT *
+ le16_to_cpu(log_data->active_threshold_a+1));
+ printf(" Active Threshold B %d ms\n",
+ C3_ACTIVE_THRESHOLD_INCREMENT *
+ le16_to_cpu(log_data->active_threshold_b+1));
+ printf(" Active Threshold C %d ms\n",
+ C3_ACTIVE_THRESHOLD_INCREMENT *
+ le16_to_cpu(log_data->active_threshold_c+1));
+ printf(" Active Threshold D %d ms\n",
+ C3_ACTIVE_THRESHOLD_INCREMENT *
+ le16_to_cpu(log_data->active_threshold_d+1));
+ printf(" Active Latency Configuration 0x%x \n",
+ le16_to_cpu(log_data->active_latency_config));
+ printf(" Active Latency Minimum Window %d ms\n",
+ C3_MINIMUM_WINDOW_INCREMENT *
+ le16_to_cpu(log_data->active_latency_min_window));
+ printf(" Active Latency Stamp Units %d\n",
+ le16_to_cpu(log_data->active_latency_stamp_units));
+ printf(" Static Latency Stamp Units %d\n",
+ le16_to_cpu(log_data->static_latency_stamp_units));
+ printf(" Debug Log Trigger Enable %d\n",
+ le16_to_cpu(log_data->debug_log_trigger_enable));
+ printf(" Debug Log Measured Latency %d\n",
+ le16_to_cpu(log_data->debug_log_measured_latency));
+ if (le64_to_cpu(log_data->debug_log_latency_stamp) == -1) {
+ printf(" Debug Log Latency Time Stamp N/A\n");
+ } else {
+ convert_ts(le64_to_cpu(log_data->debug_log_latency_stamp), ts_buf);
+ printf(" Debug Log Latency Time Stamp %s\n", ts_buf);
+ }
+ printf(" Debug Log Pointer %d\n",
+ le16_to_cpu(log_data->debug_log_ptr));
+ printf(" Debug Counter Trigger Source %d\n",
+ le16_to_cpu(log_data->debug_log_counter_trigger));
+ printf(" Debug Log Stamp Units %d\n",
+ le16_to_cpu(log_data->debug_log_stamp_units));
+ printf(" Log Page Version %d\n",
+ le16_to_cpu(log_data->log_page_version));
+
+ char guid[(C3_GUID_LENGTH * 2) + 1];
+ char *ptr = &guid[0];
+
+ for (i = C3_GUID_LENGTH - 1; i >= 0; i--)
+ ptr += sprintf(ptr, "%02X", log_data->log_page_guid[i]);
+
+ printf(" Log Page GUID %s\n", guid);
+ printf("\n");
+
+ printf(" Read Write Deallocate/Trim\n");
+ for (i = 0; i < C3_BUCKET_NUM; i++) {
+ printf(" Active Bucket Counter: Bucket %d %27d %27d %27d\n",
+ i,
+ le32_to_cpu(log_data->active_bucket_counter[i][READ]),
+ le32_to_cpu(log_data->active_bucket_counter[i][WRITE]),
+ le32_to_cpu(log_data->active_bucket_counter[i][TRIM]));
+ }
+
+ for (i = 0; i < C3_BUCKET_NUM; i++) {
+ printf(" Active Latency Time Stamp: Bucket %d ", i);
+ for (j = 2; j >= 0; j--) {
+ if (le64_to_cpu(log_data->active_latency_timestamp[3-i][j]) == -1) {
+ printf(" N/A ");
+ } else {
+ convert_ts(le64_to_cpu(log_data->active_latency_timestamp[3-i][j]), ts_buf);
+ printf("%s ", ts_buf);
+ }
+ }
+ printf("\n");
+ }
+
+ for (i = 0; i < C3_BUCKET_NUM; i++) {
+ printf(" Active Measured Latency: Bucket %d %27d ms %27d ms %27d ms\n",
+ i,
+ le16_to_cpu(log_data->active_measured_latency[3-i][READ-1]),
+ le16_to_cpu(log_data->active_measured_latency[3-i][WRITE-1]),
+ le16_to_cpu(log_data->active_measured_latency[3-i][TRIM-1]));
+ }
+
+ printf("\n");
+ for (i = 0; i < C3_BUCKET_NUM; i++) {
+ printf(" Static Bucket Counter: Bucket %d %27d %27d %27d\n",
+ i,
+ le32_to_cpu(log_data->static_bucket_counter[i][READ]),
+ le32_to_cpu(log_data->static_bucket_counter[i][WRITE]),
+ le32_to_cpu(log_data->static_bucket_counter[i][TRIM]));
+ }
+
+ for (i = 0; i < C3_BUCKET_NUM; i++) {
+ printf(" Static Latency Time Stamp: Bucket %d ", i);
+ for (j = 2; j >= 0; j--) {
+ if (le64_to_cpu(log_data->static_latency_timestamp[3-i][j]) == -1) {
+ printf(" N/A ");
+ } else {
+ convert_ts(le64_to_cpu(log_data->static_latency_timestamp[3-i][j]), ts_buf);
+ printf("%s ", ts_buf);
+ }
+ }
+ printf("\n");
+ }
+
+ for (i = 0; i < C3_BUCKET_NUM; i++) {
+ printf(" Static Measured Latency: Bucket %d %27d ms %27d ms %27d ms\n",
+ i,
+ le16_to_cpu(log_data->static_measured_latency[3-i][READ-1]),
+ le16_to_cpu(log_data->static_measured_latency[3-i][WRITE-1]),
+ le16_to_cpu(log_data->static_measured_latency[3-i][TRIM-1]));
+ }
+
+ return 0;
+}
+
+static void ocp_print_C3_log_json(struct ssd_latency_monitor_log *log_data)
+{
+ struct json_object *root;
+ char ts_buf[128];
+ char buf[128];
+ int i, j;
+ char *operation[3] = {"Trim", "Write", "Read"};
+
+ root = json_create_object();
+
+ json_object_add_value_uint(root, "Feature Status",
+ log_data->feature_status);
+ json_object_add_value_uint(root, "Active Bucket Timer",
+ C3_ACTIVE_BUCKET_TIMER_INCREMENT *
+ le16_to_cpu(log_data->active_bucket_timer));
+ json_object_add_value_uint(root, "Active Bucket Timer Threshold",
+ C3_ACTIVE_BUCKET_TIMER_INCREMENT *
+ le16_to_cpu(log_data->active_bucket_timer_threshold));
+ json_object_add_value_uint(root, "Active Threshold A",
+ C3_ACTIVE_THRESHOLD_INCREMENT *
+ le16_to_cpu(log_data->active_threshold_a + 1));
+ json_object_add_value_uint(root, "Active Threshold B",
+ C3_ACTIVE_THRESHOLD_INCREMENT *
+ le16_to_cpu(log_data->active_threshold_b + 1));
+ json_object_add_value_uint(root, "Active Threshold C",
+ C3_ACTIVE_THRESHOLD_INCREMENT *
+ le16_to_cpu(log_data->active_threshold_c + 1));
+ json_object_add_value_uint(root, "Active Threshold D",
+ C3_ACTIVE_THRESHOLD_INCREMENT *
+ le16_to_cpu(log_data->active_threshold_d + 1));
+ json_object_add_value_uint(root, "Active Latency Configuration",
+ le16_to_cpu(log_data->active_latency_config));
+ json_object_add_value_uint(root, "Active Latency Minimum Window",
+ C3_MINIMUM_WINDOW_INCREMENT *
+ le16_to_cpu(log_data->active_latency_min_window));
+
+ for (i = 0; i < C3_BUCKET_NUM; i++) {
+ struct json_object *bucket;
+
+ bucket = json_create_object();
+ sprintf(buf, "Active Bucket Counter: Bucket %d", i);
+ for (j = 2; j >= 0; j--) {
+ json_object_add_value_uint(bucket, operation[j],
+ le32_to_cpu(log_data->active_bucket_counter[i][j+1]));
+ }
+ json_object_add_value_object(root, buf, bucket);
+ }
+
+ for (i = 0; i < C3_BUCKET_NUM; i++) {
+ struct json_object *bucket;
+
+ bucket = json_create_object();
+ sprintf(buf, "Active Latency Time Stamp: Bucket %d", i);
+ for (j = 2; j >= 0; j--) {
+ if (le64_to_cpu(log_data->active_latency_timestamp[3-i][j]) == -1) {
+ json_object_add_value_string(bucket, operation[j], "NA");
+ } else {
+ convert_ts(le64_to_cpu(log_data->active_latency_timestamp[3-i][j]), ts_buf);
+ json_object_add_value_string(bucket, operation[j], ts_buf);
+ }
+ }
+ json_object_add_value_object(root, buf, bucket);
+ }
+
+ for (i = 0; i < C3_BUCKET_NUM; i++) {
+ struct json_object *bucket;
+
+ bucket = json_create_object();
+ sprintf(buf, "Active Measured Latency: Bucket %d", i);
+ for (j = 2; j >= 0; j--) {
+ json_object_add_value_uint(bucket, operation[j],
+ le16_to_cpu(log_data->active_measured_latency[3-i][j]));
+ }
+ json_object_add_value_object(root, buf, bucket);
+ }
+
+ json_object_add_value_uint(root, "Active Latency Stamp Units",
+ le16_to_cpu(log_data->active_latency_stamp_units));
+
+ for (i = 0; i < C3_BUCKET_NUM; i++) {
+ struct json_object *bucket;
+
+ bucket = json_create_object();
+ sprintf(buf, "Static Bucket Counter: Bucket %d", i);
+ for (j = 2; j >= 0; j--) {
+ json_object_add_value_uint(bucket, operation[j],
+ le32_to_cpu(log_data->static_bucket_counter[i][j+1]));
+ }
+ json_object_add_value_object(root, buf, bucket);
+ }
+
+ for (i = 0; i < C3_BUCKET_NUM; i++) {
+ struct json_object *bucket;
+
+ bucket = json_create_object();
+ sprintf(buf, "Static Latency Time Stamp: Bucket %d", i);
+ for (j = 2; j >= 0; j--) {
+ if (le64_to_cpu(log_data->static_latency_timestamp[3-i][j]) == -1) {
+ json_object_add_value_string(bucket, operation[j], "NA");
+ } else {
+ convert_ts(le64_to_cpu(log_data->static_latency_timestamp[3-i][j]), ts_buf);
+ json_object_add_value_string(bucket, operation[j], ts_buf);
+ }
+ }
+ json_object_add_value_object(root, buf, bucket);
+ }
+
+ for (i = 0; i < C3_BUCKET_NUM; i++) {
+ struct json_object *bucket;
+
+ bucket = json_create_object();
+ sprintf(buf, "Static Measured Latency: Bucket %d", i);
+ for (j = 2; j >= 0; j--) {
+ json_object_add_value_uint(bucket, operation[j],
+ le16_to_cpu(log_data->static_measured_latency[3-i][j]));
+ }
+ json_object_add_value_object(root, buf, bucket);
+ }
+
+ json_object_add_value_uint(root, "Static Latency Stamp Units",
+ le16_to_cpu(log_data->static_latency_stamp_units));
+ json_object_add_value_uint(root, "Debug Log Trigger Enable",
+ le16_to_cpu(log_data->debug_log_trigger_enable));
+ json_object_add_value_uint(root, "Debug Log Measured Latency",
+ le16_to_cpu(log_data->debug_log_measured_latency));
+ if (le64_to_cpu(log_data->debug_log_latency_stamp) == -1) {
+ json_object_add_value_string(root, "Debug Log Latency Time Stamp", "NA");
+ } else {
+ convert_ts(le64_to_cpu(log_data->debug_log_latency_stamp), ts_buf);
+ json_object_add_value_string(root, "Debug Log Latency Time Stamp", ts_buf);
+ }
+ json_object_add_value_uint(root, "Debug Log Pointer",
+ le16_to_cpu(log_data->debug_log_ptr));
+ json_object_add_value_uint(root, "Debug Counter Trigger Source",
+ le16_to_cpu(log_data->debug_log_counter_trigger));
+ json_object_add_value_uint(root, "Debug Log Stamp Units",
+ le16_to_cpu(log_data->debug_log_stamp_units));
+ json_object_add_value_uint(root, "Log Page Version",
+ le16_to_cpu(log_data->log_page_version));
+
+ char guid[(C3_GUID_LENGTH * 2) + 1];
+ char *ptr = &guid[0];
+
+ for (i = C3_GUID_LENGTH - 1; i >= 0; i--)
+ ptr += sprintf(ptr, "%02X", log_data->log_page_guid[i]);
+
+ json_object_add_value_string(root, "Log Page GUID", guid);
+
+ json_print_object(root, NULL);
+ printf("\n");
+
+ json_free_object(root);
+}
+
+static int get_c3_log_page(struct nvme_dev *dev, char *format)
+{
+ struct ssd_latency_monitor_log *log_data;
+ enum nvme_print_flags fmt;
+ int ret;
+ __u8 *data;
+ int i;
+
+ ret = validate_output_format(format, &fmt);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR : OCP : invalid output format\n");
+ return ret;
+ }
+
+ data = malloc(sizeof(__u8) * C3_LATENCY_MON_LOG_BUF_LEN);
+ if (!data) {
+ fprintf(stderr, "ERROR : OCP : malloc : %s\n", strerror(errno));
+ return -1;
+ }
+ memset(data, 0, sizeof(__u8) * C3_LATENCY_MON_LOG_BUF_LEN);
+
+ ret = nvme_get_log_simple(dev_fd(dev), C3_LATENCY_MON_OPCODE,
+ C3_LATENCY_MON_LOG_BUF_LEN, data);
+
+ if (strcmp(format, "json"))
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret, false), ret);
+
+ if (!ret) {
+ log_data = (struct ssd_latency_monitor_log *)data;
+
+ /* check log page version */
+ if (log_data->log_page_version != C3_LATENCY_MON_VERSION) {
+ fprintf(stderr,
+ "ERROR : OCP : invalid latency monitor version\n");
+ ret = -1;
+ goto out;
+ }
+
+ /*
+ * check log page guid
+ * Verify GUID matches
+ */
+ for (i = 0; i < 16; i++) {
+ if (lat_mon_guid[i] != log_data->log_page_guid[i]) {
+ int j;
+
+ fprintf(stderr, "ERROR : OCP : Unknown GUID in C3 Log Page data\n");
+ fprintf(stderr, "ERROR : OCP : Expected GUID: 0x");
+ for (j = 0; j < 16; j++)
+ fprintf(stderr, "%x", lat_mon_guid[j]);
+
+ fprintf(stderr, "\nERROR : OCP : Actual GUID: 0x");
+ for (j = 0; j < 16; j++)
+ fprintf(stderr, "%x", log_data->log_page_guid[j]);
+ fprintf(stderr, "\n");
+
+ ret = -1;
+ goto out;
+ }
+ }
+
+ switch (fmt) {
+ case NORMAL:
+ ocp_print_C3_log_normal(dev, log_data);
+ break;
+ case JSON:
+ ocp_print_C3_log_json(log_data);
+ break;
+ default:
+ fprintf(stderr, "unhandled output format\n");
+
+ }
+ } else {
+ fprintf(stderr,
+ "ERROR : OCP : Unable to read C3 data from buffer\n");
+ }
+
+out:
+ free(data);
+ return ret;
+}
+
+static int ocp_latency_monitor_log(int argc, char **argv,
+ struct command *command,
+ struct plugin *plugin)
+{
+ const char *desc = "Retrieve latency monitor log data.";
+ struct nvme_dev *dev;
+ int ret = 0;
+
+ 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()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ ret = get_c3_log_page(dev, cfg.output_format);
+ if (ret)
+ fprintf(stderr,
+ "ERROR : OCP : Failure reading the C3 Log Page, ret = %d\n",
+ ret);
+
+ dev_close(dev);
+ return ret;
+}
+
+int ocp_set_latency_monitor_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ int err = -1;
+ struct nvme_dev *dev;
+ __u32 result;
+ struct feature_latency_monitor buf = {0,};
+ __u32 nsid = NVME_NSID_ALL;
+ struct stat nvme_stat;
+ struct nvme_id_ctrl ctrl;
+
+ const char *desc = "Set Latency Monitor feature.";
+ const char *active_bucket_timer_threshold = "This is the value that loads the Active Bucket Timer Threshold.";
+ const char *active_threshold_a = "This is the value that loads into the Active Threshold A.";
+ const char *active_threshold_b = "This is the value that loads into the Active Threshold B.";
+ const char *active_threshold_c = "This is the value that loads into the Active Threshold C.";
+ const char *active_threshold_d = "This is the value that loads into the Active Threshold D.";
+ const char *active_latency_config = "This is the value that loads into the Active Latency Configuration.";
+ const char *active_latency_minimum_window = "This is the value that loads into the Active Latency Minimum Window.";
+ const char *debug_log_trigger_enable = "This is the value that loads into the Debug Log Trigger Enable.";
+ const char *discard_debug_log = "Discard Debug Log.";
+ const char *latency_monitor_feature_enable = "Latency Monitor Feature Enable.";
+
+ struct config {
+ __u16 active_bucket_timer_threshold;
+ __u8 active_threshold_a;
+ __u8 active_threshold_b;
+ __u8 active_threshold_c;
+ __u8 active_threshold_d;
+ __u16 active_latency_config;
+ __u8 active_latency_minimum_window;
+ __u16 debug_log_trigger_enable;
+ __u8 discard_debug_log;
+ __u8 latency_monitor_feature_enable;
+ };
+
+ struct config cfg = {
+ .active_bucket_timer_threshold = 0x7E0,
+ .active_threshold_a = 0x5,
+ .active_threshold_b = 0x13,
+ .active_threshold_c = 0x1E,
+ .active_threshold_d = 0x2E,
+ .active_latency_config = 0xFFF,
+ .active_latency_minimum_window = 0xA,
+ .debug_log_trigger_enable = 0,
+ .discard_debug_log = 0,
+ .latency_monitor_feature_enable = 0x7,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("active_bucket_timer_threshold", 't', &cfg.active_bucket_timer_threshold, active_bucket_timer_threshold),
+ OPT_UINT("active_threshold_a", 'a', &cfg.active_threshold_a, active_threshold_a),
+ OPT_UINT("active_threshold_b", 'b', &cfg.active_threshold_b, active_threshold_b),
+ OPT_UINT("active_threshold_c", 'c', &cfg.active_threshold_c, active_threshold_c),
+ OPT_UINT("active_threshold_d", 'd', &cfg.active_threshold_d, active_threshold_d),
+ OPT_UINT("active_latency_config", 'f', &cfg.active_latency_config, active_latency_config),
+ OPT_UINT("active_latency_minimum_window", 'w', &cfg.active_latency_minimum_window, active_latency_minimum_window),
+ OPT_UINT("debug_log_trigger_enable", 'r', &cfg.debug_log_trigger_enable, debug_log_trigger_enable),
+ OPT_UINT("discard_debug_log", 'l', &cfg.discard_debug_log, discard_debug_log),
+ OPT_UINT("latency_monitor_feature_enable", 'e', &cfg.latency_monitor_feature_enable, latency_monitor_feature_enable),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ err = fstat(dev_fd(dev), &nvme_stat);
+ if (err < 0)
+ return err;
+
+ if (S_ISBLK(nvme_stat.st_mode)) {
+ err = nvme_get_nsid(dev_fd(dev), &nsid);
+ if (err < 0) {
+ perror("invalid-namespace-id");
+ return err;
+ }
+ }
+
+ err = nvme_identify_ctrl(dev_fd(dev), &ctrl);
+ if (err)
+ return err;
+
+ memset(&buf, 0, sizeof(struct feature_latency_monitor));
+
+ buf.active_bucket_timer_threshold = cfg.active_bucket_timer_threshold;
+ buf.active_threshold_a = cfg.active_threshold_a;
+ buf.active_threshold_b = cfg.active_threshold_b;
+ buf.active_threshold_c = cfg.active_threshold_c;
+ buf.active_threshold_d = cfg.active_threshold_d;
+ buf.active_latency_config = cfg.active_latency_config;
+ buf.active_latency_minimum_window = cfg.active_latency_minimum_window;
+ buf.debug_log_trigger_enable = cfg.debug_log_trigger_enable;
+ buf.discard_debug_log = cfg.discard_debug_log;
+ buf.latency_monitor_feature_enable = cfg.latency_monitor_feature_enable;
+
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .fid = NVME_FEAT_OCP_LATENCY_MONITOR,
+ .nsid = 0,
+ .cdw12 = 0,
+ .save = 1,
+ .data_len = sizeof(struct feature_latency_monitor),
+ .data = (void *)&buf,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+
+ err = nvme_set_features(&args);
+ if (err < 0) {
+ perror("set-feature");
+ } else if (!err) {
+ printf("NVME_FEAT_OCP_LATENCY_MONITOR: 0x%02x\n", NVME_FEAT_OCP_LATENCY_MONITOR);
+ printf("active bucket timer threshold: 0x%x\n", buf.active_bucket_timer_threshold);
+ printf("active threshold a: 0x%x\n", buf.active_threshold_a);
+ printf("active threshold b: 0x%x\n", buf.active_threshold_b);
+ printf("active threshold c: 0x%x\n", buf.active_threshold_c);
+ printf("active threshold d: 0x%x\n", buf.active_threshold_d);
+ printf("active latency config: 0x%x\n", buf.active_latency_config);
+ printf("active latency minimum window: 0x%x\n", buf.active_latency_minimum_window);
+ printf("debug log trigger enable: 0x%x\n", buf.debug_log_trigger_enable);
+ printf("discard debug log: 0x%x\n", buf.discard_debug_log);
+ printf("latency monitor feature enable: 0x%x\n", buf.latency_monitor_feature_enable);
+ } else if (err > 0) {
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(err, false), err);
+ }
+
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+/// EOL/PLP Failure Mode
+
+static const char *eol_plp_failure_mode_to_string(__u8 mode)
+{
+ switch (mode) {
+ case 1:
+ return "Read only mode (ROM)";
+ case 2:
+ return "Write through mode (WTM)";
+ case 3:
+ return "Normal mode";
+ default:
+ break;
+ }
+
+ return "Reserved";
+}
+
+static int eol_plp_failure_mode_get(struct nvme_dev *dev, const __u32 nsid,
+ const __u8 fid, __u8 sel)
+{
+ __u32 result;
+ int err;
+
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .fid = fid,
+ .nsid = nsid,
+ .sel = sel,
+ .cdw11 = 0,
+ .uuidx = 0,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+
+ err = nvme_get_features(&args);
+ if (!err) {
+ nvme_show_result("End of Life Behavior (feature: %#0*x): %#0*x (%s: %s)",
+ fid ? 4 : 2, fid, result ? 10 : 8, result,
+ nvme_select_to_string(sel),
+ eol_plp_failure_mode_to_string(result));
+ if (sel == NVME_GET_FEATURES_SEL_SUPPORTED)
+ nvme_show_select_result(fid, result);
+ } else {
+ nvme_show_error("Could not get feature: %#0*x.", fid ? 4 : 2, fid);
+ }
+
+ return err;
+}
+
+static int eol_plp_failure_mode_set(struct nvme_dev *dev, const __u32 nsid,
+ const __u8 fid, __u8 mode, bool save,
+ bool uuid)
+{
+ __u32 result;
+ int err;
+ int uuid_index = 0;
+
+ if (uuid) {
+ /* OCP 2.0 requires UUID index support */
+ err = ocp_get_uuid_index(dev, &uuid_index);
+ if (err || !uuid_index) {
+ nvme_show_error("ERROR: No OCP UUID index found");
+ return err;
+ }
+ }
+
+
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .fid = fid,
+ .nsid = nsid,
+ .cdw11 = mode << 30,
+ .cdw12 = 0,
+ .save = save,
+ .uuidx = uuid_index,
+ .cdw15 = 0,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+
+ err = nvme_set_features(&args);
+ if (err > 0) {
+ nvme_show_status(err);
+ } else if (err < 0) {
+ nvme_show_perror("Define EOL/PLP failure mode");
+ fprintf(stderr, "Command failed while parsing.\n");
+ } else {
+ nvme_show_result("Successfully set mode (feature: %#0*x): %#0*x (%s: %s).",
+ fid ? 4 : 2, fid, mode ? 10 : 8, mode,
+ save ? "Save" : "Not save",
+ eol_plp_failure_mode_to_string(mode));
+ }
+
+ return err;
+}
+
+static int eol_plp_failure_mode(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ const char *desc = "Define EOL or PLP circuitry failure mode.\n"
+ "No argument prints current mode.";
+ const char *mode = "[0-3]: default/rom/wtm/normal";
+ const char *save = "Specifies that the controller shall save the attribute";
+ const char *sel = "[0-3,8]: current/default/saved/supported/changed";
+ const __u32 nsid = 0;
+ const __u8 fid = 0xc2;
+ struct nvme_dev *dev;
+ int err;
+
+ struct config {
+ __u8 mode;
+ bool save;
+ __u8 sel;
+ };
+
+ struct config cfg = {
+ .mode = 0,
+ .save = false,
+ .sel = 0,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_BYTE("mode", 'm', &cfg.mode, mode),
+ OPT_FLAG("save", 's', &cfg.save, save),
+ OPT_BYTE("sel", 'S', &cfg.sel, sel),
+ OPT_FLAG("no-uuid", 'n', NULL,
+ "Skip UUID index search (UUID index not required for OCP 1.0)"),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ if (argconfig_parse_seen(opts, "mode"))
+ err = eol_plp_failure_mode_set(dev, nsid, fid, cfg.mode,
+ cfg.save,
+ !argconfig_parse_seen(opts, "no-uuid"));
+ else
+ err = eol_plp_failure_mode_get(dev, nsid, fid, cfg.sel);
+
+ dev_close(dev);
+
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+/// Telemetry Log
+
+#define TELEMETRY_HEADER_SIZE 512
+#define TELEMETRY_BYTE_PER_BLOCK 512
+#define TELEMETRY_TRANSFER_SIZE 1024
+#define FILE_NAME_SIZE 2048
+
+enum TELEMETRY_TYPE {
+ TELEMETRY_TYPE_NONE = 0,
+ TELEMETRY_TYPE_HOST = 7,
+ TELEMETRY_TYPE_CONTROLLER = 8,
+ TELEMETRY_TYPE_HOST_0 = 9,
+ TELEMETRY_TYPE_HOST_1 = 10,
+};
+
+struct telemetry_initiated_log {
+ __u8 LogIdentifier;
+ __u8 Reserved1[4];
+ __u8 IEEE[3];
+ __le16 DataArea1LastBlock;
+ __le16 DataArea2LastBlock;
+ __le16 DataArea3LastBlock;
+ __u8 Reserved2[368];
+ __u8 DataAvailable;
+ __u8 DataGenerationNumber;
+ __u8 ReasonIdentifier[128];
+};
+
+struct telemetry_data_area_1 {
+ __le16 major_version;
+ __le16 minor_version;
+ __u8 reserved1[4];
+ __le64 timestamp;
+ __u8 log_page_guid[16];
+ __u8 no_of_tps_supp;
+ __u8 tps;
+ __u8 reserved2[6];
+ __le16 sls;
+ __u8 reserved3[8];
+ __le16 fw_revision;
+ __u8 reserved4[32];
+ __le16 da1_stat_start;
+ __le16 da1_stat_size;
+ __le16 da2_stat_start;
+ __le16 da2_stat_size;
+ __u8 reserved5[32];
+ __u8 event_fifo_da[16];
+ __le64 event_fifo_start[16];
+ __le64 event_fifo_size[16];
+ __u8 reserved6[80];
+ __u8 smart_health_info[512];
+ __u8 smart_health_info_extended[512];
+};
+static void get_serial_number(struct nvme_id_ctrl *ctrl, char *sn)
+{
+ int i;
+ /* Remove trailing spaces from the name */
+ for (i = 0; i < sizeof(ctrl->sn); i++) {
+ if (ctrl->sn[i] == ' ')
+ break;
+ sn[i] = ctrl->sn[i];
+ }
+}
+
+static int get_telemetry_header(struct nvme_dev *dev, __u32 ns, __u8 tele_type,
+ __u32 data_len, void *data, __u8 nLSP, __u8 nRAE)
+{
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_get_log_page,
+ .nsid = ns,
+ .addr = (__u64)(uintptr_t) data,
+ .data_len = data_len,
+ };
+
+ __u32 numd = (data_len >> 2) - 1;
+ __u16 numdu = numd >> 16;
+ __u16 numdl = numd & 0xffff;
+
+ cmd.cdw10 = tele_type | (nLSP & 0x0F) << 8 | (nRAE & 0x01) << 15 | (numdl & 0xFFFF) << 16;
+ cmd.cdw11 = numdu;
+ cmd.cdw12 = 0;
+ cmd.cdw13 = 0;
+ cmd.cdw14 = 0;
+
+ return nvme_submit_admin_passthru(dev_fd(dev), &cmd, NULL);
+}
+
+static void print_telemetry_header(struct telemetry_initiated_log *logheader,
+ int tele_type)
+{
+ if (logheader) {
+ unsigned int i = 0, j = 0;
+
+ if (tele_type == TELEMETRY_TYPE_HOST)
+ printf("============ Telemetry Host Header ============\n");
+ else
+ printf("========= Telemetry Controller Header =========\n");
+
+ printf("Log Identifier : 0x%02X\n", logheader->LogIdentifier);
+ printf("IEEE : 0x%02X%02X%02X\n",
+ logheader->IEEE[0], logheader->IEEE[1], logheader->IEEE[2]);
+ printf("Data Area 1 Last Block : 0x%04X\n",
+ le16_to_cpu(logheader->DataArea1LastBlock));
+ printf("Data Area 2 Last Block : 0x%04X\n",
+ le16_to_cpu(logheader->DataArea2LastBlock));
+ printf("Data Area 3 Last Block : 0x%04X\n",
+ le16_to_cpu(logheader->DataArea3LastBlock));
+ printf("Data Available : 0x%02X\n", logheader->DataAvailable);
+ printf("Data Generation Number : 0x%02X\n", logheader->DataGenerationNumber);
+ printf("Reason Identifier :\n");
+
+ for (i = 0; i < 8; i++) {
+ for (j = 0; j < 16; j++)
+ printf("%02X ", logheader->ReasonIdentifier[127 - ((i * 16) + j)]);
+ printf("\n");
+ }
+ printf("===============================================\n\n");
+ }
+}
+static int get_telemetry_data(struct nvme_dev *dev, __u32 ns, __u8 tele_type,
+ __u32 data_len, void *data, __u8 nLSP, __u8 nRAE,
+ __u64 offset)
+{
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_get_log_page,
+ .nsid = ns,
+ .addr = (__u64)(uintptr_t) data,
+ .data_len = data_len,
+ };
+ __u32 numd = (data_len >> 2) - 1;
+ __u16 numdu = numd >> 16;
+ __u16 numdl = numd & 0xffff;
+ cmd.cdw10 = tele_type | (nLSP & 0x0F) << 8 | (nRAE & 0x01) << 15 | (numdl & 0xFFFF) << 16;
+ cmd.cdw11 = numdu;
+ cmd.cdw12 = offset;
+ cmd.cdw13 = 0;
+ cmd.cdw14 = 0;
+ return nvme_submit_admin_passthru(dev_fd(dev), &cmd, NULL);
+}
+static void print_telemetry_data_area_1(struct telemetry_data_area_1 *da1,
+ int tele_type)
+{
+ if (da1) {
+ unsigned int i = 0;
+ if (tele_type == TELEMETRY_TYPE_HOST)
+ printf("============ Telemetry Host Data area 1 ============\n");
+ else
+ printf("========= Telemetry Controller Data area 1 =========\n");
+ printf("Major Version : 0x%x\n", le16_to_cpu(da1->major_version));
+ printf("Minor Version : 0x%x\n", le16_to_cpu(da1->minor_version));
+ for (i = 0; i < 4; i++)
+ printf("reserved1 : 0x%x\n", da1->reserved1[i]);
+ printf("Timestamp : %"PRIu64"\n", le64_to_cpu(da1->timestamp));
+ for (i = 15; i >= 0; i--)
+ printf("%x", da1->log_page_guid[i]);
+ printf("Number Telemetry Profiles Supported : 0x%x\n", da1->no_of_tps_supp);
+ printf("Telemetry Profile Selected (TPS) : 0x%x\n", da1->tps);
+ for (i = 0; i < 6; i++)
+ printf("reserved2 : 0x%x\n", da1->reserved2[i]);
+ printf("Telemetry String Log Size (SLS) : 0x%x\n", le16_to_cpu(da1->sls));
+ for (i = 0; i < 8; i++)
+ printf("reserved3 : 0x%x\n", da1->reserved3[i]);
+ printf("Firmware Revision : 0x%x\n", le16_to_cpu(da1->fw_revision));
+ for (i = 0; i < 32; i++)
+ printf("reserved4 : 0x%x\n", da1->reserved4[i]);
+ printf("Data Area 1 Statistic Start : 0x%x\n", le16_to_cpu(da1->da1_stat_start));
+ printf("Data Area 1 Statistic Size : 0x%x\n", le16_to_cpu(da1->da1_stat_size));
+ printf("Data Area 2 Statistic Start : 0x%x\n", le16_to_cpu(da1->da2_stat_start));
+ printf("Data Area 2 Statistic Size : 0x%x\n", le16_to_cpu(da1->da2_stat_size));
+ for (i = 0; i < 32; i++)
+ printf("reserved5 : 0x%x\n", da1->reserved5[i]);
+ for (i = 0; i < 17; i++){
+ printf("Event FIFO %d Data Area : 0x%x\n", i, da1->event_fifo_da[i]);
+ printf("Event FIFO %d Start : %"PRIu64"\n", i, le64_to_cpu(da1->event_fifo_start[i]));
+ printf("Event FIFO %d Size : %"PRIu64"\n", i, le64_to_cpu(da1->event_fifo_size[i]));
+ }
+ for (i = 0; i < 80; i++)
+ printf("reserved6 : 0x%x\n", da1->reserved6[i]);
+ for (i = 0; i < 512; i++){
+ printf("SMART / Health Information : 0x%x\n", da1->smart_health_info[i]);
+ printf("SMART / Health Information Extended : 0x%x\n", da1->smart_health_info_extended[i]);
+ }
+ printf("===============================================\n\n");
+ }
+}
+static void print_telemetry_da1_stat(__u8 *da1_stat, int tele_type, __u16 buf_size)
+{
+ if (da1_stat) {
+ unsigned int i = 0;
+ if (tele_type == TELEMETRY_TYPE_HOST)
+ printf("============ Telemetry Host Data area 1 Statistics ============\n");
+ else
+ printf("========= Telemetry Controller Data area 1 Statistics =========\n");
+ while((i + 8) < buf_size) {
+ printf("Statistics Identifier : 0x%x\n", (da1_stat[i] | da1_stat[i+1] << 8));
+ printf("Statistics info : 0x%x\n", da1_stat[i+2]);
+ printf("NS info : 0x%x\n", da1_stat[i+3]);
+ printf("Statistic Data Size : 0x%x\n", (da1_stat[i+4] | da1_stat[i+5] << 8));
+ printf("Reserved : 0x%x\n", (da1_stat[i+6] | da1_stat[i+7] << 8));
+ i = 8 + ((da1_stat[i+4] | da1_stat[i+5] << 8) * 4);
+ }
+ printf("===============================================\n\n");
+ }
+}
+static void print_telemetry_da1_fifo(__u8 *da1_fifo, int tele_type, __u16 buf_size)
+{
+ if (da1_fifo) {
+ unsigned int i = 0;
+ if (tele_type == TELEMETRY_TYPE_HOST)
+ printf("============ Telemetry Host Data area 1 FIFO ============\n");
+ else
+ printf("========= Telemetry Controller Data area 1 FIFO =========\n");
+ while((i + 4) < buf_size) {
+ printf("Debug Event Class Type : 0x%x\n", da1_fifo[i]);
+ printf("Event ID : 0x%x\n", (da1_fifo[i+1] | da1_fifo[i+2] << 8));
+ printf("Event Data Size : 0x%x\n", da1_fifo[3]);
+ i = 4 + ((da1_fifo[3]) * 4);
+ }
+ printf("===============================================\n\n");
+ }
+}
+static void print_telemetry_da2_stat(__u8 *da1_stat, int tele_type, __u16 buf_size)
+{
+ if (da1_stat) {
+ unsigned int i = 0;
+ if (tele_type == TELEMETRY_TYPE_HOST)
+ printf("============ Telemetry Host Data area 1 Statistics ============\n");
+ else
+ printf("========= Telemetry Controller Data area 1 Statistics =========\n");
+ while((i + 8) < buf_size) {
+ printf("Statistics Identifier : 0x%x\n", (da1_stat[i] | da1_stat[i+1] << 8));
+ printf("Statistics info : 0x%x\n", da1_stat[i+2]);
+ printf("NS info : 0x%x\n", da1_stat[i+3]);
+ printf("Statistic Data Size : 0x%x\n", (da1_stat[i+4] | da1_stat[i+5] << 8));
+ printf("Reserved : 0x%x\n", (da1_stat[i+6] | da1_stat[i+7] << 8));
+ i = 8 + ((da1_stat[i+4] | da1_stat[i+5] << 8) * 4);
+ }
+ printf("===============================================\n\n");
+ }
+}
+static void print_telemetry_da2_fifo(__u8 *da1_fifo, int tele_type, __u16 buf_size)
+{
+ if (da1_fifo) {
+ unsigned int i = 0;
+ if (tele_type == TELEMETRY_TYPE_HOST)
+ printf("============ Telemetry Host Data area 1 Statistics ============\n");
+ else
+ printf("========= Telemetry Controller Data area 1 Statistics =========\n");
+ while((i + 4) < buf_size) {
+ printf("Debug Event Class Type : 0x%x\n", da1_fifo[i]);
+ printf("Event ID : 0x%x\n", (da1_fifo[i+1] | da1_fifo[i+2] << 8));
+ printf("Event Data Size : 0x%x\n", da1_fifo[3]);
+ i = 4 + ((da1_fifo[3]) * 4);
+ }
+ printf("===============================================\n\n");
+ }
+}
+
+static int extract_dump_get_log(struct nvme_dev *dev, char *featurename, char *filename, char *sn,
+ int dumpsize, int transfersize, __u32 nsid, __u8 log_id,
+ __u8 lsp, __u64 offset, bool rae)
+{
+ int i = 0, err = 0;
+
+ char *data = calloc(transfersize, sizeof(char));
+ char filepath[FILE_NAME_SIZE] = {0,};
+ int output = 0;
+ int total_loop_cnt = dumpsize / transfersize;
+ int last_xfer_size = dumpsize % transfersize;
+
+ if (last_xfer_size)
+ total_loop_cnt++;
+ else
+ last_xfer_size = transfersize;
+
+ if (filename == 0)
+ snprintf(filepath, FILE_NAME_SIZE, "%s_%s.bin", featurename, sn);
+ else
+ snprintf(filepath, FILE_NAME_SIZE, "%s%s_%s.bin", filename, featurename, sn);
+
+ for (i = 0; i < total_loop_cnt; i++) {
+ memset(data, 0, transfersize);
+
+ struct nvme_get_log_args args = {
+ .lpo = offset,
+ .result = NULL,
+ .log = (void *)data,
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .lid = log_id,
+ .len = transfersize,
+ .nsid = nsid,
+ .lsp = lsp,
+ .uuidx = 0,
+ .rae = rae,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .csi = NVME_CSI_NVM,
+ .ot = false,
+ };
+
+ err = nvme_get_log(&args);
+ if (err) {
+ if (i > 0)
+ goto close_output;
+ else
+ goto end;
+ }
+
+ if (i != total_loop_cnt - 1) {
+ if (!i) {
+ output = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (output < 0) {
+ err = -13;
+ goto end;
+ }
+ }
+ if (write(output, data, transfersize) < 0) {
+ err = -10;
+ goto close_output;
+ }
+ } else {
+ if (write(output, data, last_xfer_size) < 0) {
+ err = -10;
+ goto close_output;
+ }
+ }
+ offset += transfersize;
+ printf("%d%%\r", (i + 1) * 100 / total_loop_cnt);
+ }
+ printf("100%%\nThe log file was saved at \"%s\"\n", filepath);
+
+close_output:
+ close(output);
+
+end:
+ free(data);
+ return err;
+}
+
+static int get_telemetry_dump(struct nvme_dev *dev, char *filename, char *sn,
+ enum TELEMETRY_TYPE tele_type, int data_area, bool header_print)
+{
+ __u32 err = 0, nsid = 0;
+ __u8 lsp = 0, rae = 0;
+ unsigned int i = 0;
+ char data[TELEMETRY_TRANSFER_SIZE] = { 0 };
+ char data1[1536] = { 0 };
+ char *featurename = 0;
+ struct telemetry_initiated_log *logheader = (struct telemetry_initiated_log *)data;
+ struct telemetry_data_area_1 *da1 = (struct telemetry_data_area_1 *)data1;
+ __u64 offset = 0, size = 0;
+ char dumpname[FILE_NAME_SIZE] = { 0 };
+
+ if (tele_type == TELEMETRY_TYPE_HOST_0) {
+ featurename = "Host(0)";
+ lsp = 0;
+ rae = 0;
+ tele_type = TELEMETRY_TYPE_HOST;
+ } else if (tele_type == TELEMETRY_TYPE_HOST_1) {
+ featurename = "Host(1)";
+ lsp = 1;
+ rae = 0;
+ tele_type = TELEMETRY_TYPE_HOST;
+ } else {
+ featurename = "Controller";
+ lsp = 0;
+ rae = 1;
+ }
+
+ err = get_telemetry_header(dev, nsid, tele_type, TELEMETRY_HEADER_SIZE,
+ (void *)data, lsp, rae);
+ if (err)
+ return err;
+
+ if (header_print)
+ print_telemetry_header(logheader, tele_type);
+ err = get_telemetry_data(dev, nsid, tele_type, 1536,
+ (void *)data1, lsp, rae, 512);
+ if (err)
+ return err;
+ print_telemetry_data_area_1(da1, tele_type);
+ char *da1_stat = calloc((da1->da1_stat_size * 4), sizeof(char));
+ err = get_telemetry_data(dev, nsid, tele_type, (da1->da1_stat_size) * 4,
+ (void *)da1_stat, lsp, rae, (da1->da1_stat_start) * 4);
+ if (err)
+ return err;
+ print_telemetry_da1_stat((void *)da1_stat, tele_type, (da1->da1_stat_size) * 4);
+ for (i = 0; i < 17 ; i++){
+ if (da1->event_fifo_da[i] == 1){
+ char *da1_fifo = calloc((da1->event_fifo_size[i]) * 4, sizeof(char));
+ err = get_telemetry_data(dev, nsid, tele_type, (da1->event_fifo_size[i]) * 4,
+ (void *)da1_stat, lsp, rae, (da1->event_fifo_start[i]) * 4);
+ if (err)
+ return err;
+ print_telemetry_da1_fifo((void *)da1_fifo, tele_type, (da1->event_fifo_size[i]) * 4);
+ }
+ }
+ char *da2_stat = calloc((da1->da2_stat_size * 4), sizeof(char));
+ err = get_telemetry_data(dev, nsid, tele_type, (da1->da2_stat_size) * 4,
+ (void *)da2_stat, lsp, rae, (da1->da2_stat_start) * 4);
+ if (err)
+ return err;
+ print_telemetry_da2_stat((void *)da2_stat, tele_type, (da1->da2_stat_size) * 4);
+ for (i = 0; i < 17 ; i++){
+ if (da1->event_fifo_da[i] == 2){
+ char *da1_fifo = calloc((da1->event_fifo_size[i]) * 4, sizeof(char));
+ err = get_telemetry_data(dev, nsid, tele_type, (da1->event_fifo_size[i]) * 4,
+ (void *)da1_stat, lsp, rae, (da1->event_fifo_start[i]) * 4);
+ if (err)
+ return err;
+ print_telemetry_da2_fifo((void *)da1_fifo, tele_type, (da1->event_fifo_size[i]) * 4);
+ }
+ }
+
+ switch (data_area) {
+ case 1:
+ offset = TELEMETRY_HEADER_SIZE;
+ size = le16_to_cpu(logheader->DataArea1LastBlock);
+ break;
+ case 2:
+ offset = TELEMETRY_HEADER_SIZE
+ + (le16_to_cpu(logheader->DataArea1LastBlock) * TELEMETRY_BYTE_PER_BLOCK);
+ size = le16_to_cpu(logheader->DataArea2LastBlock)
+ - le16_to_cpu(logheader->DataArea1LastBlock);
+ break;
+ case 3:
+ offset = TELEMETRY_HEADER_SIZE
+ + (le16_to_cpu(logheader->DataArea2LastBlock) * TELEMETRY_BYTE_PER_BLOCK);
+ size = le16_to_cpu(logheader->DataArea3LastBlock)
+ - le16_to_cpu(logheader->DataArea2LastBlock);
+ break;
+ default:
+ break;
+ }
+
+ if (!size) {
+ printf("Telemetry %s Area %d is empty.\n", featurename, data_area);
+ return err;
+ }
+
+ snprintf(dumpname, FILE_NAME_SIZE,
+ "Telemetry_%s_Area_%d", featurename, data_area);
+ err = extract_dump_get_log(dev, dumpname, filename, sn, size * TELEMETRY_BYTE_PER_BLOCK,
+ TELEMETRY_TRANSFER_SIZE, nsid, tele_type,
+ 0, offset, rae);
+
+ return err;
+}
+
+static int ocp_telemetry_log(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ struct nvme_dev *dev;
+ int err = 0;
+ const char *desc = "Retrieve and save telemetry log.";
+ const char *type = "Telemetry Type; 'host[Create bit]' or 'controller'";
+ const char *area = "Telemetry Data Area; 1 or 3";
+ const char *file = "Output file name with path;\n"
+ "e.g. '-o ./path/name'\n'-o ./path1/path2/';\n"
+ "If requested path does not exist, the directory will be newly created.";
+
+ __u32 nsid = NVME_NSID_ALL;
+ struct stat nvme_stat;
+ char sn[21] = {0,};
+ struct nvme_id_ctrl ctrl;
+ bool is_support_telemetry_controller;
+
+ int tele_type = 0;
+ int tele_area = 0;
+
+ struct config {
+ char *type;
+ int area;
+ char *file;
+ };
+
+ struct config cfg = {
+ .type = NULL,
+ .area = 0,
+ .file = NULL,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_STR("telemetry_type", 't', &cfg.type, type),
+ OPT_INT("telemetry_data_area", 'a', &cfg.area, area),
+ OPT_FILE("output-file", 'o', &cfg.file, file),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ err = fstat(dev_fd(dev), &nvme_stat);
+ if (err < 0)
+ return err;
+
+ if (S_ISBLK(nvme_stat.st_mode)) {
+ err = nvme_get_nsid(dev_fd(dev), &nsid);
+ if (err < 0)
+ return err;
+ }
+
+ err = nvme_identify_ctrl(dev_fd(dev), &ctrl);
+ if (err)
+ return err;
+
+ get_serial_number(&ctrl, sn);
+
+ is_support_telemetry_controller = ((ctrl.lpa & 0x8) >> 3);
+
+ if (!cfg.type && !cfg.area) {
+ tele_type = TELEMETRY_TYPE_NONE;
+ tele_area = 0;
+ } else if (cfg.type && cfg.area) {
+ if (!strcmp(cfg.type, "host0"))
+ tele_type = TELEMETRY_TYPE_HOST_0;
+ else if (!strcmp(cfg.type, "host1"))
+ tele_type = TELEMETRY_TYPE_HOST_1;
+ else if (!strcmp(cfg.type, "controller"))
+ tele_type = TELEMETRY_TYPE_CONTROLLER;
+
+ tele_area = cfg.area;
+
+ if ((tele_area != 1 && tele_area != 3) ||
+ (tele_type == TELEMETRY_TYPE_CONTROLLER && tele_area != 3)) {
+ printf("\nUnsupported parameters entered.\n");
+ printf("Possible combinations; {'host0',1}, {'host0',3}, {'host1',1}, {'host1',3}, {'controller',3}\n");
+ return err;
+ }
+ } else {
+ printf("\nShould provide these all; 'telemetry_type' and 'telemetry_data_area'\n");
+ return err;
+ }
+
+ if (tele_type == TELEMETRY_TYPE_NONE) {
+ printf("\n-------------------------------------------------------------\n");
+ /* Host 0 (lsp == 0) must be executed before Host 1 (lsp == 1). */
+ printf("\nExtracting Telemetry Host 0 Dump (Data Area 1)...\n");
+
+ err = get_telemetry_dump(dev, cfg.file, sn,
+ TELEMETRY_TYPE_HOST_0, 1, true);
+ if (err)
+ fprintf(stderr, "NVMe Status: %s(%x)\n", nvme_status_to_string(err, false), err);
+
+ printf("\n-------------------------------------------------------------\n");
+
+ printf("\nExtracting Telemetry Host 0 Dump (Data Area 3)...\n");
+
+ err = get_telemetry_dump(dev, cfg.file, sn,
+ TELEMETRY_TYPE_HOST_0, 3, false);
+ if (err)
+ fprintf(stderr, "NVMe Status: %s(%x)\n", nvme_status_to_string(err, false), err);
+
+ printf("\n-------------------------------------------------------------\n");
+
+ printf("\nExtracting Telemetry Host 1 Dump (Data Area 1)...\n");
+
+ err = get_telemetry_dump(dev, cfg.file, sn,
+ TELEMETRY_TYPE_HOST_1, 1, true);
+ if (err)
+ fprintf(stderr, "NVMe Status: %s(%x)\n", nvme_status_to_string(err, false), err);
+
+ printf("\n-------------------------------------------------------------\n");
+
+ printf("\nExtracting Telemetry Host 1 Dump (Data Area 3)...\n");
+
+ err = get_telemetry_dump(dev, cfg.file, sn,
+ TELEMETRY_TYPE_HOST_1, 3, false);
+ if (err)
+ fprintf(stderr, "NVMe Status: %s(%x)\n", nvme_status_to_string(err, false), err);
+
+ printf("\n-------------------------------------------------------------\n");
+
+ printf("\nExtracting Telemetry Controller Dump (Data Area 3)...\n");
+
+ if (is_support_telemetry_controller == true) {
+ err = get_telemetry_dump(dev, cfg.file, sn,
+ TELEMETRY_TYPE_CONTROLLER, 3, true);
+ if (err)
+ fprintf(stderr, "NVMe Status: %s(%x)\n", nvme_status_to_string(err, false), err);
+ }
+
+ printf("\n-------------------------------------------------------------\n");
+ } else if (tele_type == TELEMETRY_TYPE_CONTROLLER) {
+ printf("Extracting Telemetry Controller Dump (Data Area %d)...\n", tele_area);
+
+ if (is_support_telemetry_controller == true) {
+ err = get_telemetry_dump(dev, cfg.file, sn, tele_type, tele_area, true);
+ if (err)
+ fprintf(stderr, "NVMe Status: %s(%x)\n", nvme_status_to_string(err, false), err);
+ }
+ } else {
+ printf("Extracting Telemetry Host(%d) Dump (Data Area %d)...\n",
+ (tele_type == TELEMETRY_TYPE_HOST_0) ? 0 : 1, tele_area);
+
+ err = get_telemetry_dump(dev, cfg.file, sn, tele_type, tele_area, true);
+ if (err)
+ fprintf(stderr, "NVMe Status: %s(%x)\n", nvme_status_to_string(err, false), err);
+ }
+
+ printf("telemetry-log done.\n");
+
+return err;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+/// Unsupported Requirement Log Page (LID : C5h)
+
+/* C5 Unsupported Requirement Log Page */
+#define C5_GUID_LENGTH 16
+#define C5_UNSUPPORTED_REQS_LEN 4096
+#define C5_UNSUPPORTED_REQS_OPCODE 0xC5
+#define C5_UNSUPPORTED_REQS_LOG_VERSION 0x1
+#define C5_NUM_UNSUPPORTED_REQ_ENTRIES 253
+
+static __u8 unsupported_req_guid[C5_GUID_LENGTH] = {
+ 0x2F, 0x72, 0x9C, 0x0E,
+ 0x99, 0x23, 0x2C, 0xBB,
+ 0x63, 0x48, 0x32, 0xD0,
+ 0xB7, 0x98, 0xBB, 0xC7
+};
+
+/*
+ * struct unsupported_requirement_log - unsupported requirement list
+ * @unsupported_count: Number of Unsupported Requirement IDs
+ * @rsvd1: Reserved
+ * @unsupported_req_list: Unsupported Requirements lists upto 253.
+ * @rsvd2: Reserved
+ * @log_page_version: indicates the version of the mapping this log page uses.
+ * Shall be set to 0001h
+ * @log_page_guid: Shall be set to C7BB98B7D0324863BB2C23990E9C722Fh.
+ */
+struct __packed unsupported_requirement_log {
+ __le16 unsupported_count;
+ __u8 rsvd1[14];
+ __u8 unsupported_req_list[C5_NUM_UNSUPPORTED_REQ_ENTRIES][16];
+ __u8 rsvd2[14];
+ __le16 log_page_version;
+ __u8 log_page_guid[C5_GUID_LENGTH];
+};
+
+/* Function declaration for unsupported requirement log page (LID:C5h) */
+static int ocp_unsupported_requirements_log(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin);
+
+static int ocp_print_C5_log_normal(struct nvme_dev *dev,
+ struct unsupported_requirement_log *log_data)
+{
+ int j;
+
+ printf("Unsupported Requirement-C5 Log Page Data-\n");
+
+ printf(" Number Unsupported Req IDs : 0x%x\n", le16_to_cpu(log_data->unsupported_count));
+
+ for (j = 0; j < le16_to_cpu(log_data->unsupported_count); j++)
+ printf(" Unsupported Requirement List %d : %s\n", j, log_data->unsupported_req_list[j]);
+
+ printf(" Log Page Version : 0x%x\n", le16_to_cpu(log_data->log_page_version));
+ printf(" Log page GUID : 0x");
+ for (j = C5_GUID_LENGTH - 1; j >= 0; j--)
+ printf("%x", log_data->log_page_guid[j]);
+ printf("\n");
+
+ return 0;
+}
+
+static void ocp_print_C5_log_json(struct unsupported_requirement_log *log_data)
+{
+ int j;
+ struct json_object *root;
+ char unsup_req_list_str[40];
+ char guid_buf[C5_GUID_LENGTH];
+ char *guid = guid_buf;
+
+ root = json_create_object();
+
+ json_object_add_value_int(root, "Number Unsupported Req IDs", le16_to_cpu(log_data->unsupported_count));
+
+ memset((void *)unsup_req_list_str, 0, 40);
+ for (j = 0; j < le16_to_cpu(log_data->unsupported_count); j++) {
+ sprintf((char *)unsup_req_list_str, "Unsupported Requirement List %d", j);
+ json_object_add_value_string(root, unsup_req_list_str, (char *)log_data->unsupported_req_list[j]);
+ }
+
+ json_object_add_value_int(root, "Log Page Version", le16_to_cpu(log_data->log_page_version));
+
+ memset((void *)guid, 0, C5_GUID_LENGTH);
+ for (j = C5_GUID_LENGTH - 1; j >= 0; j--)
+ guid += sprintf(guid, "%02x", log_data->log_page_guid[j]);
+ json_object_add_value_string(root, "Log page GUID", guid_buf);
+
+ json_print_object(root, NULL);
+ printf("\n");
+
+ json_free_object(root);
+}
+
+static void ocp_print_c5_log_binary(struct unsupported_requirement_log *log_data)
+{
+ return d_raw((unsigned char *)log_data, sizeof(*log_data));
+}
+
+static int get_c5_log_page(struct nvme_dev *dev, char *format)
+{
+ enum nvme_print_flags fmt;
+ int ret;
+ __u8 *data;
+ int i;
+ struct unsupported_requirement_log *log_data;
+ int j;
+
+ ret = validate_output_format(format, &fmt);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR : OCP : invalid output format\n");
+ return ret;
+ }
+
+ data = (__u8 *)malloc(sizeof(__u8) * C5_UNSUPPORTED_REQS_LEN);
+ if (!data) {
+ fprintf(stderr, "ERROR : OCP : malloc : %s\n", strerror(errno));
+ return -1;
+ }
+ memset(data, 0, sizeof(__u8) * C5_UNSUPPORTED_REQS_LEN);
+
+ ret = nvme_get_log_simple(dev_fd(dev), C5_UNSUPPORTED_REQS_OPCODE,
+ C5_UNSUPPORTED_REQS_LEN, data);
+ if (!ret) {
+ log_data = (struct unsupported_requirement_log *)data;
+
+ /* check log page version */
+ if (log_data->log_page_version != C5_UNSUPPORTED_REQS_LOG_VERSION) {
+ fprintf(stderr, "ERROR : OCP : invalid unsupported requirement version\n");
+ ret = -1;
+ goto out;
+ }
+
+ /*
+ * check log page guid
+ * Verify GUID matches
+ */
+ for (i = 0; i < 16; i++) {
+ if (unsupported_req_guid[i] != log_data->log_page_guid[i]) {
+ fprintf(stderr, "ERROR : OCP : Unknown GUID in C5 Log Page data\n");
+ fprintf(stderr, "ERROR : OCP : Expected GUID: 0x");
+ for (j = 0; j < 16; j++)
+ fprintf(stderr, "%x", unsupported_req_guid[j]);
+ fprintf(stderr, "\nERROR : OCP : Actual GUID: 0x");
+ for (j = 0; j < 16; j++)
+ fprintf(stderr, "%x", log_data->log_page_guid[j]);
+ fprintf(stderr, "\n");
+
+ ret = -1;
+ goto out;
+ }
+ }
+
+ switch (fmt) {
+ case NORMAL:
+ ocp_print_C5_log_normal(dev, log_data);
+ break;
+ case JSON:
+ ocp_print_C5_log_json(log_data);
+ break;
+ case BINARY:
+ ocp_print_c5_log_binary(log_data);
+ break;
+ default:
+ break;
+ }
+ } else {
+ fprintf(stderr, "ERROR : OCP : Unable to read C3 data from buffer\n");
+ }
+
+out:
+ free(data);
+ return ret;
+}
+
+
+static int ocp_unsupported_requirements_log(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ const char *desc = "Retrieve unsupported requirements log data.";
+ struct nvme_dev *dev;
+ int ret = 0;
+
+ 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()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ ret = get_c5_log_page(dev, cfg.output_format);
+ if (ret)
+ fprintf(stderr, "ERROR : OCP : Failure reading the C5 Log Page, ret = %d\n", ret);
+
+ dev_close(dev);
+ return ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+/// Error Recovery Log Page(0xC1)
+
+#define C1_ERROR_RECOVERY_LOG_BUF_LEN 0x200
+#define C1_ERROR_RECOVERY_OPCODE 0xC1
+#define C1_ERROR_RECOVERY_VERSION 0x0002
+#define C1_GUID_LENGTH 16
+static __u8 error_recovery_guid[C1_GUID_LENGTH] = {
+ 0x44, 0xd9, 0x31, 0x21,
+ 0xfe, 0x30, 0x34, 0xae,
+ 0xab, 0x4d, 0xfd, 0x3d,
+ 0xba, 0x83, 0x19, 0x5a
+};
+
+/**
+ * struct ocp_error_recovery_log_page - Error Recovery Log Page
+ * @panic_reset_wait_time: Panic Reset Wait Time
+ * @panic_reset_action: Panic Reset Action
+ * @device_recover_action_1: Device Recovery Action 1
+ * @panic_id: Panic ID
+ * @device_capabilities: Device Capabilities
+ * @vendor_specific_recovery_opcode: Vendor Specific Recovery Opcode
+ * @reserved: Reserved
+ * @vendor_specific_command_cdw12: Vendor Specific Command CDW12
+ * @vendor_specific_command_cdw13: Vendor Specific Command CDW13
+ * @vendor_specific_command_timeout: Vendor Specific Command Timeout
+ * @device_recover_action_2: Device Recovery Action 2
+ * @device_recover_action_2_timeout: Device Recovery Action 2 Timeout
+ * @reserved2: Reserved
+ * @log_page_version: Log Page Version
+ * @log_page_guid: Log Page GUID
+ */
+struct __packed ocp_error_recovery_log_page {
+ __le16 panic_reset_wait_time; /* 2 bytes - 0x00 - 0x01 */
+ __u8 panic_reset_action; /* 1 byte - 0x02 */
+ __u8 device_recover_action_1; /* 1 byte - 0x03 */
+ __le64 panic_id; /* 8 bytes - 0x04 - 0x0B */
+ __le32 device_capabilities; /* 4 bytes - 0x0C - 0x0F */
+ __u8 vendor_specific_recovery_opcode; /* 1 byte - 0x10 */
+ __u8 reserved[0x3]; /* 3 bytes - 0x11 - 0x13 */
+ __le32 vendor_specific_command_cdw12; /* 4 bytes - 0x14 - 0x17 */
+ __le32 vendor_specific_command_cdw13; /* 4 bytes - 0x18 - 0x1B */
+ __u8 vendor_specific_command_timeout; /* 1 byte - 0x1C */
+ __u8 device_recover_action_2; /* 1 byte - 0x1D */
+ __u8 device_recover_action_2_timeout; /* 1 byte - 0x1E */
+ __u8 reserved2[0x1cf]; /* 463 bytes - 0x1F - 0x1ED */
+ __le16 log_page_version; /* 2 bytes - 0x1EE - 0x1EF */
+ __u8 log_page_guid[0x10]; /* 16 bytes - 0x1F0 - 0x1FF */
+};
+
+static void ocp_print_c1_log_normal(struct ocp_error_recovery_log_page *log_data);
+static void ocp_print_c1_log_json(struct ocp_error_recovery_log_page *log_data);
+static void ocp_print_c1_log_binary(struct ocp_error_recovery_log_page *log_data);
+static int get_c1_log_page(struct nvme_dev *dev, char *format);
+static int ocp_error_recovery_log(int argc, char **argv, struct command *cmd, struct plugin *plugin);
+
+static void ocp_print_c1_log_normal(struct ocp_error_recovery_log_page *log_data)
+{
+ int i;
+
+ printf(" Error Recovery/C1 Log Page Data\n");
+ printf(" Panic Reset Wait Time : 0x%x\n", le16_to_cpu(log_data->panic_reset_wait_time));
+ printf(" Panic Reset Action : 0x%x\n", log_data->panic_reset_action);
+ printf(" Device Recovery Action 1 : 0x%x\n", log_data->device_recover_action_1);
+ printf(" Panic ID : 0x%x\n", le32_to_cpu(log_data->panic_id));
+ printf(" Device Capabilities : 0x%x\n", le32_to_cpu(log_data->device_capabilities));
+ printf(" Vendor Specific Recovery Opcode : 0x%x\n", log_data->vendor_specific_recovery_opcode);
+ printf(" Vendor Specific Command CDW12 : 0x%x\n", le32_to_cpu(log_data->vendor_specific_command_cdw12));
+ printf(" Vendor Specific Command CDW13 : 0x%x\n", le32_to_cpu(log_data->vendor_specific_command_cdw13));
+ printf(" Vendor Specific Command Timeout : 0x%x\n", log_data->vendor_specific_command_timeout);
+ printf(" Device Recovery Action 2 : 0x%x\n", log_data->device_recover_action_2);
+ printf(" Device Recovery Action 2 Timeout : 0x%x\n", log_data->device_recover_action_2_timeout);
+ printf(" Log Page Version : 0x%x\n", le16_to_cpu(log_data->log_page_version));
+ printf(" Log page GUID : 0x");
+ for (i = C1_GUID_LENGTH - 1; i >= 0; i--)
+ printf("%x", log_data->log_page_guid[i]);
+ printf("\n");
+}
+
+static void ocp_print_c1_log_json(struct ocp_error_recovery_log_page *log_data)
+{
+ struct json_object *root;
+
+ root = json_create_object();
+ char guid[64];
+
+ json_object_add_value_int(root, "Panic Reset Wait Time", le16_to_cpu(log_data->panic_reset_wait_time));
+ json_object_add_value_int(root, "Panic Reset Action", log_data->panic_reset_action);
+ json_object_add_value_int(root, "Device Recovery Action 1", log_data->device_recover_action_1);
+ json_object_add_value_int(root, "Panic ID", le32_to_cpu(log_data->panic_id));
+ json_object_add_value_int(root, "Device Capabilities", le32_to_cpu(log_data->device_capabilities));
+ json_object_add_value_int(root, "Vendor Specific Recovery Opcode", log_data->vendor_specific_recovery_opcode);
+ json_object_add_value_int(root, "Vendor Specific Command CDW12", le32_to_cpu(log_data->vendor_specific_command_cdw12));
+ json_object_add_value_int(root, "Vendor Specific Command CDW13", le32_to_cpu(log_data->vendor_specific_command_cdw13));
+ json_object_add_value_int(root, "Vendor Specific Command Timeout", log_data->vendor_specific_command_timeout);
+ json_object_add_value_int(root, "Device Recovery Action 2", log_data->device_recover_action_2);
+ json_object_add_value_int(root, "Device Recovery Action 2 Timeout", log_data->device_recover_action_2_timeout);
+ json_object_add_value_int(root, "Log Page Version", le16_to_cpu(log_data->log_page_version));
+
+ memset((void *)guid, 0, 64);
+ sprintf((char *)guid, "0x%"PRIx64"%"PRIx64"", (uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[8]),
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[0]));
+ json_object_add_value_string(root, "Log page GUID", guid);
+
+ json_print_object(root, NULL);
+ printf("\n");
+ json_free_object(root);
+}
+
+static void ocp_print_c1_log_binary(struct ocp_error_recovery_log_page *log_data)
+{
+ return d_raw((unsigned char *)log_data, sizeof(*log_data));
+}
+
+static int get_c1_log_page(struct nvme_dev *dev, char *format)
+{
+ struct ocp_error_recovery_log_page *log_data;
+ enum nvme_print_flags fmt;
+ int ret;
+ __u8 *data;
+ int i, j;
+
+ ret = validate_output_format(format, &fmt);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR : OCP : invalid output format\n");
+ return ret;
+ }
+
+ data = (__u8 *)malloc(sizeof(__u8) * C1_ERROR_RECOVERY_LOG_BUF_LEN);
+ if (!data) {
+ fprintf(stderr, "ERROR : OCP : malloc : %s\n", strerror(errno));
+ return -1;
+ }
+ memset(data, 0, sizeof(__u8) * C1_ERROR_RECOVERY_LOG_BUF_LEN);
+
+ ret = nvme_get_log_simple(dev_fd(dev), C1_ERROR_RECOVERY_OPCODE, C1_ERROR_RECOVERY_LOG_BUF_LEN, data);
+
+ if (!ret) {
+ log_data = (struct ocp_error_recovery_log_page *)data;
+
+ /* check log page version */
+ if (log_data->log_page_version != C1_ERROR_RECOVERY_VERSION) {
+ fprintf(stderr, "ERROR : OCP : invalid error recovery log page version\n");
+ ret = -1;
+ goto out;
+ }
+
+ /*
+ * check log page guid
+ * Verify GUID matches
+ */
+ for (i = 0; i < 16; i++) {
+ if (error_recovery_guid[i] != log_data->log_page_guid[i]) {
+ fprintf(stderr, "ERROR : OCP : Unknown GUID in C1 Log Page data\n");
+ fprintf(stderr, "ERROR : OCP : Expected GUID: 0x");
+ for (j = 0; j < 16; j++)
+ fprintf(stderr, "%x", error_recovery_guid[j]);
+ fprintf(stderr, "\nERROR : OCP : Actual GUID: 0x");
+ for (j = 0; j < 16; j++)
+ fprintf(stderr, "%x", log_data->log_page_guid[j]);
+ fprintf(stderr, "\n");
+
+ ret = -1;
+ goto out;
+ }
+ }
+
+ switch (fmt) {
+ case NORMAL:
+ ocp_print_c1_log_normal(log_data);
+ break;
+ case JSON:
+ ocp_print_c1_log_json(log_data);
+ break;
+ case BINARY:
+ ocp_print_c1_log_binary(log_data);
+ break;
+ default:
+ break;
+ }
+ } else {
+ fprintf(stderr, "ERROR : OCP : Unable to read C1 data from buffer\n");
+ }
+
+out:
+ free(data);
+ return ret;
+}
+
+static int ocp_error_recovery_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Retrieve C1h Error Recovery Log data.";
+ struct nvme_dev *dev;
+ int ret = 0;
+
+ 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|binary"),
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ ret = get_c1_log_page(dev, cfg.output_format);
+ if (ret)
+ fprintf(stderr, "ERROR : OCP : Failure reading the C1h Log Page, ret = %d\n", ret);
+ dev_close(dev);
+ return ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+/// Device Capabilities (Log Identifier C4h) Requirements
+
+#define C4_DEV_CAP_REQ_LEN 0x1000
+#define C4_DEV_CAP_REQ_OPCODE 0xC4
+#define C4_DEV_CAP_REQ_VERSION 0x0001
+#define C4_GUID_LENGTH 16
+static __u8 dev_cap_req_guid[C4_GUID_LENGTH] = {
+ 0x97, 0x42, 0x05, 0x0d,
+ 0xd1, 0xe1, 0xc9, 0x98,
+ 0x5d, 0x49, 0x58, 0x4b,
+ 0x91, 0x3c, 0x05, 0xb7
+};
+
+/**
+ * struct ocp_device_capabilities_log_page - Device Capability Log page
+ * @pcie_exp_port: PCI Express Ports
+ * @oob_management_support: OOB Management Support
+ * @wz_cmd_support: Write Zeroes Command Support
+ * @sanitize_cmd_support: Sanitize Command Support
+ * @dsm_cmd_support: Dataset Management Command Support
+ * @wu_cmd_support: Write Uncorrectable Command Support
+ * @fused_operation_support: Fused Operation Support
+ * @min_valid_dssd_pwr_state: Minimum Valid DSSD Power State
+ * @dssd_pwr_state_desc: DSSD Power State Descriptors
+ * @vendor_specific_command_timeout: Vendor Specific Command Timeout
+ * @reserved: Reserved
+ * @log_page_version: Log Page Version
+ * @log_page_guid: Log Page GUID
+ */
+struct __packed ocp_device_capabilities_log_page {
+ __le16 pcie_exp_port;
+ __le16 oob_management_support;
+ __le16 wz_cmd_support;
+ __le16 sanitize_cmd_support;
+ __le16 dsm_cmd_support;
+ __le16 wu_cmd_support;
+ __le16 fused_operation_support;
+ __le16 min_valid_dssd_pwr_state;
+ __u8 dssd_pwr_state_desc[128];
+ __u8 reserved[3934];
+ __le16 log_page_version;
+ __u8 log_page_guid[16];
+};
+
+static void ocp_print_c4_log_normal(struct ocp_device_capabilities_log_page *log_data);
+static void ocp_print_c4_log_json(struct ocp_device_capabilities_log_page *log_data);
+static void ocp_print_c4_log_binary(struct ocp_device_capabilities_log_page *log_data);
+static int get_c4_log_page(struct nvme_dev *dev, char *format);
+static int ocp_device_capabilities_log(int argc, char **argv, struct command *cmd, struct plugin *plugin);
+
+static void ocp_print_c4_log_normal(struct ocp_device_capabilities_log_page *log_data)
+{
+ int i;
+
+ printf(" Device Capability/C4 Log Page Data\n");
+ printf(" PCI Express Ports : 0x%x\n", le16_to_cpu(log_data->pcie_exp_port));
+ printf(" OOB Management Support : 0x%x\n", le16_to_cpu(log_data->oob_management_support));
+ printf(" Write Zeroes Command Support : 0x%x\n", le16_to_cpu(log_data->wz_cmd_support));
+ printf(" Sanitize Command Support : 0x%x\n", le16_to_cpu(log_data->sanitize_cmd_support));
+ printf(" Dataset Management Command Support : 0x%x\n", le16_to_cpu(log_data->dsm_cmd_support));
+ printf(" Write Uncorrectable Command Support : 0x%x\n", le16_to_cpu(log_data->wu_cmd_support));
+ printf(" Fused Operation Support : 0x%x\n", le16_to_cpu(log_data->fused_operation_support));
+ printf(" Minimum Valid DSSD Power State : 0x%x\n", le16_to_cpu(log_data->min_valid_dssd_pwr_state));
+ printf(" DSSD Power State Descriptors : 0x");
+ for (i = 0; i <= 127; i++)
+ printf("%x", log_data->dssd_pwr_state_desc[i]);
+ printf("\n");
+ printf(" Log Page Version : 0x%x\n", le16_to_cpu(log_data->log_page_version));
+ printf(" Log page GUID : 0x");
+ for (i = C4_GUID_LENGTH - 1; i >= 0; i--)
+ printf("%x", log_data->log_page_guid[i]);
+ printf("\n");
+}
+
+static void ocp_print_c4_log_json(struct ocp_device_capabilities_log_page *log_data)
+{
+ struct json_object *root = json_create_object();
+ char guid[64];
+ int i;
+
+ json_object_add_value_int(root, "PCI Express Ports", le16_to_cpu(log_data->pcie_exp_port));
+ json_object_add_value_int(root, "OOB Management Support", le16_to_cpu(log_data->oob_management_support));
+ json_object_add_value_int(root, "Write Zeroes Command Support", le16_to_cpu(log_data->wz_cmd_support));
+ json_object_add_value_int(root, "Sanitize Command Support", le16_to_cpu(log_data->sanitize_cmd_support));
+ json_object_add_value_int(root, "Dataset Management Command Support", le16_to_cpu(log_data->dsm_cmd_support));
+ json_object_add_value_int(root, "Write Uncorrectable Command Support", le16_to_cpu(log_data->wu_cmd_support));
+ json_object_add_value_int(root, "Fused Operation Support", le16_to_cpu(log_data->fused_operation_support));
+ json_object_add_value_int(root, "Minimum Valid DSSD Power State", le16_to_cpu(log_data->min_valid_dssd_pwr_state));
+ for (i = 0; i <= 127; i++)
+ json_object_add_value_int(root, "DSSD Power State Descriptors", log_data->dssd_pwr_state_desc[i]);
+ json_object_add_value_int(root, "Log Page Version", le16_to_cpu(log_data->log_page_version));
+
+ memset((void *)guid, 0, 64);
+ sprintf((char *)guid, "0x%"PRIx64"%"PRIx64"", (uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[8]),
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[0]));
+ json_object_add_value_string(root, "Log page GUID", guid);
+
+ json_print_object(root, NULL);
+ printf("\n");
+ json_free_object(root);
+}
+
+static void ocp_print_c4_log_binary(struct ocp_device_capabilities_log_page *log_data)
+{
+ return d_raw((unsigned char *)log_data, sizeof(*log_data));
+}
+
+static int get_c4_log_page(struct nvme_dev *dev, char *format)
+{
+ struct ocp_device_capabilities_log_page *log_data;
+ enum nvme_print_flags fmt;
+ int ret;
+ __u8 *data;
+ int i, j;
+
+ ret = validate_output_format(format, &fmt);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR : OCP : invalid output format\n");
+ return ret;
+ }
+
+ data = (__u8 *)malloc(sizeof(__u8) * C4_DEV_CAP_REQ_LEN);
+ if (!data) {
+ fprintf(stderr, "ERROR : OCP : malloc : %s\n", strerror(errno));
+ return -1;
+ }
+ memset(data, 0, sizeof(__u8) * C4_DEV_CAP_REQ_LEN);
+
+ ret = nvme_get_log_simple(dev_fd(dev), C4_DEV_CAP_REQ_OPCODE, C4_DEV_CAP_REQ_LEN, data);
+
+ if (!ret) {
+ log_data = (struct ocp_device_capabilities_log_page *)data;
+
+ /* check log page version */
+ if (log_data->log_page_version != C4_DEV_CAP_REQ_VERSION) {
+ fprintf(stderr, "ERROR : OCP : invalid device capabilities log page version\n");
+ ret = -1;
+ goto out;
+ }
+
+ /*
+ * check log page guid
+ * Verify GUID matches
+ */
+ for (i = 0; i < 16; i++) {
+ if (dev_cap_req_guid[i] != log_data->log_page_guid[i]) {
+ fprintf(stderr, "ERROR : OCP : Unknown GUID in C4 Log Page data\n");
+ fprintf(stderr, "ERROR : OCP : Expected GUID: 0x");
+ for (j = 0; j < 16; j++)
+ fprintf(stderr, "%x", dev_cap_req_guid[j]);
+ fprintf(stderr, "\nERROR : OCP : Actual GUID: 0x");
+ for (j = 0; j < 16; j++)
+ fprintf(stderr, "%x", log_data->log_page_guid[j]);
+ fprintf(stderr, "\n");
+
+ ret = -1;
+ goto out;
+ }
+ }
+
+ switch (fmt) {
+ case NORMAL:
+ ocp_print_c4_log_normal(log_data);
+ break;
+ case JSON:
+ ocp_print_c4_log_json(log_data);
+ break;
+ case BINARY:
+ ocp_print_c4_log_binary(log_data);
+ break;
+ default:
+ break;
+ }
+ } else {
+ fprintf(stderr, "ERROR : OCP : Unable to read C4 data from buffer\n");
+ }
+
+out:
+ free(data);
+ return ret;
+}
+
+static int ocp_device_capabilities_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Retrieve C4h Device Capabilities Log data.";
+ struct nvme_dev *dev;
+ int ret = 0;
+
+ 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|binary"),
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ ret = get_c4_log_page(dev, cfg.output_format);
+ if (ret)
+ fprintf(stderr, "ERROR : OCP : Failure reading the C4h Log Page, ret = %d\n", ret);
+ dev_close(dev);
+ return ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+/// DSSD Power State (Feature Identifier C7h) Set Feature
+
+static int set_dssd_power_state(struct nvme_dev *dev, const __u32 nsid,
+ const __u8 fid, __u8 power_state, bool save,
+ bool uuid)
+{
+ __u32 result;
+ int err;
+ int uuid_index = 0;
+
+ if (uuid) {
+ /* OCP 2.0 requires UUID index support */
+ err = ocp_get_uuid_index(dev, &uuid_index);
+ if (err || !uuid_index) {
+ nvme_show_error("ERROR: No OCP UUID index found");
+ return err;
+ }
+ }
+
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .fid = fid,
+ .nsid = nsid,
+ .cdw11 = power_state,
+ .cdw12 = 0,
+ .save = save,
+ .uuidx = uuid_index,
+ .cdw15 = 0,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+
+ err = nvme_set_features(&args);
+ if (err > 0) {
+ nvme_show_status(err);
+ } else if (err < 0) {
+ nvme_show_perror("Define DSSD Power State");
+ fprintf(stderr, "Command failed while parsing.\n");
+ } else {
+ printf("Successfully set DSSD Power State (feature: 0xC7) to below values\n");
+ printf("DSSD Power State: 0x%x\n", power_state);
+ printf("Save bit Value: 0x%x\n", save);
+ }
+
+ return err;
+}
+
+static int set_dssd_power_state_feature(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ const char *desc = "Define DSSD Power State (Feature Identifier C7h) Set Feature.";
+ const char *power_state = "DSSD Power State to set in watts";
+ const char *save = "Specifies that the controller shall save the attribute";
+ const __u32 nsid = 0;
+ const __u8 fid = 0xC7;
+ struct nvme_dev *dev;
+ int err;
+
+ struct config {
+ __u8 power_state;
+ bool save;
+ };
+
+ struct config cfg = {
+ .power_state = 0,
+ .save = false,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_BYTE("power-state", 'p', &cfg.power_state, power_state),
+ OPT_FLAG("save", 's', &cfg.save, save),
+ OPT_FLAG("no-uuid", 'n', NULL,
+ "Skip UUID index search (UUID index not required for OCP 1.0)"),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ if (argconfig_parse_seen(opts, "power state"))
+ err = set_dssd_power_state(dev, nsid, fid, cfg.power_state,
+ cfg.save,
+ !argconfig_parse_seen(opts, "no-uuid"));
+
+ dev_close(dev);
+
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+/// plp_health_check_interval
+
+static int set_plp_health_check_interval(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+
+ const char *desc = "Define Issue Set Feature command (FID : 0xC6) PLP Health Check Interval";
+ const char *plp_health_interval = "[31:16]:PLP Health Check Interval";
+ const char *save = "Specifies that the controller shall save the attribute";
+ const __u32 nsid = 0;
+ const __u8 fid = 0xc6;
+ struct nvme_dev *dev;
+ int err;
+ __u32 result;
+ int uuid_index = 0;
+
+ struct config {
+ __le16 plp_health_interval;
+ bool save;
+ };
+
+ struct config cfg = {
+ .plp_health_interval = 0,
+ .save = false,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_BYTE("plp_health_interval", 'p', &cfg.plp_health_interval, plp_health_interval),
+ OPT_FLAG("save", 's', &cfg.save, save),
+ OPT_FLAG("no-uuid", 'n', NULL,
+ "Skip UUID index search (UUID index not required for OCP 1.0)"),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+
+ if (!argconfig_parse_seen(opts, "no-uuid")) {
+ /* OCP 2.0 requires UUID index support */
+ err = ocp_get_uuid_index(dev, &uuid_index);
+ if (err || !uuid_index) {
+ printf("ERROR: No OCP UUID index found");
+ return err;
+ }
+ }
+
+
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .fid = fid,
+ .nsid = nsid,
+ .cdw11 = cfg.plp_health_interval << 16,
+ .cdw12 = 0,
+ .save = cfg.save,
+ .uuidx = uuid_index,
+ .cdw15 = 0,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+
+ err = nvme_set_features(&args);
+ if (err > 0) {
+ nvme_show_status(err);
+ } else if (err < 0) {
+ nvme_show_perror("Define PLP Health Check Interval");
+ fprintf(stderr, "Command failed while parsing.\n");
+ } else {
+ printf("Successfully set the PLP Health Check Interval");
+ printf("PLP Health Check Interval: 0x%x\n", cfg.plp_health_interval);
+ printf("Save bit Value: 0x%x\n", cfg.save);
+ }
+ return err;
+}
+
+static int get_plp_health_check_interval(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+
+ const char *desc = "Define Issue Get Feature command (FID : 0xC6) PLP Health Check Interval";
+ const char *sel = "[0-3,8]: current/default/saved/supported/changed";
+ const __u32 nsid = 0;
+ const __u8 fid = 0xc6;
+ struct nvme_dev *dev;
+ __u32 result;
+ int err;
+
+ struct config {
+ __u8 sel;
+ };
+
+ struct config cfg = {
+ .sel = 0,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_BYTE("sel", 'S', &cfg.sel, sel),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .fid = fid,
+ .nsid = nsid,
+ .sel = cfg.sel,
+ .cdw11 = 0,
+ .uuidx = 0,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+
+ err = nvme_get_features(&args);
+ if (!err) {
+ printf("get-feature:0xC6 %s value: %#08x\n", nvme_select_to_string(cfg.sel), result);
+
+ if (cfg.sel == NVME_GET_FEATURES_SEL_SUPPORTED)
+ nvme_show_select_result(fid, result);
+ } else {
+ nvme_show_error("Could not get feature: 0xC6");
+ }
+
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+/// Telemetry String Log Format Log Page (LID : C9h)
+
+/* C9 Telemetry String Log Format Log Page */
+#define C9_GUID_LENGTH 16
+#define C9_TELEMETRY_STRING_LOG_ENABLE_OPCODE 0xC9
+#define C9_TELEMETRY_STR_LOG_LEN 432
+#define C9_TELEMETRY_STR_LOG_SIST_OFST 431
+
+/**
+ * struct telemetry_str_log_format - Telemetry String Log Format
+ * @log_page_version: indicates the version of the mapping this log page uses
+ * Shall be set to 01h.
+ * @reserved1: Reserved.
+ * @log_page_guid: Shall be set to B13A83691A8F408B9EA495940057AA44h.
+ * @sls: Shall be set to the number of DWORDS in the String Log.
+ * @reserved2: reserved.
+ * @sits: shall be set to the number of DWORDS in the Statistics
+ * Identifier String Table
+ * @ests: Shall be set to the number of DWORDS from byte 0 of this
+ * log page to the start of the Event String Table
+ * @estsz: shall be set to the number of DWORDS in the Event String Table
+ * @vu_eve_sts: Shall be set to the number of DWORDS from byte 0 of this
+ * log page to the start of the VU Event String Table
+ * @vu_eve_st_sz: shall be set to the number of DWORDS in the VU Event String Table
+ * @ascts: the number of DWORDS from byte 0 of this log page until the ASCII Table Starts.
+ * @asctsz: the number of DWORDS in the ASCII Table
+ * @fifo1: FIFO 0 ASCII String
+ * @fifo2: FIFO 1 ASCII String
+ * @fifo3: FIFO 2 ASCII String
+ * @fifo4: FIFO 3 ASCII String
+ * @fif05: FIFO 4 ASCII String
+ * @fifo6: FIFO 5 ASCII String
+ * @fifo7: FIFO 6 ASCII String
+ * @fifo8: FIFO 7 ASCII String
+ * @fifo9: FIFO 8 ASCII String
+ * @fifo10: FIFO 9 ASCII String
+ * @fif011: FIFO 10 ASCII String
+ * @fif012: FIFO 11 ASCII String
+ * @fifo13: FIFO 12 ASCII String
+ * @fif014: FIFO 13 ASCII String
+ * @fif015: FIFO 14 ASCII String
+ * @fif016: FIFO 15 ASCII String
+ * @reserved3: reserved
+ */
+struct __attribute__((__packed__)) telemetry_str_log_format {
+ __u8 log_page_version;
+ __u8 reserved1[15];
+ __u8 log_page_guid[C9_GUID_LENGTH];
+ __le64 sls;
+ __u8 reserved2[24];
+ __le64 sits;
+ __le64 sitsz;
+ __le64 ests;
+ __le64 estsz;
+ __le64 vu_eve_sts;
+ __le64 vu_eve_st_sz;
+ __le64 ascts;
+ __le64 asctsz;
+ __u8 fifo1[16];
+ __u8 fifo2[16];
+ __u8 fifo3[16];
+ __u8 fifo4[16];
+ __u8 fifo5[16];
+ __u8 fifo6[16];
+ __u8 fifo7[16];
+ __u8 fifo8[16];
+ __u8 fifo9[16];
+ __u8 fifo10[16];
+ __u8 fifo11[16];
+ __u8 fifo12[16];
+ __u8 fifo13[16];
+ __u8 fifo14[16];
+ __u8 fifo15[16];
+ __u8 fifo16[16];
+ __u8 reserved3[48];
+};
+
+/*
+ * struct statistics_id_str_table_entry - Statistics Identifier String Table Entry
+ * @vs_si: Shall be set the Vendor Unique Statistic Identifier number.
+ * @reserved1: Reserved
+ * @ascii_id_len: Shall be set the number of ASCII Characters that are valid.
+ * @ascii_id_ofst: Shall be set to the offset from DWORD 0/Byte 0 of the Start
+ * of the ASCII Table to the first character of the string for
+ * this Statistic Identifier string..
+ * @reserved2 reserved
+ */
+struct __attribute__((__packed__)) statistics_id_str_table_entry {
+ __le16 vs_si;
+ __u8 reserved1;
+ __u8 ascii_id_len;
+ __le64 ascii_id_ofst;
+ __le32 reserved2;
+};
+
+/*
+ * struct event_id_str_table_entry - Event Identifier String Table Entry
+ * @deb_eve_class: Shall be set the Debug Class.
+ * @ei: Shall be set to the Event Identifier
+ * @ascii_id_len: Shall be set the number of ASCII Characters that are valid.
+ * @ascii_id_ofst: This is the offset from DWORD 0/ Byte 0 of the start of the
+ * ASCII table to the ASCII data for this identifier
+ * @reserved2 reserved
+ */
+struct __attribute__((__packed__)) event_id_str_table_entry {
+ __u8 deb_eve_class;
+ __le16 ei;
+ __u8 ascii_id_len;
+ __le64 ascii_id_ofst;
+ __le32 reserved2;
+};
+
+/*
+ * struct vu_event_id_str_table_entry - VU Event Identifier String Table Entry
+ * @deb_eve_class: Shall be set the Debug Class.
+ * @vu_ei: Shall be set to the VU Event Identifier
+ * @ascii_id_len: Shall be set the number of ASCII Characters that are valid.
+ * @ascii_id_ofst: This is the offset from DWORD 0/ Byte 0 of the start of the
+ * ASCII table to the ASCII data for this identifier
+ * @reserved reserved
+ */
+struct __attribute__((__packed__)) vu_event_id_str_table_entry {
+ __u8 deb_eve_class;
+ __le16 vu_ei;
+ __u8 ascii_id_len;
+ __le64 ascii_id_ofst;
+ __le32 reserved;
+};
+
+/* Function declaration for Telemetry String Log Format (LID:C9h) */
+static int ocp_telemetry_str_log_format(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin);
+
+
+static int ocp_print_C9_log_normal(struct telemetry_str_log_format *log_data,__u8 *log_data_buf)
+{
+ //calculating the index value for array
+ __le64 stat_id_index = (log_data->sitsz * 4) / 16;
+ __le64 eve_id_index = (log_data->estsz * 4) / 16;
+ __le64 vu_eve_index = (log_data->vu_eve_st_sz * 4) / 16;
+ __le64 ascii_table_index = (log_data->asctsz * 4);
+ //Calculating the offset for dynamic fields.
+ __le64 stat_id_str_table_ofst = C9_TELEMETRY_STR_LOG_SIST_OFST + (log_data->sitsz * 4);
+ __le64 event_str_table_ofst = stat_id_str_table_ofst + (log_data->estsz * 4);
+ __le64 vu_event_str_table_ofst = event_str_table_ofst + (log_data->vu_eve_st_sz * 4);
+ __le64 ascii_table_ofst = vu_event_str_table_ofst + (log_data->asctsz * 4);
+ struct statistics_id_str_table_entry stat_id_str_table_arr[stat_id_index];
+ struct event_id_str_table_entry event_id_str_table_arr[eve_id_index];
+ struct vu_event_id_str_table_entry vu_event_id_str_table_arr[vu_eve_index];
+ __u8 ascii_table_info_arr[ascii_table_index];
+ int j;
+
+ printf(" Log Page Version : 0x%x\n", log_data->log_page_version);
+
+ printf(" Reserved : ");
+ for (j = 0; j < 15; j++)
+ printf("%d", log_data->reserved1[j]);
+ printf("\n");
+
+ printf(" Log page GUID : 0x");
+ for (j = C9_GUID_LENGTH - 1; j >= 0; j--)
+ printf("%x", log_data->log_page_guid[j]);
+ printf("\n");
+
+ printf(" Telemetry String Log Size : 0x%lx\n", le64_to_cpu(log_data->sls));
+
+ printf(" Reserved : ");
+ for (j = 0; j < 24; j++)
+ printf("%d", log_data->reserved2[j]);
+ printf("\n");
+
+ printf(" Statistics Identifier String Table Start : 0x%lx\n", le64_to_cpu(log_data->sits));
+ printf(" Statistics Identifier String Table Size : 0x%lx\n", le64_to_cpu(log_data->sitsz));
+ printf(" Event String Table Start : 0x%lx\n", le64_to_cpu(log_data->ests));
+ printf(" Event String Table Size : 0x%lx\n", le64_to_cpu(log_data->estsz));
+ printf(" VU Event String Table Start : 0x%lx\n", le64_to_cpu(log_data->vu_eve_sts));
+ printf(" VU Event String Table Size : 0x%lx\n", le64_to_cpu(log_data->vu_eve_st_sz));
+ printf(" ASCII Table Start : 0x%lx\n", le64_to_cpu(log_data->ascts));
+ printf(" ASCII Table Size : 0x%lx\n", le64_to_cpu(log_data->asctsz));
+
+ printf(" FIFO 1 ASCII String\n");
+ printf(" index value ascii_val\n");
+ for (j = 0; j < 16; j++){
+ printf(" %d %d %c \n", j, log_data->fifo1[j], log_data->fifo1[j]);
+ }
+
+ printf(" FIFO 2 ASCII String\n");
+ printf(" index value ascii_val\n");
+ for (j = 0; j < 16; j++){
+ printf(" %d %d %c \n", j, log_data->fifo2[j], log_data->fifo2[j]);
+ }
+
+ printf(" FIFO 3 ASCII String\n");
+ printf(" index value ascii_val\n");
+ for (j = 0; j < 16; j++){
+ printf(" %d %d %c \n", j, log_data->fifo3[j], log_data->fifo3[j]);
+ }
+
+ printf(" FIFO 4 ASCII String\n");
+ printf(" index value ascii_val\n");
+ for (j = 0; j < 16; j++){
+
+ printf(" %d %d %c \n", j, log_data->fifo4[j], log_data->fifo4[j]);
+ }
+
+ printf(" FIFO 5 ASCII String\n");
+ printf(" index value ascii_val\n");
+ for (j = 0; j < 16; j++){
+ printf(" %d %d %c \n", j, log_data->fifo5[j], log_data->fifo5[j]);
+ }
+
+ printf(" FIFO 6 ASCII String\n");
+ printf(" index value ascii_val\n");
+ for (j = 0; j < 16; j++){
+ printf(" %d %d %c \n", j, log_data->fifo6[j], log_data->fifo6[j]);
+ }
+
+ printf(" FIFO 7 ASCII String\n");
+ printf(" index value ascii_val\n");
+ for (j = 0; j < 16; j++){
+ printf(" %d %d %c \n", j, log_data->fifo7[j], log_data->fifo7[j]);
+ }
+
+ printf(" FIFO 8 ASCII String\n");
+ printf(" index value ascii_val\n");
+ for (j = 0; j < 16; j++){
+ printf("index value ascii_val");
+ printf(" %d %d %c \n", j, log_data->fifo8[j], log_data->fifo8[j]);
+ }
+
+ printf(" FIFO 9 ASCII String\n");
+ printf(" index value ascii_val\n");
+ for (j = 0; j < 16; j++){
+ printf(" %d %d %c \n", j, log_data->fifo9[j], log_data->fifo9[j]);
+ }
+
+ printf(" FIFO 10 ASCII String\n");
+ printf(" index value ascii_val\n");
+ for (j = 0; j < 16; j++){
+ printf(" %d %d %c \n", j, log_data->fifo10[j], log_data->fifo10[j]);
+ }
+
+ printf(" FIFO 11 ASCII String\n");
+ printf(" index value ascii_val\n");
+ for (j = 0; j < 16; j++){
+ printf(" %d %d %c \n", j, log_data->fifo11[j], log_data->fifo11[j]);
+ }
+
+ printf(" FIFO 12 ASCII String\n");
+ printf(" index value ascii_val\n");
+ for (j = 0; j < 16; j++){
+ printf(" %d %d %c \n", j, log_data->fifo12[j], log_data->fifo12[j]);
+ }
+
+ printf(" FIFO 13 ASCII String\n");
+ printf(" index value ascii_val\n");
+ for (j = 0; j < 16; j++){
+ printf(" %d %d %c \n", j, log_data->fifo13[j], log_data->fifo13[j]);
+ }
+
+ printf(" FIFO 14 ASCII String\n");
+ printf(" index value ascii_val\n");
+ for (j = 0; j < 16; j++){
+ printf(" %d %d %c \n", j, log_data->fifo14[j], log_data->fifo14[j]);
+ }
+
+ printf(" FIFO 15 ASCII String\n");
+ printf(" index value ascii_val\n");
+ for (j = 0; j < 16; j++){
+ printf(" %d %d %c \n", j, log_data->fifo15[j], log_data->fifo16[j]);
+ }
+
+ printf(" FIFO 16 ASCII String\n");
+ printf(" index value ascii_val\n");
+ for (j = 0; j < 16; j++){
+ printf(" %d %d %c \n", j, log_data->fifo16[j], log_data->fifo16[j]);
+ }
+
+ printf(" Reserved : ");
+ for (j = 0; j < 48; j++)
+ printf("%d", log_data->reserved3[j]);
+ printf("\n");
+
+ memcpy(stat_id_str_table_arr, (__u8*)log_data_buf + stat_id_str_table_ofst, (log_data->sitsz * 4));
+ memcpy(event_id_str_table_arr, (__u8*)log_data_buf + event_str_table_ofst, (log_data->estsz * 4));
+ memcpy(vu_event_id_str_table_arr, (__u8*)log_data_buf + vu_event_str_table_ofst, (log_data->vu_eve_st_sz * 4));
+ memcpy(ascii_table_info_arr, (__u8*)log_data_buf + ascii_table_ofst, (log_data->asctsz * 4));
+
+ printf(" Statistics Identifier String Table\n");
+ for (j = 0; j < stat_id_index; j++){
+ printf(" Vendor Specific Statistic Identifier : 0x%x\n",le16_to_cpu(stat_id_str_table_arr[j].vs_si));
+ printf(" Reserved : 0x%d",stat_id_str_table_arr[j].reserved1);
+ printf(" ASCII ID Length : 0x%x\n",stat_id_str_table_arr[j].ascii_id_len);
+ printf(" ASCII ID offset : 0x%lx\n",le64_to_cpu(stat_id_str_table_arr[j].ascii_id_ofst));
+ printf(" Reserved : 0x%d\n",stat_id_str_table_arr[j].reserved2);
+ }
+
+ printf(" Event Identifier String Table Entry\n");
+ for (j = 0; j < eve_id_index; j++){
+ printf(" Debug Event Class : 0x%x\n",event_id_str_table_arr[j].deb_eve_class);
+ printf(" Event Identifier : 0x%x\n",le16_to_cpu(event_id_str_table_arr[j].ei));
+ printf(" ASCII ID Length : 0x%x\n",event_id_str_table_arr[j].ascii_id_len);
+ printf(" ASCII ID offset : 0x%lx\n",le64_to_cpu(event_id_str_table_arr[j].ascii_id_ofst));
+ printf(" Reserved : 0x%d\n",event_id_str_table_arr[j].reserved2);
+
+ }
+
+ printf(" VU Event Identifier String Table Entry\n");
+ for (j = 0; j < vu_eve_index; j++){
+ printf(" Debug Event Class : 0x%x\n",vu_event_id_str_table_arr[j].deb_eve_class);
+ printf(" VU Event Identifier : 0x%x\n",le16_to_cpu(vu_event_id_str_table_arr[j].vu_ei));
+ printf(" ASCII ID Length : 0x%x\n",vu_event_id_str_table_arr[j].ascii_id_len);
+ printf(" ASCII ID offset : 0x%lx\n",le64_to_cpu(vu_event_id_str_table_arr[j].ascii_id_ofst));
+ printf(" Reserved : 0x%d\n",vu_event_id_str_table_arr[j].reserved);
+
+ }
+
+ printf(" ASCII Table\n");
+ printf(" Byte Data_Byte ASCII_Character\n");
+ for (j = 0; j < ascii_table_index; j++){
+ printf(" %lld 0x%x %c \n",ascii_table_ofst+j,ascii_table_info_arr[j],ascii_table_info_arr[j]);
+ }
+ return 0;
+}
+
+static int ocp_print_C9_log_json(struct telemetry_str_log_format *log_data,__u8 *log_data_buf)
+{
+ struct json_object *root = json_create_object();
+ struct json_object *stat_table = json_create_object();
+ struct json_object *eve_table = json_create_object();
+ struct json_object *vu_eve_table = json_create_object();
+ struct json_object *entry = json_create_object();
+ char res_arr[48];
+ char *res = res_arr;
+ char guid_buf[C9_GUID_LENGTH];
+ char *guid = guid_buf;
+ char fifo_arr[16];
+ char *fifo = fifo_arr;
+ //calculating the index value for array
+ __le64 stat_id_index = (log_data->sitsz * 4) / 16;
+ __le64 eve_id_index = (log_data->estsz * 4) / 16;
+ __le64 vu_eve_index = (log_data->vu_eve_st_sz * 4) / 16;
+ __le64 ascii_table_index = (log_data->asctsz * 4);
+ //Calculating the offset for dynamic fields.
+ __le64 stat_id_str_table_ofst = C9_TELEMETRY_STR_LOG_SIST_OFST + (log_data->sitsz * 4);
+ __le64 event_str_table_ofst = stat_id_str_table_ofst + (log_data->estsz * 4);
+ __le64 vu_event_str_table_ofst = event_str_table_ofst + (log_data->vu_eve_st_sz * 4);
+ __le64 ascii_table_ofst = vu_event_str_table_ofst + (log_data->asctsz * 4);
+ struct statistics_id_str_table_entry stat_id_str_table_arr[stat_id_index];
+ struct event_id_str_table_entry event_id_str_table_arr[eve_id_index];
+ struct vu_event_id_str_table_entry vu_event_id_str_table_arr[vu_eve_index];
+ __u8 ascii_table_info_arr[ascii_table_index];
+ char ascii_buf[ascii_table_index];
+ char *ascii = ascii_buf;
+ int j;
+
+ json_object_add_value_int(root, "Log Page Version", le16_to_cpu(log_data->log_page_version));
+
+ memset((__u8 *)res, 0, 15);
+ for (j = 0; j < 15; j++)
+ res += sprintf(res, "%d", log_data->reserved1[j]);
+ json_object_add_value_string(root, "Reserved", res_arr);
+
+ memset((void *)guid, 0, C9_GUID_LENGTH);
+ for (j = C9_GUID_LENGTH - 1; j >= 0; j--)
+ guid += sprintf(guid, "%02x", log_data->log_page_guid[j]);
+ json_object_add_value_string(root, "Log page GUID", guid_buf);
+
+ json_object_add_value_int(root, "Telemetry String Log Size", le64_to_cpu(log_data->sls));
+
+ memset((__u8 *)res, 0, 24);
+ for (j = 0; j < 24; j++)
+ res += sprintf(res, "%d", log_data->reserved2[j]);
+ json_object_add_value_string(root, "Reserved", res_arr);
+
+ json_object_add_value_int(root, "Statistics Identifier String Table Start", le64_to_cpu(log_data->sits));
+ json_object_add_value_int(root, "Event String Table Start", le64_to_cpu(log_data->ests));
+ json_object_add_value_int(root, "Event String Table Size", le64_to_cpu(log_data->estsz));
+ json_object_add_value_int(root, "VU Event String Table Start", le64_to_cpu(log_data->vu_eve_sts));
+ json_object_add_value_int(root, "VU Event String Table Size", le64_to_cpu(log_data->vu_eve_st_sz));
+ json_object_add_value_int(root, "ASCII Table Start", le64_to_cpu(log_data->ascts));
+ json_object_add_value_int(root, "ASCII Table Size", le64_to_cpu(log_data->asctsz));
+
+ memset((void *)fifo, 0, 16);
+ for (j = 0; j < 16; j++)
+ fifo += sprintf(fifo, "%c", log_data->fifo1[j]);
+ json_object_add_value_string(root, "FIFO 1 ASCII String", fifo_arr);
+
+ memset((void *)fifo, 0, 16);
+ for (j = 0; j < 16; j++)
+ fifo += sprintf(fifo, "%c", log_data->fifo2[j]);
+ json_object_add_value_string(root, "FIFO 2 ASCII String", fifo_arr);
+
+ memset((void *)fifo, 0, 16);
+ for (j = 0; j < 16; j++)
+ fifo += sprintf(fifo, "%c", log_data->fifo3[j]);
+ json_object_add_value_string(root, "FIFO 3 ASCII String", fifo_arr);
+
+ memset((void *)fifo, 0, 16);
+ for (j = 0; j < 16; j++)
+ fifo += sprintf(fifo, "%c", log_data->fifo4[j]);
+ json_object_add_value_string(root, "FIFO 4 ASCII String", fifo_arr);
+
+ memset((void *)fifo, 0, 16);
+ for (j = 0; j < 16; j++)
+ fifo += sprintf(fifo, "%c", log_data->fifo5[j]);
+ json_object_add_value_string(root, "FIFO 5 ASCII String", fifo_arr);
+
+ memset((void *)fifo, 0, 16);
+ for (j = 0; j < 16; j++)
+ fifo += sprintf(fifo, "%c", log_data->fifo6[j]);
+ json_object_add_value_string(root, "FIFO 6 ASCII String", fifo_arr);
+
+ memset((void *)fifo, 0, 16);
+ for (j = 0; j < 16; j++)
+ fifo += sprintf(fifo, "%c", log_data->fifo7[j]);
+ json_object_add_value_string(root, "FIFO 7 ASCII String", fifo_arr);
+
+ memset((void *)fifo, 0, 16);
+ for (j = 0; j < 16; j++)
+ fifo += sprintf(fifo, "%c", log_data->fifo8[j]);
+ json_object_add_value_string(root, "FIFO 8 ASCII String", fifo_arr);
+
+ memset((void *)fifo, 0, 16);
+ for (j = 0; j < 16; j++)
+ fifo += sprintf(fifo, "%c", log_data->fifo9[j]);
+ json_object_add_value_string(root, "FIFO 9 ASCII String", fifo_arr);
+
+ memset((void *)fifo, 0, 16);
+ for (j = 0; j < 16; j++)
+ fifo += sprintf(fifo, "%c", log_data->fifo10[j]);
+ json_object_add_value_string(root, "FIFO 10 ASCII String", fifo_arr);
+
+ memset((void *)fifo, 0, 16);
+ for (j = 0; j < 16; j++)
+ fifo += sprintf(fifo, "%c", log_data->fifo11[j]);
+ json_object_add_value_string(root, "FIFO 11 ASCII String", fifo_arr);
+
+ memset((void *)fifo, 0, 16);
+ for (j = 0; j < 16; j++)
+ fifo += sprintf(fifo, "%c", log_data->fifo12[j]);
+ json_object_add_value_string(root, "FIFO 12 ASCII String", fifo_arr);
+
+ memset((void *)fifo, 0, 16);
+ for (j = 0; j < 16; j++)
+ fifo += sprintf(fifo, "%c", log_data->fifo13[j]);
+ json_object_add_value_string(root, "FIFO 13 ASCII String", fifo_arr);
+
+ memset((void *)fifo, 0, 16);
+ for (j = 0; j < 16; j++)
+ fifo += sprintf(fifo, "%c", log_data->fifo14[j]);
+ json_object_add_value_string(root, "FIFO 14 ASCII String", fifo_arr);
+
+ memset((void *)fifo, 0, 16);
+ for (j = 0; j < 16; j++)
+ fifo += sprintf(fifo, "%c", log_data->fifo15[j]);
+ json_object_add_value_string(root, "FIFO 15 ASCII String", fifo_arr);
+
+ memset((void *)fifo, 0, 16);
+ for (j = 0; j < 16; j++)
+ fifo += sprintf(fifo, "%c", log_data->fifo16[j]);
+ json_object_add_value_string(root, "FIFO 16 ASCII String", fifo_arr);
+
+ memset((__u8 *)res, 0, 48);
+ for (j = 0; j < 48; j++)
+ res += sprintf(res, "%d", log_data->reserved3[j]);
+ json_object_add_value_string(root, "Reserved", res_arr);
+
+ memcpy(stat_id_str_table_arr, (__u8*)log_data_buf + stat_id_str_table_ofst, (log_data->sitsz * 4));
+ memcpy(event_id_str_table_arr, (__u8*)log_data_buf + event_str_table_ofst, (log_data->estsz * 4));
+ memcpy(vu_event_id_str_table_arr, (__u8*)log_data_buf + vu_event_str_table_ofst, (log_data->vu_eve_st_sz * 4));
+ memcpy(ascii_table_info_arr, (__u8*)log_data_buf + ascii_table_ofst, (log_data->asctsz * 4));
+
+ for (j = 0; j < stat_id_index; j++){
+ json_object_add_value_int(entry, "Vendor Specific Statistic Identifier", le16_to_cpu(stat_id_str_table_arr[j].vs_si));
+ json_object_add_value_int(entry, "Reserved", le64_to_cpu(stat_id_str_table_arr[j].reserved1));
+ json_object_add_value_int(entry, "ASCII ID Length", le64_to_cpu(stat_id_str_table_arr[j].ascii_id_len));
+ json_object_add_value_int(entry, "ASCII ID offset", le64_to_cpu(stat_id_str_table_arr[j].ascii_id_ofst));
+ json_object_add_value_int(entry, "Reserved", le64_to_cpu(stat_id_str_table_arr[j].reserved2));
+ json_array_add_value_object(stat_table, entry);
+ }
+ json_object_add_value_array(root, "Statistics Identifier String Table", stat_table);
+
+ for (j = 0; j < eve_id_index; j++){
+ json_object_add_value_int(entry, "Debug Event Class", le16_to_cpu(event_id_str_table_arr[j].deb_eve_class));
+ json_object_add_value_int(entry, "Event Identifier", le16_to_cpu(event_id_str_table_arr[j].ei));
+ json_object_add_value_int(entry, "ASCII ID Length", le64_to_cpu(event_id_str_table_arr[j].ascii_id_len));
+ json_object_add_value_int(entry, "ASCII ID offset", le64_to_cpu(event_id_str_table_arr[j].ascii_id_ofst));
+ json_object_add_value_int(entry, "Reserved", le64_to_cpu(event_id_str_table_arr[j].reserved2));
+ json_array_add_value_object(eve_table, entry);
+ }
+ json_object_add_value_array(root, "Event Identifier String Table Entry", eve_table);
+
+ for (j = 0; j < vu_eve_index; j++){
+ json_object_add_value_int(entry, "Debug Event Class", le16_to_cpu(vu_event_id_str_table_arr[j].deb_eve_class));
+ json_object_add_value_int(entry, "VU Event Identifier", le16_to_cpu(vu_event_id_str_table_arr[j].vu_ei));
+ json_object_add_value_int(entry, "ASCII ID Length", le64_to_cpu(vu_event_id_str_table_arr[j].ascii_id_len));
+ json_object_add_value_int(entry, "ASCII ID offset", le64_to_cpu(vu_event_id_str_table_arr[j].ascii_id_ofst));
+ json_object_add_value_int(entry, "Reserved", le64_to_cpu(vu_event_id_str_table_arr[j].reserved));
+ json_array_add_value_object(vu_eve_table, entry);
+ }
+ json_object_add_value_array(root, "VU Event Identifier String Table Entry", vu_eve_table);
+
+ memset((void *)ascii, 0, ascii_table_index);
+ for (j = 0; j < ascii_table_index; j++)
+ ascii += sprintf(ascii, "%c", ascii_table_info_arr[j]);
+ json_object_add_value_string(root, "ASCII Table", ascii_buf);
+
+ json_print_object(root, NULL);
+ printf("\n");
+ json_free_object(root);
+ json_free_object(stat_table);
+ json_free_object(eve_table);
+ json_free_object(vu_eve_table);
+
+ return 0;
+}
+
+static void ocp_print_c9_log_binary(__u8 *log_data_buf,int total_log_page_size)
+{
+ return d_raw((unsigned char *)log_data_buf, total_log_page_size);
+}
+
+static int get_c9_log_page(struct nvme_dev *dev, char *format)
+{
+ int ret = 0;
+ __u8 *header_data;
+ struct telemetry_str_log_format *log_data;
+ enum nvme_print_flags fmt;
+ __u8 *full_log_buf_data = NULL;
+ __le64 stat_id_str_table_ofst = 0;
+ __le64 event_str_table_ofst = 0;
+ __le64 vu_event_str_table_ofst = 0;
+ __le64 ascii_table_ofst = 0;
+ __le64 total_log_page_sz = 0;
+
+ ret = validate_output_format(format, &fmt);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR : OCP : invalid output format\n");
+ return ret;
+ }
+
+ header_data = (__u8 *)malloc(sizeof(__u8) * C9_TELEMETRY_STR_LOG_LEN);
+ if (!header_data) {
+ fprintf(stderr, "ERROR : OCP : malloc : %s\n", strerror(errno));
+ return -1;
+ }
+ memset(header_data, 0, sizeof(__u8) * C9_TELEMETRY_STR_LOG_LEN);
+
+ ret = nvme_get_log_simple(dev_fd(dev), C9_TELEMETRY_STRING_LOG_ENABLE_OPCODE,
+ C9_TELEMETRY_STR_LOG_LEN, header_data);
+
+ if (!ret) {
+ log_data = (struct telemetry_str_log_format *)header_data;
+ printf("Statistics Identifier String Table Size = %lld\n",log_data->sitsz);
+ printf("Event String Table Size = %lld\n",log_data->estsz);
+ printf("VU Event String Table Size = %lld\n",log_data->vu_eve_st_sz);
+ printf("ASCII Table Size = %lld\n",log_data->asctsz);
+
+ //Calculating the offset for dynamic fields.
+ stat_id_str_table_ofst = C9_TELEMETRY_STR_LOG_SIST_OFST + (log_data->sitsz * 4);
+ event_str_table_ofst = stat_id_str_table_ofst + (log_data->estsz * 4);
+ vu_event_str_table_ofst = event_str_table_ofst + (log_data->vu_eve_st_sz * 4);
+ ascii_table_ofst = vu_event_str_table_ofst + (log_data->asctsz * 4);
+ total_log_page_sz = stat_id_str_table_ofst + event_str_table_ofst + vu_event_str_table_ofst + ascii_table_ofst;
+
+ printf("stat_id_str_table_ofst = %lld\n",stat_id_str_table_ofst);
+ printf("event_str_table_ofst = %lld\n",event_str_table_ofst);
+ printf("vu_event_str_table_ofst = %lld\n",vu_event_str_table_ofst);
+ printf("ascii_table_ofst = %lld\n",ascii_table_ofst);
+ printf("total_log_page_sz = %lld\n",total_log_page_sz);
+
+ full_log_buf_data = (__u8 *)malloc(sizeof(__u8) * total_log_page_sz);
+ if (!full_log_buf_data) {
+ fprintf(stderr, "ERROR : OCP : malloc : %s\n", strerror(errno));
+ return -1;
+ }
+ memset(full_log_buf_data, 0, sizeof(__u8) * total_log_page_sz);
+
+ ret = nvme_get_log_simple(dev_fd(dev), C9_TELEMETRY_STRING_LOG_ENABLE_OPCODE,
+ total_log_page_sz, full_log_buf_data);
+
+ if (!ret) {
+ switch (fmt) {
+ case NORMAL:
+ ocp_print_C9_log_normal(log_data,full_log_buf_data);
+ break;
+ case JSON:
+ ocp_print_C9_log_json(log_data,full_log_buf_data);
+ break;
+ case BINARY:
+ ocp_print_c9_log_binary(full_log_buf_data,total_log_page_sz);
+ break;
+ default:
+ fprintf(stderr, "unhandled output format\n");
+ break;
+ }
+ } else{
+ fprintf(stderr, "ERROR : OCP : Unable to read C9 data from buffer\n");
+ }
+ } else {
+ fprintf(stderr, "ERROR : OCP : Unable to read C9 data from buffer\n");
+ }
+
+ free(header_data);
+ free(full_log_buf_data);
+
+ return ret;
+}
+
+static int ocp_telemetry_str_log_format(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ struct nvme_dev *dev;
+ int ret = 0;
+ const char *desc = "Retrieve telemetry string log format";
+
+ 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()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ ret = get_c9_log_page(dev, cfg.output_format);
+ if (ret)
+ fprintf(stderr, "ERROR : OCP : Failure reading the C9 Log Page, ret = %d\n", ret);
+
+ dev_close(dev);
+
+ return ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+/// Misc
+
+static int clear_fw_update_history(int argc, char **argv,
+ struct command *cmd, struct plugin *plugin)
+{
+ return ocp_clear_fw_update_history(argc, argv, cmd, plugin);
+}
+
+static int smart_add_log(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ return ocp_smart_add_log(argc, argv, cmd, plugin);
+}
+
+static int clear_pcie_correctable_error_counters(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ return ocp_clear_pcie_correctable_errors(argc, argv, cmd, plugin);
+}
+
+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);
+}
diff --git a/plugins/ocp/ocp-nvme.h b/plugins/ocp/ocp-nvme.h
new file mode 100644
index 0000000..95539b0
--- /dev/null
+++ b/plugins/ocp/ocp-nvme.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (c) 2022 Meta Platforms, Inc.
+ *
+ * Authors: Arthur Shau <arthurshau@fb.com>,
+ * Wei Zhang <wzhang@fb.com>,
+ * Venkat Ramesh <venkatraghavan@fb.com>
+ */
+#undef CMD_INC_FILE
+#define CMD_INC_FILE plugins/ocp/ocp-nvme
+
+#if !defined(OCP_NVME) || defined(CMD_HEADER_MULTI_READ)
+#define OCP_NVME
+
+#include "cmd.h"
+
+PLUGIN(NAME("ocp", "OCP cloud SSD extensions", NVME_VERSION),
+ COMMAND_LIST(
+ ENTRY("smart-add-log", "Retrieve extended SMART Information", smart_add_log)
+ ENTRY("latency-monitor-log", "Get Latency Monitor Log Page", ocp_latency_monitor_log)
+ ENTRY("set-latency-monitor-feature", "Set Latency Monitor feature", ocp_set_latency_monitor_feature)
+ ENTRY("internal-log", "Retrieve and save internal device telemetry log", ocp_telemetry_log)
+ 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-errors", "Clear PCIe correctable error counters", clear_pcie_correctable_error_counters)
+ ENTRY("fw-activate-history", "Get firmware activation history log", fw_activation_history_log)
+ ENTRY("unsupported-reqs-log", "Get Unsupported Requirements Log Page", ocp_unsupported_requirements_log)
+ ENTRY("error-recovery-log", "Retrieve Error Recovery Log Page", ocp_error_recovery_log)
+ ENTRY("device-capability-log", "Get Device capabilities Requirements Log Page", ocp_device_capabilities_log)
+ ENTRY("set-dssd-power-state-feature", "Get Device capabilities Requirements Log Page", set_dssd_power_state_feature)
+ ENTRY("set-plp-health-check-interval", "Set PLP Health Check Interval", set_plp_health_check_interval)
+ ENTRY("get-plp-health-check-interval", "Get PLP Health Check Interval", get_plp_health_check_interval)
+ ENTRY("telemetry-string-log", "Retrieve Telemetry string Log Page", ocp_telemetry_str_log_format)
+ )
+);
+
+#endif
+
+#include "define_cmd.h"
diff --git a/plugins/ocp/ocp-smart-extended-log.c b/plugins/ocp/ocp-smart-extended-log.c
new file mode 100644
index 0000000..0d8ba81
--- /dev/null
+++ b/plugins/ocp/ocp-smart-extended-log.c
@@ -0,0 +1,352 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Copyright (c) 2022 Meta Platforms, Inc.
+ *
+ * Authors: Arthur Shau <arthurshau@fb.com>,
+ * Wei Zhang <wzhang@fb.com>,
+ * Venkat Ramesh <venkatraghavan@fb.com>
+ */
+
+#include "ocp-smart-extended-log.h"
+
+#include <errno.h>
+#include <stdio.h>
+
+#include "common.h"
+#include "nvme-print.h"
+
+/* C0 SCAO Log Page */
+#define C0_SMART_CLOUD_ATTR_LEN 0x200
+#define C0_SMART_CLOUD_ATTR_OPCODE 0xC0
+#define C0_GUID_LENGTH 16
+
+static __u8 scao_guid[C0_GUID_LENGTH] = {
+ 0xC5, 0xAF, 0x10, 0x28,
+ 0xEA, 0xBF, 0xF2, 0xA4,
+ 0x9C, 0x4F, 0x6F, 0x7C,
+ 0xC9, 0x14, 0xD5, 0xAF
+};
+
+enum {
+ SCAO_PMUW = 0, /* Physical media units written */
+ SCAO_PMUR = 16, /* Physical media units read */
+ SCAO_BUNBR = 32, /* Bad user nand blocks raw */
+ SCAO_BUNBN = 38, /* Bad user nand blocks normalized */
+ SCAO_BSNBR = 40, /* Bad system nand blocks raw */
+ SCAO_BSNBN = 46, /* Bad system nand blocks normalized */
+ SCAO_XRC = 48, /* XOR recovery count */
+ SCAO_UREC = 56, /* Uncorrectable read error count */
+ SCAO_SEEC = 64, /* Soft ecc error count */
+ SCAO_EEDC = 72, /* End to end detected errors */
+ SCAO_EECE = 76, /* End to end corrected errors */
+ SCAO_SDPU = 80, /* System data percent used */
+ SCAO_RFSC = 81, /* Refresh counts */
+ SCAO_MXUDEC = 88, /* Max User data erase counts */
+ SCAO_MNUDEC = 92, /* Min User data erase counts */
+ SCAO_NTTE = 96, /* Number of Thermal throttling events */
+ SCAO_CTS = 97, /* Current throttling status */
+ SCAO_EVF = 98, /* Errata Version Field */
+ SCAO_PVF = 99, /* Point Version Field */
+ SCAO_MIVF = 101, /* Minor Version Field */
+ SCAO_MAVF = 103, /* Major Version Field */
+ SCAO_PCEC = 104, /* PCIe correctable error count */
+ SCAO_ICS = 112, /* Incomplete shutdowns */
+ SCAO_PFB = 120, /* Percent free blocks */
+ SCAO_CPH = 128, /* Capacitor health */
+ SCAO_NEV = 130, /* NVMe Errata Version */
+ SCAO_UIO = 136, /* Unaligned I/O */
+ SCAO_SVN = 144, /* Security Version Number */
+ SCAO_NUSE = 152, /* NUSE - Namespace utilization */
+ SCAO_PSC = 160, /* PLP start count */
+ SCAO_EEST = 176, /* Endurance estimate */
+ SCAO_PLRC = 192, /* PCIe Link Retraining Count */
+ SCAO_PSCC = 200, /* Power State Change Count */
+ SCAO_LPV = 494, /* Log page version */
+ SCAO_LPG = 496, /* Log page GUID */
+};
+
+static void ocp_print_C0_log_normal(void *data)
+{
+ uint16_t smart_log_ver = 0;
+ __u8 *log_data = data;
+
+ printf("SMART Cloud Attributes :-\n");
+
+ printf(" Physical media units written - %"PRIu64" %"PRIu64"\n",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUW + 8] & 0xFFFFFFFFFFFFFFFF),
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUW] & 0xFFFFFFFFFFFFFFFF));
+ printf(" Physical media units read - %"PRIu64" %"PRIu64"\n",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUR + 8] & 0xFFFFFFFFFFFFFFFF),
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUR] & 0xFFFFFFFFFFFFFFFF));
+ printf(" Bad user nand blocks - Raw %"PRIu64"\n",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BUNBR] & 0x0000FFFFFFFFFFFF));
+ printf(" Bad user nand blocks - Normalized %d\n",
+ (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BUNBN]));
+ printf(" Bad system nand blocks - Raw %"PRIu64"\n",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BSNBR] & 0x0000FFFFFFFFFFFF));
+ printf(" Bad system nand blocks - Normalized %d\n",
+ (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BSNBN]));
+ printf(" XOR recovery count %"PRIu64"\n",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_XRC]));
+ printf(" Uncorrectable read error count %"PRIu64"\n",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UREC]));
+ printf(" Soft ecc error count %"PRIu64"\n",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SEEC]));
+ printf(" End to end detected errors %"PRIu32"\n",
+ (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EEDC]));
+ printf(" End to end corrected errors %"PRIu32"\n",
+ (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EECE]));
+ printf(" System data percent used %d\n",
+ (__u8)log_data[SCAO_SDPU]);
+ printf(" Refresh counts %"PRIu64"\n",
+ (uint64_t)(le64_to_cpu(*(uint64_t *)&log_data[SCAO_RFSC]) & 0x00FFFFFFFFFFFFFF));
+ printf(" Max User data erase counts %"PRIu32"\n",
+ (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MXUDEC]));
+ printf(" Min User data erase counts %"PRIu32"\n",
+ (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MNUDEC]));
+ printf(" Number of Thermal throttling events %d\n",
+ (__u8)log_data[SCAO_NTTE]);
+ printf(" Current throttling status 0x%x\n",
+ (__u8)log_data[SCAO_CTS]);
+ printf(" PCIe correctable error count %"PRIu64"\n",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PCEC]));
+ printf(" Incomplete shutdowns %"PRIu32"\n",
+ (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_ICS]));
+ printf(" Percent free blocks %d\n",
+ (__u8)log_data[SCAO_PFB]);
+ printf(" Capacitor health %"PRIu16"\n",
+ (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_CPH]));
+ printf(" Unaligned I/O %"PRIu64"\n",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UIO]));
+ printf(" Security Version Number %"PRIu64"\n",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SVN]));
+ printf(" NUSE - Namespace utilization %"PRIu64"\n",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_NUSE]));
+ printf(" PLP start count %s\n",
+ uint128_t_to_string(le128_to_cpu(&log_data[SCAO_PSC])));
+ printf(" Endurance estimate %s\n",
+ uint128_t_to_string(le128_to_cpu(&log_data[SCAO_EEST])));
+ smart_log_ver = (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_LPV]);
+ printf(" Log page version %"PRIu16"\n", smart_log_ver);
+ printf(" Log page GUID 0x");
+ printf("%"PRIx64"%"PRIx64"\n", (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG + 8]),
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG]));
+ if (smart_log_ver > 2) {
+ printf(" Errata Version Field %d\n",
+ (__u8)log_data[SCAO_EVF]);
+ printf(" Point Version Field %"PRIu16"\n",
+ le16_to_cpu(*(uint16_t *)&log_data[SCAO_PVF]));
+ printf(" Minor Version Field %"PRIu16"\n",
+ le16_to_cpu(*(uint16_t *)&log_data[SCAO_MIVF]));
+ printf(" Major Version Field %d\n",
+ (__u8)log_data[SCAO_MAVF]);
+ printf(" NVMe Errata Version %d\n",
+ (__u8)log_data[SCAO_NEV]);
+ printf(" PCIe Link Retraining Count %"PRIu64"\n",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PLRC]));
+ printf(" Power State Change Count %"PRIu64"\n",
+ le64_to_cpu(*(uint64_t *)&log_data[SCAO_PSCC]));
+ }
+ printf("\n");
+}
+
+static void ocp_print_C0_log_json(void *data)
+{
+ struct json_object *root;
+ struct json_object *pmuw;
+ struct json_object *pmur;
+ uint16_t smart_log_ver = 0;
+ __u8 *log_data = data;
+ char guid[40];
+
+ root = json_create_object();
+ pmuw = json_create_object();
+ pmur = json_create_object();
+
+ json_object_add_value_uint64(pmuw, "hi",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUW + 8] & 0xFFFFFFFFFFFFFFFF));
+ json_object_add_value_uint64(pmuw, "lo",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUW] & 0xFFFFFFFFFFFFFFFF));
+ json_object_add_value_object(root, "Physical media units written", pmuw);
+ json_object_add_value_uint64(pmur, "hi",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUR + 8] & 0xFFFFFFFFFFFFFFFF));
+ json_object_add_value_uint64(pmur, "lo",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUR] & 0xFFFFFFFFFFFFFFFF));
+ json_object_add_value_object(root, "Physical media units read", pmur);
+ json_object_add_value_uint64(root, "Bad user nand blocks - Raw",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BUNBR] & 0x0000FFFFFFFFFFFF));
+ json_object_add_value_uint(root, "Bad user nand blocks - Normalized",
+ (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BUNBN]));
+ json_object_add_value_uint64(root, "Bad system nand blocks - Raw",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_BSNBR] & 0x0000FFFFFFFFFFFF));
+ json_object_add_value_uint(root, "Bad system nand blocks - Normalized",
+ (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_BSNBN]));
+ json_object_add_value_uint64(root, "XOR recovery count",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_XRC]));
+ json_object_add_value_uint64(root, "Uncorrectable read error count",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UREC]));
+ json_object_add_value_uint64(root, "Soft ecc error count",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SEEC]));
+ json_object_add_value_uint(root, "End to end detected errors",
+ (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EEDC]));
+ json_object_add_value_uint(root, "End to end corrected errors",
+ (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_EECE]));
+ json_object_add_value_uint(root, "System data percent used",
+ (__u8)log_data[SCAO_SDPU]);
+ json_object_add_value_uint64(root, "Refresh counts",
+ (uint64_t)(le64_to_cpu(*(uint64_t *)&log_data[SCAO_RFSC]) & 0x00FFFFFFFFFFFFFF));
+ json_object_add_value_uint(root, "Max User data erase counts",
+ (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MXUDEC]));
+ json_object_add_value_uint(root, "Min User data erase counts",
+ (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_MNUDEC]));
+ json_object_add_value_uint(root, "Number of Thermal throttling events",
+ (__u8)log_data[SCAO_NTTE]);
+ json_object_add_value_uint(root, "Current throttling status",
+ (__u8)log_data[SCAO_CTS]);
+ json_object_add_value_uint64(root, "PCIe correctable error count",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PCEC]));
+ json_object_add_value_uint(root, "Incomplete shutdowns",
+ (uint32_t)le32_to_cpu(*(uint32_t *)&log_data[SCAO_ICS]));
+ json_object_add_value_uint(root, "Percent free blocks",
+ (__u8)log_data[SCAO_PFB]);
+ json_object_add_value_uint(root, "Capacitor health",
+ (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_CPH]));
+ json_object_add_value_uint64(root, "Unaligned I/O",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UIO]));
+ json_object_add_value_uint64(root, "Security Version Number",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SVN]));
+ json_object_add_value_uint64(root, "NUSE - Namespace utilization",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_NUSE]));
+ json_object_add_value_uint128(root, "PLP start count",
+ le128_to_cpu(&log_data[SCAO_PSC]));
+ json_object_add_value_uint128(root, "Endurance estimate",
+ le128_to_cpu(&log_data[SCAO_EEST]));
+ smart_log_ver = (uint16_t)le16_to_cpu(*(uint16_t *)&log_data[SCAO_LPV]);
+
+ json_object_add_value_uint(root, "Log page version", smart_log_ver);
+
+ memset((void *)guid, 0, 40);
+ sprintf((char *)guid, "0x%"PRIx64"%"PRIx64"", (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG + 8]),
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG]));
+ json_object_add_value_string(root, "Log page GUID", guid);
+
+ if (smart_log_ver > 2) {
+ json_object_add_value_uint(root, "Errata Version Field",
+ (__u8)log_data[SCAO_EVF]);
+ json_object_add_value_uint(root, "Point Version Field",
+ le16_to_cpu(*(uint16_t *)&log_data[SCAO_PVF]));
+ json_object_add_value_uint(root, "Minor Version Field",
+ le16_to_cpu(*(uint16_t *)&log_data[SCAO_MIVF]));
+ json_object_add_value_uint(root, "Major Version Field",
+ (__u8)log_data[SCAO_MAVF]);
+ json_object_add_value_uint(root, "NVMe Errata Version",
+ (__u8)log_data[SCAO_NEV]);
+ json_object_add_value_uint(root, "PCIe Link Retraining Count",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PLRC]));
+ json_object_add_value_uint(root, "Power State Change Count",
+ le64_to_cpu(*(uint64_t *)&log_data[SCAO_PSCC]));
+ }
+ json_print_object(root, NULL);
+ printf("\n");
+ json_free_object(root);
+}
+
+static int get_c0_log_page(int fd, char *format)
+{
+ enum nvme_print_flags fmt;
+ __u8 *data;
+ int i;
+ int ret;
+
+ ret = validate_output_format(format, &fmt);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR : OCP : invalid output format\n");
+ return ret;
+ }
+
+ data = malloc(sizeof(__u8) * C0_SMART_CLOUD_ATTR_LEN);
+ if (!data) {
+ fprintf(stderr, "ERROR : OCP : malloc : %s\n", strerror(errno));
+ return -1;
+ }
+ memset(data, 0, sizeof(__u8) * C0_SMART_CLOUD_ATTR_LEN);
+
+ ret = nvme_get_log_simple(fd, C0_SMART_CLOUD_ATTR_OPCODE,
+ C0_SMART_CLOUD_ATTR_LEN, data);
+
+ if (strcmp(format, "json"))
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(ret, false), ret);
+
+ if (ret == 0) {
+ /* check log page guid */
+ /* Verify GUID matches */
+ for (i = 0; i < 16; i++) {
+ if (scao_guid[i] != data[SCAO_LPG + i]) {
+ int j;
+
+ fprintf(stderr, "ERROR : OCP : Unknown GUID in C0 Log Page data\n");
+ fprintf(stderr, "ERROR : OCP : Expected GUID: 0x");
+ for (j = 0; j < 16; j++)
+ fprintf(stderr, "%x", scao_guid[j]);
+
+ fprintf(stderr, "\nERROR : OCP : Actual GUID: 0x");
+ for (j = 0; j < 16; j++)
+ fprintf(stderr, "%x", data[SCAO_LPG + j]);
+ fprintf(stderr, "\n");
+
+ ret = -1;
+ goto out;
+ }
+ }
+
+ /* print the data */
+ switch (fmt) {
+ case NORMAL:
+ ocp_print_C0_log_normal(data);
+ break;
+ case JSON:
+ ocp_print_C0_log_json(data);
+ break;
+ default:
+ break;
+ }
+ } else {
+ fprintf(stderr, "ERROR : OCP : Unable to read C0 data from buffer\n");
+ }
+
+out:
+ free(data);
+ return ret;
+}
+
+int ocp_smart_add_log(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ const char *desc = "Retrieve the extended SMART health data.";
+ struct nvme_dev *dev;
+ int ret = 0;
+
+ 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()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ ret = get_c0_log_page(dev_fd(dev), cfg.output_format);
+ if (ret)
+ fprintf(stderr, "ERROR : OCP : Failure reading the C0 Log Page, ret = %d\n",
+ ret);
+ dev_close(dev);
+ return ret;
+}
diff --git a/plugins/ocp/ocp-smart-extended-log.h b/plugins/ocp/ocp-smart-extended-log.h
new file mode 100644
index 0000000..42c1f98
--- /dev/null
+++ b/plugins/ocp/ocp-smart-extended-log.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (c) 2022 Meta Platforms, Inc.
+ *
+ * Authors: Arthur Shau <arthurshau@fb.com>,
+ * Wei Zhang <wzhang@fb.com>,
+ * Venkat Ramesh <venkatraghavan@fb.com>
+ */
+
+#ifndef OCP_SMART_EXTENDED_LOG_H
+#define OCP_SMART_EXTENDED_LOG_H
+
+struct command;
+struct plugin;
+
+int ocp_smart_add_log(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin);
+
+#endif
diff --git a/plugins/ocp/ocp-utils.c b/plugins/ocp/ocp-utils.c
new file mode 100644
index 0000000..1257b30
--- /dev/null
+++ b/plugins/ocp/ocp-utils.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: leonardo.da.cunha@solidigm.com
+ */
+
+#include <unistd.h>
+#include "ocp-utils.h"
+#include "nvme-print.h"
+
+const unsigned char ocp_uuid[NVME_UUID_LEN] = {
+ 0xc1, 0x94, 0xd5, 0x5b, 0xe0, 0x94, 0x47, 0x94, 0xa2, 0x1d,
+ 0x29, 0x99, 0x8f, 0x56, 0xbe, 0x6f };
+
+int ocp_get_uuid_index(struct nvme_dev *dev, int *index)
+{
+ struct nvme_id_uuid_list uuid_list;
+ int err = nvme_identify_uuid(dev_fd(dev), &uuid_list);
+
+ *index = 0;
+ if (err)
+ return err;
+
+ for (int i = 0; i < NVME_ID_UUID_LIST_MAX; i++) {
+ if (memcmp(ocp_uuid, &uuid_list.entry[i].uuid, NVME_UUID_LEN) == 0) {
+ *index = i + 1;
+ break;
+ }
+ }
+ return err;
+}
diff --git a/plugins/ocp/ocp-utils.h b/plugins/ocp/ocp-utils.h
new file mode 100644
index 0000000..d02bea9
--- /dev/null
+++ b/plugins/ocp/ocp-utils.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: leonardo.da.cunha@solidigm.com
+ */
+
+#include "nvme.h"
+
+/**
+ * ocp_get_uuid_index() - Get OCP UUID index
+ * @dev: nvme device
+ * @index: integer pointer to here to save the index
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: Zero if nvme device has UUID list log page, or result of get uuid list otherwise.
+ */
+int ocp_get_uuid_index(struct nvme_dev *dev, int *index);