From c83df1a90372e5732fb22a7ac01f2330885d32fd Mon Sep 17 00:00:00 2001 From: Sivaprasad Gutha Date: Mon, 7 Apr 2025 12:00:21 +0530 Subject: [PATCH] updating micron plugin --- plugins/micron/micron-nvme.c | 1500 ++++++++++++++++++++---- plugins/micron/micron-nvme.h | 13 +- plugins/micron/micron-ocp-telemetry.c | 1517 +++++++++++++++++++++++++ plugins/micron/micron-ocp-telemetry.h | 519 +++++++++ plugins/micron/micron-utils.c | 276 +++++ plugins/micron/micron-utils.h | 100 ++ 6 files changed, 3682 insertions(+), 243 deletions(-) create mode 100644 plugins/micron/micron-ocp-telemetry.c create mode 100644 plugins/micron/micron-ocp-telemetry.h create mode 100644 plugins/micron/micron-utils.c create mode 100644 plugins/micron/micron-utils.h diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c index c3fcb6441e..4dd2a07f2d 100644 --- a/plugins/micron/micron-nvme.c +++ b/plugins/micron/micron-nvme.c @@ -36,7 +36,12 @@ #define MICRON_FEATURE_CLEAR_PCI_CORRECTABLE_ERRORS 0xC3 #define MICRON_FEATURE_CLEAR_FW_ACTIVATION_HISTORY 0xC1 #define MICRON_FEATURE_TELEMETRY_CONTROL_OPTION 0xCF -#define MICRON_FEATURE_SMBUS_OPTION 0xD5 +#define MICRON_FEATURE_SMBUS_OPTION 0xD5 +#define MICRON_FEATURE_OCP_ENHANCED_TELEMETRY 0x16 + +/* Micron Supported Customer ID*/ +#define MICRON_CUST_ID_GENERAL 0x10 +#define MICRON_CUST_ID_GG 0x16 /* Supported Vendor specific log page sizes */ #define C5_log_size (((452 + 16 * 1024) / 4) * 4096) @@ -47,6 +52,8 @@ #define E1_log_size 256 #define MaxLogChunk (16 * 1024) #define CommonChunkSize (16 * 4096) +#define C6_log_size 512 +#define C5_MicronWorkLoad_log_size 256 #define min(x, y) ((x) > (y) ? (y) : (x)) #define SensorCount 8 @@ -54,7 +61,7 @@ /* Plugin version major_number.minor_number.patch */ static const char *__version_major = "1"; static const char *__version_minor = "0"; -static const char *__version_patch = "14"; +static const char *__version_patch = "22"; /* * supported models of micron plugin; new models should be added at the end @@ -64,9 +71,14 @@ enum eDriveModel { M5410 = 0, M51AX, M51BX, + M51BY, + M51CY, M51CX, M5407, M5411, + M6001, + M6003, + M6004, UNKNOWN_MODEL }; @@ -79,15 +91,25 @@ static char *fdeviceid2 = "/sys/class/misc/nvme%d/device/device"; static unsigned short vendor_id; static unsigned short device_id; +/* Additional log page IDs */ +#define NVME_LOG_PERSISTENT_EVENT 0xD + struct LogPageHeader_t { - unsigned char numDwordsInLogPageHeaderLo; - unsigned char logPageHeaderFormatVersion; - unsigned char logPageId; - unsigned char numDwordsInLogPageHeaderHi; - unsigned int numValidDwordsInPayload; - unsigned int numDwordsInEntireLogPage; + unsigned char numDwordsInLogPageHeaderLo; + unsigned char logPageHeaderFormatVersion; + unsigned char logPageId; + unsigned char numDwordsInLogPageHeaderHi; + unsigned int numValidDwordsInPayload; + unsigned int numDwordsInEntireLogPage; }; +typedef struct _MICRON_WORKLOAD_LOG_HDR +{ + unsigned short usNumEntries; + unsigned short usVersion; + unsigned int uiLength; +} MICRON_WORKLOAD_LOG_HDR; + static void WriteData(__u8 *data, __u32 len, const char *dir, const char *file, const char *msg) { char tempFolder[8192] = { 0 }; @@ -153,10 +175,20 @@ static enum eDriveModel GetDriveModel(int idx) case 0x51B2: eModel = M51BX; break; + case 0x51B7: + case 0x51B8: + case 0x51B9: + eModel = M51BY; + break; + case 0x51BB: + case 0x51BD: + eModel = M51CY; + break; case 0x51C0: case 0x51C1: case 0x51C2: case 0x51C3: + case 0x51C4: eModel = M51CX; break; case 0x5405: @@ -169,7 +201,16 @@ static enum eDriveModel GetDriveModel(int idx) break; case 0x5411: eModel = M5411; + break; + case 0x6001: + eModel = M6001; break; + case 0x6004: + eModel = M6004; + break; + case 0x6003: + eModel = M6003; + break; default: break; } @@ -364,7 +405,7 @@ static int GetLogPageSize(int nFD, unsigned char ucLogID, int *nLogSize) return err; } -static int NVMEGetLogPage(int nFD, unsigned char ucLogID, unsigned char *pBuffer, int nBuffSize) +static int NVMEGetLogPage(int nFD, unsigned char ucLogID, unsigned char *pBuffer, int nBuffSize, int offset) { int err = 0; struct nvme_passthru_cmd cmd = { 0 }; @@ -372,7 +413,7 @@ static int NVMEGetLogPage(int nFD, unsigned char ucLogID, unsigned char *pBuffer unsigned int uiMaxChunk = uiNumDwords; unsigned int uiNumChunks = 1; unsigned int uiXferDwords = 0; - unsigned long long ullBytesRead = 0; + unsigned long long ullBytesRead = offset; unsigned char *pTempPtr = pBuffer; unsigned char ucOpCode = 0x02; @@ -381,9 +422,15 @@ static int NVMEGetLogPage(int nFD, unsigned char ucLogID, unsigned char *pBuffer else if (uiMaxChunk > 16 * 1024) uiMaxChunk = 16 * 1024; - uiNumChunks = uiNumDwords / uiMaxChunk; - if (uiNumDwords % uiMaxChunk > 0) - uiNumChunks += 1; + if(ucLogID == 0xE9) { + uiMaxChunk = 0x1D8C; + ullBytesRead = offset; + } + + uiNumChunks = uiNumDwords / uiMaxChunk; + if (uiNumDwords % uiMaxChunk > 0) { + uiNumChunks += 1; + } for (unsigned int i = 0; i < uiNumChunks; i++) { memset(&cmd, 0, sizeof(cmd)); @@ -395,8 +442,9 @@ static int NVMEGetLogPage(int nFD, unsigned char ucLogID, unsigned char *pBuffer cmd.cdw10 |= ucLogID; cmd.cdw10 |= ((uiXferDwords - 1) & 0x0000FFFF) << 16; - if (ucLogID == 0x7) - cmd.cdw10 |= 0x80; + if (ucLogID == 0x7 && offset == 0) { + cmd.cdw10 |= 0x100; + } if (!ullBytesRead && (ucLogID == 0xE6 || ucLogID == 0xE7)) cmd.cdw11 = 1; if (ullBytesRead > 0 && !(ucLogID == 0xE6 || ucLogID == 0xE7)) { @@ -411,7 +459,11 @@ static int NVMEGetLogPage(int nFD, unsigned char ucLogID, unsigned char *pBuffer cmd.data_len = uiXferDwords * 4; err = nvme_submit_admin_passthru(nFD, &cmd, NULL); ullBytesRead += uiXferDwords * 4; - pTempPtr = pBuffer + ullBytesRead; + if (ucLogID == 0x07 || ucLogID == 0x08 || ucLogID == 0xE9) { + pTempPtr = pBuffer + (ullBytesRead - offset); + } else { + pTempPtr = pBuffer + ullBytesRead; + } } return err; @@ -428,7 +480,7 @@ static int NVMEResetLog(int nFD, unsigned char ucLogID, int nBufferSize, return err; while (!err && llMaxSize > 0) { - err = NVMEGetLogPage(nFD, ucLogID, (unsigned char *)pBuffer, nBufferSize); + err = NVMEGetLogPage(nFD, ucLogID, (unsigned char *)pBuffer, nBufferSize, 0); if (err) { free(pBuffer); return err; @@ -667,7 +719,7 @@ static int micron_smbus_option(int argc, char **argv, if (err < 0) return err; - if (model != M5407 && model != M5411) { + if (model != M5407 && model != M5411 && model != M6003 && model != M6004) { printf("This option is not supported for specified drive\n"); dev_close(dev); return err; @@ -737,12 +789,10 @@ static int micron_temp_stats(int argc, char **argv, struct command *cmd, struct format cfg = { .fmt = "normal", }; - bool is_json = false; struct json_object *root; - struct json_object *logPages; + struct json_object *logPages; struct nvme_dev *dev; - nvme_print_flags_t flags; OPT_ARGS(opts) = { OPT_FMT("format", 'f', &cfg.fmt, fmt), @@ -755,13 +805,7 @@ static int micron_temp_stats(int argc, char **argv, struct command *cmd, return -1; } - err = validate_output_format(nvme_cfg.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - - if (flags & JSON) + if (!strcmp(cfg.fmt, "json")) is_json = true; err = nvme_get_log_smart(dev_fd(dev), 0xffffffff, false, &smart_log); @@ -772,7 +816,6 @@ static int micron_temp_stats(int argc, char **argv, struct command *cmd, tempSensors[i] = le16_to_cpu(smart_log.temp_sensor[i]); tempSensors[i] = tempSensors[i] ? tempSensors[i] - 273 : 0; } - if (is_json) { struct json_object *stats = json_create_object(); char tempstr[64] = { 0 }; @@ -805,33 +848,7 @@ static int micron_temp_stats(int argc, char **argv, struct command *cmd, return err; } -static int micron_pcie_stats(int argc, char **argv, - struct command *cmd, struct plugin *plugin) -{ - int i, err = 0, bus, domain, device, function, ctrlIdx; - char strTempFile[1024], strTempFile2[1024], command[1024]; - struct nvme_dev *dev; - char *businfo = NULL; - char *devicename = NULL; - char tdevice[NAME_MAX] = { 0 }; - ssize_t sLinkSize = 0; - FILE *fp; - char correctable[8] = { 0 }; - char uncorrectable[8] = { 0 }; - struct nvme_passthru_cmd admin_cmd = { 0 }; - enum eDriveModel eModel = UNKNOWN_MODEL; - char *res; - bool is_json = true; - bool counters = false; - struct format { - char *fmt; - }; - const char *desc = "Retrieve PCIe event counters"; - const char *fmt = "output format json|normal"; - struct format cfg = { - .fmt = "json", - }; - struct pcie_error_counters { +struct pcie_error_counters { __u16 receiver_error; __u16 bad_tlp; __u16 bad_dllp; @@ -849,49 +866,76 @@ static int micron_pcie_stats(int argc, char **argv, __u16 ecrc_error; __u16 unsupported_request_error; } pcie_error_counters = { 0 }; - nvme_print_flags_t flags; struct { char *err; int bit; int val; } pcie_correctable_errors[] = { - { "Unsupported Request Error Status (URES)", 20, + { (char *)"Unsupported Request Error Status (URES)", 20, offsetof(struct pcie_error_counters, unsupported_request_error)}, - { "ECRC Error Status (ECRCES)", 19, + { (char *)"ECRC Error Status (ECRCES)", 19, offsetof(struct pcie_error_counters, ecrc_error)}, - { "Malformed TLP Status (MTS)", 18, + { (char *)"Malformed TLP Status (MTS)", 18, offsetof(struct pcie_error_counters, malformed_tlp)}, - { "Receiver Overflow Status (ROS)", 17, + { (char *)"Receiver Overflow Status (ROS)", 17, offsetof(struct pcie_error_counters, receiver_overflow)}, - { "Unexpected Completion Status (UCS)", 16, + { (char *)"Unexpected Completion Status (UCS)", 16, offsetof(struct pcie_error_counters, unexpected_completion)}, - { "Completer Abort Status (CAS)", 15, + { (char *)"Completer Abort Status (CAS)", 15, offsetof(struct pcie_error_counters, completion_abort)}, - { "Completion Timeout Status (CTS)", 14, + { (char *)"Completion Timeout Status (CTS)", 14, offsetof(struct pcie_error_counters, completion_timeout)}, - { "Flow Control Protocol Error Status (FCPES)", 13, + { (char *)"Flow Control Protocol Error Status (FCPES)", 13, offsetof(struct pcie_error_counters, FCPC)}, - { "Poisoned TLP Status (PTS)", 12, + { (char *)"Poisoned TLP Status (PTS)", 12, offsetof(struct pcie_error_counters, poisoned_tlp)}, - { "Data Link Protocol Error Status (DLPES)", 4, + { (char *)"Data Link Protocol Error Status (DLPES)", 4, offsetof(struct pcie_error_counters, DLPES)}, }, pcie_uncorrectable_errors[] = { - { "Advisory Non-Fatal Error Status (ANFES)", 13, + { (char *)"Advisory Non-Fatal Error Status (ANFES)", 13, offsetof(struct pcie_error_counters, advisory_non_fatal_error)}, - { "Replay Timer Timeout Status (RTS)", 12, + { (char *)"Replay Timer Timeout Status (RTS)", 12, offsetof(struct pcie_error_counters, replay_timer_timeout)}, - { "REPLAY_NUM Rollover Status (RRS)", 8, + { (char *)"REPLAY_NUM Rollover Status (RRS)", 8, offsetof(struct pcie_error_counters, replay_num_rollover)}, - { "Bad DLLP Status (BDS)", 7, + { (char *)"Bad DLLP Status (BDS)", 7, offsetof(struct pcie_error_counters, bad_dllp)}, - { "Bad TLP Status (BTS)", 6, + { (char *)"Bad TLP Status (BTS)", 6, offsetof(struct pcie_error_counters, bad_tlp)}, - { "Receiver Error Status (RES)", 0, + { (char *)"Receiver Error Status (RES)", 0, offsetof(struct pcie_error_counters, receiver_error)}, }; + +static int micron_pcie_stats(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + int i, err = 0, bus= 0, domain = 0, device = 0, function = 0, ctrlIdx; + char strTempFile[1024], strTempFile2[1024], command[1024]; + struct nvme_dev *dev; + char *businfo = NULL; + char *devicename = NULL; + char tdevice[NAME_MAX] = { 0 }; + ssize_t sLinkSize = 0; + FILE *fp; + char correctable[8] = { 0 }; + char uncorrectable[8] = { 0 }; + struct nvme_passthru_cmd admin_cmd = { 0 }; + enum eDriveModel eModel = UNKNOWN_MODEL; + char *res; + bool is_json = true; + bool counters = false; + struct format { + char *fmt; + }; + const char *desc = "Retrieve PCIe event counters"; + const char *fmt = "output format json|normal"; + struct format cfg = { + .fmt = "json", + }; + __u32 correctable_errors; __u32 uncorrectable_errors; @@ -915,13 +959,7 @@ static int micron_pcie_stats(int argc, char **argv, goto out; } - err = validate_output_format(cfg.fmt, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - - if (flags & NORMAL) + if (!strcmp(cfg.fmt, "normal")) is_json = false; if (eModel == M5407) { @@ -1005,7 +1043,7 @@ static int micron_pcie_stats(int argc, char **argv, print_stats: if (is_json) { struct json_object *root = json_create_object(); - struct json_object *pcieErrors = json_create_array(); + struct json_object *pcieErrors = json_create_array(); struct json_object *stats = json_create_object(); __u8 *pcounter = (__u8 *)&pcie_error_counters; @@ -1079,8 +1117,11 @@ static int micron_clear_pcie_correctable_errors(int argc, char **argv, if (err < 0) return err; - /* For M51CX models, PCIe errors are cleared using 0xC3 feature */ - if (model == M51CX) { + /* For M51CX models, PCIe errors are cleared using 0xC3 feature + * and for M5407 models, PCIe errors are cleared using 0xD6 command + * If these fail, proceed with sysfs interface to set/clear bits + */ + if (model == M51CX || model == M51BY || model== M51CY) { err = nvme_set_features_simple(dev_fd(dev), fid, 0, (1 << 31), false, &result); if (!err) @@ -1218,6 +1259,118 @@ static void init_d0_log_page(__u8 *buf, __u8 nsze) sprintf(d0_log_page[6].datastr, "0x%x", logD0[1]); } +static struct nand_stats { + const char *field; + char datastr[128]; +} hyperscale_BSSD_nand_stats[] = { + {"Physical Media Units Written - TLC", {0}}, + {"Physical Media Units Written - SLC", {0}}, + {"Bad User NAND Block Count (Normalized)", {0}}, + {"Bad User NAND Block Count (Raw)", {0}}, + {"XOR Recovery count", {0}}, + {"Uncorrectable read error count", {0}}, + { "User Data Erase Counts (Minimum TLC)", {0}}, + { "User Data Erase Counts (Maximum TLC)", {0}}, + { "User Data Erase Counts (Average TLC)", {0}}, + { "User Data Erase Counts (Minimum SLC)", {0}}, + { "User Data Erase Counts (Maximum SLC)", {0}}, + { "User Data Erase Counts (Average SLC)", {0}}, + { "Program Fail Count (Normalized)", {0}}, + { "Program Fail Count (Raw)", {0}}, + { "Erase Fail Count (Normalized)", {0}}, + { "Erase Fail Count (Raw)", {0}}, + { "Total # of Soft ECC Error Count", {0}}, + { "Bad System NAND Block Count (Normalized)",{0}}, + { "Bad System NAND Block Count (Raw)", {0}}, + {"Endurance Estimate", {0}}, + {"Physical Media Units Read", {0}}, + {"Boot SSD Spec Version", {0}}, +}; + +static void micron_readFixedBytesFromBuffer(__u8 *buf, int offset, int numBytes, char *datastr) +{ + __u16 u16Val; + __u32 u32Val; + __u64 count_lo, count_hi, count; + + switch(numBytes) + { + case 16: + { + count_lo = *((__u64 *)(&buf[offset])); + count_hi = *((__u64 *)(&buf[offset + 8])); + if (count_hi) + sprintf(datastr, "0x%"PRIx64"%016"PRIx64"", + le64_to_cpu(count_hi), + le64_to_cpu(count_lo)); + else + sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(count_lo)); + + } + break; + case 8: + { + count = *((__u64 *)(&buf[offset])); + sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(count)); + } + break; + case 6: + { + u32Val = *((__u32 *)(&buf[offset])); + u16Val = *((__u16 *)(&buf[offset + 4])); + count = (((__u64)u32Val << 32) | u16Val); + sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(count)); + } + break; + case 2: + { + u16Val = *((__u16 *)(&buf[offset])); + sprintf(datastr,"0x%04x", le16_to_cpu(u16Val)); + } + break; + } +} + +static void init_hyperscale_BSSD_nand_stats(__u8 *buf) +{ + + __u16 majorVer, minorVer, pointVer, errataVer; + + micron_readFixedBytesFromBuffer (buf, 0, 16, hyperscale_BSSD_nand_stats[0].datastr); + micron_readFixedBytesFromBuffer (buf, 16, 16, hyperscale_BSSD_nand_stats[1].datastr); + micron_readFixedBytesFromBuffer (buf, 32, 2, hyperscale_BSSD_nand_stats[2].datastr); + micron_readFixedBytesFromBuffer (buf, 34, 6, hyperscale_BSSD_nand_stats[3].datastr); + micron_readFixedBytesFromBuffer (buf, 40, 8, hyperscale_BSSD_nand_stats[4].datastr); + micron_readFixedBytesFromBuffer (buf, 48, 8, hyperscale_BSSD_nand_stats[5].datastr); + micron_readFixedBytesFromBuffer (buf, 84, 8, hyperscale_BSSD_nand_stats[6].datastr); + micron_readFixedBytesFromBuffer (buf, 92, 8, hyperscale_BSSD_nand_stats[7].datastr); + micron_readFixedBytesFromBuffer (buf, 100, 8, hyperscale_BSSD_nand_stats[8].datastr); + micron_readFixedBytesFromBuffer (buf, 108, 8, hyperscale_BSSD_nand_stats[9].datastr); + micron_readFixedBytesFromBuffer (buf, 116, 8, hyperscale_BSSD_nand_stats[10].datastr); + micron_readFixedBytesFromBuffer (buf, 124, 8, hyperscale_BSSD_nand_stats[11].datastr); + micron_readFixedBytesFromBuffer (buf, 132, 2, hyperscale_BSSD_nand_stats[12].datastr); + micron_readFixedBytesFromBuffer (buf, 134, 6, hyperscale_BSSD_nand_stats[13].datastr); + micron_readFixedBytesFromBuffer (buf, 140, 2, hyperscale_BSSD_nand_stats[14].datastr); + micron_readFixedBytesFromBuffer (buf, 142, 6, hyperscale_BSSD_nand_stats[15].datastr); + micron_readFixedBytesFromBuffer (buf, 202, 8, hyperscale_BSSD_nand_stats[16].datastr); + micron_readFixedBytesFromBuffer (buf, 218, 2, hyperscale_BSSD_nand_stats[17].datastr); + micron_readFixedBytesFromBuffer (buf, 220, 6, hyperscale_BSSD_nand_stats[18].datastr); + micron_readFixedBytesFromBuffer (buf, 226, 16, hyperscale_BSSD_nand_stats[19].datastr); + micron_readFixedBytesFromBuffer (buf, 252, 16, hyperscale_BSSD_nand_stats[20].datastr); + + majorVer = *((__u16 *)(&buf[300])); + minorVer = *((__u16 *)(&buf[302])); + pointVer = *((__u16 *)(&buf[304])); + errataVer = *((__u16 *)(&buf[306])); + sprintf(hyperscale_BSSD_nand_stats[21].datastr, + "%x.%x.%x.%x", + le16_to_cpu(majorVer), + le16_to_cpu(minorVer), + le16_to_cpu(pointVer), + le16_to_cpu(errataVer)); +} + + /* Smart Health Log information as per OCP spec M51CX models */ struct request_data ocp_c0_log_page[] = { { "Physical Media Units Written", 16}, @@ -1254,6 +1407,103 @@ struct request_data ocp_c0_log_page[] = { { "Log Page Version", 2}, { "Log Page GUID", 16}, }, + +/* Smart Health Log information Extended as per Hyperscale NVME Boot SSD spec M51CX models */ +hyperscale_c0_log_page[] = { + { "Physical Media Units Written - TLC", 16}, + { "Physical Media Units Written - SLC", 16}, + { "Bad User NAND Block Count (Normalized)", 2}, + { "Bad User NAND Block Count (Raw)", 6}, + { "XOR Recovery Count", 8}, + { "Uncorrectable Read Error Count", 8}, + { "SSD End to End correction counts (Corrected Errors)", 8}, + { "SSD End to End correction counts (Detected Counts)", 8}, + { "SSD End to End correction counts (Uncorrected Counts)", 8}, + { "System data % life-used", 1}, + { "Reserved", 3}, + { "User Data Erase Counts (Minimum TLC)", 8}, + { "User Data Erase Counts (Maximum TLC)", 8}, + { "User Data Erase Counts (Average TLC)", 8}, + { "User Data Erase Counts (Minimum SLC)", 8}, + { "User Data Erase Counts (Maximum SLC)", 8}, + { "User Data Erase Counts (Average SLC)", 8}, + { "Program Fail Count (Normalized)", 2}, + { "Program Fail Count (Raw)", 6}, + { "Erase Fail Count (Normalized)", 2}, + { "Erase Fail Count (Raw)", 6}, + { "Pcie Correctable Error Count", 8}, + { "% Free Blocks (User)", 1}, + { "Reserved", 3}, + { "Security Version Number", 8}, + { "% Free Blocks (System)", 1}, + { "Reserved", 3}, + { "NVMe Stats (# Data set Management/TRIM Commands Completed", 16}, + { "Total Namespace Utilization (nvme0n1 NUSE)", 8}, + { "NVMe Stats (#NVMe Format Commands Completed)", 2}, + { "Background Back-Pressure Gauge(%)", 1}, + { "Reserved", 3}, + { "Total # of Soft ECC Error Count", 8}, + { "Total # of Read Refresh Count", 8}, + { "Bad System NAND Block Count (Normalized)", 2}, + { "Bad System NAND Block Count (Raw)", 6}, + { "Endurance Estimate (Total Writable Lifetime Bytes)", 16}, + { "Thermal Throttling Status & Count (Number of thermal throttling events)", 2}, + { "Total # Unaligned I/O", 8}, + { "Total Physical Media Units Read (Bytes)", 16}, + { "Command Timeout (# of READ CMDs exceeding threshold)", 4}, + { "Command Timeout (# of WRITE CMDs exceeding threshold)", 4}, + { "Command Timeout (# of TRIMs CMDs exceeding threshold)", 4}, + { "Reserved", 4}, + { "Total PCIe Link Retraining Count", 8}, + { "Active Power State Change Count", 8}, + { "Boot SSD Spec Version", 8}, + { "FTL Unit Size", 4}, + { "TCG Ownership Status", 4}, + { "Reserved", 178}, + { "Log Page Version",2}, + { "Log Page GUID", 16}, +}, + +/* Smart Health Log information as per datacenter-nvme-ssd-specification-v2 M51BY models */ +datacenter_c0_log_page[] = { + { "Physical Media Units Written", 16}, + { "Physical Media Units Read", 16 }, + { "Raw Bad User NAND Block Count", 6}, + { "Normalized Bad User NAND Block Count", 2}, + { "Raw Bad System NAND Block Count", 6}, + { "Normalized Bad System NAND Block Count", 2}, + { "XOR Recovery Count", 8}, + { "Uncorrectable Read Error Count", 8}, + { "Soft ECC Error Count", 8}, + { "SSD End to End Detected Counts", 4}, + { "SSD End to End Corrected Errors", 4}, + { "System data % life-used", 1}, + { "Refresh Count", 7}, + { "Maximum User Data Erase Count", 4}, + { "Minimum User Data Erase Count", 4}, + { "Thermal Throttling Count", 1}, + { "Thermal Throttling Status", 1}, + { "DSSD Spec Version", 6}, + { "PCIe Correctable Error count", 8}, + { "Incomplete Shutdowns", 4}, + { "Reserved", 4}, + { "% Free Blocks", 1}, + { "Reserved", 7}, + { "Capacitor Health", 2}, + { "NVMe Errata Version", 1}, + { "Reserved", 5}, + { "Unaligned I/O", 8}, + { "Security Version Number", 8}, + { "Total NUSE", 8}, + { "PLP Start Count", 16}, + { "Endurance Estimate", 16}, + { "PCIe Link Retraining Count", 8}, + { "Power State Change Count", 8}, + { "Reserved", 286}, + { "Log Page Version", 2}, + { "Log Page GUID", 16}, +}, + /* Extended SMART log information */ e1_log_page[] = { { "Reserved", 12}, @@ -1336,24 +1586,117 @@ fb_log_page[] = { { "Reserved", 0, 210}, { "Log Page Version", 0, 2}, { "Log Page GUID", 0, 16}, +}, + +/* SMARTS for 0x6001 Nitro model */ +//Extended Health Information (Log Identifier D0h) +D0_log_page[] = { + { "Reserved", 2},//1:0 + { "Version", 2},//3:2 + { "Grown Bad Block Count", 4},//7:4 + { "Total SRAM SBE Count", 4},//11:8 + { "Total SRAM DBE Count", 4},//15:12 + { "Write Protect Reason", 4},//19:16 + { "Total Erase Count", 4},//23:20 + { "Erase Fail Count", 4},//27:24 + { "Program Fail Counts", 4},//31:28 + { "Reserved", 40},//71:32 + { "Completed PLN PLA Cycles", 4},//75:72 + { "PLN Assert Count", 4},//79:76 + { "PLN Deassert Count", 4},//83:80 + { "PLA Assert Count", 4},//87:84 + { "PLA Deassert Count", 4},//91:88 + { "Correctable NAND UECCs", 4},//95:92 + { "Reported Uncorrectable Errors UECCCs", 4},//99:96 + { "TLC Super Block Min Erase Count", 4},//103:100 + { "TLC Super Block Avg Erase Count", 4},//107:104 + { "TLC Super Block Max Erase Count", 4},//111:108 + { "Min ASIC Temp Recorded", 2},//113:112 + { "Max ASIC Temp Recorded", 2},//115:114 + { "Min NAND Temp Recorded", 2},//171:116 + { "Max NAND Temp Recorded", 2},//119:118 + { "SLC Super Block Min Erase Count", 4},//123:120 + { "SLC Super Block Avg Erase Count", 4},//127:124 + { "SLC Super Block Max Erase Count", 4},//131:128 + { "SLC Lifetime Used", 2},//133:132 + { "TLC Lifetime Used", 2},//135:134 + { "Unmapped LBA Count", 8},//143:136 + { "PWR1 Voltage Detection Threshold #1", 4},//147:144 + { "PWR1 Voltage Detection Threshold #2", 4},//151:148 +}, +//Micron Workload Log (Log Identifier C5h) +C5_log_page[] = { + { "Reserved", 4},//3:0 + { "Number of Downshifts", 1},//4 + { "Reserved", 71},//75:5 + { "Number of LBAs Deallocated Trimmed", 4},//79:76 + { "Reserved", 41},//120:80 + { "Number of Security Send Commands", 4},//124:121 + { "Number of Security Receive Commands", 4},//128:125 + { "Total Sanitize Events", 4},//132:129 +}, +//Vendor Telemetry Log (Log Identifier C6h)) +C6_log_page[] = { + { "Reserved", 240},//239:0 + { "Total TLC NAND Write Count", 8},//247:240 + { "Total SLC NAND Write Count", 8},//255:248 + { "Total SLC Host Write Count", 8},//263:256 + { "Total TLC Host Write Count", 8},//272:264 }; -static void print_smart_cloud_health_log(__u8 *buf, bool is_json) +static void print_smart_cloud_health_log(__u8 *buf, bool is_json, enum eDriveModel eModel) { - struct json_object *root; - struct json_object *logPages; + struct json_object *root = NULL; + struct json_object *logPages = NULL; struct json_object *stats = NULL; - int field_count = ARRAY_SIZE(ocp_c0_log_page); + int field_count = 0; + + if (eModel == M51CX) + field_count = ARRAY_SIZE(ocp_c0_log_page); + else if(eModel == M51BY || eModel == M51CY) + field_count = ARRAY_SIZE(datacenter_c0_log_page); if (is_json) { root = json_create_object(); stats = json_create_object(); logPages = json_create_array(); + if(eModel == M51BY || eModel == M51CY) + json_object_add_value_array(root, "OCP DataCenter SMART Health Log: 0xC0", + logPages); + else if (eModel == M51CX) json_object_add_value_array(root, "OCP SMART Cloud Health Log: 0xC0", logPages); } - generic_structure_parser(buf, ocp_c0_log_page, field_count, stats, 0, NULL); + if(eModel == M51BY || eModel == M51CY) + generic_structure_parser(buf, datacenter_c0_log_page, field_count, stats, 0, NULL); + else if (eModel == M51CX) + generic_structure_parser(buf, ocp_c0_log_page, field_count, stats, 0, NULL); + + if (is_json) { + json_array_add_value_object(logPages, stats); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } +} + +static void print_hyperscale_cloud_health_log(__u8 *buf, bool is_json) +{ + struct json_object *root; + struct json_object *logPages; + struct json_object *stats = NULL; + int field_count = ARRAY_SIZE(hyperscale_c0_log_page); + + if (is_json) { + root = json_create_object(); + stats = json_create_object(); + logPages = json_create_array(); + json_object_add_value_array(root, "OCP Hyperscale Cloud Health Log: 0xC0", + logPages); + } + + generic_structure_parser(buf, hyperscale_c0_log_page, field_count, stats, 0, NULL); if (is_json) { json_array_add_value_object(logPages, stats); @@ -1366,7 +1709,7 @@ static void print_smart_cloud_health_log(__u8 *buf, bool is_json) static void print_nand_stats_fb(__u8 *buf, __u8 *buf2, __u8 nsze, bool is_json, __u8 spec) { struct json_object *root; - struct json_object *logPages; + struct json_object *logPages; struct json_object *stats = NULL; int field_count = ARRAY_SIZE(fb_log_page); @@ -1410,28 +1753,59 @@ static void print_nand_stats_d0(__u8 *buf, __u8 oacs, bool is_json) if (is_json) { struct json_object *root = json_create_object(); struct json_object *stats = json_create_object(); - struct json_object *logPages = json_create_array(); + struct json_object *logPages = json_create_array(); json_object_add_value_array(root, "Extended Smart Log Page : 0xD0", logPages); - for (int i = 0; i < 7; i++) - json_object_add_value_string(stats, - d0_log_page[i].field, - d0_log_page[i].datastr); + for (int i = 0; i < 7; i++) { + json_object_add_value_string(stats, + d0_log_page[i].field, + d0_log_page[i].datastr); + } + + json_array_add_value_object(logPages, stats); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } else { + for (int i = 0; i < 7; i++) { + printf("%-40s : %s\n", d0_log_page[i].field, d0_log_page[i].datastr); + } + } +} - json_array_add_value_object(logPages, stats); - json_print_object(root, NULL); - printf("\n"); - json_free_object(root); - } else { - for (int i = 0; i < 7; i++) - printf("%-40s : %s\n", d0_log_page[i].field, d0_log_page[i].datastr); - } +static void print_hyperscale_nand_stats(__u8 *buf, bool is_json) +{ + init_hyperscale_BSSD_nand_stats(buf); + + if (is_json) { + struct json_object *root = json_create_object(); + struct json_object *stats = json_create_object(); + struct json_object *logPages = json_create_array(); + json_object_add_value_array(root, + "Extended Smart Log Page : 0xC0", + logPages); + + for (int i = 0; i < 22; i++) { + json_object_add_value_string(stats, + hyperscale_BSSD_nand_stats[i].field, + hyperscale_BSSD_nand_stats[i].datastr); + } + + json_array_add_value_object(logPages, stats); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } else { + for (int i = 0; i < 22; i++) { + printf("%-40s : %s\n", hyperscale_BSSD_nand_stats[i].field, hyperscale_BSSD_nand_stats[i].datastr); + } + } } -static bool nsze_from_oacs; /* read nsze for now from idd[4059] */ +static bool nsze_from_oacs = false; /* read nsze for now from idd[4059] */ static int micron_nand_stats(int argc, char **argv, struct command *cmd, struct plugin *plugin) @@ -1439,6 +1813,7 @@ static int micron_nand_stats(int argc, char **argv, const char *desc = "Retrieve Micron NAND stats for the given device "; unsigned int extSmartLog[D0_log_size/sizeof(int)] = { 0 }; unsigned int logFB[FB_log_size/sizeof(int)] = { 0 }; + unsigned char logC0[C0_log_size] = { 0 }; enum eDriveModel eModel = UNKNOWN_MODEL; struct nvme_id_ctrl ctrl; struct nvme_dev *dev; @@ -1479,17 +1854,34 @@ static int micron_nand_stats(int argc, char **argv, if (sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx) != 1) ctrlIdx = 0; eModel = GetDriveModel(ctrlIdx); - if ((eModel == UNKNOWN_MODEL) || (eModel == M51CX)) { + if (eModel == UNKNOWN_MODEL) { printf("Unsupported drive model for vs-nand-stats command\n"); err = -1; goto out; } - err = nvme_get_log_simple(dev_fd(dev), 0xD0, D0_log_size, extSmartLog); - has_d0_log = !err; + err = nvme_identify_ctrl(dev_fd(dev), &ctrl); + if (err) { + fprintf(stderr, "ERROR : identify_ctrl() failed with 0x%x\n", err); + return -1; + } + + if ((ctrl.vs[536] == MICRON_CUST_ID_GG) && (eModel == M51CX)) { + err = nvme_get_log_simple(dev_fd(dev), 0xC0, C0_log_size, logC0); + if (err == 0) { + print_hyperscale_nand_stats((__u8 *)logC0, is_json); + goto out; + } else if (err < 0) { + printf("Unable to retrieve extended smart log 0xC0 for the drive\n"); + return -1; + } + } + + err = nvme_get_log_simple(dev_fd(dev), 0xD0, D0_log_size, extSmartLog); + has_d0_log = (0 == err); /* should check for firmware version if this log is supported or not */ - if (eModel == M5407 || eModel == M5410) { + if (eModel != M5407 && eModel != M5410) { err = nvme_get_log_simple(dev_fd(dev), 0xFB, FB_log_size, logFB); has_fb_log = !err; } @@ -1504,6 +1896,7 @@ static int micron_nand_stats(int argc, char **argv, print_nand_stats_fb((__u8 *)logFB, (__u8 *)extSmartLog, nsze, is_json, spec); } else if (has_d0_log) { print_nand_stats_d0((__u8 *)extSmartLog, nsze, is_json); + err = 0; } else { printf("Unable to retrieve extended smart log for the drive\n"); err = -ENOTTY; @@ -1516,30 +1909,65 @@ static int micron_nand_stats(int argc, char **argv, return err; } -static void print_ext_smart_logs_e1(__u8 *buf, bool is_json) +static void print_log(__u8 *buf, bool is_json, unsigned char ucLogID) { - struct json_object *root; - struct json_object *logPages; - struct json_object *stats = NULL; - int field_count = ARRAY_SIZE(e1_log_page); - - if (is_json) { - root = json_create_object(); - stats = json_create_object(); - logPages = json_create_array(); - json_object_add_value_array(root, "SMART Extended Log:0xE1", logPages); - } else { - printf("SMART Extended Log:0xE1\n"); - } - - generic_structure_parser(buf, e1_log_page, field_count, stats, 0, NULL); - - if (is_json) { - json_array_add_value_object(logPages, stats); - json_print_object(root, NULL); - printf("\n"); - json_free_object(root); - } + struct json_object *root; + struct json_object *logPages; + struct json_object *stats = NULL; + int field_count = 0; + struct request_data *log_pageID; + + char tempStr[256] = { 0 }; + if(ucLogID == 0xE1) + { + log_pageID = e1_log_page; + field_count = ARRAY_SIZE(e1_log_page); + sprintf(tempStr, "SMART Extended Log:0x%X", ucLogID); + } + else if(ucLogID == 0xD0) + { + log_pageID = D0_log_page; + field_count = ARRAY_SIZE(D0_log_page); + sprintf(tempStr, "SMART Extended Log:0x%X", ucLogID); + } + else if(ucLogID == 0xC5) + { + log_pageID = C5_log_page; + field_count = ARRAY_SIZE(C5_log_page); + sprintf(tempStr, "Micron Workload Log:0x%X", ucLogID); + } + else if(ucLogID == 0xC6) + { + log_pageID = C6_log_page; + field_count = ARRAY_SIZE(C6_log_page); + sprintf(tempStr, "Vendor Telemetry Log:0x%X", ucLogID); + } + else + { + log_pageID = NULL; + } + + if (is_json) { + root = json_create_object(); + stats = json_create_object(); + logPages = json_create_array(); + json_object_add_value_array(root, tempStr, logPages); + } + else { + printf("%s\n", tempStr); + } + + if(log_pageID != NULL) + { + generic_structure_parser(buf, log_pageID, field_count, stats, 0, NULL); + } + + if (is_json) { + json_array_add_value_object(logPages, stats); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } } static int micron_smart_ext_log(int argc, char **argv, @@ -1548,7 +1976,8 @@ static int micron_smart_ext_log(int argc, char **argv, const char *desc = "Retrieve extended SMART logs for the given device "; unsigned int extSmartLog[E1_log_size/sizeof(int)] = { 0 }; enum eDriveModel eModel = UNKNOWN_MODEL; - int err = 0, ctrlIdx; + int err = 0, ctrlIdx = 0; + __u8 log_id; struct nvme_dev *dev; bool is_json = true; struct format { @@ -1562,34 +1991,35 @@ static int micron_smart_ext_log(int argc, char **argv, OPT_FMT("format", 'f', &cfg.fmt, fmt), OPT_END() }; - nvme_print_flags_t flags; err = parse_and_open(&dev, argc, argv, desc, opts); if (err) { printf("\nDevice not found\n"); return -1; } - - err = validate_output_format(nvme_cfg.output_format, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } - if (!strcmp(cfg.fmt, "normal")) is_json = false; if (sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx) != 1) ctrlIdx = 0; eModel = GetDriveModel(ctrlIdx); - if (eModel != M51CX) { + if (eModel == M51CX || eModel == M51BY || eModel == M51CY || eModel == M6003 || eModel == M6004) + { + log_id = 0xE1; + } + else if (eModel == M6001) + { + log_id = 0xD0; + } + else + { printf("Unsupported drive model for vs-smart-ext-log command\n"); err = -1; goto out; } - err = nvme_get_log_simple(dev_fd(dev), 0xE1, E1_log_size, extSmartLog); + err = nvme_get_log_simple(dev_fd(dev), log_id, E1_log_size, extSmartLog); if (!err) - print_ext_smart_logs_e1((__u8 *)extSmartLog, is_json); + print_log((__u8 *)extSmartLog, is_json, log_id); out: dev_close(dev); @@ -1598,6 +2028,112 @@ static int micron_smart_ext_log(int argc, char **argv, return err; } +static int micron_work_load_log(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Retrieve Micron Workload logs for the given device "; + unsigned int micronWorkLoadLog[C5_MicronWorkLoad_log_size/sizeof(int)] = { 0 }; + enum eDriveModel eModel = UNKNOWN_MODEL; + struct nvme_dev *dev; + int err = 0, ctrlIdx = 0; + bool is_json = true; + struct format { + char *fmt; + }; + const char *fmt = "output format json|normal"; + struct format cfg = { + .fmt = "json", + }; + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) { + printf("\nDevice not found \n");; + return -1; + } + if (strcmp(cfg.fmt, "normal") == 0) + is_json = false; + + sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); + eModel = GetDriveModel(ctrlIdx); + if (eModel == M6001 || eModel == M6004 || eModel == M6003) + { + err = nvme_get_log_simple(dev_fd(dev), 0xC5, C5_MicronWorkLoad_log_size, micronWorkLoadLog); + if (!err) + { + print_log((__u8 *)micronWorkLoadLog, is_json, 0xC5); + } + } + else + { + printf ("Unsupported drive model for vs-work-load-log command\n"); + err = -1; + goto out; + } + +out: + dev_close(dev); + if (err > 0) + nvme_show_status(err); + return err; +} + +static int micron_vendor_telemetry_log(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Retrieve Vendor Telemetry logs for the given device "; + unsigned int vendorTelemetryLog[C6_log_size/sizeof(int)] = { 0 }; + enum eDriveModel eModel = UNKNOWN_MODEL; + int err = 0, ctrlIdx = 0; + bool is_json = true; + struct nvme_dev *dev; + struct format { + char *fmt; + }; + const char *fmt = "output format json|normal"; + struct format cfg = { + .fmt = "json", + }; + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) { + printf("\nDevice not found \n");; + return -1; + } + if (strcmp(cfg.fmt, "normal") == 0) + is_json = false; + + sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); + eModel = GetDriveModel(ctrlIdx); + if (eModel == M6001 || eModel == M6004 || eModel == M6003) + { + err = nvme_get_log_simple(dev_fd(dev), 0xC6, C6_log_size, vendorTelemetryLog); + if (!err) + { + print_log((__u8 *)vendorTelemetryLog, is_json, 0xC6); + } + } + else + { + printf ("Unsupported drive model for vs-vendor-telemetry-log command\n"); + err = -1; + goto out; + } + +out: + dev_close(dev); + if (err > 0) + nvme_show_status(err); + return err; +} + static void GetDriveInfo(const char *strOSDirName, int nFD, struct nvme_id_ctrl *ctrlp) { @@ -1709,7 +2245,6 @@ static void GetGenericLogs(int fd, const char *dir) struct nvme_firmware_slot fw_log; struct nvme_cmd_effects_log effects; struct nvme_persistent_event_log pevent_log; - _cleanup_huge_ struct nvme_mem_huge mh = { 0, }; void *pevent_log_info = NULL; __u32 log_len = 0; @@ -1965,27 +2500,30 @@ static int micron_drive_info(int argc, char **argv, struct command *cmd, const char *desc = "Get drive HW information"; struct nvme_id_ctrl ctrl = { 0 }; struct nvme_passthru_cmd admin_cmd = { 0 }; + unsigned char logC0[C0_log_size] = { 0 }; struct fb_drive_info { unsigned char hw_ver_major; unsigned char hw_ver_minor; unsigned char ftl_unit_size; - unsigned char bs_ver_major; - unsigned char bs_ver_minor; + unsigned short bs_ver_major; + unsigned short bs_ver_minor; + unsigned int ownership_status; } dinfo = { 0 }; enum eDriveModel model = UNKNOWN_MODEL; + __u8 custId = 0x10; /* default Micron generic */ bool is_json = false; - struct json_object *root, *driveInfo; + struct json_object *root; + struct json_object *driveInfo; struct nvme_dev *dev; struct format { char *fmt; }; int err = 0; - nvme_print_flags_t flags; - const char *fmt = "output format normal"; - struct format cfg = { - .fmt = "normal", - }; + const char *fmt = "output format normal|json"; + struct format cfg = { + .fmt = "normal", + }; OPT_ARGS(opts) = { OPT_FMT("format", 'f', &cfg.fmt, fmt), @@ -1997,22 +2535,22 @@ static int micron_drive_info(int argc, char **argv, struct command *cmd, return err; if (model == UNKNOWN_MODEL) { - fprintf(stderr, "ERROR : Unsupported drive for vs-drive-info cmd\n"); + fprintf(stderr, "ERROR : Unsupported drive for vs-drive-info cmd"); dev_close(dev); return -1; } - - err = validate_output_format(cfg.fmt, &flags); - if (err < 0) { - nvme_show_error("Invalid output format"); - return err; - } + + if (strcmp(cfg.fmt, "normal") || strcmp(cfg.fmt, "json")) { + fprintf(stderr, "Invalid output format\n"); + dev_close(dev); + return -1; + } if (!strcmp(cfg.fmt, "json")) is_json = true; if (model == M5407) { - admin_cmd.opcode = 0xD4, + admin_cmd.opcode = 0xDA; admin_cmd.addr = (__u64) (uintptr_t) &dinfo; admin_cmd.data_len = (__u32)sizeof(dinfo); admin_cmd.cdw12 = 3; @@ -2032,43 +2570,98 @@ static int micron_drive_info(int argc, char **argv, struct command *cmd, dinfo.hw_ver_major = ctrl.vs[820]; dinfo.hw_ver_minor = ctrl.vs[821]; dinfo.ftl_unit_size = ctrl.vs[822]; + custId = ctrl.vs[536]; } - if (is_json) { - struct json_object *pinfo = json_create_object(); - char tempstr[64] = { 0 }; - - root = json_create_object(); - driveInfo = json_create_array(); - json_object_add_value_array(root, "Micron Drive HW Information", driveInfo); - sprintf(tempstr, "%u.%u", dinfo.hw_ver_major, dinfo.hw_ver_minor); - json_object_add_value_string(pinfo, "Drive Hardware Version", tempstr); - - if (dinfo.ftl_unit_size) { - sprintf(tempstr, "%u KB", dinfo.ftl_unit_size); - json_object_add_value_string(pinfo, "FTL_unit_size", tempstr); - } - - if (dinfo.bs_ver_major || dinfo.bs_ver_minor) { - sprintf(tempstr, "%u.%u", dinfo.bs_ver_major, dinfo.bs_ver_minor); - json_object_add_value_string(pinfo, "Boot Spec.Version", tempstr); - } - - json_array_add_value_object(driveInfo, pinfo); - json_print_object(root, NULL); - printf("\n"); - json_free_object(root); - } else { - printf("Drive Hardware Version: %u.%u\n", - dinfo.hw_ver_major, dinfo.hw_ver_minor); + if ((custId == MICRON_CUST_ID_GG) && (model == M51CX)) { + err = nvme_get_log_simple(dev_fd(dev), 0xC0, C0_log_size, logC0); + if (err == 0) { + dinfo.bs_ver_major = *((__u16 *)(logC0+300)); + dinfo.bs_ver_minor = *((__u16 *)(logC0+302)); + dinfo.ownership_status = *((__u32 *)(logC0+312)); + } else if (err < 0) { + printf("Unable to retrieve extended smart log 0xC0 for the drive\n"); + return -1; + } + } + + if (is_json) { + struct json_object *pinfo = json_create_object(); + char tempstr[64] = { 0 }; + root = json_create_object(); + driveInfo = json_create_array(); + json_object_add_value_array(root, "Micron Drive HW Information", driveInfo); + + sprintf(tempstr, "%hhu.%hhu", dinfo.hw_ver_major, dinfo.hw_ver_minor); + json_object_add_value_string(pinfo, "Drive Hardware Version", tempstr); + + if (custId == MICRON_CUST_ID_GG) { + if (dinfo.ftl_unit_size) { + sprintf(tempstr, "%hhu B", dinfo.ftl_unit_size*1024); + json_object_add_value_string(pinfo, "FTL_unit_size", tempstr); + } + + if (dinfo.bs_ver_major != 0 || dinfo.bs_ver_minor != 0) { + sprintf(tempstr, "HyperScale Boot Version Spec.%hhu.%hhu", dinfo.bs_ver_major, dinfo.bs_ver_minor); + json_object_add_value_string(pinfo, "Boot Spec.Version", tempstr); + } + + if ( dinfo.ownership_status == 0) + sprintf(tempstr, "N/A"); + else if ( dinfo.ownership_status == 1) + sprintf(tempstr, "UNSET"); + else if ( dinfo.ownership_status == 2) + sprintf(tempstr, "SET"); + else if ( dinfo.ownership_status == 3) + sprintf(tempstr, "BLOCKED"); + json_object_add_value_string(pinfo, "Drive Ownership Status", tempstr); + + } + else { + if (dinfo.ftl_unit_size) { + sprintf(tempstr, "%hhu KB", dinfo.ftl_unit_size); + json_object_add_value_string(pinfo, "FTL_unit_size", tempstr); + } + if (dinfo.bs_ver_major != 0 || dinfo.bs_ver_minor != 0) { + sprintf(tempstr, "%hhu.%hhu", dinfo.bs_ver_major, dinfo.bs_ver_minor); + json_object_add_value_string(pinfo, "Boot Spec.Version", tempstr); + } + } + json_array_add_value_object(driveInfo, pinfo); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } else { + printf("Drive Hardware Version: %hhu.%hhu\n", + dinfo.hw_ver_major, dinfo.hw_ver_minor); + + if (custId == MICRON_CUST_ID_GG) { + if (dinfo.ftl_unit_size) + printf("FTL_unit_size: %hhu B\n", dinfo.ftl_unit_size*1024); + + if (dinfo.bs_ver_major != 0 || dinfo.bs_ver_minor != 0) { + printf("Boot Spec.Version: HyperScale Boot Version Spec.%hhu.%hhu\n", + dinfo.bs_ver_major, dinfo.bs_ver_minor); + } + + if ( dinfo.ownership_status == 0) + printf("Drive Ownership Status: N/A\n"); + else if ( dinfo.ownership_status == 1) + printf("Drive Ownership Status: UNSET\n"); + else if ( dinfo.ownership_status == 2) + printf("Drive Ownership Status: SET\n"); + else if ( dinfo.ownership_status == 3) + printf("Drive Ownership Status: BLOCKED\n"); + + } else { + if (dinfo.ftl_unit_size) + printf("FTL_unit_size: %hhu KB\n", dinfo.ftl_unit_size); + if (dinfo.bs_ver_major != 0 || dinfo.bs_ver_minor != 0) { + printf("Boot Spec.Version: %hhu.%hhu\n", dinfo.bs_ver_major, dinfo.bs_ver_minor); + } + } - if (dinfo.ftl_unit_size) - printf("FTL_unit_size: %u KB\n", dinfo.ftl_unit_size); - - if (dinfo.bs_ver_major || dinfo.bs_ver_minor) - printf("Boot Spec.Version: %u.%u\n", - dinfo.bs_ver_major, dinfo.bs_ver_minor); - } + } dev_close(dev); return 0; @@ -2127,49 +2720,101 @@ static int display_fw_activate_entry(int entry_count, struct fw_activation_histo static const char * const ca[] = {"000b", "001b", "010b", "011b"}; char *ptr = formatted_entry; int index = 0, entry_size = 82; + bool is_json = false; if ((entry->version != 1 && entry->version != 2) || entry->length != 64) return -EINVAL; + if(stats) + { + is_json = true; + } + sprintf(ptr, "%d", entry_count); + if(is_json) + { + json_object_add_value_int(stats, "Entry Number", le32_to_cpu(entry_count)); + } + ptr += 10; timestamp = (le64_to_cpu(entry->power_on_hour) & 0x0000FFFFFFFFFFFFUL) / 1000; hours = timestamp / 3600; minutes = (timestamp % 3600) / 60; seconds = (timestamp % 3600) % 60; - sprintf(ptr, "|%"PRIu64":%u:%u", (uint64_t)hours, minutes, seconds); - ptr += 12; + sprintf(ptr, "|%"PRIu64":%hhu:%hhu", (uint64_t)hours, minutes, seconds); + if(is_json) + { + json_object_add_value_string(stats, "Power On Hour", ptr+1); + } + + ptr += 16; sprintf(ptr, "| %"PRIu64, le64_to_cpu(entry->power_cycle_count)); + if(is_json) + { + json_object_add_value_int(stats, "Power cycle count", le32_to_cpu(entry->power_cycle_count)); + } + ptr += 10; /* firmware details */ memset(buffer, 0, sizeof(buffer)); memcpy(buffer, entry->previous_fw, sizeof(entry->previous_fw)); sprintf(ptr, "| %s", buffer); + if(is_json) + { + json_object_add_value_string(stats, "Previous firmware", buffer); + } + ptr += 11; memset(buffer, 0, sizeof(buffer)); memcpy(buffer, entry->activated_fw, sizeof(entry->activated_fw)); sprintf(ptr, "| %s", buffer); + if(is_json) + { + json_object_add_value_string(stats, "New FW activated", buffer); + } + ptr += 12; /* firmware slot and commit action*/ sprintf(ptr, "| %d", entry->slot); + if(is_json) + { + json_object_add_value_int(stats, "Slot number", entry->slot); + } + ptr += 9; if (entry->commit_action_type <= 3) - sprintf(ptr, "| %s", ca[entry->commit_action_type]); + { + sprintf(ptr, "| %s", ca[entry->commit_action_type]); + } else - sprintf(ptr, "| xxxb"); + { + sprintf(ptr, "| xxxb"); + } + + if(is_json) + { + json_object_add_value_string(stats, "Commit Action Type", ptr+2); + } + ptr += 9; /* result */ - if (entry->result) - sprintf(ptr, "| Fail #%d", entry->result); - else - sprintf(ptr, "| pass"); + if (entry->result) { + sprintf(ptr, "| Fail #%d", entry->result); + } else { + sprintf(ptr, "| pass"); + } + + if(is_json) { + json_object_add_value_string(stats, "Result", ptr+2); + return 0; + } /* replace all null characters with spaces */ ptr = formatted_entry; @@ -2201,34 +2846,35 @@ static int micron_fw_activation_history(int argc, char **argv, struct command *c unsigned int logC2[C2_log_size/sizeof(int)] = { 0 }; enum eDriveModel eModel = UNKNOWN_MODEL; struct nvme_dev *dev; + int err; + bool is_json = false; + struct json_object *root, *fw_act, *element; + struct json_object *entry; struct format { char *fmt; }; - int err; - const char *fmt = "output format normal"; - struct format cfg = { - .fmt = "normal", - }; + const char *fmt = "output format normal|json"; + struct format cfg = { + .fmt = "normal", + }; - OPT_ARGS(opts) = { - OPT_FMT("format", 'f', &cfg.fmt, fmt), - OPT_END() - }; + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; err = micron_parse_options(&dev, argc, argv, desc, opts, &eModel); if (err < 0) return -1; - if (strcmp(cfg.fmt, "normal")) { - fprintf(stderr, "only normal format is supported currently\n"); - dev_close(dev); - return -1; - } + + if (!strcmp(cfg.fmt, "json")) + is_json = true; /* check if product supports fw_history log */ err = -EINVAL; - if (eModel != M51CX) { + if ((eModel != M51CX) && (eModel != M51BY) && (eModel != M51CY) && (eModel != M6003) && (eModel != M6004)) { fprintf(stderr, "Unsupported drive model for vs-fw-activate-history command\n"); goto out; } @@ -2255,13 +2901,35 @@ static int micron_fw_activation_history(int argc, char **argv, struct command *c goto out; } - micron_fw_activation_history_header_print(); - for (count = 0; count < table->num_entries; count++) { - memset(formatted_output, '\0', 100); - if (!display_fw_activate_entry(count, &table->entries[count], formatted_output, - NULL)) - printf("%s\n", formatted_output); - } + if(is_json) { + root = json_create_object(); + fw_act = json_create_object(); + json_object_add_value_object(root, "vs-fw-activation-history", fw_act); + json_object_add_value_int(fw_act, "Total Entry Num", + le32_to_cpu(table->num_entries)); + entry = json_create_array(); + json_object_add_value_array(fw_act, "Entry", entry); + for(count = 0; count < table->num_entries; count++) { + element = json_create_object(); + if (display_fw_activate_entry(count, &table->entries[count], + formatted_output, element) == 0) + { + json_array_add_value_object(entry, element); + } + } + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } else { + micron_fw_activation_history_header_print(); + for (count = 0; count < table->num_entries; count++) { + memset(formatted_output, '\0', 100); + if (!display_fw_activate_entry(count, &table->entries[count], formatted_output, NULL)) + { + printf("%s\n", formatted_output); + } + } + } out: dev_close(dev); return err; @@ -2498,7 +3166,7 @@ static int micron_latency_stats_info(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "display command latency statistics"; - const char *command = "command to display stats - all|read|write|trimdefault is all"; + const char *command = "command to display stats - all|read|write|trim, default is all"; int err = 0; struct nvme_dev *dev; enum eDriveModel model = UNKNOWN_MODEL; @@ -2645,7 +3313,7 @@ static int micron_ocp_smart_health_logs(int argc, char **argv, struct command *c } /* check for models that support 0xC0 log */ - if (eModel != M51CX) { + if ((eModel != M51CX) && (eModel != M51BY) && (eModel != M51CY) && (eModel != M6003) && (eModel != M6004)) { printf("Unsupported drive model for vs-smart-add-log command\n"); err = -1; goto out; @@ -2653,7 +3321,7 @@ static int micron_ocp_smart_health_logs(int argc, char **argv, struct command *c err = nvme_get_log_simple(dev_fd(dev), 0xC0, C0_log_size, logC0); if (!err) - print_smart_cloud_health_log((__u8 *)logC0, is_json); + print_smart_cloud_health_log((__u8 *)logC0, is_json, eModel); else if (err < 0) printf("Unable to retrieve extended smart log 0xC0 for the drive\n"); out: @@ -2681,11 +3349,11 @@ static int micron_clr_fw_activation_history(int argc, char **argv, if (err < 0) return err; - if (model != M51CX) { - printf("This option is not supported for specified drive\n"); - dev_close(dev); - return err; - } + if ((model != M51CX) && (model != M51BY) && (model != M51CY) && (model != M6003) && (model != M6004)) { + printf ("This option is not supported for specified drive\n"); + dev_close(dev); + return err; + } err = nvme_set_features_simple(dev_fd(dev), fid, 1 << 31, 0, 0, &result); if (!err) @@ -2959,6 +3627,132 @@ static int get_common_log(int fd, uint8_t id, uint8_t **buf, int *size) return ret; } +static int GetOcpEnhancedTelemetryLog(int fd, const char *dir,int nLogID) { + int err =0; + unsigned char* pTelemetryDataHeader = 0; + unsigned int nallocSize = 0; + unsigned int nOffset = 0; + unsigned char *pTelemetryBuffer = 0; + unsigned int usAreaLastBlock[4]= {0}; + bool bTeleheaderWrite = true; + + /* Enable ETDAS */ + unsigned int uiBufferSize = 512; + unsigned char pBuffer[512] = { 0 }; + pBuffer[1] = 1; + __u32 result = 0; + + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = MICRON_FEATURE_OCP_ENHANCED_TELEMETRY, + .nsid = NVME_NSID_ALL, + .cdw11 = 0, + .cdw12 = 0, + .save = 1, + .uuidx = 0, + .cdw15 = 0, + .data_len = uiBufferSize, + .data = pBuffer, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + err = nvme_set_features(&args); + + if (err !=0) { + printf("Failed to set ETDAS, Data Area 4 won't be avialable >>> "); + } + + /* Read Telemetry header information */ + pTelemetryDataHeader = (unsigned char*)calloc(512, sizeof(unsigned char)); + + if (!pTelemetryDataHeader) { + printf("Unable to allocate buffer of size 0x%X bytes for telemetry header", 512); + return -1; + } + err = NVMEGetLogPage(fd, nLogID, pTelemetryDataHeader, 512, 0); + + if(err !=0) { + return err; + } + nOffset += 512; + int n = 8; + /* Get size of log page */ + for (int i = 0; i < 3; i++) { + usAreaLastBlock[i] = (pTelemetryDataHeader[n + 1] << 8) | pTelemetryDataHeader[n]; + n += 2; + } + n += 2; + usAreaLastBlock[3] = (pTelemetryDataHeader[n + 3] << 24) | (pTelemetryDataHeader[n + 2] << 16) | (pTelemetryDataHeader[n + 1] << 8) | pTelemetryDataHeader[n]; + + for (int nArea = 0; nArea <= 3; nArea++) { + if (nArea != 0) { + nallocSize = (usAreaLastBlock[nArea] - usAreaLastBlock[nArea - 1]) * 512; + } + else { + nallocSize = usAreaLastBlock[nArea] * 512; + } + if(nallocSize == 0) { + printf("Enhanced Telemetry log Data Area %d Size is zero, continuing with next available Data Area\n", (nArea + 1)); + continue; + } + //printf("Enhanced Telemetry log Data Area %d Size is %d\n", (nArea + 1), nallocSize); + + pTelemetryBuffer = (unsigned char*)calloc(nallocSize, 1); + if (!pTelemetryBuffer) { + printf("Unable to allocate buffer of size 0x%X bytes for Data Area %d", nallocSize, (nArea + 1)); + nOffset += nallocSize; + continue; + } + /* Fetch the Data */ + err = NVMEGetLogPage(fd, nLogID, pTelemetryBuffer, nallocSize, nOffset); + + if (err != 0){ + + printf("Failed to fetch telemetry data of size : %u from offset : %u!\n", nallocSize, nOffset); + free(pTelemetryBuffer); + pTelemetryBuffer = NULL; + nOffset += nallocSize; + continue; + } + + /* Increment the Offset value */ + nOffset += nallocSize; + + if ((nArea + 1) <= 4) { + char strBuffer[256] = { 0 }; + if(0x07 == nLogID) { + sprintf(strBuffer, "%s", "nvme_host_telemetry_log.bin"); + if (bTeleheaderWrite) { + WriteData(pTelemetryDataHeader, 512, dir, "nvme_host_telemetry_log.bin", strBuffer); + bTeleheaderWrite = false; + } + WriteData(pTelemetryBuffer, nallocSize, dir, "nvme_host_telemetry_log.bin", strBuffer); + } + else if(0x08 == nLogID) { + sprintf(strBuffer, "%s", "nvme_controller_telemetry_log.bin"); + if (bTeleheaderWrite) { + WriteData(pTelemetryDataHeader, 512, dir, "nvme_controller_telemetry_log.bin", strBuffer); + bTeleheaderWrite = false; + } + WriteData(pTelemetryBuffer, nallocSize, dir, "nvme_controller_telemetry_log.bin", strBuffer); + } + } + + if (NULL != pTelemetryBuffer) { + free(pTelemetryBuffer); + pTelemetryBuffer = NULL; + } + } + // free mem of header, all areas + if (NULL != pTelemetryDataHeader) { + free(pTelemetryDataHeader); + } + + return err; +} + + static int micron_internal_logs(int argc, char **argv, struct command *cmd, struct plugin *plugin) { @@ -3023,7 +3817,7 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, { 0xE5, "nvmelog_E5.bin", 0, 0 }, { 0xE8, "nvmelog_E8.bin", 0, 0 }, { 0xE9, "nvmelog_E9.bin", 0, 0 }, - { 0xEA, "nvmelog_EA.bin", 0, 0 }, + { 0xEA, "nvmelog_EA.bin", 0, 0 } }; enum eDriveModel eModel; @@ -3035,6 +3829,7 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, unsigned char *dataBuffer = NULL; int bSize = 0; int maxSize = 0; + MICRON_WORKLOAD_LOG_HDR stWllHdr = { 0 }; struct config { char *type; @@ -3146,9 +3941,20 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, GetErrorlogData(dev_fd(dev), ctrl.elpe, strCtrlDirName); GetGenericLogs(dev_fd(dev), strCtrlDirName); /* pull if telemetry log data is supported */ - if ((ctrl.lpa & 0x8) == 0x8) - GetTelemetryData(dev_fd(dev), strCtrlDirName); - + if ((ctrl.lpa & 0x8) == 0x8) { + if (eModel == M51BY){ + err = GetOcpEnhancedTelemetryLog(dev_fd(dev), strCtrlDirName,0x07); + if(err!=0) { + printf("Failed to fetch the host telemetry log"); + } + err = GetOcpEnhancedTelemetryLog(dev_fd(dev), strCtrlDirName,0x08); + if(err!=0) { + printf("Failed to fetch the controller telemetry log"); + } + } else { + GetTelemetryData(dev_fd(dev), strCtrlDirName); + } + } GetFeatureSettings(dev_fd(dev), strCtrlDirName); if (eModel != M5410 && eModel != M5407) { @@ -3158,7 +3964,7 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, memcpy((char *)&aVendorLogs[c_logs_index], aM51AXLogs, sizeof(aM51AXLogs)); else if (eModel == M51BX) memcpy((char *)&aVendorLogs[c_logs_index], aM51BXLogs, sizeof(aM51BXLogs)); - else if (eModel == M51CX) + else if (eModel == M51CX || eModel == M51BY || eModel == M51CY ) memcpy((char *)&aVendorLogs[c_logs_index], aM51CXLogs, sizeof(aM51CXLogs)); } @@ -3167,10 +3973,37 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, switch (aVendorLogs[i].ucLogPage) { case 0xE1: case 0xE5: + err = 1; + break; case 0xE9: - err = 1; + if(eModel == M51CX || eModel == M51BY) { + err = NVMEGetLogPage(dev_fd(dev), aVendorLogs[i].ucLogPage, (unsigned char *)&stWllHdr, sizeof(MICRON_WORKLOAD_LOG_HDR),0); + if(err == 0){ + bSize = stWllHdr.uiLength; + if(bSize > 0 ) { + dataBuffer = (unsigned char*)calloc(bSize, 1); + if (!dataBuffer) + { + printf(" Memory allocation failed for log id : 0x%02X \n", aVendorLogs[i].ucLogPage); + continue; + } + } + memcpy(dataBuffer, &stWllHdr, sizeof(MICRON_WORKLOAD_LOG_HDR)); + err = NVMEGetLogPage(dev_fd(dev), aVendorLogs[i].ucLogPage, (dataBuffer + sizeof(MICRON_WORKLOAD_LOG_HDR)), (bSize - sizeof(MICRON_WORKLOAD_LOG_HDR)), sizeof(MICRON_WORKLOAD_LOG_HDR)); + if(err != 0){ + printf("Failed to fetch the E9 logs \n"); + } + } + }else { + err = 1; + } break; case 0xE2: + if(eModel == M51CX || eModel == M51BY || eModel == M51CY) { + continue; + } + err = get_common_log(dev_fd(dev), aVendorLogs[i].ucLogPage, &dataBuffer, &bSize); + break; case 0xE3: case 0xE4: case 0xE8: @@ -3180,7 +4013,17 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, break; case 0xC1: case 0xC2: + if(eModel == M51CX || eModel == M51BY || eModel == M51CY) { + continue; + } + err = GetLogPageSize(dev_fd(dev), aVendorLogs[i].ucLogPage, &bSize); + if (err == 0 && bSize > 0) + err = GetCommonLogPage(dev_fd(dev), aVendorLogs[i].ucLogPage, &dataBuffer, bSize); + break; case 0xC4: + if(eModel == M51BY || eModel == M51CY) { + continue; + } err = GetLogPageSize(dev_fd(dev), aVendorLogs[i].ucLogPage, &bSize); if (!err && bSize > 0) @@ -3205,7 +4048,7 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, if (eModel == M5410 || eModel == M5407) err = NVMEGetLogPage(dev_fd(dev), aVendorLogs[i].ucLogPage, dataBuffer, - bSize); + bSize, 0); else err = nvme_get_log_simple(dev_fd(dev), aVendorLogs[i].ucLogPage, @@ -3219,7 +4062,7 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, if (eModel == M51BX) (void)NVMEResetLog(dev_fd(dev), aVendorLogs[i].ucLogPage, aVendorLogs[i].nLogSize, aVendorLogs[i].nMaxSize); - fallthrough; + /*fallthrough; */ default: bSize = aVendorLogs[i].nLogSize; dataBuffer = (unsigned char *)malloc(bSize); @@ -3321,7 +4164,186 @@ static int micron_logpage_dir(int argc, char **argv, struct command *cmd, if (err) continue; printf("%02Xh : %s\n", log_list[i].log_id, log_list[i].desc); + } + + return err; +} + +static int micron_cloud_boot_SSD_version(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Prints HyperScale Boot Version"; + unsigned char logC0[C0_log_size] = { 0 }; + struct nvme_id_ctrl ctrl; + enum eDriveModel eModel = UNKNOWN_MODEL; + int err = 0; + struct nvme_dev *dev; + struct format { + char *fmt; + }; + const char *fmt = "output format normal"; + struct format cfg = { + .fmt = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + err = micron_parse_options(&dev, argc, argv, desc, opts, &eModel); + if (err < 0) + return -1; + + err = nvme_identify_ctrl(dev_fd(dev), &ctrl); + if (err == 0) { + if (ctrl.vs[536] != MICRON_CUST_ID_GG) { + printf("cloud-boot-SSD-version option is not supported for specified drive\n"); + goto out; + } + } + else { + printf("Error %d retrieving controller identification data\n", err); + goto out; + } + + err = nvme_get_log_simple(dev_fd(dev), 0xC0, C0_log_size, logC0); + if (err == 0) { + __u16 major, minor; + major = *((__u16 *)(logC0+300)); + minor = *((__u16 *)(logC0+302)); + printf("HyperScale Boot Version Spec.%x.%x\n", le16_to_cpu(major), le16_to_cpu(minor)); + } else if (err < 0) { + printf("Error %d retrieving extended smart log 0xC0 for the drive\n", err); + goto out; + } +out: + dev_close(dev); + return err; +} + +static int micron_device_waf(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "Prints device Write Amplification Factor(WAF)"; + unsigned char logC0[C0_log_size] = { 0 }; + struct nvme_id_ctrl ctrl; + struct nvme_smart_log smart_log; + enum eDriveModel eModel = UNKNOWN_MODEL; + int err = 0; + struct nvme_dev *dev; + long double tlc_units_written, slc_units_written; + long double data_units_written, write_amplification_factor; + struct format { + char *fmt; + }; + const char *fmt = "output format normal"; + struct format cfg = { + .fmt = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + err = micron_parse_options(&dev, argc, argv, desc, opts, &eModel); + if (err < 0) + return -1; + + err = nvme_identify_ctrl(dev_fd(dev), &ctrl); + if (err == 0) { + if (ctrl.vs[536] != MICRON_CUST_ID_GG) { + printf("vs-device-waf option is not supported for specified drive\n"); + goto out; + } + } + else { + printf("Error %d retrieving controller identification data\n", err); + goto out; + } + + err = nvme_get_log_smart(dev_fd(dev), 0xffffffff, false, &smart_log); + if (err != 0) { + fprintf(stderr, "nvme_smart_log() failed, err = %d\n", err); + goto out; + } + + err = nvme_get_log_simple(dev_fd(dev), 0xC0, C0_log_size, logC0); + if (err != 0) { + fprintf(stderr, "Failed to get extended smart log, err = %d\n", err); + goto out; } + data_units_written = int128_to_double(smart_log.data_units_written); + tlc_units_written = int128_to_double((__u8 *)logC0); + slc_units_written = int128_to_double((__u8 *)(logC0+16)); + write_amplification_factor = (data_units_written/(tlc_units_written + slc_units_written)); + printf("Write Amplification Factor %.0Lf\n", write_amplification_factor); + +out: + dev_close(dev); return err; } + +static int micron_cloud_log(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "Retrieve Smart or Extended Smart Health log for the given device "; + unsigned int logC0[C0_log_size/sizeof(int)] = { 0 }; + struct nvme_id_ctrl ctrl; + enum eDriveModel eModel = UNKNOWN_MODEL; + int err = 0; + struct nvme_dev *dev; + bool is_json = true; + struct format { + char *fmt; + }; + const char *fmt = "output format normal|json"; + struct format cfg = { + .fmt = "json", + }; + + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + err = micron_parse_options(&dev, argc, argv, desc, opts, &eModel); + if (err < 0) + return -1; + + if (strcmp(cfg.fmt, "normal") == 0) + is_json = false; + + /* check for models that support 0xC0 log */ + if (eModel != M51CX) { + printf ("Unsupported drive model for vs-cloud-log commmand\n"); + err = -1; + goto out; + } + + err = nvme_identify_ctrl(dev_fd(dev), &ctrl); + if (err == 0) { + if (ctrl.vs[536] != MICRON_CUST_ID_GG) { + printf("vs-cloud-log option is not supported for specified drive\n"); + goto out; + } + } + else { + printf("Error %d retrieving controller identification data\n", err); + goto out; + } + + err = nvme_get_log_simple(dev_fd(dev), 0xC0, C0_log_size, logC0); + if (err == 0) { + print_hyperscale_cloud_health_log((__u8 *)logC0, is_json); + } else if (err < 0) { + printf("Unable to retrieve extended smart log 0xC0 for the drive\n"); + } +out: + dev_close(dev); + if (err > 0) + nvme_show_status(err); + return err; +} diff --git a/plugins/micron/micron-nvme.h b/plugins/micron/micron-nvme.h index c9e3ca7a6a..3492c9279b 100644 --- a/plugins/micron/micron-nvme.h +++ b/plugins/micron/micron-nvme.h @@ -1,11 +1,11 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) Micron, Inc 2024. - * +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (c) Micron, Inc 2024. + * * @file: micron-nvme.h * @brief: This module contains all the constructs needed for micron nvme-cli plugin. * @authors:Chaithanya Shoba , */ + #undef CMD_INC_FILE #define CMD_INC_FILE plugins/micron/micron-nvme @@ -35,6 +35,11 @@ PLUGIN(NAME("micron", "Micron vendor specific extensions", NVME_VERSION), ENTRY("vs-smart-add-log", "Retrieve extended SMART data", micron_ocp_smart_health_logs) ENTRY("clear-fw-activate-history", "Clear FW activation history", micron_clr_fw_activation_history) ENTRY("vs-smbus-option", "Enable/Disable SMBUS on the drive", micron_smbus_option) + ENTRY("cloud-boot-SSD-version", "Prints HyperScale Boot Version", micron_cloud_boot_SSD_version) + ENTRY("vs-device-waf", "Reports SLC and TLC WAF ratio", micron_device_waf) + ENTRY("vs-cloud-log", "Retrieve Extended Health Information of Hyperscale NVMe Boot SSD", micron_cloud_log) + ENTRY("vs-work-load-log", "Retrieve Workload logs", micron_work_load_log) + ENTRY("vs-vendor-telemetry-log", "Retrieve Vendor Telemetry logs", micron_vendor_telemetry_log) ) ); diff --git a/plugins/micron/micron-ocp-telemetry.c b/plugins/micron/micron-ocp-telemetry.c new file mode 100644 index 0000000000..99d1800765 --- /dev/null +++ b/plugins/micron/micron-ocp-telemetry.c @@ -0,0 +1,1517 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (c) Micron, Inc 2024. + * + * @file: micron-ocp-telemetry.c + * @brief: This module contains all the constructs needed for parsing (or) decoding ocp telemetry log files. + * @author: Chaithanya Shoba + */ + +#include "micron-ocp-telemetry.h" + +//global buffers +static unsigned char *ptelemetry_buffer; +static unsigned char *pstring_buffer; + +#define NVME_HOST_TELEMETRY_LOG 0x07 +#define NVME_CNTRL_TELEMETRY_LOG 0x08 +#define MAX_BUFFER_32_KB 0x8000 +#define OCP_TELEMETRY_DATA_BLOCK_SIZE 512 +#define SIZE_OF_DWORD 4 +#define MAX_NUM_FIFOS 16 +#define DA1_OFFSET 512 + +#define STR_LOG_PAGE_HEADER "Log Page Header" +#define STR_REASON_IDENTIFIER "Reason Identifier" +#define STR_TELEMETRY_HOST_DATA_BLOCK_1 "Telemetry Host-Initiated Data Block 1" +#define STR_SMART_HEALTH_INFO "SMART / Health Information Log(LID-02h)" +#define STR_SMART_HEALTH_INTO_EXTENDED "SMART / Health Information Extended(LID-C0h)" +#define STR_DA_1_STATS "Data Area 1 Statistics" +#define STR_DA_2_STATS "Data Area 2 Statistics" +#define STR_DA_1_EVENT_FIFO_INFO "Data Area 1 Event FIFO info" +#define STR_DA_2_EVENT_FIFO_INFO "Data Area 2 Event FIFO info" +#define STR_STATISTICS_IDENTIFIER "Statistics Identifier" +#define STR_STATISTICS_IDENTIFIER_STR "Statistic Identifier String" +#define STR_STATISTICS_INFO_BEHAVIOUR_TYPE "Statistics Info Behavior Type" +#define STR_STATISTICS_INFO_RESERVED "Statistics Info Reserved" +#define STR_NAMESPACE_IDENTIFIER "Namespace Identifier" +#define STR_NAMESPACE_INFO_VALID "Namespace Information Valid" +#define STR_STATISTICS_DATA_SIZE "Statistic Data Size" +#define STR_RESERVED "Reserved" +#define STR_STATISTICS_SPECIFIC_DATA "Statistic Specific Data" +#define STR_CLASS_SPECIFIC_DATA "Class Specific Data" +#define STR_DBG_EVENT_CLASS_TYPE "Debug Event Class type" +#define STR_EVENT_IDENTIFIER "Event Identifier" +#define STR_EVENT_STRING "Event String" +#define STR_EVENT_DATA_SIZE "Event Data Size" +#define STR_VU_EVENT_STRING "VU Event String" +#define STR_VU_EVENT_ID_STRING "VU Event Identifier" +#define STR_VU_DATA "VU Data" + +#define JSON_ADD_FORMATTED_UINT32_STR(pobject, msg, pdata) {\ + char data_str[70] = { 0 };\ + sprintf(data_str, "0x%x", pdata);\ + json_object_add_value_string(pobject, msg, data_str);\ +} + +#define JSON_ADD_STR(pobject, msg, description_str) {\ + json_object_add_value_string(pobject, msg, description_str);\ +} + +#define JSON_ADD_FORMATTED_VAR_SIZE_STR(pobject, msg, pdata, data_size) {\ + char description_str[256] = "";\ + char temp_buffer[3] = { 0 };\ + for (size_t i = 0; i < data_size; ++i)\ + {\ + sprintf(temp_buffer, "%02X", pdata[i]);\ + strcat(description_str, temp_buffer);\ + }\ + JSON_ADD_STR(pobject, msg, description_str);\ +} + +statistic_entry_t statistic_identifiers_map[] = { + { 0x00, "Error, this entry does not exist." }, + { 0x01, "Outstanding Admin Commands" }, + { 0x02, "Host Write Bandwidth"}, + { 0x03, "GC Write Bandwidth"}, + { 0x04, "Active Namespaces"}, + { 0x05, "Internal Write Workload"}, + { 0x06, "Internal Read Workload"}, + { 0x07, "Internal Write Queue Depth"}, + { 0x08, "Internal Read Queue Depth"}, + { 0x09, "Pending Trim LBA Count"}, + { 0x0A, "Host Trim LBA Request Count"}, + { 0x0B, "Current NVMe Power State"}, + { 0x0C, "Current DSSD Power State"}, + { 0x0D, "Program Fail Count"}, + { 0x0E, "Erase Fail Count"}, + { 0x0F, "Read Disturb Writes"}, + { 0x10, "Retention Writes"}, + { 0x11, "Wear Leveling Writes"}, + { 0x12, "Read Recovery Writes"}, + { 0x13, "GC Writes"}, + { 0x14, "SRAM Correctable Count"}, + { 0x15, "DRAM Correctable Count"}, + { 0x16, "SRAM Uncorrectable Count"}, + { 0x17, "DRAM Uncorrectable Count"}, + { 0x18, "Data Integrity Error Count"}, + { 0x19, "Read Retry Error Count"}, + { 0x1A, "PERST Events Count"}, + { 0x1B, "Max Die Bad Block"}, + { 0x1C, "Max NAND Channel Bad Block"}, + { 0x1D, "Minimum NAND Channel Bad Block"} + //7FFFh:001Eh:Reserved,Statistic Identifier’s between 001Eh and 7FFFh are reserved for future expansion. + //FFFFh:8000h:Vendor Unique:Statistic Identifier’s between 8000h and FFFFh are vendor unique. +}; + +micron_vs_logpage_t host_log_page_header[] = { + { "LogIdentifier",1 }, //00 Log Identifier + { "Reserved1", 4 }, //04:01 Reserved + { "IEEE OUI Identifier", 3 }, //07:05 IEEE OUI Identifier + { "Telemetry Host-Initiated Data Area 1 Last Block", 2 }, //09:08 Data Area 1 Last Block + { "Telemetry Host-Initiated Data Area 2 Last Block", 2 }, //11:10 Data Area 2 Last Block + { "Telemetry Host-Initiated Data Area 3 Last Block", 2 }, //13:12 Data Area 3 Last Block + { "Reserved2", 2 }, //15:14 Reserved + { "Telemetry Host-Initiated Data Area 4 Last Block", 4 }, //19:16 Data Area 4 Last Block + { "Reserved3", 360 }, //379:20 Reserved + { "Telemetry Host-Initiated Scope", 1 }, //380 Telemetry Host-Initiated Scope + { "Telemetry Host Initiated Generation Number", 1 }, //381 Telemetry Host Initiated Generation Number + { "Telemetry Host-Initiated Data Available", 1 }, //382 Telemetry Host-Initiated Data Available + { "Telemetry Controller-Initiated Data Generation Number", 1 } //383 Telemetry Controller Initiated Generation Number + //511:384 Reason string +}; + +micron_vs_logpage_t controller_log_page_header[] = { + { "LogIdentifier",1 }, //00 Log Identifier + { "Reserved1", 4 }, //04:01 Reserved + { "IEEE OUI Identifier", 3 }, //07:05 IEEE OUI Identifier + { "Telemetry Host-Initiated Data Area 1 Last Block", 2 }, //09:08 Data Area 1 Last Block + { "Telemetry Host-Initiated Data Area 2 Last Block", 2 }, //11:10 Data Area 2 Last Block + { "Telemetry Host-Initiated Data Area 3 Last Block", 2 }, //13:12 Data Area 3 Last Block + { "Reserved2", 2 }, //15:14 Reserved + { "Telemetry Host-Initiated Data Area 4 Last Block", 4 }, //19:16 Data Area 4 Last Block + { "Reserved3", 361 }, //380:20 Reserved + { "Telemetry Controller-Initiated Scope", 1 }, //381 Telemetry Controller-Initiated Scope + { "Telemetry Controller-Initiated Data Available", 1 }, //382 Telemetry Controller-Initiated Data Available + { "Telemetry Controller-Initiated Data Generation Number", 1 }//383 Telemetry Controller Initiated Generation Number + //511:384 Reason string +}; + +micron_vs_logpage_t reason_identifier[] = { + { "Error ID", 64 }, //63:00 Error ID + { "File ID", 8 }, //71:64 File ID + { "Line Number", 2 }, //73:72 Line Number + { "Valid Flags", 1 }, //74 Valid Flags + { "Reserved", 21 }, //95:75 Reserved + { "VU Reason Extension", 32 } //127:96 VU Reason Extension +}; + +micron_vs_logpage_t ocp_header_in_da1[] = { + { "Major Version",2 }, + { "Minor Version", 2 }, + { "Reserved1", 4 }, + { "Timestamp", 8 }, + { "Log page GUID", 16 }, + { "Number Telemetry Profiles Supported", 1 }, + { "Telemetry Profile Selected", 1 }, + { "Reserved2", 6 }, + { "Telemetry String Log Size", 8 }, + { "Reserved3", 8 }, + { "Firmware Revision", 8 }, + { "Reserved4", 32 }, + { "Data Area 1 Statistic Start", 8 }, + { "Data Area 1 Statistic Size", 8 }, + { "Data Area 2 Statistic Start", 8 }, + { "Data Area 2 Statistic Size", 8 }, + { "Reserved5", 32 }, + { "Event FIFO 1 Data Area", 1 }, + { "Event FIFO 2 Data Area", 1 }, + { "Event FIFO 3 Data Area", 1 }, + { "Event FIFO 4 Data Area", 1 }, + { "Event FIFO 5 Data Area", 1 }, + { "Event FIFO 6 Data Area", 1 }, + { "Event FIFO 7 Data Area", 1 }, + { "Event FIFO 8 Data Area", 1 }, + { "Event FIFO 9 Data Area", 1 }, + { "Event FIFO 10 Data Area", 1 }, + { "Event FIFO 11 Data Area", 1 }, + { "Event FIFO 12 Data Area", 1 }, + { "Event FIFO 13 Data Area", 1 }, + { "Event FIFO 14 Data Area", 1 }, + { "Event FIFO 15 Data Area", 1 }, + { "Event FIFO 16 Data Area", 1 }, + { "Event FIFO 1 Start", 8 }, + { "Event FIFO 1 Size", 8 }, + { "Event FIFO 2 Start", 8 }, + { "Event FIFO 2 Size", 8 }, + { "Event FIFO 3 Start", 8 }, + { "Event FIFO 3 Size", 8 }, + { "Event FIFO 4 Start", 8 }, + { "Event FIFO 4 Size", 8 }, + { "Event FIFO 5 Start", 8 }, + { "Event FIFO 5 Size", 8 }, + { "Event FIFO 6 Start", 8 }, + { "Event FIFO 6 Size", 8 }, + { "Event FIFO 7 Start", 8 }, + { "Event FIFO 7 Size", 8 }, + { "Event FIFO 8 Start", 8 }, + { "Event FIFO 8 Size", 8 }, + { "Event FIFO 9 Start", 8 }, + { "Event FIFO 9 Size", 8 }, + { "Event FIFO 10 Start", 8 }, + { "Event FIFO 10 Size", 8 }, + { "Event FIFO 11 Start", 8 }, + { "Event FIFO 11 Size", 8 }, + { "Event FIFO 12 Start", 8 }, + { "Event FIFO 12 Size", 8 }, + { "Event FIFO 13 Start", 8 }, + { "Event FIFO 13 Size", 8 }, + { "Event FIFO 14 Start", 8 }, + { "Event FIFO 14 Size", 8 }, + { "Event FIFO 15 Start", 8 }, + { "Event FIFO 15 Size", 8 }, + { "Event FIFO 16 Start", 8 }, + { "Event FIFO 16 Size", 8 }, + { "Reserved6", 80 } +}; + +micron_vs_logpage_t smart[] = { + { "Critical Warning",1 }, + { "Composite Temperature", 2 }, + { "Available Spare", 1 }, + { "Available Spare Threshold",1 }, + { "Percentage Used", 1 }, + { "Reserved1", 26 }, + { "Data Units Read",16 }, + { "Data Units Written", 16 }, + { "Host Read Commands",16 }, + { "Host Write Commands", 16 }, + { "Controller Busy Time", 16 }, + { "Power Cycles",16 }, + { "Power On Hours", 16 }, + { "Unsafe Shutdowns", 16 }, + { "Media and Data Integrity Errors",16 }, + { "Number of Error Information Log Entries", 16 }, + { "Warning Composite Temperature Time",4 }, + { "Critical Composite Temperature Time", 4 }, + { "Temperature Sensor 1", 2 }, + { "Temperature Sensor 2", 2 }, + { "Temperature Sensor 3", 2 }, + { "Temperature Sensor 4", 2 }, + { "Temperature Sensor 5", 2 }, + { "Temperature Sensor 6", 2 }, + { "Temperature Sensor 7", 2 }, + { "Temperature Sensor 8", 2 }, + { "Thermal Management Temperature 1 Transition Count", 4 }, + { "Thermal Management Temperature 2 Transition Count", 4 }, + { "Total Time for Thermal Management Temperature 1",4 }, + { "Total Time for Thermal Management Temperature 2", 4 }, + { "Reserved2", 280 } +}; + +micron_vs_logpage_t smart_extended[] = { + { "Physical Media Units Written",16 }, + { "Physical Media Units Read", 16 }, + { "Bad User NAND Blocks Raw Count", 6 }, + { "Bad User NAND Blocks Normalized Value", 2 }, + { "Bad System NAND Blocks Raw Count",6 }, + { "Bad System NAND Blocks Normalized Value",2 }, + { "XOR Recovery Count", 8 }, + { "Uncorrectable Read Error Count", 8 }, + { "Soft ECC Error Count",8 }, + { "End to End Correction Counts Detected Errors", 4 }, + { "End to End Correction Counts Corrected Errors", 4 }, + { "System Data Percent Used", 1 }, + { "Refresh Counts",7 }, + { "Maximum User Data Erase Count", 4 }, + { "Minimum User Data Erase Count", 4 }, + { "Number of thermal throttling events", 1 }, + { "Current Throttling Status", 1 }, + { "Errata Version Field",1 }, + { "Point Version Field",2 }, + { "Minor Version Field",2 }, + { "Major Version Field",1 }, + { "PCIe Correctable Error Count", 8 }, + { "Incomplete Shutdowns", 4 }, + { "Reserved1",4 }, + { "Percent Free Blocks", 1 }, + { "Reserved2", 7 }, + { "Capacitor Health",2 }, + { "NVMe Base Errata Version", 1 }, + { "NVMe Command Set Errata Version", 1 }, + { "Reserved3",4 }, + { "Unaligned IO", 8 }, + { "Security Version Number", 8 }, + { "Total NUSE",8 }, + { "PLP Start Count", 16 }, + { "Endurance Estimate", 16 }, + { "PCIe Link Retraining Count",8 }, + { "Power State Change Count", 8 }, + { "Lowest Permitted Firmware Revision", 8 }, + { "Reserved4",278 }, + { "Log Page Version", 2 }, + { "Log page GUID", 16 } +}; + +int get_telemetry_das_offset_and_size(pnvme_ocp_telemetry_common_header ptelemetry_common_header, pnvme_ocp_telemetry_offsets ptelemetry_das_offset) +{ + if (NULL == ptelemetry_common_header || NULL == ptelemetry_das_offset) + { + nvme_show_error("Invalid input arguments."); + return -1; + } + + if (ptelemetry_common_header->log_id == NVME_HOST_TELEMETRY_LOG) + { + ptelemetry_das_offset->header_size = sizeof(nvme_ocp_telemetry_host_initiated_header_t); + } + else if (ptelemetry_common_header->log_id == NVME_CNTRL_TELEMETRY_LOG) + { + ptelemetry_das_offset->header_size = sizeof(nvme_ocp_telemetry_controller_initiated_header_t); + } + else + { + return -1; + } + + ptelemetry_das_offset->da1_start_offset = ptelemetry_das_offset->header_size; + ptelemetry_das_offset->da1_size = ptelemetry_common_header->da1_last_block * OCP_TELEMETRY_DATA_BLOCK_SIZE; + + ptelemetry_das_offset->da2_start_offset = ptelemetry_das_offset->da1_start_offset + ptelemetry_das_offset->da1_size; + ptelemetry_das_offset->da2_size = (ptelemetry_common_header->da2_last_block - ptelemetry_common_header->da1_last_block) * OCP_TELEMETRY_DATA_BLOCK_SIZE; + + ptelemetry_das_offset->da3_start_offset = ptelemetry_das_offset->da2_start_offset + ptelemetry_das_offset->da2_size; + ptelemetry_das_offset->da3_size = (ptelemetry_common_header->da3_last_block - ptelemetry_common_header->da2_last_block) * OCP_TELEMETRY_DATA_BLOCK_SIZE; + + ptelemetry_das_offset->da4_start_offset = ptelemetry_das_offset->da3_start_offset + ptelemetry_das_offset->da3_size; + ptelemetry_das_offset->da4_size = (ptelemetry_common_header->da4_last_block - ptelemetry_common_header->da3_last_block) * OCP_TELEMETRY_DATA_BLOCK_SIZE; + + return 0; +} + +int parse_ocp_telemetry_string_log(int event_fifo_num, int identifier, int debug_event_class, ocp_telemetry_string_tables_t string_table, char *description) +{ + if (pstring_buffer == NULL) + { + return -1; + } + + pnvme_ocp_telemetry_string_header pocp_ts_header = (pnvme_ocp_telemetry_string_header)pstring_buffer; + + if (event_fifo_num != 0) + { + __u8 fifo_ascii_string[17] = { '\0' }; + switch (event_fifo_num) + { + case 1: + { + memcpy(fifo_ascii_string,pocp_ts_header->fifo1_ascii_string, 16); + } + break; + case 2: + { + memcpy(fifo_ascii_string, pocp_ts_header->fifo2_ascii_string, 16); + } + break; + case 3: + { + memcpy(fifo_ascii_string, pocp_ts_header->fifo3_ascii_string, 16); + } + break; + case 4: + { + memcpy(fifo_ascii_string, pocp_ts_header->fifo4_ascii_string, 16); + } + break; + case 5: + { + memcpy(fifo_ascii_string, pocp_ts_header->fifo5_ascii_string, 16); + } + break; + case 6: + { + memcpy(fifo_ascii_string, pocp_ts_header->fifo6_ascii_string, 16); + } + break; + case 7: + { + memcpy(fifo_ascii_string, pocp_ts_header->fifo7_ascii_string, 16); + } + break; + case 8: + { + memcpy(fifo_ascii_string, pocp_ts_header->fifo8_ascii_string, 16); + } + break; + case 9: + { + memcpy(fifo_ascii_string, pocp_ts_header->fifo9_ascii_string, 16); + } + break; + case 10: + { + memcpy(fifo_ascii_string, pocp_ts_header->fifo10_ascii_string, 16); + } + break; + case 11: + { + memcpy(fifo_ascii_string, pocp_ts_header->fifo11_ascii_string, 16); + } + break; + case 12: + { + memcpy(fifo_ascii_string, pocp_ts_header->fifo12_ascii_string, 16); + } + break; + case 13: + { + memcpy(fifo_ascii_string, pocp_ts_header->fifo13_ascii_string, 16); + } + break; + case 14: + { + memcpy(fifo_ascii_string, pocp_ts_header->fifo14_ascii_string, 16); + } + break; + case 15: + { + memcpy(fifo_ascii_string, pocp_ts_header->fifo15_ascii_string, 16); + } + break; + case 16: + { + memcpy(fifo_ascii_string, pocp_ts_header->fifo16_ascii_string, 16); + } + break; + default: + { + description = ""; + return 0; + } + break; + } + + if (fifo_ascii_string[0] != '\0') + { + memcpy(description, fifo_ascii_string, 16); + } + + return 0; + } + + //Calculating the sizes of the tables. Note: Data is present in the form of DWORDS, So multiplying with sizeof(DWORD) + unsigned long long sits_table_size = (pocp_ts_header->sitsz) * SIZE_OF_DWORD; + unsigned long long ests_table_size = (pocp_ts_header->estsz) * SIZE_OF_DWORD; + unsigned long long vuests_table_size = (pocp_ts_header->vu_estsz) * SIZE_OF_DWORD; + + //Calculating number of entries present in all 3 tables + int sits_entries = (int)sits_table_size / sizeof(nvme_ocp_statistics_identifier_string_table_t); + int ests_entries = (int)ests_table_size / sizeof(nvme_ocp_event_string_table_t); + int vu_ests_entries = (int)vuests_table_size / sizeof(nvme_ocp_vu_event_string_table_t); + + if (string_table == STATISTICS_IDENTIFIER_STRING) + { + for (int sits_entry = 0; sits_entry < sits_entries; sits_entry++) + { + pnvme_ocp_statistics_identifier_string_table peach_statistic_entry = NULL; + peach_statistic_entry = (pnvme_ocp_statistics_identifier_string_table)(pstring_buffer + (pocp_ts_header->sits * SIZE_OF_DWORD) + (sits_entry * sizeof(nvme_ocp_statistics_identifier_string_table_t))); + if (identifier == (int)peach_statistic_entry->vs_statistic_identifier) + { + char *pdescription = (char *)(pstring_buffer + (pocp_ts_header->ascts * SIZE_OF_DWORD) + (peach_statistic_entry->ascii_id_offset * SIZE_OF_DWORD)); + memcpy(description, pdescription, peach_statistic_entry->ascii_id_length + 1); + + //If ASCII string isn't found, see in our internal Map for 2.5 Spec defined strings (id < 0x1D). + if ((description == NULL) && (identifier < 0x1D)) + { + memcpy(description, statistic_identifiers_map[identifier].description, peach_statistic_entry->ascii_id_length + 1); + } + return 0; + } + } + } + else if (string_table == EVENT_STRING) + { + for (int ests_entry = 0; ests_entry < ests_entries; ests_entry++) + { + pnvme_ocp_event_string_table peach_event_entry = NULL; + peach_event_entry = (pnvme_ocp_event_string_table)(pstring_buffer + (pocp_ts_header->ests * SIZE_OF_DWORD) + (ests_entry * sizeof(nvme_ocp_event_string_table_t))); + if (identifier == (int)peach_event_entry->event_identifier && debug_event_class == (int)peach_event_entry->debug_event_class) + { + char *pdescription = (char *)(pstring_buffer + (pocp_ts_header->ascts * SIZE_OF_DWORD) + (peach_event_entry->ascii_id_offset * SIZE_OF_DWORD)); + memcpy(description, pdescription, peach_event_entry->ascii_id_length + 1); + return 0; + } + } + } + else if (string_table == VU_EVENT_STRING) + { + for (int vu_ests_entry = 0; vu_ests_entry < vu_ests_entries; vu_ests_entry++) + { + pnvme_ocp_vu_event_string_table peach_vu_event_entry = NULL; + peach_vu_event_entry = (pnvme_ocp_vu_event_string_table)(pstring_buffer + (pocp_ts_header->vu_ests * SIZE_OF_DWORD) + (vu_ests_entry * sizeof(nvme_ocp_vu_event_string_table_t))); + if (identifier == (int)peach_vu_event_entry->vu_event_identifier && debug_event_class == (int)peach_vu_event_entry->debug_event_class) + { + char *pdescription = (char *)(pstring_buffer + (pocp_ts_header->ascts * SIZE_OF_DWORD) + (peach_vu_event_entry->ascii_id_offset * SIZE_OF_DWORD)); + memcpy(description, pdescription, peach_vu_event_entry->ascii_id_length + 1); + return 0; + } + } + } + + return 0; +} + +int parse_event_fifo(unsigned int fifo_num, unsigned char *pfifo_start, struct json_object *pevent_fifos_object, unsigned char *pstring_buffer, pnvme_ocp_telemetry_offsets poffsets, __u64 fifo_size, FILE *fp) +{ + if (NULL == pfifo_start || NULL == poffsets) + { + nvme_show_error("Input buffer was NULL"); + return -1; + } + + int status = 0; + unsigned int event_fifo_number = fifo_num + 1; + char* description = (char*)malloc((40 + 1) * sizeof(char)); + memset(description, 0 ,sizeof(40)); + status = parse_ocp_telemetry_string_log(event_fifo_number, 0, 0, EVENT_STRING, description); + + if (status != 0) + { + nvme_show_error("Failed to get C9 String. status: %d\n", status); + return -1; + } + + char event_fifo_name[100] = {0}; + snprintf(event_fifo_name, sizeof(event_fifo_name), "%s%d%s%s", "EVENT FIFO ", event_fifo_number, " - ", description); + + struct json_object *pevent_fifo_array = NULL; + + if(pevent_fifos_object != NULL) + { + pevent_fifo_array = json_create_array(); + } + else + { + if(fp) + { + fprintf(fp, "=====================================================================================\n"); + fprintf(fp, "%s\n", event_fifo_name); + fprintf(fp, "=====================================================================================\n"); + } + else + { + printf("=====================================================================================\n"); + printf("%s\n", event_fifo_name); + printf("=====================================================================================\n"); + } + } + + int offset_to_move = 0; + int event_des_size = sizeof(nvme_ocp_telemetry_event_descriptor_t); + while ((fifo_size > 0) &&(offset_to_move < fifo_size)) + { + struct json_object *pevent_descriptor_obj = ((pevent_fifos_object != NULL)?json_create_object():NULL); + + pnvme_ocp_telemetry_event_descriptor pevent_descriptor = (pnvme_ocp_telemetry_event_descriptor)(pfifo_start + offset_to_move); + + if (pevent_descriptor->event_data_size >= 0) + { + //Data is present in the form of DWORDS, So multiplying with sizeof(DWORD) + unsigned int data_size = pevent_descriptor->event_data_size * SIZE_OF_DWORD; + __u8 *pevent_specific_data = (__u8*)pevent_descriptor + event_des_size; + + char description_str[256] = ""; + parse_ocp_telemetry_string_log(0, pevent_descriptor->event_id, pevent_descriptor->debug_event_class_type, EVENT_STRING, description_str); + + if(pevent_fifos_object != NULL) + { + JSON_ADD_FORMATTED_UINT32_STR(pevent_descriptor_obj, STR_DBG_EVENT_CLASS_TYPE, pevent_descriptor->debug_event_class_type); + JSON_ADD_FORMATTED_UINT32_STR(pevent_descriptor_obj, STR_EVENT_IDENTIFIER, pevent_descriptor->event_id); + JSON_ADD_STR(pevent_descriptor_obj, STR_EVENT_STRING, description_str); + JSON_ADD_FORMATTED_UINT32_STR(pevent_descriptor_obj, STR_EVENT_DATA_SIZE, pevent_descriptor->event_data_size); + + if (pevent_descriptor->debug_event_class_type >= 0x80) + { + JSON_ADD_FORMATTED_VAR_SIZE_STR(pevent_descriptor_obj, STR_VU_DATA, pevent_specific_data, data_size); + } + } + else + { + if(fp) + { + fprintf(fp,"%s: 0x%x\n", STR_DBG_EVENT_CLASS_TYPE, pevent_descriptor->debug_event_class_type); + fprintf(fp,"%s: 0x%x\n", STR_EVENT_IDENTIFIER, pevent_descriptor->event_id); + fprintf(fp,"%s: %s\n", STR_EVENT_STRING, description_str); + fprintf(fp,"%s: 0x%x\n", STR_EVENT_DATA_SIZE, pevent_descriptor->event_data_size); + } + else + { + printf("%s: 0x%x\n", STR_DBG_EVENT_CLASS_TYPE, pevent_descriptor->debug_event_class_type); + printf("%s: 0x%x\n", STR_EVENT_IDENTIFIER, pevent_descriptor->event_id); + printf("%s: %s\n", STR_EVENT_STRING, description_str); + printf("%s: 0x%x\n", STR_EVENT_DATA_SIZE, pevent_descriptor->event_data_size); + } + + if (pevent_descriptor->debug_event_class_type >= 0x80) + { + print_formatted_var_size_str(STR_VU_DATA, pevent_specific_data, data_size, fp); + } + } + + switch (pevent_descriptor->debug_event_class_type) + { + case RESERVED_CLASS_TYPE: + { + } + break; + case TIME_STAMP_CLASS_TYPE: + { + pnvme_ocp_time_stamp_dbg_evt_class_format ptime_stamp_event = (pnvme_ocp_time_stamp_dbg_evt_class_format)pevent_specific_data; + int vu_event_id = (int)ptime_stamp_event->vu_event_identifier; + unsigned int data_size = ((pevent_descriptor->event_data_size * SIZE_OF_DWORD) - sizeof(nvme_ocp_time_stamp_dbg_evt_class_format_t)); + __u8 *pdata = (__u8*)ptime_stamp_event + sizeof(nvme_ocp_time_stamp_dbg_evt_class_format_t); + + char description_str[256] = ""; + parse_ocp_telemetry_string_log(0, ptime_stamp_event->vu_event_identifier, pevent_descriptor->debug_event_class_type, VU_EVENT_STRING, description_str); + + if(pevent_fifos_object != NULL) + { + JSON_ADD_FORMATTED_VAR_SIZE_STR(pevent_descriptor_obj, STR_CLASS_SPECIFIC_DATA, ptime_stamp_event->time_stamp, DATA_SIZE_8); + JSON_ADD_FORMATTED_UINT32_STR(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING, vu_event_id); + JSON_ADD_STR(pevent_descriptor_obj, STR_VU_EVENT_STRING, description_str); + JSON_ADD_FORMATTED_VAR_SIZE_STR(pevent_descriptor_obj, STR_VU_DATA, pdata, data_size); + } + else + { + if(fp) + { + print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, ptime_stamp_event->time_stamp, DATA_SIZE_8, fp); + fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id); + fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING, description_str); + print_formatted_var_size_str(STR_VU_DATA, pdata, data_size, fp); + } + else + { + print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, ptime_stamp_event->time_stamp, DATA_SIZE_8, fp); + printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id); + printf("%s: %s\n", STR_VU_EVENT_STRING, description_str); + print_formatted_var_size_str(STR_VU_DATA, pdata, data_size, fp); + } + } + } + break; + case PCIE_CLASS_TYPE: + { + pnvme_ocp_pcie_dbg_evt_class_format ppcie_event = (pnvme_ocp_pcie_dbg_evt_class_format)pevent_specific_data; + int vu_event_id = (int)ppcie_event->vu_event_identifier; + unsigned int data_size = ((pevent_descriptor->event_data_size * SIZE_OF_DWORD) - sizeof(nvme_ocp_pcie_dbg_evt_class_format_t)); + __u8 *pdata = (__u8*)ppcie_event + sizeof(nvme_ocp_pcie_dbg_evt_class_format_t); + + char description_str[256] = ""; + parse_ocp_telemetry_string_log(0, ppcie_event->vu_event_identifier, pevent_descriptor->debug_event_class_type, VU_EVENT_STRING, description_str); + + if(pevent_fifos_object != NULL) + { + JSON_ADD_FORMATTED_VAR_SIZE_STR(pevent_descriptor_obj, STR_CLASS_SPECIFIC_DATA, ppcie_event->pCIeDebugEventData, DATA_SIZE_4); + JSON_ADD_FORMATTED_UINT32_STR(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING, vu_event_id); + JSON_ADD_STR(pevent_descriptor_obj, STR_VU_EVENT_STRING, description_str); + JSON_ADD_FORMATTED_VAR_SIZE_STR(pevent_descriptor_obj, STR_VU_DATA, pdata, data_size); + } + else + { + if(fp) + { + print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, ppcie_event->pCIeDebugEventData, DATA_SIZE_4, fp); + fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id); + fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING, description_str); + print_formatted_var_size_str(STR_VU_DATA, pdata, data_size, fp); + } + else + { + print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, ppcie_event->pCIeDebugEventData, DATA_SIZE_4, fp); + printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id); + printf("%s: %s\n", STR_VU_EVENT_STRING, description_str); + print_formatted_var_size_str(STR_VU_DATA, pdata, data_size, fp); + } + } + } + break; + case NVME_CLASS_TYPE: + { + pnvme_ocp_nvme_dbg_evt_class_format pnvme_event = (pnvme_ocp_nvme_dbg_evt_class_format)pevent_specific_data; + int vu_event_id = (int)pnvme_event->vu_event_identifier; + unsigned int data_size = ((pevent_descriptor->event_data_size * SIZE_OF_DWORD) - sizeof(nvme_ocp_nvme_dbg_evt_class_format_t)); + __u8 *pdata = (__u8*)pnvme_event + sizeof(nvme_ocp_nvme_dbg_evt_class_format_t); + + char description_str[256] = ""; + parse_ocp_telemetry_string_log(0, pnvme_event->vu_event_identifier, pevent_descriptor->debug_event_class_type, VU_EVENT_STRING, description_str); + + if(pevent_fifos_object != NULL) + { + JSON_ADD_FORMATTED_VAR_SIZE_STR(pevent_descriptor_obj, STR_CLASS_SPECIFIC_DATA, pnvme_event->nvmeDebugEventData, DATA_SIZE_8); + JSON_ADD_FORMATTED_UINT32_STR(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING, vu_event_id); + JSON_ADD_STR(pevent_descriptor_obj, STR_VU_EVENT_STRING, description_str); + JSON_ADD_FORMATTED_VAR_SIZE_STR(pevent_descriptor_obj, STR_VU_DATA, pdata, data_size); + } + else + { + if(fp) + { + print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, pnvme_event->nvmeDebugEventData, DATA_SIZE_8, fp); + fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id); + fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING, description_str); + print_formatted_var_size_str(STR_VU_DATA, pdata, data_size, fp); + } + else + { + print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, pnvme_event->nvmeDebugEventData, DATA_SIZE_8, fp); + printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id); + printf("%s: %s\n", STR_VU_EVENT_STRING, description_str); + print_formatted_var_size_str(STR_VU_DATA, pdata, data_size, fp); + } + } + } + break; + case RESET_CLASS_TYPE: + case BOOT_SEQUENCE_CLASS_TYPE: + case FIRMWARE_ASSERT_CLASS_TYPE: + case TEMPERATURE_CLASS_TYPE: + case MEDIA_CLASS_TYPE: + { + pnvme_ocp_common_dbg_evt_class_format pcommon_debug_event = (pnvme_ocp_common_dbg_evt_class_format)pevent_specific_data; + int vu_event_id = (int)pcommon_debug_event->vu_event_identifier; + unsigned int data_size = ((pevent_descriptor->event_data_size * SIZE_OF_DWORD) - sizeof(nvme_ocp_common_dbg_evt_class_format_t)); + __u8 *pdata = (__u8*)pcommon_debug_event + sizeof(nvme_ocp_common_dbg_evt_class_format_t); + + char description_str[256] = ""; + parse_ocp_telemetry_string_log(0, pcommon_debug_event->vu_event_identifier, pevent_descriptor->debug_event_class_type, VU_EVENT_STRING, description_str); + + if(pevent_fifos_object != NULL) + { + JSON_ADD_FORMATTED_UINT32_STR(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING, vu_event_id); + JSON_ADD_STR(pevent_descriptor_obj, STR_VU_EVENT_STRING, description_str); + JSON_ADD_FORMATTED_VAR_SIZE_STR(pevent_descriptor_obj, STR_VU_DATA, pdata, data_size); + } + else + { + if(fp) + { + fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id); + fprintf(fp,"%s: %s\n", STR_VU_EVENT_STRING, description_str); + print_formatted_var_size_str(STR_VU_DATA, pdata, data_size, fp); + } + else + { + printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id); + printf("%s: %s\n", STR_VU_EVENT_STRING, description_str); + print_formatted_var_size_str(STR_VU_DATA, pdata, data_size, fp); + } + } + } + break; + case MEDIA_WEAR_CLASS_TYPE: + { + pnvme_ocp_media_wear_dbg_evt_class_format pmedia_wear_event = (pnvme_ocp_media_wear_dbg_evt_class_format)pevent_specific_data; + int vu_event_id = (int)pmedia_wear_event->vu_event_identifier; + unsigned int data_size = ((pevent_descriptor->event_data_size * SIZE_OF_DWORD) - sizeof(nvme_ocp_media_wear_dbg_evt_class_format_t)); + __u8 *pdata = (__u8*)pmedia_wear_event + sizeof(nvme_ocp_media_wear_dbg_evt_class_format_t); + + char description_str[256] = ""; + parse_ocp_telemetry_string_log(0, pmedia_wear_event->vu_event_identifier, pevent_descriptor->debug_event_class_type, VU_EVENT_STRING, description_str); + + if(pevent_fifos_object != NULL) + { + JSON_ADD_FORMATTED_VAR_SIZE_STR(pevent_descriptor_obj, STR_CLASS_SPECIFIC_DATA, pmedia_wear_event->currentMediaWear, DATA_SIZE_12); + JSON_ADD_FORMATTED_UINT32_STR(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING, vu_event_id); + JSON_ADD_STR(pevent_descriptor_obj, STR_VU_EVENT_STRING, description_str); + JSON_ADD_FORMATTED_VAR_SIZE_STR(pevent_descriptor_obj, STR_VU_DATA, pdata, data_size); + } + else + { + if(fp) + { + print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, pmedia_wear_event->currentMediaWear, DATA_SIZE_12, fp); + fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id); + fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING, description_str); + print_formatted_var_size_str(STR_VU_DATA, pdata, data_size, fp); + } + else + { + print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, pmedia_wear_event->currentMediaWear, DATA_SIZE_12, NULL); + printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING, vu_event_id); + printf("%s: %s\n", STR_VU_EVENT_STRING, description_str); + print_formatted_var_size_str(STR_VU_DATA, pdata, data_size, fp); + } + + } + } + break; + case STATISTIC_SNAPSHOT_CLASS_TYPE: + { + pnvme_ocp_statistic_snapshot_evt_class_format pStaticSnapshotEvent = (pnvme_ocp_statistic_snapshot_evt_class_format)pevent_specific_data; + pnvme_ocp_telemetry_statistic_descriptor pstatistic_entry = (pnvme_ocp_telemetry_statistic_descriptor)(&pStaticSnapshotEvent->statisticDescriptorData); + parse_statistic(pstatistic_entry, pevent_descriptor_obj, fp); + } + break; + default: + { + //Reserved 7Fh-0Bh and Vendor Unique FFh-80h Classes will fall here, Nothing to Parse + } + break; + } + + if (pevent_descriptor_obj != NULL && pevent_fifo_array != NULL) + { + json_array_add_value_object(pevent_fifo_array, pevent_descriptor_obj); + } + else + { + if(fp) + { + fprintf(fp,"-----------------------------------------------------------------------------\n"); + } + else + { + printf("-----------------------------------------------------------------------------\n"); + } + } + } + else + { + break; + } + offset_to_move += (pevent_descriptor->event_data_size * SIZE_OF_DWORD + event_des_size); + } + + + if (pevent_fifos_object != NULL && pevent_fifo_array != NULL) + { + json_object_add_value_array(pevent_fifos_object, event_fifo_name, pevent_fifo_array); + } + + free(description); + return 0; +} + +int parse_event_fifos(struct json_object *root, pnvme_ocp_telemetry_offsets poffsets, FILE *fp) +{ + if (NULL == poffsets) + { + nvme_show_error("Input buffer was NULL"); + return -1; + } + + struct json_object* pevent_fifos_object = NULL; + if(NULL != root) + { + pevent_fifos_object = json_create_object(); + } + + __u8 *pda1_header_offset = ptelemetry_buffer + poffsets->da1_start_offset;//512 + __u8 *pda2_offset = ptelemetry_buffer + poffsets->da2_start_offset; + pnvme_ocp_header_in_da1 pda1_header = (pnvme_ocp_header_in_da1)pda1_header_offset; + + nvme_ocp_event_fifo_data_t event_fifo[MAX_NUM_FIFOS]; + for (int fifo_num = 0; fifo_num < MAX_NUM_FIFOS; fifo_num++) + { + switch (fifo_num) + { + case 0: + { + event_fifo[fifo_num].event_fifo_num = fifo_num; + event_fifo[fifo_num].event_fifo_da = pda1_header->event_fifo_1_da; + event_fifo[fifo_num].event_fifo_start = pda1_header->event_fifo_1_start; + event_fifo[fifo_num].event_fifo_size = pda1_header->event_fifo_1_size; + } + break; + case 1: + { + event_fifo[fifo_num].event_fifo_num = fifo_num; + event_fifo[fifo_num].event_fifo_da = pda1_header->event_fifo_2_da; + event_fifo[fifo_num].event_fifo_start = pda1_header->event_fifo_2_start; + event_fifo[fifo_num].event_fifo_size = pda1_header->event_fifo_2_size; + } + break; + case 2: + { + event_fifo[fifo_num].event_fifo_num = fifo_num; + event_fifo[fifo_num].event_fifo_da = pda1_header->event_fifo_3_da; + event_fifo[fifo_num].event_fifo_start = pda1_header->event_fifo_3_start; + event_fifo[fifo_num].event_fifo_size = pda1_header->event_fifo_3_size; + } + break; + case 3: + { + event_fifo[fifo_num].event_fifo_num = fifo_num; + event_fifo[fifo_num].event_fifo_da = pda1_header->event_fifo_4_da; + event_fifo[fifo_num].event_fifo_start = pda1_header->event_fifo_4_start; + event_fifo[fifo_num].event_fifo_size = pda1_header->event_fifo_4_size; + } + break; + case 4: + { + event_fifo[fifo_num].event_fifo_num = fifo_num; + event_fifo[fifo_num].event_fifo_da = pda1_header->event_fifo_5_da; + event_fifo[fifo_num].event_fifo_start = pda1_header->event_fifo_5_start; + event_fifo[fifo_num].event_fifo_size = pda1_header->event_fifo_5_size; + } + break; + case 5: + { + event_fifo[fifo_num].event_fifo_num = fifo_num; + event_fifo[fifo_num].event_fifo_da = pda1_header->event_fifo_6_da; + event_fifo[fifo_num].event_fifo_start = pda1_header->event_fifo_6_start; + event_fifo[fifo_num].event_fifo_size = pda1_header->event_fifo_6_size; + } + break; + case 6: + { + event_fifo[fifo_num].event_fifo_num = fifo_num; + event_fifo[fifo_num].event_fifo_da = pda1_header->event_fifo_7_da; + event_fifo[fifo_num].event_fifo_start = pda1_header->event_fifo_7_start; + event_fifo[fifo_num].event_fifo_size = pda1_header->event_fifo_7_size; + } + break; + case 7: + { + event_fifo[fifo_num].event_fifo_num = fifo_num; + event_fifo[fifo_num].event_fifo_da = pda1_header->event_fifo_8_da; + event_fifo[fifo_num].event_fifo_start = pda1_header->event_fifo_8_start; + event_fifo[fifo_num].event_fifo_size = pda1_header->event_fifo_8_size; + } + break; + case 8: + { + event_fifo[fifo_num].event_fifo_num = fifo_num; + event_fifo[fifo_num].event_fifo_da = pda1_header->event_fifo_9_da; + event_fifo[fifo_num].event_fifo_start = pda1_header->event_fifo_9_start; + event_fifo[fifo_num].event_fifo_size = pda1_header->event_fifo_9_size; + } + break; + case 9: + { + event_fifo[fifo_num].event_fifo_num = fifo_num; + event_fifo[fifo_num].event_fifo_da = pda1_header->event_fifo_10_da; + event_fifo[fifo_num].event_fifo_start = pda1_header->event_fifo_10_start; + event_fifo[fifo_num].event_fifo_size = pda1_header->event_fifo_10_size; + } + break; + case 10: + { + event_fifo[fifo_num].event_fifo_num = fifo_num; + event_fifo[fifo_num].event_fifo_da = pda1_header->event_fifo_11_da; + event_fifo[fifo_num].event_fifo_start = pda1_header->event_fifo_11_start; + event_fifo[fifo_num].event_fifo_size = pda1_header->event_fifo_11_size; + } + break; + case 11: + { + event_fifo[fifo_num].event_fifo_num = fifo_num; + event_fifo[fifo_num].event_fifo_da = pda1_header->event_fifo_12_da; + event_fifo[fifo_num].event_fifo_start = pda1_header->event_fifo_12_start; + event_fifo[fifo_num].event_fifo_size = pda1_header->event_fifo_12_size; + } + break; + case 12: + { + event_fifo[fifo_num].event_fifo_num = fifo_num; + event_fifo[fifo_num].event_fifo_da = pda1_header->event_fifo_13_da; + event_fifo[fifo_num].event_fifo_start = pda1_header->event_fifo_13_start; + event_fifo[fifo_num].event_fifo_size = pda1_header->event_fifo_13_size; + } + break; + case 13: + { + event_fifo[fifo_num].event_fifo_num = fifo_num; + event_fifo[fifo_num].event_fifo_da = pda1_header->event_fifo_14_da; + event_fifo[fifo_num].event_fifo_start = pda1_header->event_fifo_14_start; + event_fifo[fifo_num].event_fifo_size = pda1_header->event_fifo_14_size; + } + break; + case 14: + { + event_fifo[fifo_num].event_fifo_num = fifo_num; + event_fifo[fifo_num].event_fifo_da = pda1_header->event_fifo_15_da; + event_fifo[fifo_num].event_fifo_start = pda1_header->event_fifo_15_start; + event_fifo[fifo_num].event_fifo_size = pda1_header->event_fifo_15_size; + } + break; + case 15: + { + event_fifo[fifo_num].event_fifo_num = fifo_num; + event_fifo[fifo_num].event_fifo_da = pda1_header->event_fifo_16_da; + event_fifo[fifo_num].event_fifo_start = pda1_header->event_fifo_16_start; + event_fifo[fifo_num].event_fifo_size = pda1_header->event_fifo_16_size; + } + break; + default: + break; + } + } + + //Parse all the FIFOs DA wise + for (int fifo_no = 0; fifo_no < MAX_NUM_FIFOS; fifo_no++) + { + if (event_fifo[fifo_no].event_fifo_da == poffsets->data_area) + { + __u64 fifo_offset = (event_fifo[fifo_no].event_fifo_start * SIZE_OF_DWORD); + __u64 fifo_size = (event_fifo[fifo_no].event_fifo_size * SIZE_OF_DWORD); + __u8 *pfifo_start = NULL; + + if (event_fifo[fifo_no].event_fifo_da == 1) + { + __u64 offset_in_da_1 = fifo_offset - poffsets->header_size; + + if (offset_in_da_1 > poffsets->da1_size) + { + nvme_show_error("Event FIFO %d start is outside of data area 1.\n", fifo_no); + } + if ((offset_in_da_1 + fifo_size - 1) > poffsets->da1_size) + { + nvme_show_error("Event FIFO %d size is outside of data area 1.\n", fifo_no); + } + pfifo_start = pda1_header_offset + fifo_offset; + } + else if (event_fifo[fifo_no].event_fifo_da == 2) + { + __u64 offset_in_da_2 = fifo_offset; + + if (offset_in_da_2 > poffsets->da2_size) + { + nvme_show_error("Event FIFO %d start is outside of data area 2.\n", fifo_no); + } + if ((offset_in_da_2 + fifo_size - 1) > poffsets->da2_size) + { + nvme_show_error("Event FIFO %d size is outside of data area 2.\n", fifo_no); + } + pfifo_start = pda2_offset + fifo_offset; + } + else + { + nvme_show_error("Unsupported Data Area:[%d]", poffsets->data_area); + return -1; + } + + int status = parse_event_fifo(fifo_no, pfifo_start, pevent_fifos_object, pstring_buffer, poffsets, fifo_size, fp); + if (status != 0) + { + nvme_show_error("Failed to parse Event FIFO. status: %d\n", status); + return -1; + } + } + } + + if(pevent_fifos_object != NULL && root != NULL) + { + const char* data_area = (poffsets->data_area == 1 ? STR_DA_1_EVENT_FIFO_INFO : STR_DA_2_EVENT_FIFO_INFO); + json_object_add_value_array(root, data_area, pevent_fifos_object); + } + + return 0; +} + +int parse_statistic(pnvme_ocp_telemetry_statistic_descriptor pstatistic_entry, struct json_object *pstats_array, FILE *fp) +{ + if (NULL == pstatistic_entry) + { + nvme_show_error("Input buffer was NULL"); + return -1; + } + + unsigned int data_size = pstatistic_entry->statistic_data_size * SIZE_OF_DWORD; + __u8 *pdata = (__u8*)pstatistic_entry + sizeof(nvme_ocp_telemetry_statistic_descriptor_t); + + char description_str[256] = ""; + parse_ocp_telemetry_string_log(0, pstatistic_entry->statistic_id, 0, STATISTICS_IDENTIFIER_STRING, description_str); + + if (NULL != pstats_array) + { + struct json_object* pstatistics_object = json_create_object(); + + JSON_ADD_FORMATTED_UINT32_STR(pstatistics_object, STR_STATISTICS_IDENTIFIER, pstatistic_entry->statistic_id); + JSON_ADD_STR(pstatistics_object, STR_STATISTICS_IDENTIFIER_STR, description_str); + JSON_ADD_FORMATTED_UINT32_STR(pstatistics_object, STR_STATISTICS_INFO_BEHAVIOUR_TYPE, pstatistic_entry->statistic_info_behaviour_type); + JSON_ADD_FORMATTED_UINT32_STR(pstatistics_object, STR_STATISTICS_INFO_RESERVED, pstatistic_entry->statistic_info_reserved); + JSON_ADD_FORMATTED_UINT32_STR(pstatistics_object, STR_NAMESPACE_IDENTIFIER, pstatistic_entry->ns_info_nsid); + JSON_ADD_FORMATTED_UINT32_STR(pstatistics_object, STR_NAMESPACE_INFO_VALID, pstatistic_entry->ns_info_ns_info_valid); + JSON_ADD_FORMATTED_UINT32_STR(pstatistics_object, STR_STATISTICS_DATA_SIZE, pstatistic_entry->statistic_data_size); + JSON_ADD_FORMATTED_UINT32_STR(pstatistics_object, STR_RESERVED, pstatistic_entry->reserved); + JSON_ADD_FORMATTED_VAR_SIZE_STR(pstatistics_object, STR_STATISTICS_SPECIFIC_DATA, pdata, data_size); + + if(pstatistics_object != NULL) + { + json_array_add_value_object(pstats_array, pstatistics_object); + } + } + else + { + if(fp) + { + fprintf(fp, "%s: 0x%x\n", STR_STATISTICS_IDENTIFIER, pstatistic_entry->statistic_id); + fprintf(fp, "%s: %s\n", STR_STATISTICS_IDENTIFIER_STR, description_str); + fprintf(fp, "%s: 0x%x\n", STR_STATISTICS_INFO_BEHAVIOUR_TYPE, pstatistic_entry->statistic_info_behaviour_type); + fprintf(fp, "%s: 0x%x\n", STR_STATISTICS_INFO_RESERVED, pstatistic_entry->statistic_info_reserved); + fprintf(fp, "%s: 0x%x\n", STR_NAMESPACE_IDENTIFIER, pstatistic_entry->ns_info_nsid); + fprintf(fp, "%s: 0x%x\n", STR_NAMESPACE_INFO_VALID, pstatistic_entry->ns_info_ns_info_valid); + fprintf(fp, "%s: 0x%x\n", STR_STATISTICS_DATA_SIZE, pstatistic_entry->statistic_data_size); + fprintf(fp, "%s: 0x%x\n", STR_RESERVED, pstatistic_entry->reserved); + print_formatted_var_size_str(STR_STATISTICS_SPECIFIC_DATA, pdata, data_size, fp); + fprintf(fp, "--------------------------------------------------------------------------------------\n"); + } + else + { + printf("%s: 0x%x\n", STR_STATISTICS_IDENTIFIER, pstatistic_entry->statistic_id); + printf("%s: %s\n", STR_STATISTICS_IDENTIFIER_STR, description_str); + printf("%s: 0x%x\n", STR_STATISTICS_INFO_BEHAVIOUR_TYPE, pstatistic_entry->statistic_info_behaviour_type); + printf("%s: 0x%x\n", STR_STATISTICS_INFO_RESERVED, pstatistic_entry->statistic_info_reserved); + printf("%s: 0x%x\n", STR_NAMESPACE_IDENTIFIER, pstatistic_entry->ns_info_nsid); + printf("%s: 0x%x\n", STR_NAMESPACE_INFO_VALID, pstatistic_entry->ns_info_ns_info_valid); + printf("%s: 0x%x\n", STR_STATISTICS_DATA_SIZE, pstatistic_entry->statistic_data_size); + printf("%s: 0x%x\n", STR_RESERVED, pstatistic_entry->reserved); + print_formatted_var_size_str(STR_STATISTICS_SPECIFIC_DATA, pdata, data_size, fp); + printf("--------------------------------------------------------------------------------------\n"); + } + } + + return 0; +} + +int parse_statistics(struct json_object *root, pnvme_ocp_telemetry_offsets poffsets, FILE *fp) +{ + if (NULL == poffsets) + { + nvme_show_error("Input buffer was NULL"); + return -1; + } + + __u8 *pda1_ocp_header_offset = ptelemetry_buffer + poffsets->header_size;//512 + __u32 statistics_size = 0; + __u32 stats_da_1_start_dw = 0, stats_da_1_size_dw = 0; + __u32 stats_da_2_start_dw = 0, stats_da_2_size_dw = 0; + __u8 *pstats_offset = NULL; + + if (poffsets->data_area == 1) + { + __u32 stats_da_1_start = *(__u32 *)(pda1_ocp_header_offset + offsetof(nvme_ocp_header_in_da1_t, da1_statistic_start)); + __u32 stats_da_1_size = *(__u32 *)(pda1_ocp_header_offset + offsetof(nvme_ocp_header_in_da1_t, da1_statistic_size)); + + //Data is present in the form of DWORDS, So multiplying with sizeof(DWORD) + stats_da_1_start_dw = (stats_da_1_start * SIZE_OF_DWORD); + stats_da_1_size_dw = (stats_da_1_size * SIZE_OF_DWORD); + + pstats_offset = pda1_ocp_header_offset + stats_da_1_start_dw; + statistics_size = stats_da_1_size_dw; + } + else if (poffsets->data_area == 2) + { + __u32 stats_da_2_start = *(__u32 *)(pda1_ocp_header_offset + offsetof(nvme_ocp_header_in_da1_t, da2_statistic_start)); + __u32 stats_da_2_size = *(__u32 *)(pda1_ocp_header_offset + offsetof(nvme_ocp_header_in_da1_t, da2_statistic_size)); + + stats_da_2_start_dw = (stats_da_2_start * SIZE_OF_DWORD); + stats_da_2_size_dw = (stats_da_2_size * SIZE_OF_DWORD); + + pstats_offset = pda1_ocp_header_offset + poffsets->da1_size + stats_da_2_start_dw; + statistics_size = stats_da_2_size_dw; + } + else + { + nvme_show_error("Unsupported Data Area:[%d]", poffsets->data_area); + return -1; + } + + struct json_object *pstats_array = ((NULL != root)? json_create_array() : NULL); + + __u32 stat_des_size = sizeof(nvme_ocp_telemetry_statistic_descriptor_t);//8 + __u32 offset_to_move = 0; + + while (((statistics_size > 0) && (offset_to_move < statistics_size))) + { + pnvme_ocp_telemetry_statistic_descriptor pstatistic_entry = (pnvme_ocp_telemetry_statistic_descriptor)(pstats_offset + offset_to_move); + parse_statistic(pstatistic_entry, pstats_array, fp); + offset_to_move += (pstatistic_entry->statistic_data_size * SIZE_OF_DWORD + stat_des_size); + } + + if (NULL != root && NULL != pstats_array) + { + const char *pdata_area = (poffsets->data_area == 1 ? STR_DA_1_STATS : STR_DA_2_STATS ); + json_object_add_value_array(root, pdata_area, pstats_array); + } + + return 0; +} + +int print_ocp_telemetry_normal(char *output_file) +{ + int status = 0; + if(output_file != NULL) + { + FILE *fp = fopen(output_file, "w"); + if (fp) + { + fprintf(fp, "=====================================================================================\n"); + fprintf(fp, "%s\n", STR_LOG_PAGE_HEADER); + fprintf(fp, "=====================================================================================\n"); + print_micron_vs_logs(ptelemetry_buffer, host_log_page_header, ARRAY_SIZE(host_log_page_header), NULL, 0, fp); + + fprintf(fp, "=====================================================================================\n"); + fprintf(fp, "%s\n", STR_REASON_IDENTIFIER); + fprintf(fp, "=====================================================================================\n"); + __u8 *preason_identifier_offset = ptelemetry_buffer + offsetof(nvme_ocp_telemetry_host_initiated_header_t, reason_id); + print_micron_vs_logs(preason_identifier_offset, reason_identifier, ARRAY_SIZE(reason_identifier), NULL, 0, fp); + + fprintf(fp, "=====================================================================================\n"); + fprintf(fp, "%s\n", STR_TELEMETRY_HOST_DATA_BLOCK_1); + fprintf(fp, "=====================================================================================\n"); + + //Set DA to 1 and get offsets + nvme_ocp_telemetry_offsets_t offsets = { 0 }; + offsets.data_area = 1; + pnvme_ocp_telemetry_common_header ptelemetry_common_header = (pnvme_ocp_telemetry_common_header) ptelemetry_buffer; + get_telemetry_das_offset_and_size(ptelemetry_common_header, &offsets); + + __u8 *pda1_header_offset = ptelemetry_buffer + offsets.da1_start_offset;//512 + print_micron_vs_logs(pda1_header_offset, ocp_header_in_da1, ARRAY_SIZE(ocp_header_in_da1), NULL, 0, fp); + + fprintf(fp, "=====================================================================================\n"); + fprintf(fp, "%s\n", STR_SMART_HEALTH_INFO); + fprintf(fp, "=====================================================================================\n"); + __u8 *pda1_smart_offset = pda1_header_offset + offsetof(nvme_ocp_header_in_da1_t, smart_health_info);//512+512 =1024 + print_micron_vs_logs(pda1_smart_offset, smart, ARRAY_SIZE(smart), NULL, 0, fp); + + fprintf(fp, "=====================================================================================\n"); + fprintf(fp, "%s\n", STR_SMART_HEALTH_INTO_EXTENDED); + fprintf(fp, "=====================================================================================\n"); + __u8 *pda1_smart_ext_offset = pda1_header_offset + offsetof(nvme_ocp_header_in_da1_t, smart_health_info_extended);//512+1024 =1536 + print_micron_vs_logs(pda1_smart_ext_offset, smart_extended, ARRAY_SIZE(smart_extended), NULL, 0, fp); + + fprintf(fp, "=====================================================================================\n"); + fprintf(fp, "%s\n", STR_DA_1_STATS); + fprintf(fp, "=====================================================================================\n"); + + status = parse_statistics(NULL, &offsets, fp); + if (status != 0) + { + nvme_show_error("status: %d\n", status); + return -1; + } + + fprintf(fp, "=====================================================================================\n"); + fprintf(fp, "%s\n", STR_DA_1_EVENT_FIFO_INFO); + fprintf(fp, "=====================================================================================\n"); + status = parse_event_fifos(NULL, &offsets, fp); + if (status != 0) + { + nvme_show_error("status: %d\n", status); + return -1; + } + + //Set the DA to 2 + offsets.data_area = 2; + + fprintf(fp, "=====================================================================================\n"); + fprintf(fp, "%s\n", STR_DA_2_STATS); + fprintf(fp, "=====================================================================================\n"); + status = parse_statistics(NULL, &offsets, fp); + if (status != 0) + { + nvme_show_error("status: %d\n", status); + return -1; + } + + fprintf(fp, "=====================================================================================\n"); + fprintf(fp, "%s\n", STR_DA_2_EVENT_FIFO_INFO); + fprintf(fp, "=====================================================================================\n"); + status = parse_event_fifos(NULL, &offsets, fp); + if (status != 0) + { + nvme_show_error("status: %d\n", status); + return -1; + } + + fprintf(fp, "=====================================================================================\n"); + + fclose(fp); + } else { + nvme_show_error("Failed to open %s file.\n", output_file); + return -1; + } + } + else + { + printf("=====================================================================================\n"); + printf("%s\n", STR_LOG_PAGE_HEADER); + printf("=====================================================================================\n"); + print_micron_vs_logs(ptelemetry_buffer, host_log_page_header, ARRAY_SIZE(host_log_page_header), NULL, 0, NULL); + + printf("=====================================================================================\n"); + printf("%s\n", STR_REASON_IDENTIFIER); + printf("=====================================================================================\n"); + __u8 *preason_identifier_offset = ptelemetry_buffer + offsetof(nvme_ocp_telemetry_host_initiated_header_t, reason_id); + print_micron_vs_logs(preason_identifier_offset, reason_identifier, ARRAY_SIZE(reason_identifier), NULL, 0, NULL); + + printf("=====================================================================================\n"); + printf("%s\n", STR_TELEMETRY_HOST_DATA_BLOCK_1); + printf("=====================================================================================\n"); + + //Set DA to 1 and get offsets + nvme_ocp_telemetry_offsets_t offsets = { 0 }; + offsets.data_area = 1; + pnvme_ocp_telemetry_common_header ptelemetry_common_header = (pnvme_ocp_telemetry_common_header) ptelemetry_buffer; + get_telemetry_das_offset_and_size(ptelemetry_common_header, &offsets); + + __u8 *pda1_header_offset = ptelemetry_buffer + offsets.da1_start_offset;//512 + print_micron_vs_logs(pda1_header_offset, ocp_header_in_da1, ARRAY_SIZE(ocp_header_in_da1), NULL, 0, NULL); + + printf("=====================================================================================\n"); + printf("%s\n", STR_SMART_HEALTH_INFO); + printf("=====================================================================================\n"); + __u8 *pda1_smart_offset = pda1_header_offset + offsetof(nvme_ocp_header_in_da1_t, smart_health_info);//512+512 =1024 + print_micron_vs_logs(pda1_smart_offset, smart, ARRAY_SIZE(smart), NULL, 0, NULL); + + printf("=====================================================================================\n"); + printf("%s\n", STR_SMART_HEALTH_INTO_EXTENDED); + printf("=====================================================================================\n"); + __u8 *pda1_smart_ext_offset = pda1_header_offset + offsetof(nvme_ocp_header_in_da1_t, smart_health_info_extended);//512+1024 =1536 + print_micron_vs_logs(pda1_smart_ext_offset, smart_extended, ARRAY_SIZE(smart_extended), NULL, 0, NULL); + + printf("=====================================================================================\n"); + printf("%s\n", STR_DA_1_STATS); + printf("=====================================================================================\n"); + status = parse_statistics(NULL, &offsets, NULL); + if (status != 0) + { + nvme_show_error("status: %d\n", status); + return -1; + } + + printf("=====================================================================================\n"); + printf("%s\n", STR_DA_1_EVENT_FIFO_INFO); + printf("=====================================================================================\n"); + status = parse_event_fifos(NULL, &offsets, NULL); + if (status != 0) + { + nvme_show_error("status: %d\n", status); + return -1; + } + + //Set the DA to 2 + offsets.data_area = 2; + + printf("=====================================================================================\n"); + printf("%s\n", STR_DA_2_STATS); + printf("=====================================================================================\n"); + status = parse_statistics(NULL, &offsets, NULL); + if (status != 0) + { + nvme_show_error("status: %d\n", status); + return -1; + } + + printf("=====================================================================================\n"); + printf("%s\n", STR_DA_2_EVENT_FIFO_INFO); + printf("=====================================================================================\n"); + status = parse_event_fifos(NULL, &offsets, NULL); + if (status != 0) + { + nvme_show_error("status: %d\n", status); + return -1; + } + + printf("=====================================================================================\n"); + } + + return status; +} + +int print_ocp_telemetry_json(char *output_file) +{ + int status = 0; + + //create json objects + struct json_object *root, *pheader, *preason_identifier, *da1_header, *smart_obj, *ext_smart_obj; + root = json_create_object(); + + //Add data to root json object + + //"Log Page Header" + pheader = json_create_object(); + print_micron_vs_logs(ptelemetry_buffer, host_log_page_header, ARRAY_SIZE(host_log_page_header), pheader, 0, NULL); + json_object_add_value_object(root, STR_LOG_PAGE_HEADER, pheader); + + //"Reason Identifier" + preason_identifier = json_create_object(); + __u8 *preason_identifier_offset = ptelemetry_buffer + offsetof(nvme_ocp_telemetry_host_initiated_header_t, reason_id); + print_micron_vs_logs(preason_identifier_offset, reason_identifier, ARRAY_SIZE(reason_identifier), preason_identifier, 0, NULL); + json_object_add_value_object(pheader, STR_REASON_IDENTIFIER, preason_identifier); + + nvme_ocp_telemetry_offsets_t offsets = { 0 }; + + //Set DA to 1 and get offsets + offsets.data_area = 1; + pnvme_ocp_telemetry_common_header ptelemetry_common_header = (pnvme_ocp_telemetry_common_header) ptelemetry_buffer; + get_telemetry_das_offset_and_size(ptelemetry_common_header, &offsets); + + //"Telemetry Host-Initiated Data Block 1" + __u8 *pda1_header_offset = ptelemetry_buffer + offsets.da1_start_offset;//512 + da1_header = json_create_object(); + print_micron_vs_logs(pda1_header_offset, ocp_header_in_da1, ARRAY_SIZE(ocp_header_in_da1), da1_header, 0, NULL); + json_object_add_value_object(root, STR_TELEMETRY_HOST_DATA_BLOCK_1, da1_header); + + //"SMART / Health Information Log(LID-02h)" + __u8 *pda1_smart_offset = pda1_header_offset + offsetof(nvme_ocp_header_in_da1_t, smart_health_info);//512+512 =1024 + smart_obj = json_create_object(); + print_micron_vs_logs(pda1_smart_offset, smart, ARRAY_SIZE(smart), smart_obj, 0, NULL); + json_object_add_value_object(da1_header, STR_SMART_HEALTH_INFO, smart_obj); + + //"SMART / Health Information Extended(LID-C0h)" + __u8 *pda1_smart_ext_offset = pda1_header_offset + offsetof(nvme_ocp_header_in_da1_t, smart_health_info_extended);//512+1024 =1536 + ext_smart_obj = json_create_object(); + print_micron_vs_logs(pda1_smart_ext_offset, smart_extended, ARRAY_SIZE(smart_extended), ext_smart_obj, 0, NULL); + json_object_add_value_object(da1_header, STR_SMART_HEALTH_INTO_EXTENDED, ext_smart_obj); + + //Data Area 1 Statistics + status = parse_statistics(root, &offsets, NULL); + if (status != 0) + { + nvme_show_error("status: %d\n", status); + return -1; + } + + //Data Area 1 Event FIFOs + status = parse_event_fifos(root, &offsets, NULL); + if (status != 0) + { + nvme_show_error("status: %d\n", status, NULL); + return -1; + } + + //Set the DA to 2 + offsets.data_area = 2; + + //Data Area 2 Statistics + status = parse_statistics(root, &offsets, NULL); + if (status != 0) + { + nvme_show_error("status: %d\n", status); + return -1; + } + + //Data Area 2 Event FIFOs + status = parse_event_fifos(root, &offsets, NULL); + if (status != 0) + { + nvme_show_error("status: %d\n", status); + return -1; + } + + if(output_file != NULL) + { + const char *json_string = json_object_to_json_string(root); + FILE *fp = fopen(output_file, "w"); + if (fp) + { + fputs(json_string, fp); + fclose(fp); + } else { + nvme_show_error("Failed to open %s file.\n", output_file); + return -1; + } + } + else + { + //Print root json object + json_print_object(root, NULL); + nvme_show_result("\n"); + json_free_object(root); + } + + return status; +} + +int parse_ocp_telemetry_log(pocp_telemetry_parse_options options) +{ + int status = 0; + // Read the data from the telemetry binary file + long telemetry_buffer_size = 0; + ptelemetry_buffer = read_binary_file(NULL, options->telemetry_log, &telemetry_buffer_size, 1); + if (ptelemetry_buffer == NULL) + { + nvme_show_error("Failed to read telemetry bin file.\n"); + return -1; + } + + // Read the data from the string binary file + long string_buffer_size = 0; + pstring_buffer = read_binary_file(NULL, options->string_log, &string_buffer_size, 1); + if (pstring_buffer == NULL) + { + nvme_show_error("Failed to read string log bin file.\n"); + return -1; + } + + unsigned char log_id = ptelemetry_buffer[0]; + if ((log_id != NVME_HOST_TELEMETRY_LOG) && (log_id != NVME_CNTRL_TELEMETRY_LOG)) + { + nvme_show_error("Invalid LogPageId [0x%02X]\n", log_id); + return -1; + } + + enum nvme_print_flags fmt; + status = validate_output_format(options->output_fmt, &fmt); + if (status < 0) { + nvme_show_error("Invalid output format\n"); + return status; + } + + switch (fmt) { + case NORMAL: + print_ocp_telemetry_normal(options->output_file); + break; + case JSON: + print_ocp_telemetry_json(options->output_file); + break; + default: + break; + } + + return 0; +} \ No newline at end of file diff --git a/plugins/micron/micron-ocp-telemetry.h b/plugins/micron/micron-ocp-telemetry.h new file mode 100644 index 0000000000..dccdb4d4e5 --- /dev/null +++ b/plugins/micron/micron-ocp-telemetry.h @@ -0,0 +1,519 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (c) Micron, Inc 2024. + * + * @file: micron-ocp-telemetry.h + * @brief: This module contains all the constructs needed for parsing (or) decoding ocp telemetry log files. + * @author: Chaithanya Shoba + */ + +#include "nvme.h" +#include "nvme-print.h" +#include "micron-utils.h" +#include "common.h" + +#define DATA_SIZE_12 12 +#define DATA_SIZE_8 8 +#define DATA_SIZE_4 4 + +typedef struct __packed nvme_ocp_telemetry_reason_id +{ + __u8 error_id[64]; // Bytes 63:00 + __u8 file_id[8]; // Bytes 71:64 + __le16 line_number; // Bytes 73:72 + __u8 valid_flags; // Bytes 74 + __u8 reserved[21]; // Bytes 95:75 + __u8 vu_reason_ext[32]; // Bytes 127:96 +}nvme_ocp_telemetry_reason_id_t, *pnvme_ocp_telemetry_reason_id; + +typedef struct __packed nvme_ocp_telemetry_common_header +{ + __u8 log_id; // Byte 00 + __le32 reserved1; // Bytes 04:01 + __u8 ieee_oui_id[3]; // Bytes 07:05 + __le16 da1_last_block; // Bytes 09:08 + __le16 da2_last_block; // Bytes 11:10 + __le16 da3_last_block; // Bytes 13:12 + __le16 reserved2; // Bytes 15:14 + __le32 da4_last_block; // Bytes 19:16 +}nvme_ocp_telemetry_common_header_t, *pnvme_ocp_telemetry_common_header; + +typedef struct __packed nvme_ocp_telemetry_host_initiated_header +{ + nvme_ocp_telemetry_common_header_t commonHeader; // Bytes 19:00 + __u8 reserved3[360]; // Bytes 379:20 + __u8 host_initiated_scope; // Byte 380 + __u8 host_initiated_gen_number; // Byte 381 + __u8 host_initiated_data_available; // Byte 382 + __u8 ctrl_initiated_gen_number; // Byte 383 + nvme_ocp_telemetry_reason_id_t reason_id; // Bytes 511:384 +}nvme_ocp_telemetry_host_initiated_header_t, *pnvme_ocp_telemetry_host_initiated_header; + +typedef struct __packed nvme_ocp_telemetry_controller_initiated_header +{ + nvme_ocp_telemetry_common_header_t commonHeader; // Bytes 19:00 + __u8 reserved3[361]; // Bytes 380:20 + __u8 ctrl_initiated_scope; // Byte 381 + __u8 ctrl_initiated_data_available; // Byte 382 + __u8 ctrl_initiated_gen_number; // Byte 383 + nvme_ocp_telemetry_reason_id_t reason_id; // Bytes 511:384 +}nvme_ocp_telemetry_controller_initiated_header_t, *pnvme_ocp_telemetry_controller_initiated_header; + +typedef struct __packed nvme_ocp_telemetry_smart +{ + __u8 critical_warning; // Byte 0 + __le16 composite_temperature; // Bytes 2:1 + __u8 available_spare; // Bytes 3 + __u8 available_spare_threshold; // Bytes 4 + __u8 percentage_used; // Bytes 5 + __u8 reserved1[26]; // Bytes 31:6 + __u8 data_units_read[16]; // Bytes 47:32 + __u8 data_units_written[16]; // Bytes 63:48 + __u8 host_read_commands[16]; // Byte 79:64 + __u8 host_write_commands[16]; // Bytes 95:80 + __u8 controller_busy_time[16]; // Bytes 111:96 + __u8 power_cycles[16]; // Bytes 127:112 + __u8 power_on_hours[16]; // Bytes 143:128 + __u8 unsafe_shutdowns[16]; // Bytes 159:144 + __u8 media_and_data_integrity_errors[16]; // Bytes 175:160 + __u8 number_of_error_information_log_entries[16]; // Bytes 191:176 + __le32 warning_composite_temperature_time; // Byte 195:192 + __le32 critical_composite_temperature_time; // Bytes 199:196 + __le16 temperature_sensor1; // Bytes 201:200 + __le16 temperature_sensor2; // Byte 203:202 + __le16 temperature_sensor3; // Byte 205:204 + __le16 temperature_sensor4; // Bytes 207:206 + __le16 temperature_sensor5; // Bytes 209:208 + __le16 temperature_sensor6; // Bytes 211:210 + __le16 temperature_sensor7; // Bytes 213:212 + __le16 temperature_sensor8; // Bytes 215:214 + __le32 thermal_management_temperature1_transition_count; // Bytes 219:216 + __le32 thermal_management_temperature2_transition_count; // Bytes 223:220 + __le32 total_time_for_thermal_management_temperature1; // Bytes 227:224 + __le32 total_time_for_thermal_management_temperature2; // Bytes 231:228 + __u8 reserved2[280]; // Bytes 511:232 +}nvme_ocp_telemetry_smart_t, *pnvme_ocp_telemetry_smart; + +typedef struct __packed nvme_ocp_telemetry_smart_extended +{ + __u8 physical_media_units_written[16]; // Bytes 15:0 + __u8 physical_media_units_read[16]; // Bytes 31:16 + __u8 bad_user_nand_blocks_raw_count[6]; // Bytes 37:32 + __le16 bad_user_nand_blocks_normalized_value; // Bytes 39:38 + __u8 bad_system_nand_blocks_raw_count[6]; // Bytes 45:40 + __le16 bad_system_nand_blocks_normalized_value; // Bytes 47:46 + __le64 xor_recovery_count; // Bytes 55:48 + __le64 uncorrectable_read_error_count; // Bytes 63:56 + __le64 soft_ecc_error_count; // Bytes 71:64 + __le32 end_to_end_correction_counts_detected_errors; // Bytes 75:72 + __le32 end_to_end_correction_counts_corrected_errors; // Bytes 79:76 + __u8 system_data_percent_used; // Byte 80 + __u8 refresh_counts[7]; // Bytes 87:81 + __le32 max_user_data_erase_count; // Bytes 91:88 + __le32 min_user_data_erase_count; // Bytes 95:92 + __u8 num_thermal_throttling_events; // Bytes 96 + __u8 current_throttling_status; // Bytes 97 + __u8 errata_version_field; // Byte 98 + __le16 point_version_field; // Byte 100:99 + __le16 minor_version_field; // Byte 102:101 + __u8 major_version_field; // Byte 103 + __le64 pcie_correctable_error_count; // Bytes 111:104 + __le32 incomplete_shutdowns; // Bytes 115:112 + __le32 reserved1; // Bytes 119:116 + __u8 percent_free_blocks; // Byte 120 + __u8 reserved2[7]; // Bytes 127:121 + __le16 capacitor_health; // Bytes 129:128 + __u8 nvme_base_errata_version; // Byte 130 + __u8 nvme_command_set_errata_version; // Byte 131 + __le32 reserved3; // Bytes 135:132 + __le64 unaligned_io; // Bytes 143:136 + __le64 security_version_number; // Bytes 151:144 + __le64 total_nuse; // Bytes 159:152 + __u8 plp_start_count[16]; // Bytes 175:160 + __u8 endurance_estimate[16]; // Bytes 191:176 + __le64 pcie_link_retraining_count; // Bytes 199:192 + __le64 power_state_change_count; // Bytes 207:200 + __le64 lowest_permitted_firmware_revision; // Bytes 215:208 + __u8 reserved4[278]; // Bytes 493:216 + __le16 log_page_version; // Bytes 495:494 + __u8 log_page_guid[16]; // Bytes 511:496 +}nvme_ocp_telemetry_smart_extended_t, *pnvme_ocp_telemetry_smart_extended; + +typedef struct __packed nvme_ocp_event_fifo_data +{ + __le32 event_fifo_num; + __u8 event_fifo_da; + __le64 event_fifo_start; + __le64 event_fifo_size; +}nvme_ocp_event_fifo_data_t, *pnvme_ocp_event_fifo_data; + +typedef struct __packed nvme_ocp_telemetry_offsets +{ + __le32 data_area; + __le32 header_size; + __le32 da1_start_offset; + __le32 da1_size; + __le32 da2_start_offset; + __le32 da2_size; + __le32 da3_start_offset; + __le32 da3_size; + __le32 da4_start_offset; + __le32 da4_size; +}nvme_ocp_telemetry_offsets_t, *pnvme_ocp_telemetry_offsets; + +typedef struct __packed nvme_ocp_header_in_da1 +{ + __le16 major_version; // Bytes 1:0 + __le16 minor_version; // Bytes 3:2 + __le32 reserved1; // Bytes 7:4 + __le64 time_stamp; // Bytes 15:8 + __u8 log_page_guid[16]; // Bytes 31:16 + __u8 num_telemetry_profiles_supported; // Byte 32 + __u8 telemetry_profile_selected; // Byte 33 + __u8 reserved2[6]; // Bytes 39:34 + __le64 string_log_size; // Bytes 47:40 + __le64 reserved3; // Bytes 55:48 + __le64 firmware_revision; // Bytes 63:56 + __u8 reserved4[32]; // Bytes 95:64 + __le64 da1_statistic_start; // Bytes 103:96 + __le64 da1_statistic_size; // Bytes 111:104 + __le64 da2_statistic_start; // Bytes 119:112 + __le64 da2_statistic_size; // Bytes 127:120 + __u8 reserved5[32]; // Bytes 159:128 + __u8 event_fifo_1_da; // Byte 160 + __u8 event_fifo_2_da; // Byte 161 + __u8 event_fifo_3_da; // Byte 162 + __u8 event_fifo_4_da; // Byte 163 + __u8 event_fifo_5_da; // Byte 164 + __u8 event_fifo_6_da; // Byte 165 + __u8 event_fifo_7_da; // Byte 166 + __u8 event_fifo_8_da; // Byte 167 + __u8 event_fifo_9_da; // Byte 168 + __u8 event_fifo_10_da; // Byte 169 + __u8 event_fifo_11_da; // Byte 170 + __u8 event_fifo_12_da; // Byte 171 + __u8 event_fifo_13_da; // Byte 172 + __u8 event_fifo_14_da; // Byte 173 + __u8 event_fifo_15_da; // Byte 174 + __u8 event_fifo_16_da; // Byte 175 + __le64 event_fifo_1_start; // Bytes 183:176 + __le64 event_fifo_1_size; // Bytes 191:184 + __le64 event_fifo_2_start; // Bytes 199:192 + __le64 event_fifo_2_size; // Bytes 207:200 + __le64 event_fifo_3_start; // Bytes 215:208 + __le64 event_fifo_3_size; // Bytes 223:216 + __le64 event_fifo_4_start; // Bytes 231:224 + __le64 event_fifo_4_size; // Bytes 239:232 + __le64 event_fifo_5_start; // Bytes 247:240 + __le64 event_fifo_5_size; // Bytes 255:248 + __le64 event_fifo_6_start; // Bytes 263:256 + __le64 event_fifo_6_size; // Bytes 271:264 + __le64 event_fifo_7_start; // Bytes 279:272 + __le64 event_fifo_7_size; // Bytes 287:280 + __le64 event_fifo_8_start; // Bytes 295:288 + __le64 event_fifo_8_size; // Bytes 303:296 + __le64 event_fifo_9_start; // Bytes 311:304 + __le64 event_fifo_9_size; // Bytes 319:312 + __le64 event_fifo_10_start; // Bytes 327:320 + __le64 event_fifo_10_size; // Bytes 335:328 + __le64 event_fifo_11_start; // Bytes 343:336 + __le64 event_fifo_11_size; // Bytes 351:344 + __le64 event_fifo_12_start; // Bytes 359:352 + __le64 event_fifo_12_size; // Bytes 367:360 + __le64 event_fifo_13_start; // Bytes 375:368 + __le64 event_fifo_13_size; // Bytes 383:376 + __le64 event_fifo_14_start; // Bytes 391:384 + __le64 event_fifo_14_size; // Bytes 399:392 + __le64 event_fifo_15_start; // Bytes 407:400 + __le64 event_fifo_15_size; // Bytes 415:408 + __le64 event_fifo_16_start; // Bytes 423:416 + __le64 event_fifo_16_size; // Bytes 431:424 + __u8 reserved6[80]; // Bytes 511:432 + nvme_ocp_telemetry_smart_t smart_health_info; // Bytes 1023:512 + nvme_ocp_telemetry_smart_extended_t smart_health_info_extended; // Bytes 1535:1024 +}nvme_ocp_header_in_da1_t, *pnvme_ocp_header_in_da1; + +typedef struct __packed nvme_ocp_telemetry_statistic_descriptor +{ + __le16 statistic_id; // Bytes 1:0 + __u8 statistic_info_behaviour_type : 4; // Byte 2(3:0) + __u8 statistic_info_reserved : 4; // Byte 2(7:4) + __u8 ns_info_nsid : 7; // Bytes 3(6:0) + __u8 ns_info_ns_info_valid : 1; // Bytes 3(7) + __le16 statistic_data_size; // Bytes 5:4 + __le16 reserved; // Bytes 7:6 +}nvme_ocp_telemetry_statistic_descriptor_t, *pnvme_ocp_telemetry_statistic_descriptor; + +typedef struct __packed nvme_ocp_telemetry_event_descriptor +{ + __u8 debug_event_class_type; // Byte 0 + __le16 event_id; // Bytes 2:1 + __u8 event_data_size; // Byte 3 +}nvme_ocp_telemetry_event_descriptor_t, *pnvme_ocp_telemetry_event_descriptor; + +typedef struct __packed nvme_ocp_time_stamp_dbg_evt_class_format +{ + __u8 time_stamp[DATA_SIZE_8]; // Bytes 11:4 + __le16 vu_event_identifier; // Bytes 13:12 +}nvme_ocp_time_stamp_dbg_evt_class_format_t, *pnvme_ocp_time_stamp_dbg_evt_class_format; + +typedef struct __packed nvme_ocp_pcie_dbg_evt_class_format +{ + __u8 pCIeDebugEventData[DATA_SIZE_4]; // Bytes 7:4 + __le16 vu_event_identifier; // Bytes 9:8 +}nvme_ocp_pcie_dbg_evt_class_format_t, *pnvme_ocp_pcie_dbg_evt_class_format; + +typedef struct __packed nvme_ocp_nvme_dbg_evt_class_format +{ + __u8 nvmeDebugEventData[DATA_SIZE_8]; // Bytes 11:4 + __le16 vu_event_identifier; // Bytes 13:12 +}nvme_ocp_nvme_dbg_evt_class_format_t, *pnvme_ocp_nvme_dbg_evt_class_format; + +typedef struct __packed nvme_ocp_common_dbg_evt_class_format +{ + __le16 vu_event_identifier; // Bytes 5:4 +}nvme_ocp_common_dbg_evt_class_format_t, *pnvme_ocp_common_dbg_evt_class_format; + +typedef struct __packed nvme_ocp_media_wear_dbg_evt_class_format +{ + __u8 currentMediaWear[DATA_SIZE_12]; // Bytes 15:4 + __le16 vu_event_identifier; // Bytes 17:16 +}nvme_ocp_media_wear_dbg_evt_class_format_t, *pnvme_ocp_media_wear_dbg_evt_class_format; + +typedef struct __packed nvme_ocp_statistic_snapshot_evt_class_format +{ + nvme_ocp_telemetry_statistic_descriptor_t statisticDescriptorData; // Bytes 11:10 +}nvme_ocp_statistic_snapshot_evt_class_format_t, *pnvme_ocp_statistic_snapshot_evt_class_format; + +typedef struct __packed nvme_ocp_vendor_unique_dbg_evt_class_format +{ +}nvme_ocp_vendor_unique_dbg_evt_class_format_t, *pnvme_ocp_vendor_unique_dbg_evt_class_format; + +typedef struct __packed nvme_ocp_statistics_identifier_string_table +{ + __le16 vs_statistic_identifier; //1:0 + __u8 reserved1; //2 + __u8 ascii_id_length; //3 + __le64 ascii_id_offset; //11:4 + __le32 reserved2; //15:12 +} nvme_ocp_statistics_identifier_string_table_t, *pnvme_ocp_statistics_identifier_string_table; + +typedef struct __packed nvme_ocp_event_string_table +{ + __u8 debug_event_class; //0 + __le16 event_identifier; //2:1 + __u8 ascii_id_length; //3 + __le64 ascii_id_offset; //11:4 + __le32 reserved; //15:12 +} nvme_ocp_event_string_table_t, *pnvme_ocp_event_string_table; + +typedef struct __packed nvme_ocp_vu_event_string_table +{ + __u8 debug_event_class; //0 + __le16 vu_event_identifier; //2:1 + __u8 ascii_id_length; //3 + __le64 ascii_id_offset; //11:4 + __le32 reserved; //15:12 +} nvme_ocp_vu_event_string_table_t, *pnvme_ocp_vu_event_string_table; + +typedef struct __packed nvme_ocp_telemetry_string_header +{ + __u8 version; //0:0 + __u8 reserved1[15]; //15:1 + __u8 guid[16]; //32:16 + __le64 string_log_size; //39:32 + __u8 reserved2[24]; //63:40 + __le64 sits; //71:64 Statistics Identifier String Table Start(SITS) + __le64 sitsz; //79:72 Statistics Identifier String Table Size (SITSZ) + __le64 ests; //87:80 Event String Table Start(ESTS) + __le64 estsz; //95:88 Event String Table Size(ESTSZ) + __le64 vu_ests; //103:96 VU Event String Table Start + __le64 vu_estsz; //111:104 VU Event String Table Size + __le64 ascts; //119:112 ASCII Table start + __le64 asctsz; //127:120 ASCII Table Size + __u8 fifo1_ascii_string[16]; //143:128 + __u8 fifo2_ascii_string[16]; //159:144 + __u8 fifo3_ascii_string[16]; //175:160 + __u8 fifo4_ascii_string[16]; //191:176 + __u8 fifo5_ascii_string[16]; //207:192 + __u8 fifo6_ascii_string[16]; //223:208 + __u8 fifo7_ascii_string[16]; //239:224 + __u8 fifo8_ascii_string[16]; //255:240 + __u8 fifo9_ascii_string[16]; //271:256 + __u8 fifo10_ascii_string[16]; //287:272 + __u8 fifo11_ascii_string[16]; //303:288 + __u8 fifo12_ascii_string[16]; //319:304 + __u8 fifo13_ascii_string[16]; //335:320 + __u8 fifo14_ascii_string[16]; //351:336 + __u8 fifo15_ascii_string[16]; //367:352 + __u8 fifo16_ascii_string[16]; //383:368 + __u8 reserved3[48]; //431:384 +} nvme_ocp_telemetry_string_header_t, *pnvme_ocp_telemetry_string_header; + +typedef struct __packed statistic_entry +{ + int identifier; + char* description; +}statistic_entry_t, pstatistic_entry; + +typedef struct __packed ocp_telemetry_parse_options +{ + char *telemetry_log; + char *string_log; + char *output_file; + char *output_fmt; +}ocp_telemetry_parse_options_t, *pocp_telemetry_parse_options; + +/** + * enum ocp_telemetry_data_area - Telemetry Data Areas + * @DATA_AREA_1: Data Area 1 + * @DATA_AREA_2: Data Area 2 + * @DATA_AREA_3: Data Area 3 + * @DATA_AREA_4: Data Area 4 + */ +enum ocp_telemetry_data_area +{ + DATA_AREA_1 = 0x01, + DATA_AREA_2 = 0x02, + DATA_AREA_3 = 0x03, + DATA_AREA_4 = 0x04, +}; + +/** + * enum ocp_telemetry_string_tables - OCP telemetry string tables + * @STATISTICS_IDENTIFIER_STRING: Statistic Identifier string + * @EVENT_STRING: Event String + * @VU_EVENT_STRING: VU Event String + */ +typedef enum ocp_telemetry_string_tables +{ + STATISTICS_IDENTIFIER_STRING = 0, + EVENT_STRING, + VU_EVENT_STRING +}ocp_telemetry_string_tables_t; + +/** + * enum ocp_telemetry_debug_event_class_types - OCP Debug Event Class types + * @RESERVED_CLASS_TYPE: Reserved class + * @TIME_STAMP_CLASS_TYPE: Time stamp class + * @PCIE_CLASS_TYPE: PCIe class + * @NVME_CLASS_TYPE: NVME class + * @RESET_CLASS_TYPE: Reset class + * @BOOT_SEQUENCE_CLASS_TYPE: Boot Sequence class + * @FIRMWARE_ASSERT_CLASS_TYPE: Firmware Assert class + * @TEMPERATURE_CLASS_TYPE: Temperature class + * @MEDIA_CLASS_TYPE: Media class + * @MEDIA_WEAR_CLASS_TYPE: Media wear class + * @STATISTIC_SNAPSHOT_CLASS_TYPE: Statistic snapshot class + * @RESERVED: Reserved class + * @VENDOR_UNIQUE_CLASS_TYPE: Vendor Unique class + */ +enum ocp_telemetry_debug_event_class_types +{ + RESERVED_CLASS_TYPE = 0x00, + TIME_STAMP_CLASS_TYPE = 0x01, + PCIE_CLASS_TYPE = 0x02, + NVME_CLASS_TYPE = 0x03, + RESET_CLASS_TYPE = 0x04, + BOOT_SEQUENCE_CLASS_TYPE = 0x05, + FIRMWARE_ASSERT_CLASS_TYPE = 0x06, + TEMPERATURE_CLASS_TYPE = 0x07, + MEDIA_CLASS_TYPE = 0x08, + MEDIA_WEAR_CLASS_TYPE = 0x09, + STATISTIC_SNAPSHOT_CLASS_TYPE = 0x0A, + //RESERVED = 7Fh-0Bh, + //VENDOR_UNIQUE_CLASS_TYPE = FFh-80h, +}; + +/** + * @brief parse the ocp telemetry host or controller log binary file + * into json or text + * + * @param options, input pointer for inputs like telemetry log bin file, + * string log bin file and output file etc. + * + * @return 0 success + */ +int parse_ocp_telemetry_log(pocp_telemetry_parse_options options); + +/** + * @brief parse the ocp telemetry string log binary file to json or text + * + * @param event_fifo_num, input event FIFO number + * @param debug_event_class, input debug event class id + * @param string_table, input string table + * @param description, input description string + * + * @return 0 success + */ +int parse_ocp_telemetry_string_log(int event_fifo_num, int identifier, int debug_event_class, ocp_telemetry_string_tables_t string_table, char *description); + +/** + * @brief gets the telemetry datas areas, offsets and sizes information + * + * @param ptelemetry_common_header, input telemetry common header pointer + * @param ptelemetry_das_offset, input telemetry offsets pointer + * + * @return 0 success + */ +int get_telemetry_das_offset_and_size(pnvme_ocp_telemetry_common_header ptelemetry_common_header, pnvme_ocp_telemetry_offsets ptelemetry_das_offset); + +/** + * @brief parses statistics data to text or json formats + * + * @param root, input time json root object pointer + * @param ptelemetry_das_offset, input telemetry offsets pointer + * @param fp, input file pointer + * + * @return 0 success + */ +int parse_statistics(struct json_object *root, pnvme_ocp_telemetry_offsets pOffsets, FILE *fp); + +/** + * @brief parses a single statistic data to text or json formats + * + * @param pstatistic_entry, statistic entry pointer + * @param pstats_array, stats array pointer pointer + * @param fp, input file pointer + * + * @return 0 success + */ +int parse_statistic(pnvme_ocp_telemetry_statistic_descriptor pstatistic_entry, struct json_object *pstats_array, FILE *fp); + +/** + * @brief parses event fifos data to text or json formats + * + * @param root, input time json root object pointer + * @param poffsets, input telemetry offsets pointer + * @param fp, input file pointer + * + * @return 0 success + */ +int parse_event_fifos(struct json_object *root, pnvme_ocp_telemetry_offsets poffsets, FILE *fp); + +/** + * @brief parses a single event fifo data to text or json formats + * + * @param fifo_num, input event fifo number + * @param pfifo_start, event fifo start pointer + * @param pevent_fifos_object, event fifos json object pointer + * @param ptelemetry_das_offset, input telemetry offsets pointer + * @param fifo_size, input event fifo size + * @param fp, input file pointer + * + * @return 0 success + */ +int parse_event_fifo(unsigned int fifo_num, unsigned char *pfifo_start, struct json_object *pevent_fifos_object, unsigned char *pstring_buffer, pnvme_ocp_telemetry_offsets poffsets, __u64 fifo_size, FILE *fp); + +/** + * @brief parses event fifos data to text or json formats + * + * @return 0 success + */ +int print_ocp_telemetry_normal(char *output_file); + +/** + * @brief parses event fifos data to text or json formats + * + * @return 0 success + */ +int print_ocp_telemetry_json(char *output_file); diff --git a/plugins/micron/micron-utils.c b/plugins/micron/micron-utils.c new file mode 100644 index 0000000000..c179affa4c --- /dev/null +++ b/plugins/micron/micron-utils.c @@ -0,0 +1,276 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (c) Micron, Inc 2024. + * + * @file: micron-utils.h + * @brief: This module contains all the utilities needed for micron nvme plugin and other micron modules. + * @author: Chaithanya Shoba + */ + +#include "micron-utils.h" + +int hex_to_int(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'A' && c <= 'F') + return 10 + (c - 'A'); + else if (c >= 'a' && c <= 'f') + return 10 + (c - 'a'); + else + return -1; // Invalid character +} + +char* hex_to_ascii(const char* hex) +{ + int hexLength = strlen(hex); + char* text = NULL; + + if (hexLength > 0) { + int symbolCount; + int oddHexCount = hexLength % 2 == 1; + + if (oddHexCount) + symbolCount = (hexLength / 2) + 1; + else + symbolCount = hexLength / 2; + + text = (char*)malloc(symbolCount + 1); // Allocate memory for the result + + int lastIndex = hexLength - 1; + for (int i = lastIndex; i >= 0; --i) { + if ((lastIndex - i) % 2 != 0) { + int dec = 16 * hex_to_int(hex[i]) + hex_to_int(hex[i + 1]); + if (oddHexCount) + text[i / 2 + 1] = dec; + else + text[i / 2] = dec; + } else if (i == 0) { + int dec = hex_to_int(hex[0]); + text[0] = dec; + } + } + + text[symbolCount] = '\0'; // Terminate the string + } + + return text; +} + +unsigned char *read_binary_file(char *data_dir_path, const char *bin_path, long *buffer_size, int retry_count) +{ + char *file_path = NULL; + FILE *bin_file = NULL; + size_t n_data = 0; + unsigned char *buffer = NULL; + + /* set path */ + if (data_dir_path == NULL) + { + file_path = (char *)bin_path; + } + else + { + /* +2 for the / and null terminator */ + file_path = (char *) calloc(1, strlen(data_dir_path) + strlen(bin_path) + 2); + if (!file_path) + { + return NULL; + } + if (strlen(bin_path) != 0) + { + sprintf(file_path, "%s/%s", data_dir_path, bin_path); + } + else + { + sprintf(file_path, "%s", data_dir_path); + } + } + + /* open file */ + for (int i = 0; i < retry_count; i++) + { + if ((bin_file = fopen(file_path, "rb")) != NULL) + { + break; + } + sleep((unsigned int)(retry_count > 1)); + } + + if (!bin_file) + { + nvme_show_error("\nFailed to open %s", file_path); + if (file_path != bin_path) + { + free(file_path); + } + goto ret_null; + } + + /* get size */ + fseek(bin_file, 0, SEEK_END); + *buffer_size = ftell(bin_file); + fseek(bin_file, 0, SEEK_SET); + if (*buffer_size <= 0) { + fclose(bin_file); + return NULL; + } + + /* allocate buffer */ + buffer = (unsigned char *)malloc(*buffer_size); + if (!buffer) + { + nvme_show_result("\nFailed to allocate %ld bytes!", *buffer_size); + fclose(bin_file); + goto ret_null; + } + memset(buffer, 0, *buffer_size); + + /* Read data */ + n_data = fread(buffer, 1, *buffer_size, bin_file); + + /* Close file */ + fclose(bin_file); + + /* Validate we read data */ + if (n_data != (size_t)*buffer_size) + { + nvme_show_result("\nFailed to read %ld bytes from %s", *buffer_size, file_path); + goto ret_null; + } + + if (file_path != bin_path) + { + free(file_path); + } + return(buffer); +ret_null: + return(NULL); +} + +void print_formatted_var_size_str(const char *msg, const __u8 *pdata, size_t data_size, FILE *fp) +{ + char description_str[256] = ""; + char temp_buffer[3] = { 0 }; + + for (size_t i = 0; i < data_size; ++i) { + sprintf(temp_buffer, "%02X", pdata[i]); + strcat(description_str, temp_buffer); + } + + if (fp) { + fprintf(fp, "%s: %s\n", msg, description_str); + } else { + printf("%s: %s\n", msg, description_str); + } +} + + +void print_micron_vs_logs(__u8 *buf, struct micron_vs_logpage *log_page, int field_count, + struct json_object *stats, __u8 spec, FILE *fp) +{ + __u64 lval_lo, lval_hi; + __u32 ival; + __u16 sval; + __u8 cval, lval[8] = { 0 }; + int field; + int offset = 0; + + for (field = 0; field < field_count; field++) { + char datastr[1024] = { 0 }; + char *sfield = NULL; + int size = !spec ? log_page[field].size : log_page[field].size2; + + if (!size) + continue; + sfield = log_page[field].field; + if (size == 16) { + if (strstr(sfield, "GUID")) { + sprintf(datastr, "0x%"PRIx64"%"PRIx64"", + (uint64_t)le64_to_cpu(*(uint64_t *)(&buf[offset + 8])), + (uint64_t)le64_to_cpu(*(uint64_t *)(&buf[offset]))); + } else { + lval_lo = *((__u64 *)(&buf[offset])); + lval_hi = *((__u64 *)(&buf[offset + 8])); + if (lval_hi) + sprintf(datastr, "0x%"PRIx64"%016"PRIx64"", + le64_to_cpu(lval_hi), le64_to_cpu(lval_lo)); + else + sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); + } + } else if (size == 8) { + if (strstr(sfield, "Boot SSD Spec Version")) { + sprintf(datastr, "%x.%x.%x.%x", + le16_to_cpu(*((__u16 *)(&buf[300]))), + le16_to_cpu(*((__u16 *)(&buf[302]))), + le16_to_cpu(*((__u16 *)(&buf[304]))), + le16_to_cpu(*((__u16 *)(&buf[306])))); + } else if (strstr(sfield, "Firmware Revision")){ + char buffer[30] = {'\0'}; + lval_lo = *((__u64 *)(&buf[offset])); + sprintf(buffer, "%lx", __builtin_bswap64(lval_lo)); + sprintf(datastr, "%s", hex_to_ascii(buffer)); + } else if (strstr(sfield, "Timestamp")){ + lval_lo = *((__u64 *)(&buf[offset])); + char ts_buf[128]; + convert_ts(le64_to_cpu(lval_lo), ts_buf); + sprintf(datastr, "%s", ts_buf); + } else { + lval_lo = *((__u64 *)(&buf[offset])); + sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); + } + } else if (size == 7) { + /* 7 bytes will be in little-endian format, with last byte as MSB */ + memcpy(&lval[0], &buf[offset], 7); + memcpy((void *)&lval_lo, lval, 8); + sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); + } else if (size == 6) { + if (strstr(sfield, "DSSD Spec Version")) { + sprintf(datastr, "%x.%x.%x.%x", + buf[103], + le16_to_cpu(*((__u16 *)(&buf[101]))), + le16_to_cpu(*((__u16 *)(&buf[99]))), + buf[98]); + } else { + ival = *((__u32 *)(&buf[offset])); + sval = *((__u16 *)(&buf[offset + 4])); + lval_lo = (((__u64)sval << 32) | ival); + sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); + } + } else if (size == 4) { + ival = *((__u32 *)(&buf[offset])); + sprintf(datastr, "0x%x", le32_to_cpu(ival)); + } else if (size == 3) { + sprintf(datastr, "0x%02X%02X%02X", buf[offset + 0], buf[offset + 1], buf[offset + 2]); + } else if (size == 2) { + sval = *((__u16 *)(&buf[offset])); + sprintf(datastr, "0x%04x", le16_to_cpu(sval)); + } else if (size == 1) { + cval = buf[offset]; + sprintf(datastr, "0x%02x", cval); + } + else { + char description_str[256] = "0x"; + char temp_buffer[3] = { 0 }; + + for (unsigned char i = 0; i < (unsigned char)size; i++) + { + cval = (buf[offset + i]); + sprintf(temp_buffer, "%02X", cval); + strcat(description_str, temp_buffer); + } + sprintf(datastr, "%s", description_str); + } + offset += size; + /* do not print reserved values */ + if (strstr(sfield, "Reserved")) + continue; + if (stats) { + json_object_add_value_string(stats, sfield, datastr); + } + else if(fp){ + fprintf(fp, "%-40s : %-4s\n", sfield, datastr); + } + else + printf("%-40s : %-4s\n", sfield, datastr); + } +} \ No newline at end of file diff --git a/plugins/micron/micron-utils.h b/plugins/micron/micron-utils.h new file mode 100644 index 0000000000..f0a2475af7 --- /dev/null +++ b/plugins/micron/micron-utils.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (c) Micron, Inc 2024. + * + * @file: micron-utils.h + * @brief: This module contains all the utilities needed for micron nvme plugin and other micron modules. + * @author: Chaithanya Shoba + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common.h" +#include "nvme.h" +#include "libnvme.h" +#include +#include "linux/types.h" +#include "nvme-print.h" +#include "util/cleanup.h" + +#ifdef __packed +#else /* __packed */ +#define __packed __attribute__((__packed__)) +#endif /* __packed */ + +/* OCP and Vendor specific log data format */ +typedef struct __packed micron_vs_logpage { + char *field; + int size; /* FB client spec version 1.0 sizes - M5410 models */ + int size2; /* FB client spec version 0.7 sizes - M5407 models */ +}micron_vs_logpage_t, *pmicron_vs_logpage; + +/** + * @brief converts a single hexadecimal character to its integer value. + * + * @param hex_char, input hex char + * @param ts_buf, output time string + * + * @return integer value of hexadecimal + */ +int hex_to_int(char c); + +/** + * @brief convert time_t format time to a human readable string + * + * @param hex_string, input hex string pointer + * @param ascii_buffer, output ascii buffer pointer + * + * @return nothing + */ +char* hex_to_ascii(const char* hex); + +/** + * @brief convert time_t format time to a human readable string + * + * @param data_dir_path, input data directory path pointer + * @param bin_path, input binary file path pointer + * @param buffer_size, input buffer size pointer + * @param retry_count, input retry count + * + * @return pointer to binary data buffer + */ +unsigned char *read_binary_file(char *data_dir_path, const char *bin_path, long *buffer_size, int retry_count); + +/** + * @brief prints Micron VS log pages + * + * @param buf, input raw log data + * @param log_page, input format of the data + * @param field_count, intput log field count + * @param stats, input json object to add fields + * @param spec, input ocp spec index + * @param fp, input file pointer + * + * @return 0 success + */ +void print_micron_vs_logs(__u8 *buf, struct micron_vs_logpage *log_page, int field_count, + struct json_object *stats, __u8 spec, FILE *fp); + + +/** + * @brief prints raw data to the buffer + * + * @param msg, intput buffer to write data + * @param pdata, input raw data + * @param data_size, input size of the data + * @param fp, input file pointer + * + * @return 0 success + */ +void print_formatted_var_size_str(const char *msg, const __u8 *pdata, size_t data_size, FILE *fp); \ No newline at end of file