// SPDX-License-Identifier: GPL-2.0-or-later /* Copyright (c) 2024 Western Digital Corporation or its affiliates. * * Authors: Jeff Lien , */ #include "common.h" #include "nvme.h" #include "libnvme.h" #include "plugin.h" #include "linux/types.h" #include "util/types.h" #include "nvme-print.h" #include "ocp-telemetry-decode.h" void print_vu_event_data(__u32 size, __u8 *data) { int j; __u16 vu_event_id = *(__u16 *)data; printf(" VU Event ID : 0x%02x\n", le16_to_cpu(vu_event_id)); printf(" VU Data : 0x"); for (j = 2; j < size; j++) printf("%x", data[j]); printf("\n\n"); } void print_stats_desc(struct telemetry_stats_desc *stat_desc) { int j; /* Get the statistics Identifier string name and data size */ __u16 stat_id = stat_desc->id; __u32 stat_data_sz = ((stat_desc->size) * 4); printf("Statistics Identifier : 0x%x, %s\n", stat_id, telemetry_stat_id_to_string(stat_id)); printf("Statistics info : 0x%x\n", stat_desc->info); printf("NS info : 0x%x\n", stat_desc->ns_info); printf("Statistic Data Size : 0x%x\n", le16_to_cpu(stat_data_sz)); if (stat_data_sz > 0) { printf("%s : 0x", telemetry_stat_id_to_string(stat_id)); for (j = 0; j < stat_data_sz; j++) printf("%02x", stat_desc->data[j]); printf("\n"); } printf("\n"); } void print_telemetry_fifo_event(__u8 class_type, __u16 id, __u8 size_dw, __u8 *data) { int j; const char *class_str = NULL; __u32 size = size_dw * 4; char time_str[40]; uint64_t timestamp = 0; memset((void *)time_str, '\0', 40); if (class_type) { class_str = telemetry_event_class_to_string(class_type); printf("Event Class : %s\n", class_str); printf(" Size : 0x%02x\n", size); } switch (class_type) { case TELEMETRY_TIMESTAMP_CLASS: timestamp = (0x0000FFFFFFFFFFFF & le64_to_cpu(*(uint64_t *)data)); memset((void *)time_str, 0, 9); sprintf((char *)time_str, "%04d:%02d:%02d", (int)(le64_to_cpu(timestamp)/3600), (int)((le64_to_cpu(timestamp%3600)/60)), (int)(le64_to_cpu(timestamp%60))); printf(" Event ID : 0x%04x %s\n", id, telemetry_ts_event_to_string(id)); printf(" Timestamp : %s\n", time_str); if (size > 8) { printf(" VU Data : 0x"); for (j = 8; j < size; j++) printf("%02x", data[j]); printf("\n\n"); } break; case TELEMETRY_PCIE_CLASS: printf(" Event ID : 0x%04x %s\n", id, telemetry_pcie_event_id_to_string(id)); printf(" State : 0x%02x %s\n", data[0], telemetry_pcie_state_data_to_string(data[0])); printf(" Speed : 0x%02x %s\n", data[1], telemetry_pcie_speed_data_to_string(data[1])); printf(" Width : 0x%02x %s\n", data[2], telemetry_pcie_width_data_to_string(data[2])); if (size > 4) { printf(" VU Data : "); for (j = 4; j < size; j++) printf("%x", data[j]); printf("\n\n"); } break; case TELEMETRY_NVME_CLASS: printf(" Event ID : 0x%04x %s\n", id, telemetry_nvme_event_id_to_string(id)); if ((id == ADMIN_QUEUE_NONZERO_STATUS) || (id == IO_QUEUE_NONZERO_STATUS)) { printf(" Cmd Op Code : 0x%02x\n", data[0]); __u16 status = *(__u16 *)&data[1]; __u16 cmd_id = *(__u16 *)&data[3]; __u16 sq_id = *(__u16 *)&data[5]; printf(" Status Code : 0x%04x\n", le16_to_cpu(status)); printf(" Cmd ID : 0x%04x\n", le16_to_cpu(cmd_id)); printf(" SQ ID : 0x%04x\n", le16_to_cpu(sq_id)); } else if (id == CC_REGISTER_CHANGED) { __u32 cc_reg_data = *(__u32 *)data; printf(" CC Reg Data : 0x%08x\n", le32_to_cpu(cc_reg_data)); } else if (id == CSTS_REGISTER_CHANGED) { __u32 csts_reg_data = *(__u32 *)data; printf(" CSTS Reg Data : 0x%08x\n", le32_to_cpu(csts_reg_data)); } if (size > 8) print_vu_event_data((size-8), (__u8 *)&data[8]); break; case TELEMETRY_RESET_CLASS: printf(" Event ID : 0x%04x %s\n", id, telemetry_reset_event_id_to_string(id)); if (size) print_vu_event_data(size, data); break; case TELEMETRY_BOOT_SEQ_CLASS: printf(" Event ID : 0x%04x %s\n", id, telemetry_boot_seq_event_id_to_string(id)); if (size) print_vu_event_data(size, data); break; case TELEMETRY_FW_ASSERT_CLASS: printf(" Event ID : 0x%04x %s\n", id, telemetry_fw_assert_event_id_to_string(id)); if (size) print_vu_event_data(size, data); break; case TELEMETRY_TEMPERATURE_CLASS: printf(" Event ID : 0x%04x %s\n", id, telemetry_temperature_event_id_to_string(id)); if (size) print_vu_event_data(size, data); break; case TELEMETRY_MEDIA_DBG_CLASS: printf(" Event ID : 0x%04x %s\n", id, telemetry_media_debug_event_id_to_string(id)); if (size) print_vu_event_data(size, data); break; case TELEMETRY_MEDIA_WEAR_CLASS: printf(" Event ID : 0x%04x %s\n", id, telemetry_media_debug_event_id_to_string(id)); __u32 host_tb_written = *(__u32 *)&data[0]; __u32 media_tb_written = *(__u32 *)&data[4]; __u32 media_tb_erased = *(__u32 *)&data[8]; printf(" Host TB Written : 0x%04x\n", le16_to_cpu(host_tb_written)); printf(" Media TB Written : 0x%04x\n", le16_to_cpu(media_tb_written)); printf(" Media TB Erased : 0x%04x\n", le16_to_cpu(media_tb_erased)); if (size > 12) print_vu_event_data((size-12), (__u8 *)&data[12]); break; case TELEMETRY_STAT_SNAPSHOT_CLASS: printf(" Statistic ID : 0x%02x %s\n", id, telemetry_stat_id_to_string(id)); print_stats_desc((struct telemetry_stats_desc *)data); break; default: /* * printf("Unknown Event Class Type\n"); * printf("Data : 0x"); * for (j = 0; j < size; j++) * printf("%x", data[j]); * printf("\n\n"); */ break; } } struct statistic_entry 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"} }; struct request_data host_log_page_header[] = { { "LogIdentifier", 1 }, { "Reserved1", 4 }, { "IEEE OUI Identifier", 3 }, { "Telemetry Host-Initiated Data Area 1 Last Block", 2 }, { "Telemetry Host-Initiated Data Area 2 Last Block", 2 }, { "Telemetry Host-Initiated Data Area 3 Last Block", 2 }, { "Reserved2", 2 }, { "Telemetry Host-Initiated Data Area 4 Last Block", 4 }, { "Reserved3", 360 }, { "Telemetry Host-Initiated Scope", 1 }, { "Telemetry Host Initiated Generation Number", 1 }, { "Telemetry Host-Initiated Data Available", 1 }, { "Telemetry Controller-Initiated Data Generation Number", 1 } }; struct request_data controller_log_page_header[] = { { "LogIdentifier", 1 }, { "Reserved1", 4 }, { "IEEE OUI Identifier", 3 }, { "Telemetry Host-Initiated Data Area 1 Last Block", 2 }, { "Telemetry Host-Initiated Data Area 2 Last Block", 2 }, { "Telemetry Host-Initiated Data Area 3 Last Block", 2 }, { "Reserved2", 2 }, { "Telemetry Host-Initiated Data Area 4 Last Block", 4 }, { "Reserved3", 361 }, { "Telemetry Controller-Initiated Scope", 1 }, { "Telemetry Controller-Initiated Data Available", 1 }, { "Telemetry Controller-Initiated Data Generation Number", 1 } }; struct request_data reason_identifier[] = { { "Error ID", 64 }, { "File ID", 8 }, { "Line Number", 2 }, { "Valid Flags", 1 }, { "Reserved", 21 }, { "VU Reason Extension", 32 } }; struct request_data ocp_header_in_da1[] = { { "Major Version", 2 }, { "Minor Version", 2 }, { "Reserved1", 4 }, { "Timestamp", 8 }, { "Log page GUID", GUID_LEN }, { "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 } }; struct request_data 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 } }; struct request_data 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", GUID_LEN } }; #ifdef CONFIG_JSONC void json_add_formatted_u32_str(struct json_object *pobject, const char *msg, unsigned int pdata) { char data_str[70] = { 0 }; sprintf(data_str, "0x%x", pdata); json_object_add_value_string(pobject, msg, data_str); } void json_add_formatted_var_size_str(struct json_object *pobject, const char *msg, __u8 *pdata, unsigned int 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_object_add_value_string(pobject, msg, description_str); } #endif /* CONFIG_JSONC */ int get_telemetry_das_offset_and_size( struct nvme_ocp_telemetry_common_header *ptelemetry_common_header, struct nvme_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_LOG_LID_TELEMETRY_HOST) ptelemetry_das_offset->header_size = sizeof(struct nvme_ocp_telemetry_host_initiated_header); else if (ptelemetry_common_header->log_id == NVME_LOG_LID_TELEMETRY_CTRL) ptelemetry_das_offset->header_size = sizeof(struct nvme_ocp_telemetry_controller_initiated_header); 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 get_static_id_ascii_string(int identifier, char *description) { if (pstring_buffer == NULL) return -1; struct nvme_ocp_telemetry_string_header *pocp_ts_header = (struct nvme_ocp_telemetry_string_header *)pstring_buffer; //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; //Calculating number of entries present in all 3 tables int sits_entries = (int)sits_table_size / sizeof(struct nvme_ocp_statistics_identifier_string_table); for (int sits_entry = 0; sits_entry < sits_entries; sits_entry++) { struct nvme_ocp_statistics_identifier_string_table *peach_statistic_entry = (struct nvme_ocp_statistics_identifier_string_table *) (pstring_buffer + (pocp_ts_header->sits * SIZE_OF_DWORD) + (sits_entry * sizeof(struct nvme_ocp_statistics_identifier_string_table))); 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; } } return -1; } int get_event_id_ascii_string(int identifier, int debug_event_class, char *description) { if (pstring_buffer == NULL) return -1; struct nvme_ocp_telemetry_string_header *pocp_ts_header = (struct nvme_ocp_telemetry_string_header *)pstring_buffer; //Calculating the sizes of the tables. Note: Data is present in the form of DWORDS, //So multiplying with sizeof(DWORD) unsigned long long ests_table_size = (pocp_ts_header->estsz) * SIZE_OF_DWORD; //Calculating number of entries present in all 3 tables int ests_entries = (int)ests_table_size / sizeof(struct nvme_ocp_event_string_table); for (int ests_entry = 0; ests_entry < ests_entries; ests_entry++) { struct nvme_ocp_event_string_table *peach_event_entry = (struct nvme_ocp_event_string_table *) (pstring_buffer + (pocp_ts_header->ests * SIZE_OF_DWORD) + (ests_entry * sizeof(struct nvme_ocp_event_string_table))); 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; } } return -1; } int get_vu_event_id_ascii_string(int identifier, int debug_event_class, char *description) { if (pstring_buffer == NULL) return -1; struct nvme_ocp_telemetry_string_header *pocp_ts_header = (struct nvme_ocp_telemetry_string_header *)pstring_buffer; //Calculating the sizes of the tables. Note: Data is present in the form of DWORDS, //So multiplying with sizeof(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 vu_ests_entries = (int)vuests_table_size / sizeof(struct nvme_ocp_vu_event_string_table); for (int vu_ests_entry = 0; vu_ests_entry < vu_ests_entries; vu_ests_entry++) { struct nvme_ocp_vu_event_string_table *peach_vu_event_entry = (struct nvme_ocp_vu_event_string_table *) (pstring_buffer + (pocp_ts_header->vu_ests * SIZE_OF_DWORD) + (vu_ests_entry * sizeof(struct nvme_ocp_vu_event_string_table))); 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 -1; } int parse_ocp_telemetry_string_log(int event_fifo_num, int identifier, int debug_event_class, enum ocp_telemetry_string_tables string_table, char *description) { if (pstring_buffer == NULL) return -1; if (event_fifo_num != 0) { struct nvme_ocp_telemetry_string_header *pocp_ts_header = (struct nvme_ocp_telemetry_string_header *)pstring_buffer; if (*pocp_ts_header->fifo_ascii_string[event_fifo_num-1] != '\0') memcpy(description, pocp_ts_header->fifo_ascii_string[event_fifo_num-1], 16); else description = ""; return 0; } if (string_table == STATISTICS_IDENTIFIER_STRING) get_static_id_ascii_string(identifier, description); else if (string_table == EVENT_STRING) get_event_id_ascii_string(identifier, debug_event_class, description); else if (string_table == VU_EVENT_STRING) get_vu_event_id_ascii_string(identifier, debug_event_class, description); return 0; } #ifdef CONFIG_JSONC void parse_time_stamp_event(struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, struct json_object *pevent_descriptor_obj, __u8 *pevent_specific_data, struct json_object *pevent_fifos_object, FILE *fp) { struct nvme_ocp_time_stamp_dbg_evt_class_format *ptime_stamp_event = (struct nvme_ocp_time_stamp_dbg_evt_class_format *) pevent_specific_data; struct nvme_ocp_common_dbg_evt_class_vu_data *ptime_stamp_event_vu_data = NULL; __u16 vu_event_id = 0; __u8 *pdata = NULL; char description_str[256] = ""; unsigned int vu_data_size = 0; bool vu_data_present = false; if ((pevent_descriptor->event_data_size * SIZE_OF_DWORD) > sizeof(struct nvme_ocp_time_stamp_dbg_evt_class_format)) { vu_data_present = true; vu_data_size = ((pevent_descriptor->event_data_size * SIZE_OF_DWORD) - (sizeof(struct nvme_ocp_time_stamp_dbg_evt_class_format) + SIZE_OF_VU_EVENT_ID)); ptime_stamp_event_vu_data = (struct nvme_ocp_common_dbg_evt_class_vu_data *)((__u64)ptime_stamp_event + sizeof(struct nvme_ocp_time_stamp_dbg_evt_class_format)); vu_event_id = le16_to_cpu(ptime_stamp_event_vu_data->vu_event_identifier); pdata = (__u8 *)&(ptime_stamp_event_vu_data->data); parse_ocp_telemetry_string_log(0, vu_event_id, 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); if (vu_data_present) { json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING, vu_event_id); json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING, description_str); json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA, pdata, vu_data_size); } } else { if (fp) { print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, ptime_stamp_event->time_stamp, DATA_SIZE_8, fp); if (vu_data_present) { 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, vu_data_size, fp); } } else { print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, ptime_stamp_event->time_stamp, DATA_SIZE_8, fp); if (vu_data_present) { 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, vu_data_size, fp); } } } } void parse_pcie_event(struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, struct json_object *pevent_descriptor_obj, __u8 *pevent_specific_data, struct json_object *pevent_fifos_object, FILE *fp) { struct nvme_ocp_pcie_dbg_evt_class_format *ppcie_event = (struct nvme_ocp_pcie_dbg_evt_class_format *) pevent_specific_data; struct nvme_ocp_common_dbg_evt_class_vu_data *ppcie_event_vu_data = NULL; __u16 vu_event_id = 0; __u8 *pdata = NULL; char description_str[256] = ""; unsigned int vu_data_size = 0; bool vu_data_present = false; if ((pevent_descriptor->event_data_size * SIZE_OF_DWORD) > sizeof(struct nvme_ocp_pcie_dbg_evt_class_format)) { vu_data_present = true; vu_data_size = ((pevent_descriptor->event_data_size * SIZE_OF_DWORD) - (sizeof(struct nvme_ocp_pcie_dbg_evt_class_format) + SIZE_OF_VU_EVENT_ID)); ppcie_event_vu_data = (struct nvme_ocp_common_dbg_evt_class_vu_data *)((__u64)ppcie_event + sizeof(struct nvme_ocp_pcie_dbg_evt_class_format)); vu_event_id = le16_to_cpu(ppcie_event_vu_data->vu_event_identifier); pdata = (__u8 *)&(ppcie_event_vu_data->data); parse_ocp_telemetry_string_log(0, vu_event_id, 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); if (vu_data_present) { json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING, vu_event_id); json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING, description_str); json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA, pdata, vu_data_size); } } else { if (fp) { print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, ppcie_event->pCIeDebugEventData, DATA_SIZE_4, fp); if (vu_data_present) { 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, vu_data_size, fp); } } else { print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, ppcie_event->pCIeDebugEventData, DATA_SIZE_4, fp); if (vu_data_present) { 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, vu_data_size, fp); } } } } void parse_nvme_event(struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, struct json_object *pevent_descriptor_obj, __u8 *pevent_specific_data, struct json_object *pevent_fifos_object, FILE *fp) { struct nvme_ocp_nvme_dbg_evt_class_format *pnvme_event = (struct nvme_ocp_nvme_dbg_evt_class_format *) pevent_specific_data; struct nvme_ocp_common_dbg_evt_class_vu_data *pnvme_event_vu_data = NULL; __u16 vu_event_id = 0; __u8 *pdata = NULL; char description_str[256] = ""; unsigned int vu_data_size = 0; bool vu_data_present = false; if ((pevent_descriptor->event_data_size * SIZE_OF_DWORD) > sizeof(struct nvme_ocp_nvme_dbg_evt_class_format)) { vu_data_present = true; vu_data_size = ((pevent_descriptor->event_data_size * SIZE_OF_DWORD) - (sizeof(struct nvme_ocp_nvme_dbg_evt_class_format) + SIZE_OF_VU_EVENT_ID)); pnvme_event_vu_data = (struct nvme_ocp_common_dbg_evt_class_vu_data *)((__u64)pnvme_event + sizeof(struct nvme_ocp_nvme_dbg_evt_class_format)); vu_event_id = le16_to_cpu(pnvme_event_vu_data->vu_event_identifier); pdata = (__u8 *)&(pnvme_event_vu_data->data); parse_ocp_telemetry_string_log(0, vu_event_id, 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); if (vu_data_present) { json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING, vu_event_id); json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING, description_str); json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA, pdata, vu_data_size); } } else { if (fp) { print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, pnvme_event->nvmeDebugEventData, DATA_SIZE_8, fp); if (vu_data_present) { 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, vu_data_size, fp); } } else { print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, pnvme_event->nvmeDebugEventData, DATA_SIZE_8, fp); if (vu_data_present) { 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, vu_data_size, fp); } } } } void parse_common_event(struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, struct json_object *pevent_descriptor_obj, __u8 *pevent_specific_data, struct json_object *pevent_fifos_object, FILE *fp) { if (pevent_specific_data) { struct nvme_ocp_common_dbg_evt_class_vu_data *pcommon_debug_event_vu_data = (struct nvme_ocp_common_dbg_evt_class_vu_data *) pevent_specific_data; __u16 vu_event_id = le16_to_cpu(pcommon_debug_event_vu_data->vu_event_identifier); char description_str[256] = ""; __u8 *pdata = (__u8 *)&(pcommon_debug_event_vu_data->data); unsigned int vu_data_size = ((pevent_descriptor->event_data_size * SIZE_OF_DWORD) - SIZE_OF_VU_EVENT_ID); parse_ocp_telemetry_string_log(0, vu_event_id, pevent_descriptor->debug_event_class_type, VU_EVENT_STRING, description_str); if (pevent_fifos_object != NULL) { json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING, vu_event_id); json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING, description_str); json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA, pdata, vu_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, vu_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, vu_data_size, fp); } } } } void parse_media_wear_event(struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, struct json_object *pevent_descriptor_obj, __u8 *pevent_specific_data, struct json_object *pevent_fifos_object, FILE *fp) { struct nvme_ocp_media_wear_dbg_evt_class_format *pmedia_wear_event = (struct nvme_ocp_media_wear_dbg_evt_class_format *) pevent_specific_data; struct nvme_ocp_common_dbg_evt_class_vu_data *pmedia_wear_event_vu_data = NULL; __u16 vu_event_id = 0; __u8 *pdata = NULL; char description_str[256] = ""; unsigned int vu_data_size = 0; bool vu_data_present = false; if ((pevent_descriptor->event_data_size * SIZE_OF_DWORD) > sizeof(struct nvme_ocp_media_wear_dbg_evt_class_format)) { vu_data_present = true; vu_data_size = ((pevent_descriptor->event_data_size * SIZE_OF_DWORD) - (sizeof(struct nvme_ocp_media_wear_dbg_evt_class_format) + SIZE_OF_VU_EVENT_ID)); pmedia_wear_event_vu_data = (struct nvme_ocp_common_dbg_evt_class_vu_data *)((__u64)pmedia_wear_event + sizeof(struct nvme_ocp_media_wear_dbg_evt_class_format)); vu_event_id = le16_to_cpu(pmedia_wear_event_vu_data->vu_event_identifier); pdata = (__u8 *)&(pmedia_wear_event_vu_data->data); parse_ocp_telemetry_string_log(0, vu_event_id, 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); if (vu_data_present) { json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING, vu_event_id); json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING, description_str); json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA, pdata, vu_data_size); } } else { if (fp) { print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, pmedia_wear_event->currentMediaWear, DATA_SIZE_12, fp); if (vu_data_present) { 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, vu_data_size, fp); } } else { print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA, pmedia_wear_event->currentMediaWear, DATA_SIZE_12, NULL); if (vu_data_present) { 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, vu_data_size, fp); } } } } int parse_event_fifo(unsigned int fifo_num, unsigned char *pfifo_start, struct json_object *pevent_fifos_object, unsigned char *pstring_buffer, struct nvme_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 { char buffer[1024] = {0}; sprintf(buffer, "%s%s\n%s", STR_LINE, event_fifo_name, STR_LINE); if (fp) fprintf(fp, "%s", buffer); else printf("%s", buffer); } int offset_to_move = 0; unsigned int event_des_size = sizeof(struct nvme_ocp_telemetry_event_descriptor); while ((fifo_size > 0) && (offset_to_move < fifo_size)) { struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor = (struct nvme_ocp_telemetry_event_descriptor *) (pfifo_start + offset_to_move); /* check if at the end of the list */ if (pevent_descriptor->debug_event_class_type == RESERVED_CLASS_TYPE) break; if (pevent_descriptor != NULL && 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 = NULL; __u16 event_id = 0; char description_str[256] = ""; if (pevent_descriptor != NULL && pevent_descriptor->event_data_size > 0) pevent_specific_data = (__u8 *)pevent_descriptor + event_des_size; event_id = le16_to_cpu(pevent_descriptor->event_id); parse_ocp_telemetry_string_log(0, event_id, pevent_descriptor->debug_event_class_type, EVENT_STRING, description_str); struct json_object *pevent_descriptor_obj = ((pevent_fifos_object != NULL)?json_create_object():NULL); if (pevent_descriptor_obj != NULL) { json_add_formatted_u32_str(pevent_descriptor_obj, STR_DBG_EVENT_CLASS_TYPE, pevent_descriptor->debug_event_class_type); json_add_formatted_u32_str(pevent_descriptor_obj, STR_EVENT_IDENTIFIER, event_id); json_object_add_value_string(pevent_descriptor_obj, STR_EVENT_STRING, description_str); json_add_formatted_u32_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, 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, 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 TIME_STAMP_CLASS_TYPE: parse_time_stamp_event(pevent_descriptor, pevent_descriptor_obj, pevent_specific_data, pevent_fifos_object, fp); break; case PCIE_CLASS_TYPE: parse_pcie_event(pevent_descriptor, pevent_descriptor_obj, pevent_specific_data, pevent_fifos_object, fp); break; case NVME_CLASS_TYPE: parse_nvme_event(pevent_descriptor, pevent_descriptor_obj, pevent_specific_data, pevent_fifos_object, fp); break; case RESET_CLASS_TYPE: case BOOT_SEQUENCE_CLASS_TYPE: case FIRMWARE_ASSERT_CLASS_TYPE: case TEMPERATURE_CLASS_TYPE: case MEDIA_CLASS_TYPE: parse_common_event(pevent_descriptor, pevent_descriptor_obj, pevent_specific_data, pevent_fifos_object, fp); break; case MEDIA_WEAR_CLASS_TYPE: parse_media_wear_event(pevent_descriptor, pevent_descriptor_obj, pevent_specific_data, pevent_fifos_object, fp); break; case STATISTIC_SNAPSHOT_CLASS_TYPE: { struct nvme_ocp_statistic_snapshot_evt_class_format *pStaticSnapshotEvent = (struct nvme_ocp_statistic_snapshot_evt_class_format *) pevent_specific_data; struct nvme_ocp_telemetry_statistic_descriptor *pstatistic_entry = (struct nvme_ocp_telemetry_statistic_descriptor *) (&pStaticSnapshotEvent->statisticDescriptorData); parse_statistic(pstatistic_entry, pevent_descriptor_obj, fp); break; } case RESERVED_CLASS_TYPE: default: 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, STR_LINE2); else printf(STR_LINE2); } } 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, struct nvme_ocp_telemetry_offsets *poffsets, FILE *fp) { if (poffsets == NULL) { nvme_show_error("Input buffer was NULL"); return -1; } struct json_object *pevent_fifos_object = NULL; if (root != NULL) 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; struct nvme_ocp_header_in_da1 *pda1_header = (struct nvme_ocp_header_in_da1 *) pda1_header_offset; struct nvme_ocp_event_fifo_data event_fifo[MAX_NUM_FIFOS]; for (int fifo_num = 0; fifo_num < MAX_NUM_FIFOS; fifo_num++) { event_fifo[fifo_num].event_fifo_num = fifo_num; event_fifo[fifo_num].event_fifo_da = pda1_header->event_fifo_da[fifo_num]; event_fifo[fifo_num].event_fifo_start = pda1_header->fifo_offsets[fifo_num].event_fifo_start; event_fifo[fifo_num].event_fifo_size = pda1_header->fifo_offsets[fifo_num].event_fifo_size; } //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) pfifo_start = pda1_header_offset + fifo_offset; else if (event_fifo[fifo_no].event_fifo_da == 2) 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(struct nvme_ocp_telemetry_statistic_descriptor *pstatistic_entry, struct json_object *pstats_array, FILE *fp) { if (pstatistic_entry == NULL) { 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(struct nvme_ocp_telemetry_statistic_descriptor); char description_str[256] = ""; parse_ocp_telemetry_string_log(0, pstatistic_entry->statistic_id, 0, STATISTICS_IDENTIFIER_STRING, description_str); if (pstats_array != NULL) { struct json_object *pstatistics_object = json_create_object(); json_add_formatted_u32_str(pstatistics_object, STR_STATISTICS_IDENTIFIER, pstatistic_entry->statistic_id); json_object_add_value_string(pstatistics_object, STR_STATISTICS_IDENTIFIER_STR, description_str); json_add_formatted_u32_str(pstatistics_object, STR_STATISTICS_INFO_BEHAVIOUR_TYPE, pstatistic_entry->statistic_info_behaviour_type); json_add_formatted_u32_str(pstatistics_object, STR_STATISTICS_INFO_RESERVED, pstatistic_entry->statistic_info_reserved); json_add_formatted_u32_str(pstatistics_object, STR_NAMESPACE_IDENTIFIER, pstatistic_entry->ns_info_nsid); json_add_formatted_u32_str(pstatistics_object, STR_NAMESPACE_INFO_VALID, pstatistic_entry->ns_info_ns_info_valid); json_add_formatted_u32_str(pstatistics_object, STR_STATISTICS_DATA_SIZE, pstatistic_entry->statistic_data_size); json_add_formatted_u32_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, STR_LINE2); } 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(STR_LINE2); } } return 0; } int parse_statistics(struct json_object *root, struct nvme_ocp_telemetry_offsets *poffsets, FILE *fp) { if (poffsets == NULL) { 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(struct nvme_ocp_header_in_da1, da1_statistic_start)); __u32 stats_da_1_size = *(__u32 *)(pda1_ocp_header_offset + offsetof(struct nvme_ocp_header_in_da1, 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(struct nvme_ocp_header_in_da1, da2_statistic_start)); __u32 stats_da_2_size = *(__u32 *)(pda1_ocp_header_offset + offsetof(struct nvme_ocp_header_in_da1, 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 = ((root != NULL) ? json_create_array() : NULL); __u32 stat_des_size = sizeof(struct nvme_ocp_telemetry_statistic_descriptor);//8 __u32 offset_to_move = 0; while (((statistics_size > 0) && (offset_to_move < statistics_size))) { struct nvme_ocp_telemetry_statistic_descriptor *pstatistic_entry = (struct nvme_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 (root != NULL && pstats_array != NULL) { 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(struct ocp_telemetry_parse_options *options) { int status = 0; if (options->output_file != NULL) { FILE *fp = fopen(options->output_file, "w"); if (fp) { fprintf(fp, STR_LINE); fprintf(fp, "%s\n", STR_LOG_PAGE_HEADER); fprintf(fp, STR_LINE); if (!strcmp(options->telemetry_type, "host")) { if ((ptelemetry_buffer == NULL) || (ARRAY_SIZE(host_log_page_header) == 0)) printf("skip generic_structure_parser\n"); else generic_structure_parser(ptelemetry_buffer, host_log_page_header, ARRAY_SIZE(host_log_page_header), NULL, 0, fp); } else if (!strcmp(options->telemetry_type, "controller")) generic_structure_parser(ptelemetry_buffer, controller_log_page_header, ARRAY_SIZE(controller_log_page_header), NULL, 0, fp); fprintf(fp, STR_LINE); fprintf(fp, "%s\n", STR_REASON_IDENTIFIER); fprintf(fp, STR_LINE); __u8 *preason_identifier_offset = ptelemetry_buffer + offsetof(struct nvme_ocp_telemetry_host_initiated_header, reason_id); generic_structure_parser(preason_identifier_offset, reason_identifier, ARRAY_SIZE(reason_identifier), NULL, 0, fp); fprintf(fp, STR_LINE); fprintf(fp, "%s\n", STR_TELEMETRY_HOST_DATA_BLOCK_1); fprintf(fp, STR_LINE); //Set DA to 1 and get offsets struct nvme_ocp_telemetry_offsets offsets = { 0 }; offsets.data_area = 1;// Default DA - DA1 struct nvme_ocp_telemetry_common_header *ptelemetry_common_header = (struct nvme_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 generic_structure_parser(pda1_header_offset, ocp_header_in_da1, ARRAY_SIZE(ocp_header_in_da1), NULL, 0, fp); fprintf(fp, STR_LINE); fprintf(fp, "%s\n", STR_SMART_HEALTH_INFO); fprintf(fp, STR_LINE); __u8 *pda1_smart_offset = pda1_header_offset + offsetof(struct nvme_ocp_header_in_da1, smart_health_info); //512+512 =1024 generic_structure_parser(pda1_smart_offset, smart, ARRAY_SIZE(smart), NULL, 0, fp); fprintf(fp, STR_LINE); fprintf(fp, "%s\n", STR_SMART_HEALTH_INTO_EXTENDED); fprintf(fp, STR_LINE); __u8 *pda1_smart_ext_offset = pda1_header_offset + offsetof(struct nvme_ocp_header_in_da1, smart_health_info_extended); generic_structure_parser(pda1_smart_ext_offset, smart_extended, ARRAY_SIZE(smart_extended), NULL, 0, fp); fprintf(fp, STR_LINE); fprintf(fp, "%s\n", STR_DA_1_STATS); fprintf(fp, STR_LINE); status = parse_statistics(NULL, &offsets, fp); if (status != 0) { nvme_show_error("status: %d\n", status); return -1; } fprintf(fp, STR_LINE); fprintf(fp, "%s\n", STR_DA_1_EVENT_FIFO_INFO); fprintf(fp, STR_LINE); status = parse_event_fifos(NULL, &offsets, fp); if (status != 0) { nvme_show_error("status: %d\n", status); return -1; } //Set the DA to 2 if (options->data_area == 2) { offsets.data_area = 2; fprintf(fp, STR_LINE); fprintf(fp, "%s\n", STR_DA_2_STATS); fprintf(fp, STR_LINE); status = parse_statistics(NULL, &offsets, fp); if (status != 0) { nvme_show_error("status: %d\n", status); return -1; } fprintf(fp, STR_LINE); fprintf(fp, "%s\n", STR_DA_2_EVENT_FIFO_INFO); fprintf(fp, STR_LINE); status = parse_event_fifos(NULL, &offsets, fp); if (status != 0) { nvme_show_error("status: %d\n", status); return -1; } } fprintf(fp, STR_LINE); fclose(fp); } else { nvme_show_error("Failed to open %s file.\n", options->output_file); return -1; } } else { printf(STR_LINE); printf("%s\n", STR_LOG_PAGE_HEADER); printf(STR_LINE); if (!strcmp(options->telemetry_type, "host")) { if ((ptelemetry_buffer == NULL) || (ARRAY_SIZE(host_log_page_header) == 0)) printf("skip generic_structure_parser\n"); else { generic_structure_parser(ptelemetry_buffer, host_log_page_header, ARRAY_SIZE(host_log_page_header), NULL, 0, NULL); } } else if (!strcmp(options->telemetry_type, "controller")) generic_structure_parser(ptelemetry_buffer, controller_log_page_header, ARRAY_SIZE(controller_log_page_header), NULL, 0, NULL); printf(STR_LINE); printf("%s\n", STR_REASON_IDENTIFIER); printf(STR_LINE); __u8 *preason_identifier_offset = ptelemetry_buffer + offsetof(struct nvme_ocp_telemetry_host_initiated_header, reason_id); generic_structure_parser(preason_identifier_offset, reason_identifier, ARRAY_SIZE(reason_identifier), NULL, 0, NULL); printf(STR_LINE); printf("%s\n", STR_TELEMETRY_HOST_DATA_BLOCK_1); printf(STR_LINE); //Set DA to 1 and get offsets struct nvme_ocp_telemetry_offsets offsets = { 0 }; offsets.data_area = 1; struct nvme_ocp_telemetry_common_header *ptelemetry_common_header = (struct nvme_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 generic_structure_parser(pda1_header_offset, ocp_header_in_da1, ARRAY_SIZE(ocp_header_in_da1), NULL, 0, NULL); printf(STR_LINE); printf("%s\n", STR_SMART_HEALTH_INFO); printf(STR_LINE); __u8 *pda1_smart_offset = pda1_header_offset + offsetof(struct nvme_ocp_header_in_da1, smart_health_info); generic_structure_parser(pda1_smart_offset, smart, ARRAY_SIZE(smart), NULL, 0, NULL); printf(STR_LINE); printf("%s\n", STR_SMART_HEALTH_INTO_EXTENDED); printf(STR_LINE); __u8 *pda1_smart_ext_offset = pda1_header_offset + offsetof(struct nvme_ocp_header_in_da1, smart_health_info_extended); generic_structure_parser(pda1_smart_ext_offset, smart_extended, ARRAY_SIZE(smart_extended), NULL, 0, NULL); printf(STR_LINE); printf("%s\n", STR_DA_1_STATS); printf(STR_LINE); status = parse_statistics(NULL, &offsets, NULL); if (status != 0) { nvme_show_error("status: %d\n", status); return -1; } printf(STR_LINE); printf("%s\n", STR_DA_1_EVENT_FIFO_INFO); printf(STR_LINE); status = parse_event_fifos(NULL, &offsets, NULL); if (status != 0) { nvme_show_error("status: %d\n", status); return -1; } //Set the DA to 2 if (options->data_area == 2) { offsets.data_area = 2; printf(STR_LINE); printf("%s\n", STR_DA_2_STATS); printf(STR_LINE); status = parse_statistics(NULL, &offsets, NULL); if (status != 0) { nvme_show_error("status: %d\n", status); return -1; } printf(STR_LINE); printf("%s\n", STR_DA_2_EVENT_FIFO_INFO); printf(STR_LINE); status = parse_event_fifos(NULL, &offsets, NULL); if (status != 0) { nvme_show_error("status: %d\n", status); return -1; } } printf(STR_LINE); } return status; } int print_ocp_telemetry_json(struct ocp_telemetry_parse_options *options) { 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(); generic_structure_parser(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(struct nvme_ocp_telemetry_host_initiated_header, reason_id); generic_structure_parser(preason_identifier_offset, reason_identifier, ARRAY_SIZE(reason_identifier), preason_identifier, 0, NULL); json_object_add_value_object(pheader, STR_REASON_IDENTIFIER, preason_identifier); struct nvme_ocp_telemetry_offsets offsets = { 0 }; //Set DA to 1 and get offsets offsets.data_area = 1; struct nvme_ocp_telemetry_common_header *ptelemetry_common_header = (struct nvme_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(); generic_structure_parser(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(struct nvme_ocp_header_in_da1, smart_health_info); smart_obj = json_create_object(); generic_structure_parser(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(struct nvme_ocp_header_in_da1, smart_health_info_extended); ext_smart_obj = json_create_object(); generic_structure_parser(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; } if (options->data_area == 2) { //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 (options->output_file != NULL) { const char *json_string = json_object_to_json_string(root); FILE *fp = fopen(options->output_file, "w"); if (fp) { fputs(json_string, fp); fclose(fp); } else { nvme_show_error("Failed to open %s file.\n", options->output_file); return -1; } } else { //Print root json object json_print_object(root, NULL); nvme_show_result("\n"); json_free_object(root); } return status; } #endif /* CONFIG_JSONC */