#define WDC_NVME_SN640_DEV_ID 0x2400
#define WDC_NVME_SN640_DEV_ID_1 0x2401
#define WDC_NVME_SN640_DEV_ID_2 0x2402
+#define WDC_NVME_SN740_DEV_ID 0x2600
#define WDC_NVME_SXSLCL_DEV_ID 0x2001
#define WDC_NVME_SN520_DEV_ID 0x5003
#define WDC_NVME_SN520_DEV_ID_1 0x5004
#define WDC_NVME_DUI_MAX_SECTION_V2 0x26
#define WDC_NVME_DUI_MAX_DATA_AREA 0x05
+/* Telemtery types for vs-internal-log command */
+#define WDC_TELEMETRY_TYPE_NONE 0x0
+#define WDC_TELEMETRY_TYPE_HOST 0x1
+#define WDC_TELEMETRY_TYPE_CONTROLLER 0x2
+#define WDC_TELEMETRY_HEADER_LENGTH 512
+#define WDC_TELEMETRY_BLOCK_SIZE 512
+
/* Crash dump */
#define WDC_NVME_CRASH_DUMP_SIZE_DATA_LEN WDC_NVME_LOG_SIZE_DATA_LEN
#define WDC_NVME_CRASH_DUMP_SIZE_NDT 0x02
case WDC_NVME_SN840_DEV_ID:
/* FALLTHRU */
case WDC_NVME_SN840_DEV_ID_1:
+ /* FALLTHRU */
+ case WDC_NVME_SN740_DEV_ID:
capabilities = (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG |
WDC_DRIVE_CAP_DRIVE_STATUS | WDC_DRIVE_CAP_CLEAR_ASSERT |
WDC_DRIVE_CAP_RESIZE | WDC_DRIVE_CAP_CLEAR_PCIE |
return ret;
}
-static int wdc_do_cap_diag(int fd, char *file, __u32 xfer_size)
+static int wdc_do_cap_telemetry_log(int fd, char *file, __u32 bs, int type, int data_area)
{
- int ret;
+ struct nvme_telemetry_log_page_hdr *hdr;
+ size_t full_size, offset = WDC_TELEMETRY_HEADER_LENGTH;
+ int err = 0, output;
+ void *page_log;
+ __u32 host_gen = 1;
+ int ctrl_init = 0;
+ __u32 result;
+ void *buf = NULL;
+
+
+ if (type == WDC_TELEMETRY_TYPE_HOST) {
+ host_gen = 1;
+ ctrl_init = 0;
+ } else if (type == WDC_TELEMETRY_TYPE_CONTROLLER) {
+ /* Verify the Controller Initiated Option is enabled */
+ err = nvme_get_feature(fd, 0, WDC_VU_DISABLE_CNTLR_TELEMETRY_OPTION_FEATURE_ID, 0, 0,
+ 4, buf, &result);
+ if (err == 0) {
+ if (result) {
+ host_gen = 0;
+ ctrl_init = 1;
+ }
+ else {
+ fprintf(stderr, "%s: Controller initiated option telemetry log page disabled\n", __func__);
+ err = -EINVAL;
+ goto close_fd;
+ }
+ } else {
+ fprintf(stderr, "ERROR : WDC: Get telemetry option feature failed. NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ err = -EPERM;
+ goto close_fd;
+ }
+ } else {
+ fprintf(stderr, "%s: Invalid type parameter; type = %d\n", __func__, type);
+ err = -EINVAL;
+ goto close_fd;
+ }
+
+ if (!file) {
+ fprintf(stderr, "%s: Please provide an output file!\n", __func__);
+ err = -EINVAL;
+ goto close_fd;
+ }
+
+ hdr = malloc(bs);
+ page_log = malloc(bs);
+ if (!hdr || !page_log) {
+ fprintf(stderr, "%s: Failed to allocate 0x%x bytes for log: %s\n",
+ __func__, bs, strerror(errno));
+ err = -ENOMEM;
+ goto free_mem;
+ }
+ memset(hdr, 0, bs);
+
+ output = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (output < 0) {
+ fprintf(stderr, "%s: Failed to open output file %s: %s!\n",
+ __func__, file, strerror(errno));
+ err = output;
+ goto free_mem;
+ }
+
+ err = nvme_get_telemetry_log(fd, hdr, host_gen, ctrl_init, WDC_TELEMETRY_HEADER_LENGTH, 0);
+ if (err < 0)
+ perror("get-telemetry-log");
+ else if (err > 0) {
+ nvme_show_status(err);
+ fprintf(stderr, "%s: Failed to acquire telemetry header!\n", __func__);
+ goto close_output;
+ }
+
+ err = write(output, (void *) hdr, WDC_TELEMETRY_HEADER_LENGTH);
+ if (err != WDC_TELEMETRY_HEADER_LENGTH) {
+ fprintf(stderr, "%s: Failed to flush header data to file!, err = %d\n", __func__, err);
+ goto close_output;
+ }
+
+ switch (data_area) {
+ case 1:
+ full_size = (le16_to_cpu(hdr->dalb1) * WDC_TELEMETRY_BLOCK_SIZE) + WDC_TELEMETRY_HEADER_LENGTH;
+ break;
+ case 2:
+ full_size = (le16_to_cpu(hdr->dalb2) * WDC_TELEMETRY_BLOCK_SIZE) + WDC_TELEMETRY_HEADER_LENGTH;
+ break;
+ case 3:
+ full_size = (le16_to_cpu(hdr->dalb3) * WDC_TELEMETRY_BLOCK_SIZE) + WDC_TELEMETRY_HEADER_LENGTH;
+ break;
+ default:
+ fprintf(stderr, "%s: Invalid data area requested, data area = %d\n", __func__, data_area);
+ err = -EINVAL;
+ goto close_output;
+ }
+
+ /*
+ * Continuously pull data until the offset hits the end of the last
+ * block.
+ */
+ while (offset < full_size) {
+ if ((full_size - offset) < bs)
+ bs = (full_size - offset);
+
+
+ err = nvme_get_telemetry_log(fd, page_log, 0, ctrl_init, bs, offset);
+ if (err < 0) {
+ perror("get-telemetry-log");
+ break;
+ } else if (err > 0) {
+ nvme_show_status(err);
+ fprintf(stderr, "%s: Failed to acquire full telemetry log!\n", __func__);
+ nvme_show_status(err);
+ break;
+ }
+
+ err = write(output, (void *) page_log, bs);
+ if (err != bs) {
+ fprintf(stderr, "%s: Failed to flush telemetry data to file!, err = %d\n", __func__, err);
+ break;
+ }
+ err = 0;
+ offset += bs;
+ }
+
+close_output:
+ close(output);
+free_mem:
+ free(hdr);
+ free(page_log);
+close_fd:
+ close(fd);
+
+ return err;
+
+}
+
+static int wdc_do_cap_diag(int fd, char *file, __u32 xfer_size, int type, int data_area)
+{
+ int ret = -1;
__u32 e6_log_hdr_size = WDC_NVME_CAP_DIAG_HEADER_TOC_SIZE;
struct wdc_e6_log_hdr *log_hdr;
__u32 cap_diag_length;
}
memset(log_hdr, 0, e6_log_hdr_size);
- ret = wdc_dump_length_e6(fd, WDC_NVME_CAP_DIAG_OPCODE,
- WDC_NVME_CAP_DIAG_HEADER_TOC_SIZE>>2,
- 0x00,
- log_hdr);
- if (ret == -1) {
- ret = -1;
- goto out;
- }
+ if (type == WDC_TELEMETRY_TYPE_NONE) {
+ ret = wdc_dump_length_e6(fd, WDC_NVME_CAP_DIAG_OPCODE,
+ WDC_NVME_CAP_DIAG_HEADER_TOC_SIZE>>2,
+ 0x00,
+ log_hdr);
+ if (ret == -1) {
+ ret = -1;
+ goto out;
+ }
- cap_diag_length = (log_hdr->log_size[0] << 24 | log_hdr->log_size[1] << 16 |
- log_hdr->log_size[2] << 8 | log_hdr->log_size[3]);
+ cap_diag_length = (log_hdr->log_size[0] << 24 | log_hdr->log_size[1] << 16 |
+ log_hdr->log_size[2] << 8 | log_hdr->log_size[3]);
- if (cap_diag_length == 0) {
- fprintf(stderr, "INFO : WDC : Capture Diagnostics log is empty\n");
- } else {
- ret = wdc_do_dump_e6(fd, WDC_NVME_CAP_DIAG_OPCODE, cap_diag_length,
- (WDC_NVME_CAP_DIAG_SUBCMD << WDC_NVME_SUBCMD_SHIFT) | WDC_NVME_CAP_DIAG_CMD,
- file, xfer_size, (__u8 *)log_hdr);
+ if (cap_diag_length == 0) {
+ fprintf(stderr, "INFO : WDC : Capture Diagnostics log is empty\n");
+ } else {
+ ret = wdc_do_dump_e6(fd, WDC_NVME_CAP_DIAG_OPCODE, cap_diag_length,
+ (WDC_NVME_CAP_DIAG_SUBCMD << WDC_NVME_SUBCMD_SHIFT) | WDC_NVME_CAP_DIAG_CMD,
+ file, xfer_size, (__u8 *)log_hdr);
- fprintf(stderr, "INFO : WDC : Capture Diagnostics log, length = 0x%x\n", cap_diag_length);
- }
+ fprintf(stderr, "INFO : WDC : Capture Diagnostics log, length = 0x%x\n", cap_diag_length);
+ }
+ } else if ((type == WDC_TELEMETRY_TYPE_HOST) ||
+ (type == WDC_TELEMETRY_TYPE_CONTROLLER)) {
+ /* Get the desired telemetry log page */
+ ret = wdc_do_cap_telemetry_log(fd, file, xfer_size, type, data_area);
+ } else
+ fprintf(stderr, "%s: ERROR : Invalid type : %d\n", __func__, type);
out:
free(log_hdr);
capabilities = wdc_get_drive_capabilities(fd);
if ((capabilities & WDC_DRIVE_CAP_CAP_DIAG) == WDC_DRIVE_CAP_CAP_DIAG)
- return wdc_do_cap_diag(fd, f, xfer_size);
+ return wdc_do_cap_diag(fd, f, xfer_size, 0, 0);
fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
return 0;
char *desc = "Internal Firmware Log.";
char *file = "Output file pathname.";
char *size = "Data retrieval transfer size.";
- char *data_area = "Data area to retrieve up to.";
- char *file_size = "Output file size.";
- char *offset = "Output file data offset.";
+ char *data_area = "Data area to retrieve up to. Currently only supported on the SN340, SN640, and SN840 devices.";
+ char *file_size = "Output file size. Currently only supported on the SN340 device.";
+ char *offset = "Output file data offset. Currently only supported on the SN340 device.";
+ char *type = "Telemetry type - NONE, HOST, or CONTROLLER. Currently only supported on the SN640 and SN840 devices.";
char *verbose = "Display more debug messages.";
char f[PATH_MAX] = {0};
char fileSuffix[PATH_MAX] = {0};
__u32 xfer_size = 0;
int fd;
+ int telemetry_type = 0, telemetry_data_area = 0;
UtilsTimeInfo timeInfo;
__u8 timeStamp[MAX_PATH_LEN];
__u64 capabilities = 0;
int data_area;
__u64 file_size;
__u64 offset;
+ char *type;
int verbose;
};
.data_area = 2,
.file_size = 0,
.offset = 0,
+ .type = NULL,
.verbose = 0,
};
OPT_UINT("transfer-size", 's', &cfg.xfer_size, size),
OPT_UINT("data-area", 'd', &cfg.data_area, data_area),
OPT_LONG("file-size", 'f', &cfg.file_size, file_size),
- OPT_LONG("offset", 't', &cfg.offset, offset),
+ OPT_LONG("offset", 'e', &cfg.offset, offset),
+ OPT_FILE("type", 't', &cfg.type, type),
OPT_FLAG("verbose", 'v', &cfg.verbose, verbose),
OPT_END()
};
}
capabilities = wdc_get_drive_capabilities(fd);
- if ((capabilities & WDC_DRIVE_CAP_INTERNAL_LOG) == WDC_DRIVE_CAP_INTERNAL_LOG)
- return wdc_do_cap_diag(fd, f, xfer_size);
+ if ((capabilities & WDC_DRIVE_CAP_INTERNAL_LOG) == WDC_DRIVE_CAP_INTERNAL_LOG) {
+ if ((cfg.type == NULL) ||
+ (!strcmp(cfg.type, "NONE")) ||
+ (!strcmp(cfg.type, "none"))) {
+ telemetry_type = WDC_TELEMETRY_TYPE_NONE;
+ data_area = 0;
+ } else if ((!strcmp(cfg.type, "HOST")) ||
+ (!strcmp(cfg.type, "host"))) {
+ telemetry_type = WDC_TELEMETRY_TYPE_HOST;
+ telemetry_data_area = cfg.data_area;
+ } else if ((!strcmp(cfg.type, "CONTROLLER")) ||
+ (!strcmp(cfg.type, "controller"))) {
+ telemetry_type = WDC_TELEMETRY_TYPE_CONTROLLER;
+ telemetry_data_area = cfg.data_area;
+ }
+
+ return wdc_do_cap_diag(fd, f, xfer_size, telemetry_type, telemetry_data_area);
+ }
if ((capabilities & WDC_DRIVE_CAP_SN340_DUI) == WDC_DRIVE_CAP_SN340_DUI) {
- /* FW requirement - xfer size must be 500k for data area 4 */
+ /* FW requirement - xfer size must be 256k for data area 4 */
if (cfg.data_area >= 4)
- xfer_size = 0x80000;
+ xfer_size = 0x40000;
return wdc_do_cap_dui(fd, f, xfer_size, cfg.data_area, cfg.verbose, cfg.file_size, cfg.offset);
}
if ((capabilities & WDC_DRIVE_CAP_DUI_DATA) == WDC_DRIVE_CAP_DUI_DATA)