| File: | .build-ci/../plugins/ocp/ocp-telemetry-decode.c |
| Warning: | line 1325, column 3 Potential leak of memory pointed to by 'description' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | |||
| 2 | /* Copyright (c) 2024 Western Digital Corporation or its affiliates. | |||
| 3 | * | |||
| 4 | * Authors: Jeff Lien <jeff.lien@wdc.com>, | |||
| 5 | */ | |||
| 6 | #include <libnvme.h> | |||
| 7 | ||||
| 8 | #include <ccan/array_size/array_size.h> | |||
| 9 | #include <ccan/endian/endian.h> | |||
| 10 | #include <shared/hex-util.h> | |||
| 11 | #include <shared/string-util.h> | |||
| 12 | ||||
| 13 | #include "nvme-print.h" | |||
| 14 | #include "ocp-telemetry-decode.h" | |||
| 15 | ||||
| 16 | struct statistic_entry statistic_identifiers_map[] = { | |||
| 17 | { 0x00, "Error, this entry does not exist." }, | |||
| 18 | { 0x01, "Outstanding Admin Commands" }, | |||
| 19 | { 0x02, "Host Write Bandwidth"}, | |||
| 20 | { 0x03, "GC Write Bandwidth"}, | |||
| 21 | { 0x04, "Active Namespaces"}, | |||
| 22 | { 0x05, "Internal Write Workload"}, | |||
| 23 | { 0x06, "Internal Read Workload"}, | |||
| 24 | { 0x07, "Internal Write Queue Depth"}, | |||
| 25 | { 0x08, "Internal Read Queue Depth"}, | |||
| 26 | { 0x09, "Pending Trim LBA Count"}, | |||
| 27 | { 0x0A, "Host Trim LBA Request Count"}, | |||
| 28 | { 0x0B, "Current NVMe Power State"}, | |||
| 29 | { 0x0C, "Current DSSD Power State"}, | |||
| 30 | { 0x0D, "Program Fail Count"}, | |||
| 31 | { 0x0E, "Erase Fail Count"}, | |||
| 32 | { 0x0F, "Read Disturb Writes"}, | |||
| 33 | { 0x10, "Retention Writes"}, | |||
| 34 | { 0x11, "Wear Leveling Writes"}, | |||
| 35 | { 0x12, "Read Recovery Writes"}, | |||
| 36 | { 0x13, "GC Writes"}, | |||
| 37 | { 0x14, "SRAM Correctable Count"}, | |||
| 38 | { 0x15, "DRAM Correctable Count"}, | |||
| 39 | { 0x16, "SRAM Uncorrectable Count"}, | |||
| 40 | { 0x17, "DRAM Uncorrectable Count"}, | |||
| 41 | { 0x18, "Data Integrity Error Count"}, | |||
| 42 | { 0x19, "Read Retry Error Count"}, | |||
| 43 | { 0x1A, "PERST Events Count"}, | |||
| 44 | { 0x1B, "Max Die Bad Block"}, | |||
| 45 | { 0x1C, "Max NAND Channel Bad Block"}, | |||
| 46 | { 0x1D, "Minimum NAND Channel Bad Block"}, | |||
| 47 | { 0x1E, "Physical Media Units Written"}, | |||
| 48 | { 0x1F, "Physical Media Units Read"}, | |||
| 49 | { 0x20, "Bad User NAND Blocks"}, | |||
| 50 | { 0x21, "Bad System NAND Blocks"}, | |||
| 51 | { 0x22, "XOR Recovery Count"}, | |||
| 52 | { 0x23, "Uncorrectable Read Error Count"}, | |||
| 53 | { 0x24, "Soft ECC Error Count"}, | |||
| 54 | { 0x25, "End to End Correction Counts"}, | |||
| 55 | { 0x26, "System Data % Used"}, | |||
| 56 | { 0x27, "Refresh Counts"}, | |||
| 57 | { 0x28, "User Data Erase Counts"}, | |||
| 58 | { 0x29, "Thermal Throttling Status and Count"}, | |||
| 59 | { 0x2A, "DSSD Specification Version"}, | |||
| 60 | { 0x2B, "PCIe Correctable Error Count"}, | |||
| 61 | { 0x2C, "Incomplete Shutdowns"}, | |||
| 62 | { 0x2D, "% Free Blocks"}, | |||
| 63 | { 0x2E, "Capacitor Health"}, | |||
| 64 | { 0x2F, "NVM Express Base Errata Version"}, | |||
| 65 | { 0x30, "NVM Command Set Errata Version"}, | |||
| 66 | { 0x31, "NVM Express Management Interface Errata Version"}, | |||
| 67 | { 0x32, "Unaligned I/O"}, | |||
| 68 | { 0x33, "Security Version Number"}, | |||
| 69 | { 0x34, "Total NUSE"}, | |||
| 70 | { 0x35, "PLP Start Count"}, | |||
| 71 | { 0x36, "Endurance Estimate"}, | |||
| 72 | { 0x37, "PCIe Link Retraining Count"}, | |||
| 73 | { 0x38, "Power State Change Count"}, | |||
| 74 | { 0x39, "Lowest Permitted Firmware Revision"}, | |||
| 75 | { 0x3A, "Log Page Version"}, | |||
| 76 | { 0x3B, "Media Dies Offline"}, | |||
| 77 | { 0x3C, "Max Temperature Recorded"}, | |||
| 78 | { 0x3D, "NAND Avg. Erase Count"}, | |||
| 79 | { 0x3E, "Command Timeouts"}, | |||
| 80 | { 0x3F, "System Area Program Fail Count"}, | |||
| 81 | { 0x40, "System Area Read Fail Count"}, | |||
| 82 | { 0x41, "System Area Erase Fail Count"}, | |||
| 83 | { 0x42, "Max Peak Power Capability"}, | |||
| 84 | { 0x43, "Current Average Power"}, | |||
| 85 | { 0x44, "Lifetime Power Consumed"}, | |||
| 86 | { 0x45, "Error / Assert Count"}, | |||
| 87 | { 0x46, "Device Busy Time"}, | |||
| 88 | { 0x47, "Critical Warning"}, | |||
| 89 | { 0x48, "Composite Temperature"}, | |||
| 90 | { 0x49, "Available Spare"}, | |||
| 91 | { 0x4A, "Available Spare Threshold"}, | |||
| 92 | { 0x4B, "Percentage Used"}, | |||
| 93 | { 0x4C, "Endurance Group Critical Warning Summary"}, | |||
| 94 | { 0x4D, "Data Units Read"}, | |||
| 95 | { 0x4E, "Data Units Written"}, | |||
| 96 | { 0x4F, "Host Read Commands"}, | |||
| 97 | { 0x50, "Host Write Commands"}, | |||
| 98 | { 0x51, "Controller Busy Time"}, | |||
| 99 | { 0x52, "Power Cycles"}, | |||
| 100 | { 0x53, "Power On Hours"}, | |||
| 101 | { 0x54, "Unsafe Shutdowns"}, | |||
| 102 | { 0x55, "Media and Data Integrity Errors"}, | |||
| 103 | { 0x56, "Number of Error Information Log Entries"}, | |||
| 104 | { 0x57, "Warning Composite Temperature Time"}, | |||
| 105 | { 0x58, "Critical Composite Temperature Time"}, | |||
| 106 | { 0x59, "Temperature Sensor 1"}, | |||
| 107 | { 0x5A, "Temperature Sensor 2"}, | |||
| 108 | { 0x5B, "Temperature Sensor 3"}, | |||
| 109 | { 0x5C, "Temperature Sensor 4"}, | |||
| 110 | { 0x5D, "Temperature Sensor 5"}, | |||
| 111 | { 0x5E, "Temperature Sensor 6"}, | |||
| 112 | { 0x5F, "Temperature Sensor 7"}, | |||
| 113 | { 0x60, "Temperature Sensor 8"}, | |||
| 114 | { 0x61, "Thermal Management Temperature 1 Transition Count"}, | |||
| 115 | { 0x62, "Thermal Management Temperature 2 Transition Count"}, | |||
| 116 | { 0x63, "Total Time For Thermal Management Temperature 1"}, | |||
| 117 | { 0x64, "Total Time For Thermal Management Temperature 2"}, | |||
| 118 | { 0x65, "Endurance Estimate"}, | |||
| 119 | { 0x66, "Data Units Read"}, | |||
| 120 | { 0x67, "Data Units Written"}, | |||
| 121 | { 0x68, "Media Units Written"}, | |||
| 122 | { 0x69, "Number of Error Information Log Entries"}, | |||
| 123 | { 0x6A, "Form Factor"}, | |||
| 124 | { 0x6B, "Dies In Use Bad NAND Blocks"}, | |||
| 125 | { 0x6C, "Proactive Bad Die Retirement"}, | |||
| 126 | { 0x6D, "Namespace ID Context Statistic Descriptor"}, | |||
| 127 | { 0x6E, "Controller ID Context Statistic Descriptor"}, | |||
| 128 | { 0x6F, "Queue ID Context Statistic Descriptor"} | |||
| 129 | }; | |||
| 130 | ||||
| 131 | /* OCP 2.7 Section 4.9.15.2 Event IDs; reserved and obsolete IDs are NULL. */ | |||
| 132 | static const char * const timestamp_event_id_str[] = { | |||
| 133 | [0x0000] = "Host issued a Timestamp Set Feature Command (Feature ID Eh)", | |||
| 134 | [0x0001] = "Firmware initiated Timestamp Snapshot", | |||
| 135 | }; | |||
| 136 | ||||
| 137 | static const char * const pcie_event_id_str[] = { | |||
| 138 | [0x0000] = "Link Up", | |||
| 139 | [0x0001] = "Link Down", | |||
| 140 | [0x0002] = "PCIe Error Detected", | |||
| 141 | [0x0003] = "PERST# Asserted", | |||
| 142 | [0x0004] = "PERST# De-asserted", | |||
| 143 | [0x0005] = "Refclk Stable", | |||
| 144 | [0x0006] = "Vmain Stable", | |||
| 145 | [0x0007] = "Link Speed and Width Negotiated", | |||
| 146 | }; | |||
| 147 | ||||
| 148 | static const char * const nvme_event_id_str[] = { | |||
| 149 | [0x0000] = "CC.EN transitions from 0b to 1b", | |||
| 150 | [0x0001] = "CC.EN transitions from 1b to 0b", | |||
| 151 | [0x0002] = "CSTS.RDY transitions from 0b to 1b", | |||
| 152 | [0x0003] = "CSTS.RDY transitions from 1b to 0b", | |||
| 153 | [0x0005] = "Create I/O Submission Queue Command or Create I/O Completion Queue Command Processed successfully", | |||
| 154 | [0x0006] = "In-band Admin Command Processed other than Create I/O Submission Queue Command or Create I/O Completion Queue Command Processed successfully", | |||
| 155 | [0x0007] = "An Admin command returned a non-zero Status Code", | |||
| 156 | [0x0008] = "An I/O Command Returned a Non-zero Status Code", | |||
| 157 | [0x0009] = "CSTS.CFS transitioned from 0b to 1b", | |||
| 158 | [0x000A] = "Admin Submission Queue Base Address Written (AQA) or Admin Completion Queue Based Address (ACQ) written", | |||
| 159 | [0x000B] = "Controller Configuration Register (CC) Changed except for the cases that are covered in 0000h and 0001h", | |||
| 160 | [0x000C] = "Controller Status Register (CSTS) Changed except for the cases that are covered in 0002h, 0003h, and 0009h", | |||
| 161 | [0x000D] = "Delete I/O Completion Queue Command or Delete I/O Submission Queue Command Processed", | |||
| 162 | [0x000E] = "Out-of-band Command Processed", | |||
| 163 | [0x000F] = "Out-of-band Asynchronous Event Message Transmitted", | |||
| 164 | [0x0010] = "Error Reporting AEN was sent (see EREC-5)", | |||
| 165 | }; | |||
| 166 | ||||
| 167 | static const char * const reset_event_id_str[] = { | |||
| 168 | [0x0000] = "PCIe Conventional Hot Reset", | |||
| 169 | [0x0001] = "Main Power Cycle", | |||
| 170 | [0x0002] = "PERST#", | |||
| 171 | [0x0003] = "PCIe Function Level Reset", | |||
| 172 | [0x0004] = "NVM Subsystem Reset", | |||
| 173 | }; | |||
| 174 | ||||
| 175 | static const char * const boot_sequence_event_id_str[] = { | |||
| 176 | [0x0000] = "Main Firmware Boot Complete", | |||
| 177 | [0x0001] = "FTL Load from NVM Complete", | |||
| 178 | [0x0002] = "FTL Rebuild Started", | |||
| 179 | [0x0003] = "FTL Rebuild Complete", | |||
| 180 | }; | |||
| 181 | ||||
| 182 | static const char * const firmware_assert_event_id_str[] = { | |||
| 183 | [0x0000] = "Assert in NVMe Processing Code", | |||
| 184 | [0x0001] = "Assert in Media Code", | |||
| 185 | [0x0002] = "Assert in Security Code", | |||
| 186 | [0x0003] = "Assert in Background Services Code", | |||
| 187 | [0x0004] = "FTL Rebuild Failed", | |||
| 188 | [0x0005] = "FTL Data Mismatch", | |||
| 189 | [0x0006] = "Assert in Other Code", | |||
| 190 | }; | |||
| 191 | ||||
| 192 | static const char * const temperature_event_id_str[] = { | |||
| 193 | [0x0000] = "Composite Temperature decreases to (WCTEMP - 2)", | |||
| 194 | [0x0001] = "Composite Temperature increases to WCTEMP", | |||
| 195 | [0x0002] = "Composite Temperature increases to reach CCTEMP", | |||
| 196 | }; | |||
| 197 | ||||
| 198 | static const char * const media_event_id_str[] = { | |||
| 199 | [0x0000] = "XOR (or equivalent) Recovery Invoked", | |||
| 200 | [0x0001] = "Uncorrectable Media Error", | |||
| 201 | [0x0002] = "Block Marked Bad Due to Program Error", | |||
| 202 | [0x0003] = "Block Marked Bad Due to Erase Error", | |||
| 203 | [0x0004] = "Block Marked Bad Due to Read Error", | |||
| 204 | [0x0005] = "Plane Failure Event", | |||
| 205 | }; | |||
| 206 | ||||
| 207 | static const char * const media_wear_event_id_str[] = { | |||
| 208 | [0x0000] = "Media Wear", | |||
| 209 | }; | |||
| 210 | ||||
| 211 | static const char * const virtual_fifo_event_id_str[] = { | |||
| 212 | [0x0000] = "Virtual FIFO Start", | |||
| 213 | [0x0001] = "Virtual FIFO End", | |||
| 214 | }; | |||
| 215 | ||||
| 216 | static const char * const smbus_event_id_str[] = { | |||
| 217 | [0x0000] = "Timeout Errors", | |||
| 218 | [0x0001] = "PEC Errors", | |||
| 219 | [0x0002] = "Arbitration loss", | |||
| 220 | [0x0003] = "NACK error", | |||
| 221 | }; | |||
| 222 | ||||
| 223 | static const char * const mctp_event_id_str[] = { | |||
| 224 | [0x0000] = "Dropped Packet", | |||
| 225 | [0x0001] = "Dropped Message", | |||
| 226 | [0x0002] = "Discovery and addressing errors", | |||
| 227 | [0x0003] = "Error Status", | |||
| 228 | }; | |||
| 229 | ||||
| 230 | #define EVENT_IDS(t){ t, (sizeof(t) / sizeof((t)[0]) + 0) } { t, ARRAY_SIZE(t)(sizeof(t) / sizeof((t)[0]) + 0) } | |||
| 231 | ||||
| 232 | static const struct { | |||
| 233 | const char * const *str; | |||
| 234 | size_t count; | |||
| 235 | } spec_event_id_str[] = { | |||
| 236 | [TIME_STAMP_CLASS_TYPE] = EVENT_IDS(timestamp_event_id_str){ timestamp_event_id_str, (sizeof(timestamp_event_id_str) / sizeof ((timestamp_event_id_str)[0]) + 0) }, | |||
| 237 | [PCIE_CLASS_TYPE] = EVENT_IDS(pcie_event_id_str){ pcie_event_id_str, (sizeof(pcie_event_id_str) / sizeof((pcie_event_id_str )[0]) + 0) }, | |||
| 238 | [NVME_CLASS_TYPE] = EVENT_IDS(nvme_event_id_str){ nvme_event_id_str, (sizeof(nvme_event_id_str) / sizeof((nvme_event_id_str )[0]) + 0) }, | |||
| 239 | [RESET_CLASS_TYPE] = EVENT_IDS(reset_event_id_str){ reset_event_id_str, (sizeof(reset_event_id_str) / sizeof((reset_event_id_str )[0]) + 0) }, | |||
| 240 | [BOOT_SEQUENCE_CLASS_TYPE] = EVENT_IDS(boot_sequence_event_id_str){ boot_sequence_event_id_str, (sizeof(boot_sequence_event_id_str ) / sizeof((boot_sequence_event_id_str)[0]) + 0) }, | |||
| 241 | [FIRMWARE_ASSERT_CLASS_TYPE] = EVENT_IDS(firmware_assert_event_id_str){ firmware_assert_event_id_str, (sizeof(firmware_assert_event_id_str ) / sizeof((firmware_assert_event_id_str)[0]) + 0) }, | |||
| 242 | [TEMPERATURE_CLASS_TYPE] = EVENT_IDS(temperature_event_id_str){ temperature_event_id_str, (sizeof(temperature_event_id_str) / sizeof((temperature_event_id_str)[0]) + 0) }, | |||
| 243 | [MEDIA_CLASS_TYPE] = EVENT_IDS(media_event_id_str){ media_event_id_str, (sizeof(media_event_id_str) / sizeof((media_event_id_str )[0]) + 0) }, | |||
| 244 | [MEDIA_WEAR_CLASS_TYPE] = EVENT_IDS(media_wear_event_id_str){ media_wear_event_id_str, (sizeof(media_wear_event_id_str) / sizeof((media_wear_event_id_str)[0]) + 0) }, | |||
| 245 | [VIRTUAL_FIFO_EVENT_CLASS_TYPE] = EVENT_IDS(virtual_fifo_event_id_str){ virtual_fifo_event_id_str, (sizeof(virtual_fifo_event_id_str ) / sizeof((virtual_fifo_event_id_str)[0]) + 0) }, | |||
| 246 | [SMBUS_I2C_I3C_EVENT_CLASS_TYPE] = EVENT_IDS(smbus_event_id_str){ smbus_event_id_str, (sizeof(smbus_event_id_str) / sizeof((smbus_event_id_str )[0]) + 0) }, | |||
| 247 | [MCTP_EVENT_CLASS_TYPE] = EVENT_IDS(mctp_event_id_str){ mctp_event_id_str, (sizeof(mctp_event_id_str) / sizeof((mctp_event_id_str )[0]) + 0) }, | |||
| 248 | }; | |||
| 249 | ||||
| 250 | struct request_data host_log_page_header[] = { | |||
| 251 | { "LogIdentifier", 1 }, | |||
| 252 | { "Reserved1", 4 }, | |||
| 253 | { "IEEE OUI Identifier", 3 }, | |||
| 254 | { "Telemetry Host-Initiated Data Area 1 Last Block", 2 }, | |||
| 255 | { "Telemetry Host-Initiated Data Area 2 Last Block", 2 }, | |||
| 256 | { "Telemetry Host-Initiated Data Area 3 Last Block", 2 }, | |||
| 257 | { "Reserved2", 2 }, | |||
| 258 | { "Telemetry Host-Initiated Data Area 4 Last Block", 4 }, | |||
| 259 | { "Reserved3", 360 }, | |||
| 260 | { "Telemetry Host-Initiated Scope", 1 }, | |||
| 261 | { "Telemetry Host-Initiated Data Generation Number", 1 }, | |||
| 262 | { "Telemetry Controller-Initiated Data Available", 1 }, | |||
| 263 | { "Telemetry Controller-Initiated Data Generation Number", 1 } | |||
| 264 | }; | |||
| 265 | ||||
| 266 | struct request_data controller_log_page_header[] = { | |||
| 267 | { "LogIdentifier", 1 }, | |||
| 268 | { "Reserved1", 4 }, | |||
| 269 | { "IEEE OUI Identifier", 3 }, | |||
| 270 | { "Telemetry Controller-Initiated Data Area 1 Last Block", 2 }, | |||
| 271 | { "Telemetry Controller-Initiated Data Area 2 Last Block", 2 }, | |||
| 272 | { "Telemetry Controller-Initiated Data Area 3 Last Block", 2 }, | |||
| 273 | { "Reserved2", 2 }, | |||
| 274 | { "Telemetry Controller-Initiated Data Area 4 Last Block", 4 }, | |||
| 275 | { "Reserved3", 361 }, | |||
| 276 | { "Telemetry Controller-Initiated Scope", 1 }, | |||
| 277 | { "Telemetry Controller-Initiated Data Available", 1 }, | |||
| 278 | { "Telemetry Controller-Initiated Data Generation Number", 1 } | |||
| 279 | }; | |||
| 280 | ||||
| 281 | struct request_data reason_identifier[] = { | |||
| 282 | { "Error ID", 64 }, | |||
| 283 | { "File ID", 8 }, | |||
| 284 | { "Line Number", 2 }, | |||
| 285 | { "Valid Flags", 1 }, | |||
| 286 | { "Reserved", 21 }, | |||
| 287 | { "VU Reason Extension", 32 } | |||
| 288 | }; | |||
| 289 | ||||
| 290 | struct request_data ocp_header_in_da1[] = { | |||
| 291 | { "Major Version", 2 }, | |||
| 292 | { "Minor Version", 2 }, | |||
| 293 | { "Reserved1", 4 }, | |||
| 294 | { "Timestamp", 8 }, | |||
| 295 | { "Log page GUID", GUID_LEN16 }, | |||
| 296 | { "Number Telemetry Profiles Supported", 1 }, | |||
| 297 | { "Telemetry Profile Selected", 1 }, | |||
| 298 | { "Reserved2", 6 }, | |||
| 299 | { "Telemetry String Log Size", 8 }, | |||
| 300 | { "Reserved3", 8 }, | |||
| 301 | { "Firmware Revision", 8 }, | |||
| 302 | { "Reserved4", 32 }, | |||
| 303 | { "Data Area 1 Statistic Start", 8 }, | |||
| 304 | { "Data Area 1 Statistic Size", 8 }, | |||
| 305 | { "Data Area 2 Statistic Start", 8 }, | |||
| 306 | { "Data Area 2 Statistic Size", 8 }, | |||
| 307 | { "Reserved5", 32 }, | |||
| 308 | { "Event FIFO 1 Data Area", 1 }, | |||
| 309 | { "Event FIFO 2 Data Area", 1 }, | |||
| 310 | { "Event FIFO 3 Data Area", 1 }, | |||
| 311 | { "Event FIFO 4 Data Area", 1 }, | |||
| 312 | { "Event FIFO 5 Data Area", 1 }, | |||
| 313 | { "Event FIFO 6 Data Area", 1 }, | |||
| 314 | { "Event FIFO 7 Data Area", 1 }, | |||
| 315 | { "Event FIFO 8 Data Area", 1 }, | |||
| 316 | { "Event FIFO 9 Data Area", 1 }, | |||
| 317 | { "Event FIFO 10 Data Area", 1 }, | |||
| 318 | { "Event FIFO 11 Data Area", 1 }, | |||
| 319 | { "Event FIFO 12 Data Area", 1 }, | |||
| 320 | { "Event FIFO 13 Data Area", 1 }, | |||
| 321 | { "Event FIFO 14 Data Area", 1 }, | |||
| 322 | { "Event FIFO 15 Data Area", 1 }, | |||
| 323 | { "Event FIFO 16 Data Area", 1 }, | |||
| 324 | { "Event FIFO 1 Start", 8 }, | |||
| 325 | { "Event FIFO 1 Size", 8 }, | |||
| 326 | { "Event FIFO 2 Start", 8 }, | |||
| 327 | { "Event FIFO 2 Size", 8 }, | |||
| 328 | { "Event FIFO 3 Start", 8 }, | |||
| 329 | { "Event FIFO 3 Size", 8 }, | |||
| 330 | { "Event FIFO 4 Start", 8 }, | |||
| 331 | { "Event FIFO 4 Size", 8 }, | |||
| 332 | { "Event FIFO 5 Start", 8 }, | |||
| 333 | { "Event FIFO 5 Size", 8 }, | |||
| 334 | { "Event FIFO 6 Start", 8 }, | |||
| 335 | { "Event FIFO 6 Size", 8 }, | |||
| 336 | { "Event FIFO 7 Start", 8 }, | |||
| 337 | { "Event FIFO 7 Size", 8 }, | |||
| 338 | { "Event FIFO 8 Start", 8 }, | |||
| 339 | { "Event FIFO 8 Size", 8 }, | |||
| 340 | { "Event FIFO 9 Start", 8 }, | |||
| 341 | { "Event FIFO 9 Size", 8 }, | |||
| 342 | { "Event FIFO 10 Start", 8 }, | |||
| 343 | { "Event FIFO 10 Size", 8 }, | |||
| 344 | { "Event FIFO 11 Start", 8 }, | |||
| 345 | { "Event FIFO 11 Size", 8 }, | |||
| 346 | { "Event FIFO 12 Start", 8 }, | |||
| 347 | { "Event FIFO 12 Size", 8 }, | |||
| 348 | { "Event FIFO 13 Start", 8 }, | |||
| 349 | { "Event FIFO 13 Size", 8 }, | |||
| 350 | { "Event FIFO 14 Start", 8 }, | |||
| 351 | { "Event FIFO 14 Size", 8 }, | |||
| 352 | { "Event FIFO 15 Start", 8 }, | |||
| 353 | { "Event FIFO 15 Size", 8 }, | |||
| 354 | { "Event FIFO 16 Start", 8 }, | |||
| 355 | { "Event FIFO 16 Size", 8 }, | |||
| 356 | { "Reserved6", 80 } | |||
| 357 | }; | |||
| 358 | ||||
| 359 | struct request_data smart[] = { | |||
| 360 | { "Critical Warning", 1 }, | |||
| 361 | { "Composite Temperature", 2 }, | |||
| 362 | { "Available Spare", 1 }, | |||
| 363 | { "Available Spare Threshold", 1 }, | |||
| 364 | { "Percentage Used", 1 }, | |||
| 365 | { "Reserved1", 26 }, | |||
| 366 | { "Data Units Read", 16 }, | |||
| 367 | { "Data Units Written", 16 }, | |||
| 368 | { "Host Read Commands", 16 }, | |||
| 369 | { "Host Write Commands", 16 }, | |||
| 370 | { "Controller Busy Time", 16 }, | |||
| 371 | { "Power Cycles", 16 }, | |||
| 372 | { "Power On Hours", 16 }, | |||
| 373 | { "Unsafe Shutdowns", 16 }, | |||
| 374 | { "Media and Data Integrity Errors", 16 }, | |||
| 375 | { "Number of Error Information Log Entries", 16 }, | |||
| 376 | { "Warning Composite Temperature Time", 4 }, | |||
| 377 | { "Critical Composite Temperature Time", 4 }, | |||
| 378 | { "Temperature Sensor 1", 2 }, | |||
| 379 | { "Temperature Sensor 2", 2 }, | |||
| 380 | { "Temperature Sensor 3", 2 }, | |||
| 381 | { "Temperature Sensor 4", 2 }, | |||
| 382 | { "Temperature Sensor 5", 2 }, | |||
| 383 | { "Temperature Sensor 6", 2 }, | |||
| 384 | { "Temperature Sensor 7", 2 }, | |||
| 385 | { "Temperature Sensor 8", 2 }, | |||
| 386 | { "Thermal Management Temperature 1 Transition Count", 4 }, | |||
| 387 | { "Thermal Management Temperature 2 Transition Count", 4 }, | |||
| 388 | { "Total Time for Thermal Management Temperature 1", 4 }, | |||
| 389 | { "Total Time for Thermal Management Temperature 2", 4 }, | |||
| 390 | { "Reserved2", 280 } | |||
| 391 | }; | |||
| 392 | ||||
| 393 | struct request_data smart_extended[] = { | |||
| 394 | { "Physical Media Units Written", 16 }, | |||
| 395 | { "Physical Media Units Read", 16 }, | |||
| 396 | { "Bad User NAND Blocks Raw Count", 6 }, | |||
| 397 | { "Bad User NAND Blocks Normalized Value", 2 }, | |||
| 398 | { "Bad System NAND Blocks Raw Count", 6 }, | |||
| 399 | { "Bad System NAND Blocks Normalized Value", 2 }, | |||
| 400 | { "XOR Recovery Count", 8 }, | |||
| 401 | { "Uncorrectable Read Error Count", 8 }, | |||
| 402 | { "Soft ECC Error Count", 8 }, | |||
| 403 | { "End to End Correction Counts Detected Errors", 4 }, | |||
| 404 | { "End to End Correction Counts Corrected Errors", 4 }, | |||
| 405 | { "System Data Percent Used", 1 }, | |||
| 406 | { "Refresh Counts", 7 }, | |||
| 407 | { "Maximum User Data Erase Count", 4 }, | |||
| 408 | { "Minimum User Data Erase Count", 4 }, | |||
| 409 | { "Number of thermal throttling events", 1 }, | |||
| 410 | { "Current Throttling Status", 1 }, | |||
| 411 | { "Errata Version Field", 1 }, | |||
| 412 | { "Point Version Field", 2 }, | |||
| 413 | { "Minor Version Field", 2 }, | |||
| 414 | { "Major Version Field", 1 }, | |||
| 415 | { "PCIe Correctable Error Count", 8 }, | |||
| 416 | { "Incomplete Shutdowns", 4 }, | |||
| 417 | { "Reserved1", 4 }, | |||
| 418 | { "Percent Free Blocks", 1 }, | |||
| 419 | { "Reserved2", 7 }, | |||
| 420 | { "Capacitor Health", 2 }, | |||
| 421 | { "NVMe Base Errata Version", 1 }, | |||
| 422 | { "NVMe Command Set Errata Version", 1 }, | |||
| 423 | { "Reserved3", 4 }, | |||
| 424 | { "Unaligned IO", 8 }, | |||
| 425 | { "Security Version Number", 8 }, | |||
| 426 | { "Total NUSE", 8 }, | |||
| 427 | { "PLP Start Count", 16 }, | |||
| 428 | { "Endurance Estimate", 16 }, | |||
| 429 | { "PCIe Link Retraining Count", 8 }, | |||
| 430 | { "Power State Change Count", 8 }, | |||
| 431 | { "Lowest Permitted Firmware Revision", 8 }, | |||
| 432 | { "Reserved4", 278 }, | |||
| 433 | { "Log Page Version", 2 }, | |||
| 434 | { "Log page GUID", GUID_LEN16 } | |||
| 435 | }; | |||
| 436 | ||||
| 437 | void json_add_formatted_u32_str(struct json_object *pobject, const char *msg, unsigned int pdata) | |||
| 438 | { | |||
| 439 | char data_str[70] = { 0 }; | |||
| 440 | ||||
| 441 | snprintf(data_str, sizeof(data_str), "0x%x", pdata); | |||
| 442 | json_object_add_value_string(pobject, msg, data_str); | |||
| 443 | } | |||
| 444 | ||||
| 445 | void json_add_formatted_var_size_str(struct json_object *pobject, const char *msg, __u8 *pdata, | |||
| 446 | unsigned int data_size) | |||
| 447 | { | |||
| 448 | char *description_str = NULL((void*)0); | |||
| 449 | size_t size = data_size * 2 + 1; | |||
| 450 | ||||
| 451 | /* 2 chars for each value in the data and the null terminator */ | |||
| 452 | description_str = (char *) calloc(1, size); | |||
| 453 | if (!description_str) { | |||
| 454 | nvme_show_error("Failed to allocate description buffer")nvme_show_message(1, "Failed to allocate description buffer"); | |||
| 455 | return; | |||
| 456 | } | |||
| 457 | ||||
| 458 | shr_bin_to_hex(description_str, size, pdata, data_size, SHR_HEX_UPPER(1 << 0)); | |||
| 459 | ||||
| 460 | json_object_add_value_string(pobject, msg, description_str); | |||
| 461 | free(description_str); | |||
| 462 | } | |||
| 463 | ||||
| 464 | /* | |||
| 465 | * A log can hold fewer data areas than its header describes, so each data | |||
| 466 | * area is cut to the bytes actually read. | |||
| 467 | */ | |||
| 468 | static __u64 telemetry_da_size(__u64 start, __u64 end, size_t buf_len) | |||
| 469 | { | |||
| 470 | if (end > buf_len) | |||
| 471 | end = buf_len; | |||
| 472 | ||||
| 473 | return end > start ? end - start : 0; | |||
| 474 | } | |||
| 475 | ||||
| 476 | int get_telemetry_das_offset_and_size( | |||
| 477 | struct nvme_ocp_telemetry_common_header *ptelemetry_common_header, | |||
| 478 | size_t buf_len, struct nvme_ocp_telemetry_offsets *ptelemetry_das_offset) | |||
| 479 | { | |||
| 480 | if (NULL((void*)0) == ptelemetry_common_header || NULL((void*)0) == ptelemetry_das_offset) { | |||
| 481 | nvme_show_error("Invalid input arguments.")nvme_show_message(1, "Invalid input arguments."); | |||
| 482 | return -1; | |||
| 483 | } | |||
| 484 | ||||
| 485 | if (ptelemetry_common_header->log_id == NVME_LOG_LID_TELEMETRY_HOST) | |||
| 486 | ptelemetry_das_offset->header_size = | |||
| 487 | sizeof(struct nvme_ocp_telemetry_host_initiated_header); | |||
| 488 | else if (ptelemetry_common_header->log_id == NVME_LOG_LID_TELEMETRY_CTRL) | |||
| 489 | ptelemetry_das_offset->header_size = | |||
| 490 | sizeof(struct nvme_ocp_telemetry_controller_initiated_header); | |||
| 491 | else | |||
| 492 | return -1; | |||
| 493 | ||||
| 494 | __u64 header_size = ptelemetry_das_offset->header_size; | |||
| 495 | __u64 da1_end = header_size + (__u64)le16_to_cpu(ptelemetry_common_header->da1_last_block) * | |||
| 496 | OCP_TELEMETRY_DATA_BLOCK_SIZE512; | |||
| 497 | __u64 da2_end = header_size + (__u64)le16_to_cpu(ptelemetry_common_header->da2_last_block) * | |||
| 498 | OCP_TELEMETRY_DATA_BLOCK_SIZE512; | |||
| 499 | __u64 da3_end = header_size + (__u64)le16_to_cpu(ptelemetry_common_header->da3_last_block) * | |||
| 500 | OCP_TELEMETRY_DATA_BLOCK_SIZE512; | |||
| 501 | __u64 da4_end = header_size + (__u64)le32_to_cpu(ptelemetry_common_header->da4_last_block) * | |||
| 502 | OCP_TELEMETRY_DATA_BLOCK_SIZE512; | |||
| 503 | ||||
| 504 | ptelemetry_das_offset->da1_start_offset = header_size; | |||
| 505 | ptelemetry_das_offset->da1_size = | |||
| 506 | telemetry_da_size(ptelemetry_das_offset->da1_start_offset, da1_end, buf_len); | |||
| 507 | ||||
| 508 | ptelemetry_das_offset->da2_start_offset = ptelemetry_das_offset->da1_start_offset + | |||
| 509 | ptelemetry_das_offset->da1_size; | |||
| 510 | ptelemetry_das_offset->da2_size = | |||
| 511 | telemetry_da_size(ptelemetry_das_offset->da2_start_offset, da2_end, buf_len); | |||
| 512 | ||||
| 513 | ptelemetry_das_offset->da3_start_offset = ptelemetry_das_offset->da2_start_offset + | |||
| 514 | ptelemetry_das_offset->da2_size; | |||
| 515 | ptelemetry_das_offset->da3_size = | |||
| 516 | telemetry_da_size(ptelemetry_das_offset->da3_start_offset, da3_end, buf_len); | |||
| 517 | ||||
| 518 | ptelemetry_das_offset->da4_start_offset = ptelemetry_das_offset->da3_start_offset + | |||
| 519 | ptelemetry_das_offset->da3_size; | |||
| 520 | ptelemetry_das_offset->da4_size = | |||
| 521 | telemetry_da_size(ptelemetry_das_offset->da4_start_offset, da4_end, buf_len); | |||
| 522 | ||||
| 523 | return 0; | |||
| 524 | } | |||
| 525 | ||||
| 526 | /* | |||
| 527 | * Device tables store ascii_id_length as the zero-based index of the last | |||
| 528 | * character, so the byte count is length + 1. Keep the copy bounded to the | |||
| 529 | * fixed caller buffers used throughout the telemetry parser. | |||
| 530 | */ | |||
| 531 | static size_t ocp_ascii_id_copy_len(__u8 ascii_id_length) | |||
| 532 | { | |||
| 533 | size_t copy_len = (size_t)ascii_id_length + 1; | |||
| 534 | ||||
| 535 | if (copy_len >= OCP_TELEMETRY_DESCRIPTION_MAX256) | |||
| 536 | copy_len = OCP_TELEMETRY_DESCRIPTION_MAX256 - 1; | |||
| 537 | ||||
| 538 | return copy_len; | |||
| 539 | } | |||
| 540 | ||||
| 541 | static int ocp_copy_ascii_id(__le64 ascii_id_offset, __u8 ascii_id_length, char *description) | |||
| 542 | { | |||
| 543 | struct nvme_ocp_telemetry_string_header *pocp_ts_header = | |||
| 544 | (struct nvme_ocp_telemetry_string_header *)pstring_buffer; | |||
| 545 | __u64 ascts = le64_to_cpu(pocp_ts_header->ascts); | |||
| 546 | __u64 asctsz = le64_to_cpu(pocp_ts_header->asctsz); | |||
| 547 | __u64 offset = le64_to_cpu(ascii_id_offset); | |||
| 548 | size_t copy_len = ocp_ascii_id_copy_len(ascii_id_length); | |||
| 549 | ||||
| 550 | /* With the ASCII table inside the log, neither product can overflow. */ | |||
| 551 | if (!ocp_c9_table_fits(ascts, asctsz, pstring_buffer_size) || offset > asctsz || | |||
| 552 | copy_len > (asctsz - offset) * SIZE_OF_DWORD4) | |||
| 553 | return -1; | |||
| 554 | ||||
| 555 | memcpy(description, pstring_buffer + (ascts + offset) * SIZE_OF_DWORD4, copy_len); | |||
| 556 | description[copy_len] = '\0'; | |||
| 557 | ||||
| 558 | return 0; | |||
| 559 | } | |||
| 560 | ||||
| 561 | int get_statistic_id_ascii_string(int identifier, char *description) | |||
| 562 | { | |||
| 563 | struct nvme_ocp_telemetry_string_header *pocp_ts_header; | |||
| 564 | struct nvme_ocp_statistics_identifier_string_table *entry; | |||
| 565 | __u64 entries = 0, i; | |||
| 566 | __u8 *table; | |||
| 567 | ||||
| 568 | if (!pstring_buffer || !description) | |||
| 569 | return -1; | |||
| 570 | ||||
| 571 | pocp_ts_header = (struct nvme_ocp_telemetry_string_header *)pstring_buffer; | |||
| 572 | table = ocp_string_table(pstring_buffer, pstring_buffer_size, | |||
| 573 | le64_to_cpu(pocp_ts_header->sits), | |||
| 574 | le64_to_cpu(pocp_ts_header->sitsz), sizeof(*entry), &entries); | |||
| 575 | ||||
| 576 | for (i = 0; table && i < entries; i++) { | |||
| 577 | entry = (struct nvme_ocp_statistics_identifier_string_table *) | |||
| 578 | (table + i * sizeof(*entry)); | |||
| 579 | ||||
| 580 | if (identifier == (int)le16_to_cpu(entry->vs_statistic_identifier) && | |||
| 581 | !ocp_copy_ascii_id(entry->ascii_id_offset, entry->ascii_id_length, | |||
| 582 | description)) | |||
| 583 | return 0; | |||
| 584 | } | |||
| 585 | ||||
| 586 | // If ASCII string isn't found, see in our internal Map | |||
| 587 | // for 2.5 Spec defined strings | |||
| 588 | if (identifier <= 0x6F) { | |||
| 589 | shr_strscpy(description, | |||
| 590 | statistic_identifiers_map[identifier].description, | |||
| 591 | OCP_TELEMETRY_DESCRIPTION_MAX256); | |||
| 592 | return 0; | |||
| 593 | } | |||
| 594 | ||||
| 595 | return -1; | |||
| 596 | } | |||
| 597 | ||||
| 598 | /* Event and VU event string table entries share one layout. */ | |||
| 599 | static int ocp_event_ascii_string(__le64 start, __le64 size, int identifier, | |||
| 600 | int debug_event_class, char *description) | |||
| 601 | { | |||
| 602 | struct nvme_ocp_event_string_table *entry; | |||
| 603 | __u64 entries = 0, i; | |||
| 604 | __u8 *table; | |||
| 605 | ||||
| 606 | table = ocp_string_table(pstring_buffer, pstring_buffer_size, le64_to_cpu(start), | |||
| 607 | le64_to_cpu(size), sizeof(*entry), &entries); | |||
| 608 | ||||
| 609 | for (i = 0; table && i < entries; i++) { | |||
| 610 | entry = (struct nvme_ocp_event_string_table *)(table + i * sizeof(*entry)); | |||
| 611 | ||||
| 612 | if (identifier == (int)le16_to_cpu(entry->event_identifier) && | |||
| 613 | debug_event_class == (int)entry->debug_event_class && | |||
| 614 | !ocp_copy_ascii_id(entry->ascii_id_offset, entry->ascii_id_length, | |||
| 615 | description)) | |||
| 616 | return 0; | |||
| 617 | } | |||
| 618 | ||||
| 619 | return -1; | |||
| 620 | } | |||
| 621 | ||||
| 622 | static int ocp_spec_event_id_string(int identifier, int debug_event_class, | |||
| 623 | char *description) | |||
| 624 | { | |||
| 625 | const char *str; | |||
| 626 | ||||
| 627 | if (debug_event_class < 0 || | |||
| 628 | debug_event_class >= (int)ARRAY_SIZE(spec_event_id_str)(sizeof(spec_event_id_str) / sizeof((spec_event_id_str)[0]) + 0) || | |||
| 629 | identifier < 0 || | |||
| 630 | identifier >= (int)spec_event_id_str[debug_event_class].count) | |||
| 631 | return -1; | |||
| 632 | ||||
| 633 | str = spec_event_id_str[debug_event_class].str[identifier]; | |||
| 634 | if (!str) | |||
| 635 | return -1; | |||
| 636 | ||||
| 637 | shr_strscpy(description, str, OCP_TELEMETRY_DESCRIPTION_MAX256); | |||
| 638 | return 0; | |||
| 639 | } | |||
| 640 | ||||
| 641 | int get_event_id_ascii_string(int identifier, int debug_event_class, char *description) | |||
| 642 | { | |||
| 643 | struct nvme_ocp_telemetry_string_header *pocp_ts_header = | |||
| 644 | (struct nvme_ocp_telemetry_string_header *)pstring_buffer; | |||
| 645 | ||||
| 646 | if (!pstring_buffer) | |||
| 647 | return -1; | |||
| 648 | ||||
| 649 | if (!ocp_event_ascii_string(pocp_ts_header->ests, pocp_ts_header->estsz, | |||
| 650 | identifier, debug_event_class, description)) | |||
| 651 | return 0; | |||
| 652 | ||||
| 653 | /* TELSLG-14: the String Log may omit Event IDs the spec defines. */ | |||
| 654 | return ocp_spec_event_id_string(identifier, debug_event_class, | |||
| 655 | description); | |||
| 656 | } | |||
| 657 | ||||
| 658 | int get_vu_event_id_ascii_string(int identifier, int debug_event_class, char *description) | |||
| 659 | { | |||
| 660 | struct nvme_ocp_telemetry_string_header *pocp_ts_header = | |||
| 661 | (struct nvme_ocp_telemetry_string_header *)pstring_buffer; | |||
| 662 | ||||
| 663 | if (!pstring_buffer) | |||
| 664 | return -1; | |||
| 665 | ||||
| 666 | return ocp_event_ascii_string(pocp_ts_header->vu_ests, pocp_ts_header->vu_estsz, | |||
| 667 | identifier, debug_event_class, description); | |||
| 668 | } | |||
| 669 | ||||
| 670 | int parse_ocp_telemetry_string_log(int event_fifo_num, int identifier, int debug_event_class, | |||
| 671 | enum ocp_telemetry_string_tables string_table, char *description) | |||
| 672 | { | |||
| 673 | if (pstring_buffer == NULL((void*)0)) | |||
| 674 | return -1; | |||
| 675 | ||||
| 676 | if (event_fifo_num < 0 || event_fifo_num > MAX_NUM_FIFOS16) | |||
| 677 | return -1; | |||
| 678 | ||||
| 679 | if (event_fifo_num != 0) { | |||
| 680 | struct nvme_ocp_telemetry_string_header *pocp_ts_header = | |||
| 681 | (struct nvme_ocp_telemetry_string_header *)pstring_buffer; | |||
| 682 | ||||
| 683 | /* A name that fills all 16 bytes carries no terminator. */ | |||
| 684 | memcpy(description, pocp_ts_header->fifo_ascii_string[event_fifo_num - 1], | |||
| 685 | sizeof(pocp_ts_header->fifo_ascii_string[0])); | |||
| 686 | description[sizeof(pocp_ts_header->fifo_ascii_string[0])] = '\0'; | |||
| 687 | ||||
| 688 | return 0; | |||
| 689 | } | |||
| 690 | ||||
| 691 | if (string_table == STATISTICS_IDENTIFIER_STRING) | |||
| 692 | get_statistic_id_ascii_string(identifier, description); | |||
| 693 | else if (string_table == EVENT_STRING && debug_event_class < 0x80) | |||
| 694 | get_event_id_ascii_string(identifier, debug_event_class, description); | |||
| 695 | else if (string_table == VU_EVENT_STRING || debug_event_class >= 0x80) | |||
| 696 | get_vu_event_id_ascii_string(identifier, debug_event_class, description); | |||
| 697 | ||||
| 698 | return 0; | |||
| 699 | } | |||
| 700 | ||||
| 701 | int parse_time_stamp_event( | |||
| 702 | struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, | |||
| 703 | struct json_object *pevent_descriptor_obj, | |||
| 704 | __u8 *pevent_specific_data, | |||
| 705 | struct json_object *pevent_fifos_object, | |||
| 706 | FILE *fp) | |||
| 707 | { | |||
| 708 | struct nvme_ocp_time_stamp_dbg_evt_class_format *ptime_stamp_event = | |||
| 709 | (struct nvme_ocp_time_stamp_dbg_evt_class_format *) pevent_specific_data; | |||
| 710 | struct nvme_ocp_common_dbg_evt_class_vu_data *ptime_stamp_event_vu_data = NULL((void*)0); | |||
| 711 | __u16 vu_event_id = 0; | |||
| 712 | __u8 *pdata = NULL((void*)0); | |||
| 713 | char description_str[OCP_TELEMETRY_DESCRIPTION_MAX256] = ""; | |||
| 714 | unsigned int vu_data_size = 0; | |||
| 715 | bool_Bool vu_data_present = false0; | |||
| 716 | ||||
| 717 | if ((pevent_descriptor->event_data_size * SIZE_OF_DWORD4) > | |||
| 718 | sizeof(struct nvme_ocp_time_stamp_dbg_evt_class_format)) { | |||
| 719 | vu_data_present = true1; | |||
| 720 | vu_data_size = | |||
| 721 | ((pevent_descriptor->event_data_size * SIZE_OF_DWORD4) - | |||
| 722 | (sizeof(struct nvme_ocp_time_stamp_dbg_evt_class_format) + | |||
| 723 | SIZE_OF_VU_EVENT_ID2)); | |||
| 724 | ||||
| 725 | ptime_stamp_event_vu_data = | |||
| 726 | (struct nvme_ocp_common_dbg_evt_class_vu_data *)((char *)ptime_stamp_event + | |||
| 727 | sizeof(struct nvme_ocp_time_stamp_dbg_evt_class_format)); | |||
| 728 | vu_event_id = le16_to_cpu(ptime_stamp_event_vu_data->vu_event_identifier); | |||
| 729 | pdata = (__u8 *)&(ptime_stamp_event_vu_data->data); | |||
| 730 | ||||
| 731 | parse_ocp_telemetry_string_log(0, vu_event_id, | |||
| 732 | pevent_descriptor->debug_event_class_type, | |||
| 733 | VU_EVENT_STRING, description_str); | |||
| 734 | } else if (pevent_descriptor->event_data_size < 2) | |||
| 735 | return -1; | |||
| 736 | ||||
| 737 | if (pevent_fifos_object != NULL((void*)0)) { | |||
| 738 | json_add_formatted_var_size_str(pevent_descriptor_obj, STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 739 | ptime_stamp_event->time_stamp, DATA_SIZE_88); | |||
| 740 | if (vu_data_present) { | |||
| 741 | json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING"VU Event Identifier", | |||
| 742 | vu_event_id); | |||
| 743 | json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING"VU Event String", | |||
| 744 | description_str); | |||
| 745 | json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA"VU Data", pdata, | |||
| 746 | vu_data_size); | |||
| 747 | } | |||
| 748 | } else { | |||
| 749 | if (fp) { | |||
| 750 | print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 751 | ptime_stamp_event->time_stamp, DATA_SIZE_88, fp); | |||
| 752 | if (vu_data_present) { | |||
| 753 | fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 754 | fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 755 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pdata, vu_data_size, fp); | |||
| 756 | } | |||
| 757 | } else { | |||
| 758 | print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 759 | ptime_stamp_event->time_stamp, DATA_SIZE_88, fp); | |||
| 760 | if (vu_data_present) { | |||
| 761 | printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 762 | printf("%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 763 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pdata, vu_data_size, fp); | |||
| 764 | } | |||
| 765 | } | |||
| 766 | } | |||
| 767 | ||||
| 768 | return 0; | |||
| 769 | } | |||
| 770 | ||||
| 771 | int parse_pcie_event( | |||
| 772 | struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, | |||
| 773 | struct json_object *pevent_descriptor_obj, | |||
| 774 | __u8 *pevent_specific_data, | |||
| 775 | struct json_object *pevent_fifos_object, | |||
| 776 | FILE *fp) | |||
| 777 | { | |||
| 778 | struct nvme_ocp_pcie_dbg_evt_class_format *ppcie_event = | |||
| 779 | (struct nvme_ocp_pcie_dbg_evt_class_format *) pevent_specific_data; | |||
| 780 | struct nvme_ocp_common_dbg_evt_class_vu_data *ppcie_event_vu_data = NULL((void*)0); | |||
| 781 | __u16 vu_event_id = 0; | |||
| 782 | __u8 *pdata = NULL((void*)0); | |||
| 783 | char description_str[OCP_TELEMETRY_DESCRIPTION_MAX256] = ""; | |||
| 784 | unsigned int vu_data_size = 0; | |||
| 785 | bool_Bool vu_data_present = false0; | |||
| 786 | ||||
| 787 | if ((pevent_descriptor->event_data_size * SIZE_OF_DWORD4) > | |||
| 788 | sizeof(struct nvme_ocp_pcie_dbg_evt_class_format)) { | |||
| 789 | vu_data_present = true1; | |||
| 790 | vu_data_size = | |||
| 791 | ((pevent_descriptor->event_data_size * SIZE_OF_DWORD4) - | |||
| 792 | (sizeof(struct nvme_ocp_pcie_dbg_evt_class_format) + | |||
| 793 | SIZE_OF_VU_EVENT_ID2)); | |||
| 794 | ||||
| 795 | ppcie_event_vu_data = | |||
| 796 | (struct nvme_ocp_common_dbg_evt_class_vu_data *)((char *)ppcie_event + | |||
| 797 | sizeof(struct nvme_ocp_pcie_dbg_evt_class_format)); | |||
| 798 | vu_event_id = le16_to_cpu(ppcie_event_vu_data->vu_event_identifier); | |||
| 799 | pdata = (__u8 *)&(ppcie_event_vu_data->data); | |||
| 800 | ||||
| 801 | parse_ocp_telemetry_string_log(0, vu_event_id, | |||
| 802 | pevent_descriptor->debug_event_class_type, | |||
| 803 | VU_EVENT_STRING, description_str); | |||
| 804 | } else if (pevent_descriptor->event_data_size < 1) | |||
| 805 | return -1; | |||
| 806 | ||||
| 807 | if (pevent_fifos_object != NULL((void*)0)) { | |||
| 808 | json_add_formatted_var_size_str(pevent_descriptor_obj, STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 809 | ppcie_event->pCIeDebugEventData, DATA_SIZE_44); | |||
| 810 | if (vu_data_present) { | |||
| 811 | json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING"VU Event Identifier", | |||
| 812 | vu_event_id); | |||
| 813 | json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING"VU Event String", | |||
| 814 | description_str); | |||
| 815 | json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA"VU Data", pdata, | |||
| 816 | vu_data_size); | |||
| 817 | } | |||
| 818 | } else { | |||
| 819 | if (fp) { | |||
| 820 | print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 821 | ppcie_event->pCIeDebugEventData, DATA_SIZE_44, fp); | |||
| 822 | if (vu_data_present) { | |||
| 823 | fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 824 | fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 825 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pdata, vu_data_size, fp); | |||
| 826 | } | |||
| 827 | } else { | |||
| 828 | print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 829 | ppcie_event->pCIeDebugEventData, DATA_SIZE_44, fp); | |||
| 830 | if (vu_data_present) { | |||
| 831 | printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 832 | printf("%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 833 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pdata, vu_data_size, fp); | |||
| 834 | } | |||
| 835 | } | |||
| 836 | } | |||
| 837 | ||||
| 838 | return 0; | |||
| 839 | } | |||
| 840 | ||||
| 841 | int parse_nvme_event( | |||
| 842 | struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, | |||
| 843 | struct json_object *pevent_descriptor_obj, | |||
| 844 | __u8 *pevent_specific_data, | |||
| 845 | struct json_object *pevent_fifos_object, | |||
| 846 | FILE *fp) | |||
| 847 | { | |||
| 848 | struct nvme_ocp_nvme_dbg_evt_class_format *pnvme_event = | |||
| 849 | (struct nvme_ocp_nvme_dbg_evt_class_format *) pevent_specific_data; | |||
| 850 | struct nvme_ocp_common_dbg_evt_class_vu_data *pnvme_event_vu_data = NULL((void*)0); | |||
| 851 | __u16 vu_event_id = 0; | |||
| 852 | __u8 *pdata = NULL((void*)0); | |||
| 853 | char description_str[OCP_TELEMETRY_DESCRIPTION_MAX256] = ""; | |||
| 854 | unsigned int vu_data_size = 0; | |||
| 855 | bool_Bool vu_data_present = false0; | |||
| 856 | ||||
| 857 | if ((pevent_descriptor->event_data_size * SIZE_OF_DWORD4) > | |||
| 858 | sizeof(struct nvme_ocp_nvme_dbg_evt_class_format)) { | |||
| 859 | vu_data_present = true1; | |||
| 860 | vu_data_size = | |||
| 861 | ((pevent_descriptor->event_data_size * SIZE_OF_DWORD4) - | |||
| 862 | (sizeof(struct nvme_ocp_nvme_dbg_evt_class_format) + | |||
| 863 | SIZE_OF_VU_EVENT_ID2)); | |||
| 864 | pnvme_event_vu_data = | |||
| 865 | (struct nvme_ocp_common_dbg_evt_class_vu_data *)((char *)pnvme_event + | |||
| 866 | sizeof(struct nvme_ocp_nvme_dbg_evt_class_format)); | |||
| 867 | ||||
| 868 | vu_event_id = le16_to_cpu(pnvme_event_vu_data->vu_event_identifier); | |||
| 869 | pdata = (__u8 *)&(pnvme_event_vu_data->data); | |||
| 870 | ||||
| 871 | parse_ocp_telemetry_string_log(0, vu_event_id, | |||
| 872 | pevent_descriptor->debug_event_class_type, | |||
| 873 | VU_EVENT_STRING, | |||
| 874 | description_str); | |||
| 875 | } else if (pevent_descriptor->event_data_size < 2) | |||
| 876 | return -1; | |||
| 877 | ||||
| 878 | if (pevent_fifos_object != NULL((void*)0)) { | |||
| 879 | json_add_formatted_var_size_str(pevent_descriptor_obj, STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 880 | pnvme_event->nvmeDebugEventData, DATA_SIZE_88); | |||
| 881 | if (vu_data_present) { | |||
| 882 | json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING"VU Event Identifier", | |||
| 883 | vu_event_id); | |||
| 884 | json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING"VU Event String", | |||
| 885 | description_str); | |||
| 886 | json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA"VU Data", pdata, | |||
| 887 | vu_data_size); | |||
| 888 | } | |||
| 889 | } else { | |||
| 890 | if (fp) { | |||
| 891 | print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 892 | pnvme_event->nvmeDebugEventData, DATA_SIZE_88, fp); | |||
| 893 | if (vu_data_present) { | |||
| 894 | fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 895 | fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 896 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pdata, vu_data_size, fp); | |||
| 897 | } | |||
| 898 | } else { | |||
| 899 | print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 900 | pnvme_event->nvmeDebugEventData, DATA_SIZE_88, fp); | |||
| 901 | if (vu_data_present) { | |||
| 902 | printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 903 | printf("%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 904 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pdata, vu_data_size, fp); | |||
| 905 | } | |||
| 906 | } | |||
| 907 | } | |||
| 908 | ||||
| 909 | return 0; | |||
| 910 | } | |||
| 911 | ||||
| 912 | void parse_common_event(struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, | |||
| 913 | struct json_object *pevent_descriptor_obj, __u8 *pevent_specific_data, | |||
| 914 | struct json_object *pevent_fifos_object, FILE *fp) | |||
| 915 | { | |||
| 916 | if (pevent_specific_data) { | |||
| 917 | struct nvme_ocp_common_dbg_evt_class_vu_data *pcommon_debug_event_vu_data = | |||
| 918 | (struct nvme_ocp_common_dbg_evt_class_vu_data *) pevent_specific_data; | |||
| 919 | ||||
| 920 | __u16 vu_event_id = le16_to_cpu(pcommon_debug_event_vu_data->vu_event_identifier); | |||
| 921 | char description_str[OCP_TELEMETRY_DESCRIPTION_MAX256] = ""; | |||
| 922 | __u8 *pdata = (__u8 *)&(pcommon_debug_event_vu_data->data); | |||
| 923 | ||||
| 924 | unsigned int vu_data_size = ((pevent_descriptor->event_data_size * | |||
| 925 | SIZE_OF_DWORD4) - SIZE_OF_VU_EVENT_ID2); | |||
| 926 | ||||
| 927 | parse_ocp_telemetry_string_log(0, vu_event_id, | |||
| 928 | pevent_descriptor->debug_event_class_type, | |||
| 929 | VU_EVENT_STRING, description_str); | |||
| 930 | ||||
| 931 | if (pevent_fifos_object != NULL((void*)0)) { | |||
| 932 | json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING"VU Event Identifier", | |||
| 933 | vu_event_id); | |||
| 934 | json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING"VU Event String", | |||
| 935 | description_str); | |||
| 936 | json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA"VU Data", pdata, | |||
| 937 | vu_data_size); | |||
| 938 | } else { | |||
| 939 | if (fp) { | |||
| 940 | fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 941 | fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 942 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pdata, vu_data_size, fp); | |||
| 943 | } else { | |||
| 944 | printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 945 | printf("%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 946 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pdata, vu_data_size, fp); | |||
| 947 | } | |||
| 948 | } | |||
| 949 | } | |||
| 950 | } | |||
| 951 | ||||
| 952 | int parse_media_wear_event( | |||
| 953 | struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, | |||
| 954 | struct json_object *pevent_descriptor_obj, | |||
| 955 | __u8 *pevent_specific_data, | |||
| 956 | struct json_object *pevent_fifos_object, | |||
| 957 | FILE *fp) | |||
| 958 | { | |||
| 959 | struct nvme_ocp_media_wear_dbg_evt_class_format *pmedia_wear_event = | |||
| 960 | (struct nvme_ocp_media_wear_dbg_evt_class_format *) pevent_specific_data; | |||
| 961 | struct nvme_ocp_common_dbg_evt_class_vu_data *pmedia_wear_event_vu_data = NULL((void*)0); | |||
| 962 | ||||
| 963 | __u16 vu_event_id = 0; | |||
| 964 | __u8 *pdata = NULL((void*)0); | |||
| 965 | char description_str[OCP_TELEMETRY_DESCRIPTION_MAX256] = ""; | |||
| 966 | unsigned int vu_data_size = 0; | |||
| 967 | bool_Bool vu_data_present = false0; | |||
| 968 | ||||
| 969 | if ((pevent_descriptor->event_data_size * SIZE_OF_DWORD4) > | |||
| 970 | sizeof(struct nvme_ocp_media_wear_dbg_evt_class_format)) { | |||
| 971 | vu_data_present = true1; | |||
| 972 | vu_data_size = | |||
| 973 | ((pevent_descriptor->event_data_size * SIZE_OF_DWORD4) - | |||
| 974 | (sizeof(struct nvme_ocp_media_wear_dbg_evt_class_format) + | |||
| 975 | SIZE_OF_VU_EVENT_ID2)); | |||
| 976 | ||||
| 977 | pmedia_wear_event_vu_data = | |||
| 978 | (struct nvme_ocp_common_dbg_evt_class_vu_data *)((char *)pmedia_wear_event + | |||
| 979 | sizeof(struct nvme_ocp_media_wear_dbg_evt_class_format)); | |||
| 980 | vu_event_id = le16_to_cpu(pmedia_wear_event_vu_data->vu_event_identifier); | |||
| 981 | pdata = (__u8 *)&(pmedia_wear_event_vu_data->data); | |||
| 982 | ||||
| 983 | parse_ocp_telemetry_string_log(0, vu_event_id, | |||
| 984 | pevent_descriptor->debug_event_class_type, | |||
| 985 | VU_EVENT_STRING, | |||
| 986 | description_str); | |||
| 987 | } else if (pevent_descriptor->event_data_size < 3) | |||
| 988 | return -1; | |||
| 989 | ||||
| 990 | if (pevent_fifos_object != NULL((void*)0)) { | |||
| 991 | json_add_formatted_var_size_str(pevent_descriptor_obj, STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 992 | pmedia_wear_event->currentMediaWear, DATA_SIZE_1212); | |||
| 993 | if (vu_data_present) { | |||
| 994 | json_add_formatted_u32_str(pevent_descriptor_obj, STR_VU_EVENT_ID_STRING"VU Event Identifier", | |||
| 995 | vu_event_id); | |||
| 996 | json_object_add_value_string(pevent_descriptor_obj, STR_VU_EVENT_STRING"VU Event String", | |||
| 997 | description_str); | |||
| 998 | json_add_formatted_var_size_str(pevent_descriptor_obj, STR_VU_DATA"VU Data", pdata, | |||
| 999 | vu_data_size); | |||
| 1000 | } | |||
| 1001 | } else { | |||
| 1002 | if (fp) { | |||
| 1003 | print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 1004 | pmedia_wear_event->currentMediaWear, DATA_SIZE_1212, fp); | |||
| 1005 | if (vu_data_present) { | |||
| 1006 | fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 1007 | fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 1008 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pdata, vu_data_size, fp); | |||
| 1009 | } | |||
| 1010 | } else { | |||
| 1011 | print_formatted_var_size_str(STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 1012 | pmedia_wear_event->currentMediaWear, DATA_SIZE_1212, NULL((void*)0)); | |||
| 1013 | if (vu_data_present) { | |||
| 1014 | printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 1015 | printf("%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 1016 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pdata, vu_data_size, fp); | |||
| 1017 | } | |||
| 1018 | } | |||
| 1019 | } | |||
| 1020 | ||||
| 1021 | return 0; | |||
| 1022 | } | |||
| 1023 | ||||
| 1024 | /* | |||
| 1025 | * The Virtual FIFO Event class (0Bh) carries a single Dword of class specific | |||
| 1026 | * data and no VU data: a VU Virtual FIFO Identifier and a reserved half-word. | |||
| 1027 | * The identifier names a virtual FIFO in the String Log's VU event table, and | |||
| 1028 | * splits into the enclosing physical Event FIFO number and the virtual FIFO | |||
| 1029 | * number within that physical FIFO. | |||
| 1030 | */ | |||
| 1031 | int parse_virtual_fifo_event( | |||
| 1032 | struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, | |||
| 1033 | struct json_object *pevent_descriptor_obj, | |||
| 1034 | __u8 *pevent_specific_data, | |||
| 1035 | struct json_object *pevent_fifos_object, | |||
| 1036 | FILE *fp) | |||
| 1037 | { | |||
| 1038 | struct nvme_ocp_virtual_fifo_dbg_evt_class_format *pvirtual_fifo_event = | |||
| 1039 | (struct nvme_ocp_virtual_fifo_dbg_evt_class_format *) | |||
| 1040 | pevent_specific_data; | |||
| 1041 | char description_str[OCP_TELEMETRY_DESCRIPTION_MAX256] = ""; | |||
| 1042 | char fifo_name_str[OCP_TELEMETRY_DESCRIPTION_MAX256] = ""; | |||
| 1043 | __u16 vu_virtual_fifo_id = 0; | |||
| 1044 | __u8 physical_fifo_num = 0; | |||
| 1045 | __u16 virtual_fifo_num = 0; | |||
| 1046 | ||||
| 1047 | if ((pevent_descriptor->event_data_size * SIZE_OF_DWORD4) < | |||
| 1048 | sizeof(*pvirtual_fifo_event)) | |||
| 1049 | return -1; | |||
| 1050 | ||||
| 1051 | vu_virtual_fifo_id = | |||
| 1052 | le16_to_cpu(pvirtual_fifo_event->vu_virtual_fifo_identifier); | |||
| 1053 | physical_fifo_num = vu_virtual_fifo_id >> VU_VIRTUAL_FIFO_PHY_NUM_SHIFT11; | |||
| 1054 | virtual_fifo_num = vu_virtual_fifo_id & VU_VIRTUAL_FIFO_NUM_MASK0x07ff; | |||
| 1055 | ||||
| 1056 | parse_ocp_telemetry_string_log(0, vu_virtual_fifo_id, | |||
| 1057 | pevent_descriptor->debug_event_class_type, | |||
| 1058 | VU_EVENT_STRING, description_str); | |||
| 1059 | ||||
| 1060 | /* | |||
| 1061 | * A physical FIFO number of 0h is invalid and the field is wide enough | |||
| 1062 | * to hold values past the 10h maximum, so range-check it before using | |||
| 1063 | * it to index the string log's FIFO name array. | |||
| 1064 | */ | |||
| 1065 | if (physical_fifo_num >= 1 && physical_fifo_num <= MAX_NUM_FIFOS16) | |||
| 1066 | parse_ocp_telemetry_string_log(physical_fifo_num, 0, 0, | |||
| 1067 | EVENT_STRING, fifo_name_str); | |||
| 1068 | ||||
| 1069 | if (pevent_fifos_object != NULL((void*)0)) { | |||
| 1070 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1071 | STR_VU_VIRTUAL_FIFO_ID"VU Virtual FIFO Identifier", | |||
| 1072 | vu_virtual_fifo_id); | |||
| 1073 | json_object_add_value_string(pevent_descriptor_obj, | |||
| 1074 | STR_VU_VIRTUAL_FIFO_STRING"VU Virtual FIFO String", | |||
| 1075 | description_str); | |||
| 1076 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1077 | STR_PHYSICAL_EVENT_FIFO_NUM"Physical Event FIFO Number", | |||
| 1078 | physical_fifo_num); | |||
| 1079 | json_object_add_value_string(pevent_descriptor_obj, | |||
| 1080 | STR_PHYSICAL_EVENT_FIFO_STRING"Physical Event FIFO String", | |||
| 1081 | fifo_name_str); | |||
| 1082 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1083 | STR_VIRTUAL_FIFO_NUM"Virtual FIFO Number", | |||
| 1084 | virtual_fifo_num); | |||
| 1085 | } else if (fp) { | |||
| 1086 | fprintf(fp, "%s: 0x%x\n", STR_VU_VIRTUAL_FIFO_ID"VU Virtual FIFO Identifier", | |||
| 1087 | vu_virtual_fifo_id); | |||
| 1088 | fprintf(fp, "%s: %s\n", STR_VU_VIRTUAL_FIFO_STRING"VU Virtual FIFO String", | |||
| 1089 | description_str); | |||
| 1090 | fprintf(fp, "%s: 0x%x\n", STR_PHYSICAL_EVENT_FIFO_NUM"Physical Event FIFO Number", | |||
| 1091 | physical_fifo_num); | |||
| 1092 | fprintf(fp, "%s: %s\n", STR_PHYSICAL_EVENT_FIFO_STRING"Physical Event FIFO String", | |||
| 1093 | fifo_name_str); | |||
| 1094 | fprintf(fp, "%s: 0x%x\n", STR_VIRTUAL_FIFO_NUM"Virtual FIFO Number", | |||
| 1095 | virtual_fifo_num); | |||
| 1096 | } else { | |||
| 1097 | printf("%s: 0x%x\n", STR_VU_VIRTUAL_FIFO_ID"VU Virtual FIFO Identifier", | |||
| 1098 | vu_virtual_fifo_id); | |||
| 1099 | printf("%s: %s\n", STR_VU_VIRTUAL_FIFO_STRING"VU Virtual FIFO String", | |||
| 1100 | description_str); | |||
| 1101 | printf("%s: 0x%x\n", STR_PHYSICAL_EVENT_FIFO_NUM"Physical Event FIFO Number", | |||
| 1102 | physical_fifo_num); | |||
| 1103 | printf("%s: %s\n", STR_PHYSICAL_EVENT_FIFO_STRING"Physical Event FIFO String", | |||
| 1104 | fifo_name_str); | |||
| 1105 | printf("%s: 0x%x\n", STR_VIRTUAL_FIFO_NUM"Virtual FIFO Number", | |||
| 1106 | virtual_fifo_num); | |||
| 1107 | } | |||
| 1108 | ||||
| 1109 | return 0; | |||
| 1110 | } | |||
| 1111 | ||||
| 1112 | /* | |||
| 1113 | * A VU Event Identifier and VU Data follow a class's fixed record when its | |||
| 1114 | * Event Data Size reaches past it. @vu_data_size excludes the identifier. | |||
| 1115 | */ | |||
| 1116 | static void parse_class_vu_fields( | |||
| 1117 | struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, | |||
| 1118 | struct json_object *pevent_descriptor_obj, | |||
| 1119 | __u8 *pvu_fields, unsigned int vu_data_size, | |||
| 1120 | struct json_object *pevent_fifos_object, | |||
| 1121 | FILE *fp) | |||
| 1122 | { | |||
| 1123 | struct nvme_ocp_common_dbg_evt_class_vu_data *pvu_data = | |||
| 1124 | (struct nvme_ocp_common_dbg_evt_class_vu_data *)pvu_fields; | |||
| 1125 | char description_str[OCP_TELEMETRY_DESCRIPTION_MAX256] = ""; | |||
| 1126 | __u16 vu_event_id = le16_to_cpu(pvu_data->vu_event_identifier); | |||
| 1127 | ||||
| 1128 | parse_ocp_telemetry_string_log(0, vu_event_id, | |||
| 1129 | pevent_descriptor->debug_event_class_type, | |||
| 1130 | VU_EVENT_STRING, description_str); | |||
| 1131 | ||||
| 1132 | if (pevent_fifos_object != NULL((void*)0)) { | |||
| 1133 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1134 | STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 1135 | json_object_add_value_string(pevent_descriptor_obj, | |||
| 1136 | STR_VU_EVENT_STRING"VU Event String", | |||
| 1137 | description_str); | |||
| 1138 | json_add_formatted_var_size_str(pevent_descriptor_obj, | |||
| 1139 | STR_VU_DATA"VU Data", pvu_data->data, | |||
| 1140 | vu_data_size); | |||
| 1141 | } else if (fp) { | |||
| 1142 | fprintf(fp, "%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 1143 | fprintf(fp, "%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 1144 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pvu_data->data, | |||
| 1145 | vu_data_size, fp); | |||
| 1146 | } else { | |||
| 1147 | printf("%s: 0x%x\n", STR_VU_EVENT_ID_STRING"VU Event Identifier", vu_event_id); | |||
| 1148 | printf("%s: %s\n", STR_VU_EVENT_STRING"VU Event String", description_str); | |||
| 1149 | print_formatted_var_size_str(STR_VU_DATA"VU Data", pvu_data->data, | |||
| 1150 | vu_data_size, NULL((void*)0)); | |||
| 1151 | } | |||
| 1152 | } | |||
| 1153 | ||||
| 1154 | /* | |||
| 1155 | * The SMBUS/I2C/I3C Debug Event class (0Ch) carries one Dword of class | |||
| 1156 | * specific data: the SMBUS Debug Event Data and a reserved half-word. The | |||
| 1157 | * Event Data values are defined only for the NACK error Event ID. | |||
| 1158 | */ | |||
| 1159 | int parse_smbus_event( | |||
| 1160 | struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, | |||
| 1161 | struct json_object *pevent_descriptor_obj, | |||
| 1162 | __u8 *pevent_specific_data, | |||
| 1163 | struct json_object *pevent_fifos_object, | |||
| 1164 | FILE *fp) | |||
| 1165 | { | |||
| 1166 | struct nvme_ocp_smbus_dbg_evt_class_format *psmbus_event = | |||
| 1167 | (struct nvme_ocp_smbus_dbg_evt_class_format *) | |||
| 1168 | pevent_specific_data; | |||
| 1169 | unsigned int event_size = | |||
| 1170 | pevent_descriptor->event_data_size * SIZE_OF_DWORD4; | |||
| 1171 | __u16 event_id = le16_to_cpu(pevent_descriptor->event_id); | |||
| 1172 | const char *event_data_str = NULL((void*)0); | |||
| 1173 | __u16 event_data = 0; | |||
| 1174 | ||||
| 1175 | if (event_size < sizeof(*psmbus_event)) | |||
| 1176 | return -1; | |||
| 1177 | ||||
| 1178 | event_data = le16_to_cpu(psmbus_event->smbus_debug_event_data); | |||
| 1179 | event_data_str = telemetry_smbus_event_data_to_string(event_id, | |||
| 1180 | event_data); | |||
| 1181 | ||||
| 1182 | if (pevent_fifos_object != NULL((void*)0)) { | |||
| 1183 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1184 | STR_SMBUS_DEBUG_EVENT_DATA"SMBUS Debug Event Data", | |||
| 1185 | event_data); | |||
| 1186 | json_object_add_value_string(pevent_descriptor_obj, | |||
| 1187 | STR_SMBUS_DEBUG_EVENT_DATA_STRING"SMBUS Debug Event Data String", | |||
| 1188 | event_data_str); | |||
| 1189 | } else if (fp) { | |||
| 1190 | fprintf(fp, "%s: 0x%x\n", STR_SMBUS_DEBUG_EVENT_DATA"SMBUS Debug Event Data", | |||
| 1191 | event_data); | |||
| 1192 | fprintf(fp, "%s: %s\n", STR_SMBUS_DEBUG_EVENT_DATA_STRING"SMBUS Debug Event Data String", | |||
| 1193 | event_data_str); | |||
| 1194 | } else { | |||
| 1195 | printf("%s: 0x%x\n", STR_SMBUS_DEBUG_EVENT_DATA"SMBUS Debug Event Data", event_data); | |||
| 1196 | printf("%s: %s\n", STR_SMBUS_DEBUG_EVENT_DATA_STRING"SMBUS Debug Event Data String", | |||
| 1197 | event_data_str); | |||
| 1198 | } | |||
| 1199 | ||||
| 1200 | if (event_size > sizeof(*psmbus_event)) | |||
| 1201 | parse_class_vu_fields(pevent_descriptor, pevent_descriptor_obj, | |||
| 1202 | pevent_specific_data + sizeof(*psmbus_event), | |||
| 1203 | event_size - sizeof(*psmbus_event) - SIZE_OF_VU_EVENT_ID2, | |||
| 1204 | pevent_fifos_object, fp); | |||
| 1205 | ||||
| 1206 | return 0; | |||
| 1207 | } | |||
| 1208 | ||||
| 1209 | /* | |||
| 1210 | * The MCTP Debug Event class (0Dh) carries two Dwords of class specific | |||
| 1211 | * data: the MCTP Debug Event Data, whose values depend on the Event ID, the | |||
| 1212 | * Transport Protocol Information, the Event Flags and the MCTP Transport | |||
| 1213 | * Header. The header holds captured packet bytes only when the Transport | |||
| 1214 | * Header Valid flag is set, so it is left out otherwise. | |||
| 1215 | */ | |||
| 1216 | int parse_mctp_event( | |||
| 1217 | struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor, | |||
| 1218 | struct json_object *pevent_descriptor_obj, | |||
| 1219 | __u8 *pevent_specific_data, | |||
| 1220 | struct json_object *pevent_fifos_object, | |||
| 1221 | FILE *fp) | |||
| 1222 | { | |||
| 1223 | struct nvme_ocp_mctp_dbg_evt_class_format *pmctp_event = | |||
| 1224 | (struct nvme_ocp_mctp_dbg_evt_class_format *) | |||
| 1225 | pevent_specific_data; | |||
| 1226 | unsigned int event_size = | |||
| 1227 | pevent_descriptor->event_data_size * SIZE_OF_DWORD4; | |||
| 1228 | __u16 event_id = le16_to_cpu(pevent_descriptor->event_id); | |||
| 1229 | const char *event_data_str = NULL((void*)0); | |||
| 1230 | const char *protocol_str = NULL((void*)0); | |||
| 1231 | bool_Bool header_valid = false0; | |||
| 1232 | __u16 event_data = 0; | |||
| 1233 | ||||
| 1234 | if (event_size < sizeof(*pmctp_event)) | |||
| 1235 | return -1; | |||
| 1236 | ||||
| 1237 | event_data = le16_to_cpu(pmctp_event->mctp_debug_event_data); | |||
| 1238 | event_data_str = telemetry_mctp_event_data_to_string(event_id, | |||
| 1239 | event_data); | |||
| 1240 | protocol_str = telemetry_mctp_transport_protocol_to_string( | |||
| 1241 | pmctp_event->transport_protocol); | |||
| 1242 | header_valid = pmctp_event->event_flags & | |||
| 1243 | MCTP_EVENT_FLAG_TRANSPORT_HEADER_VALID0x80; | |||
| 1244 | ||||
| 1245 | if (pevent_fifos_object != NULL((void*)0)) { | |||
| 1246 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1247 | STR_MCTP_DEBUG_EVENT_DATA"MCTP Debug Event Data", | |||
| 1248 | event_data); | |||
| 1249 | json_object_add_value_string(pevent_descriptor_obj, | |||
| 1250 | STR_MCTP_DEBUG_EVENT_DATA_STRING"MCTP Debug Event Data String", | |||
| 1251 | event_data_str); | |||
| 1252 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1253 | STR_MCTP_TRANSPORT_PROTOCOL"MCTP Transport Protocol Information", | |||
| 1254 | pmctp_event->transport_protocol); | |||
| 1255 | json_object_add_value_string(pevent_descriptor_obj, | |||
| 1256 | STR_MCTP_TRANSPORT_PROTOCOL_STRING"MCTP Transport Protocol String", | |||
| 1257 | protocol_str); | |||
| 1258 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1259 | STR_MCTP_TRANSPORT_HEADER_VALID"MCTP Transport Header Valid", | |||
| 1260 | header_valid); | |||
| 1261 | if (header_valid) | |||
| 1262 | json_add_formatted_var_size_str(pevent_descriptor_obj, | |||
| 1263 | STR_MCTP_TRANSPORT_HEADER"MCTP Transport Header", | |||
| 1264 | pmctp_event->transport_header, DATA_SIZE_44); | |||
| 1265 | } else if (fp) { | |||
| 1266 | fprintf(fp, "%s: 0x%x\n", STR_MCTP_DEBUG_EVENT_DATA"MCTP Debug Event Data", | |||
| 1267 | event_data); | |||
| 1268 | fprintf(fp, "%s: %s\n", STR_MCTP_DEBUG_EVENT_DATA_STRING"MCTP Debug Event Data String", | |||
| 1269 | event_data_str); | |||
| 1270 | fprintf(fp, "%s: 0x%x\n", STR_MCTP_TRANSPORT_PROTOCOL"MCTP Transport Protocol Information", | |||
| 1271 | pmctp_event->transport_protocol); | |||
| 1272 | fprintf(fp, "%s: %s\n", STR_MCTP_TRANSPORT_PROTOCOL_STRING"MCTP Transport Protocol String", | |||
| 1273 | protocol_str); | |||
| 1274 | fprintf(fp, "%s: 0x%x\n", STR_MCTP_TRANSPORT_HEADER_VALID"MCTP Transport Header Valid", | |||
| 1275 | header_valid); | |||
| 1276 | if (header_valid) | |||
| 1277 | print_formatted_var_size_str(STR_MCTP_TRANSPORT_HEADER"MCTP Transport Header", | |||
| 1278 | pmctp_event->transport_header, DATA_SIZE_44, fp); | |||
| 1279 | } else { | |||
| 1280 | printf("%s: 0x%x\n", STR_MCTP_DEBUG_EVENT_DATA"MCTP Debug Event Data", event_data); | |||
| 1281 | printf("%s: %s\n", STR_MCTP_DEBUG_EVENT_DATA_STRING"MCTP Debug Event Data String", | |||
| 1282 | event_data_str); | |||
| 1283 | printf("%s: 0x%x\n", STR_MCTP_TRANSPORT_PROTOCOL"MCTP Transport Protocol Information", | |||
| 1284 | pmctp_event->transport_protocol); | |||
| 1285 | printf("%s: %s\n", STR_MCTP_TRANSPORT_PROTOCOL_STRING"MCTP Transport Protocol String", | |||
| 1286 | protocol_str); | |||
| 1287 | printf("%s: 0x%x\n", STR_MCTP_TRANSPORT_HEADER_VALID"MCTP Transport Header Valid", | |||
| 1288 | header_valid); | |||
| 1289 | if (header_valid) | |||
| 1290 | print_formatted_var_size_str(STR_MCTP_TRANSPORT_HEADER"MCTP Transport Header", | |||
| 1291 | pmctp_event->transport_header, DATA_SIZE_44, NULL((void*)0)); | |||
| 1292 | } | |||
| 1293 | ||||
| 1294 | if (event_size > sizeof(*pmctp_event)) | |||
| 1295 | parse_class_vu_fields(pevent_descriptor, pevent_descriptor_obj, | |||
| 1296 | pevent_specific_data + sizeof(*pmctp_event), | |||
| 1297 | event_size - sizeof(*pmctp_event) - SIZE_OF_VU_EVENT_ID2, | |||
| 1298 | pevent_fifos_object, fp); | |||
| 1299 | ||||
| 1300 | return 0; | |||
| 1301 | } | |||
| 1302 | ||||
| 1303 | int parse_event_fifo(unsigned int fifo_num, unsigned char *pfifo_start, | |||
| 1304 | struct json_object *pevent_fifos_object, unsigned char *pstring_buffer, | |||
| 1305 | struct nvme_ocp_telemetry_offsets *poffsets, __u64 fifo_size, FILE *fp) | |||
| 1306 | { | |||
| 1307 | if (NULL((void*)0) == pfifo_start || NULL((void*)0) == poffsets) { | |||
| ||||
| 1308 | nvme_show_error("Input buffer was NULL")nvme_show_message(1, "Input buffer was NULL"); | |||
| 1309 | return -1; | |||
| 1310 | } | |||
| 1311 | ||||
| 1312 | int status = 0, ret = 0; | |||
| 1313 | unsigned int event_fifo_number = fifo_num + 1; | |||
| 1314 | __cleanup_free__attribute__((cleanup(shr_freep))) char *description = calloc(1, 40 + 1); | |||
| 1315 | ||||
| 1316 | if (!description) { | |||
| 1317 | nvme_show_error("Failed to allocate description buffer")nvme_show_message(1, "Failed to allocate description buffer"); | |||
| 1318 | return -1; | |||
| 1319 | } | |||
| 1320 | ||||
| 1321 | status = | |||
| 1322 | parse_ocp_telemetry_string_log(event_fifo_number, 0, 0, EVENT_STRING, description); | |||
| 1323 | ||||
| 1324 | if (status
| |||
| 1325 | nvme_show_error("Failed to get C9 String. status: %d", status)nvme_show_message(1, "Failed to get C9 String. status: %d", status ); | |||
| ||||
| 1326 | return -1; | |||
| 1327 | } | |||
| 1328 | ||||
| 1329 | char event_fifo_name[100] = {0}; | |||
| 1330 | ||||
| 1331 | snprintf(event_fifo_name, sizeof(event_fifo_name), "%s%d%s%s", "EVENT FIFO ", | |||
| 1332 | event_fifo_number, " - ", description); | |||
| 1333 | ||||
| 1334 | __cleanup_json_object__attribute__((cleanup(json_free_objectp))) struct json_object *pevent_fifo_array = NULL((void*)0); | |||
| 1335 | ||||
| 1336 | if (pevent_fifos_object != NULL((void*)0)) | |||
| 1337 | pevent_fifo_array = json_create_array()json_object_new_array(); | |||
| 1338 | else { | |||
| 1339 | char buffer[1024] = {0}; | |||
| 1340 | ||||
| 1341 | snprintf(buffer, sizeof(buffer), "%s%s\n%s", STR_LINE"==============================================================================\n", | |||
| 1342 | event_fifo_name, STR_LINE"==============================================================================\n"); | |||
| 1343 | if (fp) | |||
| 1344 | fprintf(fp, "%s", buffer); | |||
| 1345 | else | |||
| 1346 | printf("%s", buffer); | |||
| 1347 | } | |||
| 1348 | ||||
| 1349 | int offset_to_move = 0; | |||
| 1350 | unsigned int event_des_size = sizeof(struct nvme_ocp_telemetry_event_descriptor); | |||
| 1351 | ||||
| 1352 | while ((fifo_size > 0) && (offset_to_move < fifo_size)) { | |||
| 1353 | struct nvme_ocp_telemetry_event_descriptor *pevent_descriptor = | |||
| 1354 | (struct nvme_ocp_telemetry_event_descriptor *) | |||
| 1355 | (pfifo_start + offset_to_move); | |||
| 1356 | ||||
| 1357 | /* check if at the end of the list */ | |||
| 1358 | if (pevent_descriptor->debug_event_class_type == RESERVED_CLASS_TYPE) | |||
| 1359 | break; | |||
| 1360 | ||||
| 1361 | __u8 *pevent_specific_data = NULL((void*)0); | |||
| 1362 | __u16 event_id = 0; | |||
| 1363 | char description_str[OCP_TELEMETRY_DESCRIPTION_MAX256] = ""; | |||
| 1364 | unsigned int data_size = 0; | |||
| 1365 | __u64 remaining = fifo_size - offset_to_move; | |||
| 1366 | bool_Bool is_snapshot = pevent_descriptor->debug_event_class_type == | |||
| 1367 | STATISTIC_SNAPSHOT_CLASS_TYPE; | |||
| 1368 | struct nvme_ocp_statistic_snapshot_evt_class_format *psnapshot = | |||
| 1369 | (struct nvme_ocp_statistic_snapshot_evt_class_format *) | |||
| 1370 | pevent_descriptor; | |||
| 1371 | ||||
| 1372 | /* | |||
| 1373 | * Bound the whole entry by what is left of the FIFO before its | |||
| 1374 | * size field or any class specific data is read. A snapshot's | |||
| 1375 | * Event ID and Event Data Size bytes are reserved, so its | |||
| 1376 | * statistic's identifier and size are reported instead. | |||
| 1377 | */ | |||
| 1378 | event_des_size = is_snapshot ? sizeof(*psnapshot) : | |||
| 1379 | sizeof(struct nvme_ocp_telemetry_event_descriptor); | |||
| 1380 | if (remaining < event_des_size) { | |||
| 1381 | nvme_show_error(nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x needs a %u-byte header, %llu bytes left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , event_des_size, (unsigned long long)remaining) | |||
| 1382 | "Invalid entry at offset 0x%x of Event FIFO %u: "nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x needs a %u-byte header, %llu bytes left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , event_des_size, (unsigned long long)remaining) | |||
| 1383 | "class 0x%x needs a %u-byte header, %llu bytes left in FIFO",nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x needs a %u-byte header, %llu bytes left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , event_des_size, (unsigned long long)remaining) | |||
| 1384 | offset_to_move, event_fifo_number,nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x needs a %u-byte header, %llu bytes left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , event_des_size, (unsigned long long)remaining) | |||
| 1385 | pevent_descriptor->debug_event_class_type,nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x needs a %u-byte header, %llu bytes left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , event_des_size, (unsigned long long)remaining) | |||
| 1386 | event_des_size, (unsigned long long)remaining)nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x needs a %u-byte header, %llu bytes left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , event_des_size, (unsigned long long)remaining); | |||
| 1387 | return -1; | |||
| 1388 | } | |||
| 1389 | ||||
| 1390 | /* Data sizes are in Dwords */ | |||
| 1391 | if (is_snapshot) | |||
| 1392 | data_size = le16_to_cpu(psnapshot->stat_data_size) * | |||
| 1393 | SIZE_OF_DWORD4; | |||
| 1394 | else | |||
| 1395 | data_size = pevent_descriptor->event_data_size * | |||
| 1396 | SIZE_OF_DWORD4; | |||
| 1397 | if (remaining - event_des_size < data_size) { | |||
| 1398 | nvme_show_error(nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x, %s 0x%x declares %u data bytes, %llu left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , is_snapshot ? "Statistic ID" : "Event ID", le16_to_cpu(is_snapshot ? psnapshot->stat_id : pevent_descriptor->event_id), data_size , (unsigned long long)(remaining - event_des_size)) | |||
| 1399 | "Invalid entry at offset 0x%x of Event FIFO %u: "nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x, %s 0x%x declares %u data bytes, %llu left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , is_snapshot ? "Statistic ID" : "Event ID", le16_to_cpu(is_snapshot ? psnapshot->stat_id : pevent_descriptor->event_id), data_size , (unsigned long long)(remaining - event_des_size)) | |||
| 1400 | "class 0x%x, %s 0x%x declares %u data bytes, %llu left in FIFO",nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x, %s 0x%x declares %u data bytes, %llu left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , is_snapshot ? "Statistic ID" : "Event ID", le16_to_cpu(is_snapshot ? psnapshot->stat_id : pevent_descriptor->event_id), data_size , (unsigned long long)(remaining - event_des_size)) | |||
| 1401 | offset_to_move, event_fifo_number,nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x, %s 0x%x declares %u data bytes, %llu left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , is_snapshot ? "Statistic ID" : "Event ID", le16_to_cpu(is_snapshot ? psnapshot->stat_id : pevent_descriptor->event_id), data_size , (unsigned long long)(remaining - event_des_size)) | |||
| 1402 | pevent_descriptor->debug_event_class_type,nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x, %s 0x%x declares %u data bytes, %llu left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , is_snapshot ? "Statistic ID" : "Event ID", le16_to_cpu(is_snapshot ? psnapshot->stat_id : pevent_descriptor->event_id), data_size , (unsigned long long)(remaining - event_des_size)) | |||
| 1403 | is_snapshot ? "Statistic ID" : "Event ID",nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x, %s 0x%x declares %u data bytes, %llu left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , is_snapshot ? "Statistic ID" : "Event ID", le16_to_cpu(is_snapshot ? psnapshot->stat_id : pevent_descriptor->event_id), data_size , (unsigned long long)(remaining - event_des_size)) | |||
| 1404 | le16_to_cpu(is_snapshot ? psnapshot->stat_id :nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x, %s 0x%x declares %u data bytes, %llu left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , is_snapshot ? "Statistic ID" : "Event ID", le16_to_cpu(is_snapshot ? psnapshot->stat_id : pevent_descriptor->event_id), data_size , (unsigned long long)(remaining - event_des_size)) | |||
| 1405 | pevent_descriptor->event_id),nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x, %s 0x%x declares %u data bytes, %llu left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , is_snapshot ? "Statistic ID" : "Event ID", le16_to_cpu(is_snapshot ? psnapshot->stat_id : pevent_descriptor->event_id), data_size , (unsigned long long)(remaining - event_des_size)) | |||
| 1406 | data_size,nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x, %s 0x%x declares %u data bytes, %llu left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , is_snapshot ? "Statistic ID" : "Event ID", le16_to_cpu(is_snapshot ? psnapshot->stat_id : pevent_descriptor->event_id), data_size , (unsigned long long)(remaining - event_des_size)) | |||
| 1407 | (unsigned long long)(remaining - event_des_size))nvme_show_message(1, "Invalid entry at offset 0x%x of Event FIFO %u: " "class 0x%x, %s 0x%x declares %u data bytes, %llu left in FIFO" , offset_to_move, event_fifo_number, pevent_descriptor->debug_event_class_type , is_snapshot ? "Statistic ID" : "Event ID", le16_to_cpu(is_snapshot ? psnapshot->stat_id : pevent_descriptor->event_id), data_size , (unsigned long long)(remaining - event_des_size)); | |||
| 1408 | return -1; | |||
| 1409 | } | |||
| 1410 | ||||
| 1411 | if (!is_snapshot) { | |||
| 1412 | if (pevent_descriptor->event_data_size > 0) | |||
| 1413 | pevent_specific_data = (__u8 *)pevent_descriptor + event_des_size; | |||
| 1414 | ||||
| 1415 | event_id = le16_to_cpu(pevent_descriptor->event_id); | |||
| 1416 | ||||
| 1417 | parse_ocp_telemetry_string_log(0, event_id, | |||
| 1418 | pevent_descriptor->debug_event_class_type, EVENT_STRING, | |||
| 1419 | description_str); | |||
| 1420 | ||||
| 1421 | __cleanup_json_object__attribute__((cleanup(json_free_objectp))) | |||
| 1422 | struct json_object *pevent_descriptor_obj = | |||
| 1423 | ((pevent_fifos_object != NULL((void*)0))?json_create_object()json_object_new_object():NULL((void*)0)); | |||
| 1424 | ||||
| 1425 | if (pevent_descriptor_obj != NULL((void*)0)) { | |||
| 1426 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1427 | STR_DBG_EVENT_CLASS_TYPE"Debug Event Class type", | |||
| 1428 | pevent_descriptor->debug_event_class_type); | |||
| 1429 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1430 | STR_EVENT_IDENTIFIER"Event Identifier", event_id); | |||
| 1431 | json_object_add_value_string(pevent_descriptor_obj, | |||
| 1432 | STR_EVENT_STRING"Event String", description_str); | |||
| 1433 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1434 | STR_EVENT_DATA_SIZE"Event Data Size", pevent_descriptor->event_data_size); | |||
| 1435 | ||||
| 1436 | if (pevent_descriptor->debug_event_class_type >= 0x80) | |||
| 1437 | json_add_formatted_var_size_str(pevent_descriptor_obj, | |||
| 1438 | STR_VU_DATA"VU Data", pevent_specific_data, data_size); | |||
| 1439 | } else { | |||
| 1440 | if (fp) { | |||
| 1441 | fprintf(fp, "%s: 0x%x\n", STR_DBG_EVENT_CLASS_TYPE"Debug Event Class type", | |||
| 1442 | pevent_descriptor->debug_event_class_type); | |||
| 1443 | fprintf(fp, "%s: 0x%x\n", STR_EVENT_IDENTIFIER"Event Identifier", | |||
| 1444 | event_id); | |||
| 1445 | fprintf(fp, "%s: %s\n", STR_EVENT_STRING"Event String", description_str); | |||
| 1446 | fprintf(fp, "%s: 0x%x\n", STR_EVENT_DATA_SIZE"Event Data Size", | |||
| 1447 | pevent_descriptor->event_data_size); | |||
| 1448 | } else { | |||
| 1449 | printf("%s: 0x%x\n", STR_DBG_EVENT_CLASS_TYPE"Debug Event Class type", | |||
| 1450 | pevent_descriptor->debug_event_class_type); | |||
| 1451 | printf("%s: 0x%x\n", STR_EVENT_IDENTIFIER"Event Identifier", | |||
| 1452 | event_id); | |||
| 1453 | printf("%s: %s\n", STR_EVENT_STRING"Event String", description_str); | |||
| 1454 | printf("%s: 0x%x\n", STR_EVENT_DATA_SIZE"Event Data Size", | |||
| 1455 | pevent_descriptor->event_data_size); | |||
| 1456 | } | |||
| 1457 | ||||
| 1458 | if (pevent_descriptor->debug_event_class_type >= 0x80) | |||
| 1459 | print_formatted_var_size_str(STR_VU_DATA"VU Data", | |||
| 1460 | pevent_specific_data, data_size, fp); | |||
| 1461 | } | |||
| 1462 | ||||
| 1463 | switch (pevent_descriptor->debug_event_class_type) { | |||
| 1464 | case TIME_STAMP_CLASS_TYPE: | |||
| 1465 | ret = parse_time_stamp_event(pevent_descriptor, | |||
| 1466 | pevent_descriptor_obj, | |||
| 1467 | pevent_specific_data, | |||
| 1468 | pevent_fifos_object, | |||
| 1469 | fp); | |||
| 1470 | break; | |||
| 1471 | case PCIE_CLASS_TYPE: | |||
| 1472 | ret = parse_pcie_event(pevent_descriptor, | |||
| 1473 | pevent_descriptor_obj, | |||
| 1474 | pevent_specific_data, | |||
| 1475 | pevent_fifos_object, | |||
| 1476 | fp); | |||
| 1477 | break; | |||
| 1478 | case NVME_CLASS_TYPE: | |||
| 1479 | ret = parse_nvme_event(pevent_descriptor, | |||
| 1480 | pevent_descriptor_obj, | |||
| 1481 | pevent_specific_data, | |||
| 1482 | pevent_fifos_object, | |||
| 1483 | fp); | |||
| 1484 | break; | |||
| 1485 | case RESET_CLASS_TYPE: | |||
| 1486 | case BOOT_SEQUENCE_CLASS_TYPE: | |||
| 1487 | case FIRMWARE_ASSERT_CLASS_TYPE: | |||
| 1488 | case TEMPERATURE_CLASS_TYPE: | |||
| 1489 | case MEDIA_CLASS_TYPE: | |||
| 1490 | parse_common_event(pevent_descriptor, | |||
| 1491 | pevent_descriptor_obj, | |||
| 1492 | pevent_specific_data, | |||
| 1493 | pevent_fifos_object, | |||
| 1494 | fp); | |||
| 1495 | ret = 0; | |||
| 1496 | break; | |||
| 1497 | case MEDIA_WEAR_CLASS_TYPE: | |||
| 1498 | ret = parse_media_wear_event(pevent_descriptor, | |||
| 1499 | pevent_descriptor_obj, | |||
| 1500 | pevent_specific_data, | |||
| 1501 | pevent_fifos_object, | |||
| 1502 | fp); | |||
| 1503 | break; | |||
| 1504 | case VIRTUAL_FIFO_EVENT_CLASS_TYPE: | |||
| 1505 | ret = parse_virtual_fifo_event( | |||
| 1506 | pevent_descriptor, | |||
| 1507 | pevent_descriptor_obj, | |||
| 1508 | pevent_specific_data, | |||
| 1509 | pevent_fifos_object, | |||
| 1510 | fp); | |||
| 1511 | break; | |||
| 1512 | case SMBUS_I2C_I3C_EVENT_CLASS_TYPE: | |||
| 1513 | ret = parse_smbus_event(pevent_descriptor, | |||
| 1514 | pevent_descriptor_obj, | |||
| 1515 | pevent_specific_data, | |||
| 1516 | pevent_fifos_object, | |||
| 1517 | fp); | |||
| 1518 | break; | |||
| 1519 | case MCTP_EVENT_CLASS_TYPE: | |||
| 1520 | ret = parse_mctp_event(pevent_descriptor, | |||
| 1521 | pevent_descriptor_obj, | |||
| 1522 | pevent_specific_data, | |||
| 1523 | pevent_fifos_object, | |||
| 1524 | fp); | |||
| 1525 | break; | |||
| 1526 | case RESERVED_CLASS_TYPE: | |||
| 1527 | break; | |||
| 1528 | default: | |||
| 1529 | /* | |||
| 1530 | * Keep the payload of a class with no decoder | |||
| 1531 | * visible; vendor unique classes already print | |||
| 1532 | * theirs as VU Data. | |||
| 1533 | */ | |||
| 1534 | if (pevent_descriptor->debug_event_class_type >= 0x80) | |||
| 1535 | break; | |||
| 1536 | if (pevent_descriptor_obj != NULL((void*)0)) | |||
| 1537 | json_add_formatted_var_size_str( | |||
| 1538 | pevent_descriptor_obj, | |||
| 1539 | STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 1540 | pevent_specific_data, data_size); | |||
| 1541 | else | |||
| 1542 | print_formatted_var_size_str( | |||
| 1543 | STR_CLASS_SPECIFIC_DATA"Class Specific Data", | |||
| 1544 | pevent_specific_data, data_size, | |||
| 1545 | fp); | |||
| 1546 | break; | |||
| 1547 | } | |||
| 1548 | ||||
| 1549 | if (ret) { | |||
| 1550 | nvme_show_error(nvme_show_message(1, "ERROR : OCP : Invalid NVMe Event FIFO entry\n" ) | |||
| 1551 | "ERROR : OCP : Invalid NVMe Event FIFO entry\n")nvme_show_message(1, "ERROR : OCP : Invalid NVMe Event FIFO entry\n" ); | |||
| 1552 | nvme_show_error(nvme_show_message(1, "FIFO: %d, offset: 0x%x\n", event_fifo_number , offset_to_move) | |||
| 1553 | "FIFO: %d, offset: 0x%x\n",nvme_show_message(1, "FIFO: %d, offset: 0x%x\n", event_fifo_number , offset_to_move) | |||
| 1554 | event_fifo_number, offset_to_move)nvme_show_message(1, "FIFO: %d, offset: 0x%x\n", event_fifo_number , offset_to_move); | |||
| 1555 | nvme_show_error(nvme_show_message(1, "Type: 0x%x, ID: 0x%x, Size: 0x%x\n", pevent_descriptor ->debug_event_class_type, event_id, pevent_descriptor-> event_data_size) | |||
| 1556 | "Type: 0x%x, ID: 0x%x, Size: 0x%x\n",nvme_show_message(1, "Type: 0x%x, ID: 0x%x, Size: 0x%x\n", pevent_descriptor ->debug_event_class_type, event_id, pevent_descriptor-> event_data_size) | |||
| 1557 | pevent_descriptor->debug_event_class_type,nvme_show_message(1, "Type: 0x%x, ID: 0x%x, Size: 0x%x\n", pevent_descriptor ->debug_event_class_type, event_id, pevent_descriptor-> event_data_size) | |||
| 1558 | event_id,nvme_show_message(1, "Type: 0x%x, ID: 0x%x, Size: 0x%x\n", pevent_descriptor ->debug_event_class_type, event_id, pevent_descriptor-> event_data_size) | |||
| 1559 | pevent_descriptor->event_data_size)nvme_show_message(1, "Type: 0x%x, ID: 0x%x, Size: 0x%x\n", pevent_descriptor ->debug_event_class_type, event_id, pevent_descriptor-> event_data_size); | |||
| 1560 | return ret; | |||
| 1561 | } | |||
| 1562 | ||||
| 1563 | if (pevent_descriptor_obj != NULL((void*)0) && | |||
| 1564 | pevent_fifo_array != NULL((void*)0)) { | |||
| 1565 | json_array_add_value_object(pevent_fifo_array,json_object_array_add(pevent_fifo_array, pevent_descriptor_obj ) | |||
| 1566 | pevent_descriptor_obj)json_object_array_add(pevent_fifo_array, pevent_descriptor_obj ); | |||
| 1567 | pevent_descriptor_obj = NULL((void*)0); | |||
| 1568 | } else { | |||
| 1569 | if (fp) | |||
| 1570 | fprintf(fp, STR_LINE2"-----------------------------------------------------------------------------\n"); | |||
| 1571 | else | |||
| 1572 | printf(STR_LINE2"-----------------------------------------------------------------------------\n"); | |||
| 1573 | } | |||
| 1574 | } else { | |||
| 1575 | parse_ocp_telemetry_string_log(0, event_id, | |||
| 1576 | pevent_descriptor->debug_event_class_type, EVENT_STRING, | |||
| 1577 | description_str); | |||
| 1578 | ||||
| 1579 | __cleanup_json_object__attribute__((cleanup(json_free_objectp))) | |||
| 1580 | struct json_object *pevent_descriptor_obj = | |||
| 1581 | ((pevent_fifos_object != NULL((void*)0)) ? json_create_object()json_object_new_object() : NULL((void*)0)); | |||
| 1582 | ||||
| 1583 | if (pevent_descriptor_obj != NULL((void*)0)) { | |||
| 1584 | json_add_formatted_u32_str(pevent_descriptor_obj, | |||
| 1585 | STR_DBG_EVENT_CLASS_TYPE"Debug Event Class type", | |||
| 1586 | pevent_descriptor->debug_event_class_type); | |||
| 1587 | json_object_add_value_string(pevent_descriptor_obj, | |||
| 1588 | STR_EVENT_STRING"Event String", description_str); | |||
| 1589 | } else { | |||
| 1590 | if (fp) { | |||
| 1591 | fprintf(fp, "%s: 0x%x\n", STR_DBG_EVENT_CLASS_TYPE"Debug Event Class type", | |||
| 1592 | pevent_descriptor->debug_event_class_type); | |||
| 1593 | fprintf(fp, "%s: %s\n", STR_EVENT_STRING"Event String", description_str); | |||
| 1594 | } else { | |||
| 1595 | printf("%s: 0x%x\n", STR_DBG_EVENT_CLASS_TYPE"Debug Event Class type", | |||
| 1596 | pevent_descriptor->debug_event_class_type); | |||
| 1597 | printf("%s: %s\n", STR_EVENT_STRING"Event String", description_str); | |||
| 1598 | } | |||
| 1599 | } | |||
| 1600 | ||||
| 1601 | struct nvme_ocp_statistic_snapshot_evt_class_format | |||
| 1602 | *pStaticSnapshotEvent = | |||
| 1603 | (struct nvme_ocp_statistic_snapshot_evt_class_format *) | |||
| 1604 | pevent_descriptor; | |||
| 1605 | ||||
| 1606 | if (pStaticSnapshotEvent != NULL((void*)0) && | |||
| 1607 | pStaticSnapshotEvent->stat_data_size > 0) { | |||
| 1608 | __u8 *pstatistic_entry; | |||
| 1609 | ||||
| 1610 | pstatistic_entry = | |||
| 1611 | (__u8 *)pStaticSnapshotEvent + | |||
| 1612 | sizeof(struct nvme_ocp_telemetry_event_descriptor); | |||
| 1613 | ||||
| 1614 | parse_statistic( | |||
| 1615 | (struct nvme_ocp_telemetry_statistic_descriptor *) | |||
| 1616 | pstatistic_entry, | |||
| 1617 | pevent_descriptor_obj, | |||
| 1618 | fp); | |||
| 1619 | } | |||
| 1620 | ||||
| 1621 | if (pevent_descriptor_obj != NULL((void*)0) && | |||
| 1622 | pevent_fifo_array != NULL((void*)0)) { | |||
| 1623 | json_array_add_value_object(pevent_fifo_array,json_object_array_add(pevent_fifo_array, pevent_descriptor_obj ) | |||
| 1624 | pevent_descriptor_obj)json_object_array_add(pevent_fifo_array, pevent_descriptor_obj ); | |||
| 1625 | pevent_descriptor_obj = NULL((void*)0); | |||
| 1626 | } | |||
| 1627 | } | |||
| 1628 | offset_to_move += (data_size + event_des_size); | |||
| 1629 | } | |||
| 1630 | ||||
| 1631 | if (pevent_fifos_object != NULL((void*)0) && pevent_fifo_array != NULL((void*)0)) { | |||
| 1632 | json_object_add_value_array(pevent_fifos_object, event_fifo_name,json_object_object_add(pevent_fifos_object, event_fifo_name, pevent_fifo_array ) | |||
| 1633 | pevent_fifo_array)json_object_object_add(pevent_fifos_object, event_fifo_name, pevent_fifo_array ); | |||
| 1634 | pevent_fifo_array = NULL((void*)0); | |||
| 1635 | } | |||
| 1636 | ||||
| 1637 | return ret; | |||
| 1638 | } | |||
| 1639 | ||||
| 1640 | /* | |||
| 1641 | * Data Area 1 always begins with the mandatory OCP DA1 header (which in | |||
| 1642 | * turn carries the DA1/DA2 statistics and event FIFO offsets/sizes). A | |||
| 1643 | * controller that has never captured a telemetry log for this type | |||
| 1644 | * reports Data Area 1 as empty (da1_size == 0); reading the DA1 header, | |||
| 1645 | * SMART blocks, statistics, or event FIFOs out of the telemetry buffer in | |||
| 1646 | * that case would run past the end of what was actually fetched/read. | |||
| 1647 | */ | |||
| 1648 | static bool_Bool telemetry_da1_header_present( | |||
| 1649 | struct nvme_ocp_telemetry_offsets *poffsets) | |||
| 1650 | { | |||
| 1651 | return poffsets->da1_size >= sizeof(struct nvme_ocp_header_in_da1); | |||
| 1652 | } | |||
| 1653 | ||||
| 1654 | int parse_event_fifos(struct json_object *root, struct nvme_ocp_telemetry_offsets *poffsets, | |||
| 1655 | FILE *fp) | |||
| 1656 | { | |||
| 1657 | if (poffsets == NULL((void*)0)) { | |||
| 1658 | nvme_show_error("Input buffer was NULL")nvme_show_message(1, "Input buffer was NULL"); | |||
| 1659 | return -1; | |||
| 1660 | } | |||
| 1661 | ||||
| 1662 | if (!telemetry_da1_header_present(poffsets)) | |||
| 1663 | return 0; | |||
| 1664 | ||||
| 1665 | __cleanup_json_object__attribute__((cleanup(json_free_objectp))) struct json_object *pevent_fifos_object = NULL((void*)0); | |||
| 1666 | ||||
| 1667 | if (root != NULL((void*)0)) | |||
| 1668 | pevent_fifos_object = json_create_object()json_object_new_object(); | |||
| 1669 | ||||
| 1670 | __u8 *pda1_header_offset = ptelemetry_buffer + poffsets->da1_start_offset;//512 | |||
| 1671 | __u8 *pda2_offset = ptelemetry_buffer + poffsets->da2_start_offset; | |||
| 1672 | struct nvme_ocp_header_in_da1 *pda1_header = (struct nvme_ocp_header_in_da1 *) | |||
| 1673 | pda1_header_offset; | |||
| 1674 | struct nvme_ocp_event_fifo_data event_fifo[MAX_NUM_FIFOS16]; | |||
| 1675 | ||||
| 1676 | for (int fifo_num = 0; fifo_num < MAX_NUM_FIFOS16; fifo_num++) { | |||
| 1677 | event_fifo[fifo_num].event_fifo_num = fifo_num; | |||
| 1678 | event_fifo[fifo_num].event_fifo_da = pda1_header->event_fifo_da[fifo_num]; | |||
| 1679 | event_fifo[fifo_num].event_fifo_start = | |||
| 1680 | le64_to_cpu(pda1_header->fifo_offsets[fifo_num].event_fifo_start); | |||
| 1681 | event_fifo[fifo_num].event_fifo_size = | |||
| 1682 | le64_to_cpu(pda1_header->fifo_offsets[fifo_num].event_fifo_size); | |||
| 1683 | } | |||
| 1684 | ||||
| 1685 | //Parse all the FIFOs DA wise | |||
| 1686 | for (int fifo_no = 0; fifo_no < MAX_NUM_FIFOS16; fifo_no++) { | |||
| 1687 | if (event_fifo[fifo_no].event_fifo_da == poffsets->data_area) { | |||
| 1688 | __u64 start_dw = event_fifo[fifo_no].event_fifo_start; | |||
| 1689 | __u64 size_dw = event_fifo[fifo_no].event_fifo_size; | |||
| 1690 | __u64 da_dw = ((poffsets->data_area == 1) ? | |||
| 1691 | poffsets->da1_size : poffsets->da2_size) / SIZE_OF_DWORD4; | |||
| 1692 | __u64 fifo_offset, fifo_size; | |||
| 1693 | __u8 *pfifo_start = NULL((void*)0); | |||
| 1694 | ||||
| 1695 | if (!size_dw || start_dw > da_dw || size_dw > da_dw - start_dw) { | |||
| 1696 | nvme_show_error(nvme_show_message(1, "Invalid FIFO bounds for FIFO %d", fifo_no ) | |||
| 1697 | "Invalid FIFO bounds for FIFO %d",nvme_show_message(1, "Invalid FIFO bounds for FIFO %d", fifo_no ) | |||
| 1698 | fifo_no)nvme_show_message(1, "Invalid FIFO bounds for FIFO %d", fifo_no ); | |||
| 1699 | return -1; | |||
| 1700 | } | |||
| 1701 | fifo_offset = start_dw * SIZE_OF_DWORD4; | |||
| 1702 | fifo_size = size_dw * SIZE_OF_DWORD4; | |||
| 1703 | ||||
| 1704 | if (event_fifo[fifo_no].event_fifo_da == 1) | |||
| 1705 | pfifo_start = pda1_header_offset + fifo_offset; | |||
| 1706 | else if (event_fifo[fifo_no].event_fifo_da == 2) | |||
| 1707 | pfifo_start = pda2_offset + fifo_offset; | |||
| 1708 | else { | |||
| 1709 | nvme_show_error("Unsupported Data Area:[%d]", poffsets->data_area)nvme_show_message(1, "Unsupported Data Area:[%d]", poffsets-> data_area); | |||
| 1710 | return -1; | |||
| 1711 | } | |||
| 1712 | ||||
| 1713 | int status = parse_event_fifo(fifo_no, pfifo_start, pevent_fifos_object, | |||
| 1714 | pstring_buffer, poffsets, fifo_size, fp); | |||
| 1715 | ||||
| 1716 | if (status != 0) { | |||
| 1717 | nvme_show_error("Failed to parse Event FIFO. status:%d", status)nvme_show_message(1, "Failed to parse Event FIFO. status:%d", status); | |||
| 1718 | return -1; | |||
| 1719 | } | |||
| 1720 | } | |||
| 1721 | } | |||
| 1722 | ||||
| 1723 | if (pevent_fifos_object != NULL((void*)0) && root != NULL((void*)0)) { | |||
| 1724 | json_object_add_value_array(root,json_object_object_add(root, poffsets->data_area == 1 ? "Data Area 1 Event FIFO info" : "Data Area 2 Event FIFO info", pevent_fifos_object) | |||
| 1725 | poffsets->data_area == 1 ? STR_DA_1_EVENT_FIFO_INFO :json_object_object_add(root, poffsets->data_area == 1 ? "Data Area 1 Event FIFO info" : "Data Area 2 Event FIFO info", pevent_fifos_object) | |||
| 1726 | STR_DA_2_EVENT_FIFO_INFO,json_object_object_add(root, poffsets->data_area == 1 ? "Data Area 1 Event FIFO info" : "Data Area 2 Event FIFO info", pevent_fifos_object) | |||
| 1727 | pevent_fifos_object)json_object_object_add(root, poffsets->data_area == 1 ? "Data Area 1 Event FIFO info" : "Data Area 2 Event FIFO info", pevent_fifos_object); | |||
| 1728 | pevent_fifos_object = NULL((void*)0); | |||
| 1729 | } | |||
| 1730 | ||||
| 1731 | return 0; | |||
| 1732 | } | |||
| 1733 | ||||
| 1734 | #define STAT_NESTED_INDENT" " " " | |||
| 1735 | ||||
| 1736 | /* Where a statistic is printed: a JSON object, or text lines to fp */ | |||
| 1737 | struct stat_sink { | |||
| 1738 | struct json_object *obj; | |||
| 1739 | FILE *fp; | |||
| 1740 | const char *indent; | |||
| 1741 | }; | |||
| 1742 | ||||
| 1743 | static FILE *stat_stream(struct stat_sink *s) | |||
| 1744 | { | |||
| 1745 | return s->fp ? s->fp : stdoutstdout; | |||
| 1746 | } | |||
| 1747 | ||||
| 1748 | static void stat_add_hex(struct stat_sink *s, const char *key, __u32 value, int width) | |||
| 1749 | { | |||
| 1750 | if (s->obj) | |||
| 1751 | json_add_formatted_u32_str(s->obj, key, value); | |||
| 1752 | else | |||
| 1753 | fprintf(stat_stream(s), "%s%s: 0x%0*x\n", s->indent, key, width, value); | |||
| 1754 | } | |||
| 1755 | ||||
| 1756 | static void stat_add_str(struct stat_sink *s, const char *key, const char *value) | |||
| 1757 | { | |||
| 1758 | if (s->obj) | |||
| 1759 | json_object_add_value_string(s->obj, key, value); | |||
| 1760 | else | |||
| 1761 | fprintf(stat_stream(s), "%s%s: %s\n", s->indent, key, value); | |||
| 1762 | } | |||
| 1763 | ||||
| 1764 | static void stat_add_data(struct stat_sink *s, const char *key, __u8 *data, unsigned int size) | |||
| 1765 | { | |||
| 1766 | if (s->obj) { | |||
| 1767 | json_add_formatted_var_size_str(s->obj, key, data, size); | |||
| 1768 | } else { | |||
| 1769 | fputs(s->indent, stat_stream(s)); | |||
| 1770 | print_formatted_var_size_str(key, data, size, stat_stream(s)); | |||
| 1771 | } | |||
| 1772 | } | |||
| 1773 | ||||
| 1774 | /* Returns the JSON array to add items to, NULL in text mode */ | |||
| 1775 | static struct json_object *stat_list_begin(struct stat_sink *s, const char *key) | |||
| 1776 | { | |||
| 1777 | struct json_object *array = NULL((void*)0); | |||
| 1778 | ||||
| 1779 | if (s->obj) { | |||
| 1780 | array = json_create_array()json_object_new_array(); | |||
| 1781 | json_object_add_value_array(s->obj, key, array)json_object_object_add(s->obj, key, array); | |||
| 1782 | } else { | |||
| 1783 | fprintf(stat_stream(s), "%s%s:\n", s->indent, key); | |||
| 1784 | } | |||
| 1785 | return array; | |||
| 1786 | } | |||
| 1787 | ||||
| 1788 | static void stat_item_begin(struct stat_sink *item, struct json_object *array, FILE *fp, | |||
| 1789 | const char *indent) | |||
| 1790 | { | |||
| 1791 | item->obj = array ? json_create_object()json_object_new_object() : NULL((void*)0); | |||
| 1792 | item->fp = fp; | |||
| 1793 | item->indent = indent; | |||
| 1794 | } | |||
| 1795 | ||||
| 1796 | static void stat_item_end(struct stat_sink *item, struct json_object *array) | |||
| 1797 | { | |||
| 1798 | if (array) | |||
| 1799 | json_array_add_value_object(array, item->obj)json_object_array_add(array, item->obj); | |||
| 1800 | else | |||
| 1801 | fprintf(stat_stream(item), "%s%s", item->indent, STR_LINE2"-----------------------------------------------------------------------------\n"); | |||
| 1802 | } | |||
| 1803 | ||||
| 1804 | static __u32 get_le_field(const __u8 *p, unsigned int size) | |||
| 1805 | { | |||
| 1806 | __u32 value = 0; | |||
| 1807 | ||||
| 1808 | while (size--) | |||
| 1809 | value = (value << 8) | p[size]; | |||
| 1810 | return value; | |||
| 1811 | } | |||
| 1812 | ||||
| 1813 | /* Scope fields by offset into the context data (OCP 2.7 section 4.9.12) */ | |||
| 1814 | static const struct { | |||
| 1815 | __u16 stat_id; | |||
| 1816 | const char *name; | |||
| 1817 | __u8 offset; | |||
| 1818 | __u8 size; | |||
| 1819 | } context_scope_fields[] = { | |||
| 1820 | { NAMESPACE_ID_CONTEXT_ID, STR_CONTEXT_NAMESPACE_ID"Namespace ID", 4, 4 }, | |||
| 1821 | { CONTROLLER_ID_CONTEXT_ID, STR_CONTEXT_CONTROLLER_ID"Controller ID", 6, 2 }, | |||
| 1822 | { QUEUE_ID_CONTEXT_ID, STR_CONTEXT_CONTROLLER_ID"Controller ID", 4, 2 }, | |||
| 1823 | { QUEUE_ID_CONTEXT_ID, STR_CONTEXT_QUEUE_ID"Queue ID", 6, 2 }, | |||
| 1824 | }; | |||
| 1825 | ||||
| 1826 | static const struct { | |||
| 1827 | __u16 stat_id; | |||
| 1828 | const char *percent; | |||
| 1829 | const char *raw; | |||
| 1830 | } bad_block_statistics[] = { | |||
| 1831 | { MAX_DIE_BAD_BLOCK_ID, STR_STATISTICS_WORST_DIE_PERCENT"Worst die % of bad blocks", | |||
| 1832 | STR_STATISTICS_WORST_DIE_RAW"Worst die raw number of bad blocks" }, | |||
| 1833 | { MAX_NAND_CHANNEL_BAD_BLOCK_ID, STR_STATISTICS_WORST_NAND_CHANNEL_PERCENT"Worst NAND channel % of bad blocks", | |||
| 1834 | STR_STATISTICS_WORST_NAND_CHANNEL_RAW"Worst NAND channel number of bad blocks" }, | |||
| 1835 | { MIN_NAND_CHANNEL_BAD_BLOCK_ID, STR_STATISTICS_BEST_NAND_CHANNEL_PERCENT"Best NAND channel % of bad blocks", | |||
| 1836 | STR_STATISTICS_BEST_NAND_CHANNEL_RAW"Best NAND channel number of bad blocks" }, | |||
| 1837 | }; | |||
| 1838 | ||||
| 1839 | static bool_Bool is_context_scope_id(__u16 id) | |||
| 1840 | { | |||
| 1841 | return id >= NAMESPACE_ID_CONTEXT_ID && id <= QUEUE_ID_CONTEXT_ID; | |||
| 1842 | } | |||
| 1843 | ||||
| 1844 | static bool_Bool is_context_statistic(struct nvme_ocp_telemetry_statistic_descriptor *d) | |||
| 1845 | { | |||
| 1846 | return d->statistic_info_context_index || | |||
| 1847 | is_context_scope_id(le16_to_cpu(d->statistic_id)); | |||
| 1848 | } | |||
| 1849 | ||||
| 1850 | static void print_statistics(__u8 *pstats, unsigned int size, struct json_object *array, | |||
| 1851 | FILE *fp, bool_Bool encapsulated, const char *where); | |||
| 1852 | ||||
| 1853 | static void parse_context_statistic(__u16 id, __u8 *pdata, unsigned int data_size, | |||
| 1854 | struct stat_sink *s) | |||
| 1855 | { | |||
| 1856 | struct nvme_ocp_statistic_context_data *context = | |||
| 1857 | (struct nvme_ocp_statistic_context_data *)pdata; | |||
| 1858 | __u16 context_size = le16_to_cpu(context->context_data_size); | |||
| 1859 | char where[64]; | |||
| 1860 | struct json_object *array; | |||
| 1861 | struct stat_sink item; | |||
| 1862 | size_t i; | |||
| 1863 | ||||
| 1864 | /* | |||
| 1865 | * The encapsulated descriptors follow the context data whatever its | |||
| 1866 | * declared size, so an invalid Context Data Size is only reported. | |||
| 1867 | */ | |||
| 1868 | if (!context_size || context_size > data_size / SIZE_OF_DWORD4) | |||
| 1869 | nvme_show_error("Context Statistic 0x%x: "nvme_show_message(1, "Context Statistic 0x%x: " "Context Data Size 0x%x is outside 1 to 0x%x" , id, context_size, data_size / 4) | |||
| 1870 | "Context Data Size 0x%x is outside 1 to 0x%x",nvme_show_message(1, "Context Statistic 0x%x: " "Context Data Size 0x%x is outside 1 to 0x%x" , id, context_size, data_size / 4) | |||
| 1871 | id, context_size, data_size / SIZE_OF_DWORD)nvme_show_message(1, "Context Statistic 0x%x: " "Context Data Size 0x%x is outside 1 to 0x%x" , id, context_size, data_size / 4); | |||
| 1872 | ||||
| 1873 | stat_add_hex(s, STR_CONTEXT_DATA_SIZE"Context Data Size", context_size, 0); | |||
| 1874 | stat_add_hex(s, STR_CONTEXT_DATA_RESERVED"Context Data Reserved", le16_to_cpu(context->reserved), 0); | |||
| 1875 | stat_add_data(s, STR_CONTEXT_SCOPE"Context Scope", context->scope, sizeof(context->scope)); | |||
| 1876 | ||||
| 1877 | if (is_context_scope_id(id)) { | |||
| 1878 | array = stat_list_begin(s, STR_CONTEXT_SCOPE_FIELDS"Context Scope Fields"); | |||
| 1879 | for (i = 0; i < ARRAY_SIZE(context_scope_fields)(sizeof(context_scope_fields) / sizeof((context_scope_fields) [0]) + 0); i++) { | |||
| 1880 | __u8 offset = context_scope_fields[i].offset; | |||
| 1881 | __u8 size = context_scope_fields[i].size; | |||
| 1882 | ||||
| 1883 | if (context_scope_fields[i].stat_id != id) | |||
| 1884 | continue; | |||
| 1885 | stat_item_begin(&item, array, s->fp, STAT_NESTED_INDENT" "); | |||
| 1886 | stat_add_str(&item, STR_SCOPE_FIELD_STRING"Scope Field String", context_scope_fields[i].name); | |||
| 1887 | stat_add_hex(&item, STR_SCOPE_FIELD_OFFSET"Scope Field Offset", offset, 0); | |||
| 1888 | stat_add_hex(&item, STR_SCOPE_FIELD_SIZE"Scope Field Size", size, 0); | |||
| 1889 | stat_add_hex(&item, STR_SCOPE_FIELD_VALUE"Scope Field Value", | |||
| 1890 | get_le_field(pdata + offset, size), 0); | |||
| 1891 | stat_item_end(&item, array); | |||
| 1892 | } | |||
| 1893 | } | |||
| 1894 | ||||
| 1895 | snprintf(where, sizeof(where), | |||
| 1896 | "Encapsulated Statistic Descriptors of Context Statistic 0x%x", id); | |||
| 1897 | array = stat_list_begin(s, STR_ENCAPSULATED_STATISTICS"Encapsulated Statistic Descriptors"); | |||
| 1898 | print_statistics(pdata + sizeof(*context), data_size - sizeof(*context), array, s->fp, | |||
| 1899 | true1, where); | |||
| 1900 | } | |||
| 1901 | ||||
| 1902 | /* | |||
| 1903 | * Prints a statistic as a new object of @pstats_array, into the existing | |||
| 1904 | * object @pstat_obj, or as text. | |||
| 1905 | */ | |||
| 1906 | static void print_statistic(struct nvme_ocp_telemetry_statistic_descriptor *pstatistic_entry, | |||
| 1907 | struct json_object *pstats_array, | |||
| 1908 | struct json_object *pstat_obj, FILE *fp, | |||
| 1909 | bool_Bool encapsulated) | |||
| 1910 | { | |||
| 1911 | __u16 id = le16_to_cpu(pstatistic_entry->statistic_id); | |||
| 1912 | __u16 data_dwords = le16_to_cpu(pstatistic_entry->statistic_data_size); | |||
| 1913 | unsigned int data_size = data_dwords * SIZE_OF_DWORD4; | |||
| 1914 | __u8 *pdata = (__u8 *)pstatistic_entry + sizeof(*pstatistic_entry); | |||
| 1915 | char description_str[OCP_TELEMETRY_DESCRIPTION_MAX256] = ""; | |||
| 1916 | bool_Bool context = is_context_statistic(pstatistic_entry); | |||
| 1917 | struct stat_sink s; | |||
| 1918 | size_t i; | |||
| 1919 | ||||
| 1920 | parse_ocp_telemetry_string_log(0, id, 0, STATISTICS_IDENTIFIER_STRING, description_str); | |||
| 1921 | ||||
| 1922 | stat_item_begin(&s, pstats_array, fp, encapsulated ? STAT_NESTED_INDENT" " : ""); | |||
| 1923 | if (pstat_obj) | |||
| 1924 | s.obj = pstat_obj; | |||
| 1925 | stat_add_hex(&s, STR_STATISTICS_IDENTIFIER"Statistics Identifier", id, 0); | |||
| 1926 | stat_add_str(&s, STR_STATISTICS_IDENTIFIER_STR"Statistic Identifier String", description_str); | |||
| 1927 | stat_add_hex(&s, STR_STATISTICS_INFO_BEHAVIOUR_TYPE"Statistics Info Behavior Type", | |||
| 1928 | pstatistic_entry->statistic_info_behaviour_type, 0); | |||
| 1929 | stat_add_hex(&s, STR_STATISTICS_INFO_CONTEXT_INDEX"Statistics Info Context Index", | |||
| 1930 | pstatistic_entry->statistic_info_context_index, 0); | |||
| 1931 | stat_add_hex(&s, STR_STATISTICS_INFO_HOST_HINT_TYPE"Statistics Info Host Hint Type", | |||
| 1932 | pstatistic_entry->statistic_info_host_hint_type, 0); | |||
| 1933 | stat_add_hex(&s, STR_STATISTICS_INFO_RESERVED"Statistics Info Reserved", | |||
| 1934 | pstatistic_entry->statistic_info_reserved, 0); | |||
| 1935 | stat_add_hex(&s, STR_NAMESPACE_IDENTIFIER"Namespace Identifier", pstatistic_entry->ns_info_nsid, 0); | |||
| 1936 | stat_add_hex(&s, STR_NAMESPACE_INFO_VALID"Namespace Information Valid", pstatistic_entry->ns_info_ns_info_valid, 0); | |||
| 1937 | stat_add_hex(&s, STR_STATISTICS_DATA_SIZE"Statistic Data Size", data_dwords, 0); | |||
| 1938 | stat_add_hex(&s, STR_NAMESPACE_IDENTIFIER_15_0"Namespace Identifier[15:0]", | |||
| 1939 | le16_to_cpu(pstatistic_entry->ns_identifier_15_0), 0); | |||
| 1940 | ||||
| 1941 | /* Context Statistic Descriptors do not nest; the walk reports one that does */ | |||
| 1942 | if (context && !encapsulated) { | |||
| 1943 | if (data_dwords >= CONTEXT_DATA_DWORDS2) { | |||
| 1944 | parse_context_statistic(id, pdata, data_size, &s); | |||
| 1945 | goto out; | |||
| 1946 | } | |||
| 1947 | nvme_show_error("Context Statistic 0x%x: %u data bytes cannot hold "nvme_show_message(1, "Context Statistic 0x%x: %u data bytes cannot hold " "its context data", id, data_size) | |||
| 1948 | "its context data", id, data_size)nvme_show_message(1, "Context Statistic 0x%x: %u data bytes cannot hold " "its context data", id, data_size); | |||
| 1949 | } | |||
| 1950 | ||||
| 1951 | for (i = 0; !context && data_size >= 4 && i < ARRAY_SIZE(bad_block_statistics)(sizeof(bad_block_statistics) / sizeof((bad_block_statistics) [0]) + 0); i++) { | |||
| 1952 | if (bad_block_statistics[i].stat_id != id) | |||
| 1953 | continue; | |||
| 1954 | stat_add_hex(&s, bad_block_statistics[i].percent, pdata[0], 2); | |||
| 1955 | stat_add_hex(&s, bad_block_statistics[i].raw, get_le_field(pdata + 2, 2), 4); | |||
| 1956 | goto out; | |||
| 1957 | } | |||
| 1958 | ||||
| 1959 | stat_add_data(&s, STR_STATISTICS_SPECIFIC_DATA"Statistic Specific Data", pdata, data_size); | |||
| 1960 | out: | |||
| 1961 | if (!pstat_obj) | |||
| 1962 | stat_item_end(&s, pstats_array); | |||
| 1963 | } | |||
| 1964 | ||||
| 1965 | /* | |||
| 1966 | * Prints the statistic descriptors in the @size bytes at @pstats, up to the | |||
| 1967 | * first reserved identifier or the first descriptor that does not fit. | |||
| 1968 | */ | |||
| 1969 | static void print_statistics(__u8 *pstats, unsigned int size, struct json_object *array, | |||
| 1970 | FILE *fp, bool_Bool encapsulated, const char *where) | |||
| 1971 | { | |||
| 1972 | struct nvme_ocp_telemetry_statistic_descriptor *pstatistic_entry; | |||
| 1973 | unsigned int offset = 0, left, data_size; | |||
| 1974 | __u16 id; | |||
| 1975 | ||||
| 1976 | /* Sizes are in Dwords, so the identifier always fits */ | |||
| 1977 | while (offset < size) { | |||
| 1978 | pstatistic_entry = (struct nvme_ocp_telemetry_statistic_descriptor *) | |||
| 1979 | (pstats + offset); | |||
| 1980 | id = le16_to_cpu(pstatistic_entry->statistic_id); | |||
| 1981 | left = size - offset; | |||
| 1982 | ||||
| 1983 | if (id == STATISTICS_RESERVED_ID) | |||
| 1984 | break; | |||
| 1985 | if (left < sizeof(*pstatistic_entry)) { | |||
| 1986 | nvme_show_error("Invalid statistic at offset 0x%x of %s: "nvme_show_message(1, "Invalid statistic at offset 0x%x of %s: " "descriptor needs %zu bytes, %u left", offset, where, sizeof (*pstatistic_entry), left) | |||
| 1987 | "descriptor needs %zu bytes, %u left",nvme_show_message(1, "Invalid statistic at offset 0x%x of %s: " "descriptor needs %zu bytes, %u left", offset, where, sizeof (*pstatistic_entry), left) | |||
| 1988 | offset, where, sizeof(*pstatistic_entry), left)nvme_show_message(1, "Invalid statistic at offset 0x%x of %s: " "descriptor needs %zu bytes, %u left", offset, where, sizeof (*pstatistic_entry), left); | |||
| 1989 | break; | |||
| 1990 | } | |||
| 1991 | data_size = le16_to_cpu(pstatistic_entry->statistic_data_size) * SIZE_OF_DWORD4; | |||
| 1992 | if (left - sizeof(*pstatistic_entry) < data_size) { | |||
| 1993 | nvme_show_error("Invalid statistic at offset 0x%x of %s: "nvme_show_message(1, "Invalid statistic at offset 0x%x of %s: " "Statistic ID 0x%x declares %u data bytes, %zu left", offset , where, id, data_size, left - sizeof(*pstatistic_entry)) | |||
| 1994 | "Statistic ID 0x%x declares %u data bytes, %zu left",nvme_show_message(1, "Invalid statistic at offset 0x%x of %s: " "Statistic ID 0x%x declares %u data bytes, %zu left", offset , where, id, data_size, left - sizeof(*pstatistic_entry)) | |||
| 1995 | offset, where, id, data_size,nvme_show_message(1, "Invalid statistic at offset 0x%x of %s: " "Statistic ID 0x%x declares %u data bytes, %zu left", offset , where, id, data_size, left - sizeof(*pstatistic_entry)) | |||
| 1996 | left - sizeof(*pstatistic_entry))nvme_show_message(1, "Invalid statistic at offset 0x%x of %s: " "Statistic ID 0x%x declares %u data bytes, %zu left", offset , where, id, data_size, left - sizeof(*pstatistic_entry)); | |||
| 1997 | break; | |||
| 1998 | } | |||
| 1999 | if (encapsulated && is_context_statistic(pstatistic_entry)) | |||
| 2000 | nvme_show_error("Invalid statistic at offset 0x%x of %s: "nvme_show_message(1, "Invalid statistic at offset 0x%x of %s: " "Context Statistic Descriptor 0x%x does not nest", offset, where , id) | |||
| 2001 | "Context Statistic Descriptor 0x%x does not nest",nvme_show_message(1, "Invalid statistic at offset 0x%x of %s: " "Context Statistic Descriptor 0x%x does not nest", offset, where , id) | |||
| 2002 | offset, where, id)nvme_show_message(1, "Invalid statistic at offset 0x%x of %s: " "Context Statistic Descriptor 0x%x does not nest", offset, where , id); | |||
| 2003 | ||||
| 2004 | print_statistic(pstatistic_entry, array, NULL((void*)0), fp, | |||
| 2005 | encapsulated); | |||
| 2006 | ||||
| 2007 | /* | |||
| 2008 | * A Context Statistic Descriptor's data size spans its context | |||
| 2009 | * data and its encapsulated descriptors. | |||
| 2010 | */ | |||
| 2011 | offset += sizeof(*pstatistic_entry) + data_size; | |||
| 2012 | } | |||
| 2013 | } | |||
| 2014 | ||||
| 2015 | int parse_statistic(struct nvme_ocp_telemetry_statistic_descriptor *pstatistic_entry, | |||
| 2016 | struct json_object *pstat_obj, FILE *fp) | |||
| 2017 | { | |||
| 2018 | if (pstatistic_entry == NULL((void*)0)) { | |||
| 2019 | nvme_show_error("Statistics Input buffer was NULL")nvme_show_message(1, "Statistics Input buffer was NULL"); | |||
| 2020 | return -1; | |||
| 2021 | } | |||
| 2022 | ||||
| 2023 | if (le16_to_cpu(pstatistic_entry->statistic_id) == STATISTICS_RESERVED_ID) | |||
| 2024 | /* End of statistics entries, return -1 to stop processing the buffer */ | |||
| 2025 | return -1; | |||
| 2026 | ||||
| 2027 | print_statistic(pstatistic_entry, NULL((void*)0), pstat_obj, fp, false0); | |||
| 2028 | return 0; | |||
| 2029 | } | |||
| 2030 | ||||
| 2031 | int parse_statistics(struct json_object *root, struct nvme_ocp_telemetry_offsets *poffsets, | |||
| 2032 | FILE *fp) | |||
| 2033 | { | |||
| 2034 | if (poffsets == NULL((void*)0)) { | |||
| 2035 | nvme_show_error("Input buffer was NULL")nvme_show_message(1, "Input buffer was NULL"); | |||
| 2036 | return -1; | |||
| 2037 | } | |||
| 2038 | ||||
| 2039 | if (!telemetry_da1_header_present(poffsets)) | |||
| 2040 | return 0; | |||
| 2041 | ||||
| 2042 | __u8 *pda1_ocp_header_offset = ptelemetry_buffer + poffsets->header_size;//512 | |||
| 2043 | struct nvme_ocp_header_in_da1 *pda1_header = | |||
| 2044 | (struct nvme_ocp_header_in_da1 *)pda1_ocp_header_offset; | |||
| 2045 | __u64 start_dw, size_dw, da_dw; | |||
| 2046 | __u8 *pda_offset = NULL((void*)0); | |||
| 2047 | char where[16]; | |||
| 2048 | ||||
| 2049 | //Data is present in the form of DWORDS, So multiplying with sizeof(DWORD) | |||
| 2050 | if (poffsets->data_area == 1) { | |||
| 2051 | start_dw = le64_to_cpu(pda1_header->da1_statistic_start); | |||
| 2052 | size_dw = le64_to_cpu(pda1_header->da1_statistic_size); | |||
| 2053 | da_dw = poffsets->da1_size / SIZE_OF_DWORD4; | |||
| 2054 | pda_offset = pda1_ocp_header_offset; | |||
| 2055 | } else if (poffsets->data_area == 2) { | |||
| 2056 | start_dw = le64_to_cpu(pda1_header->da2_statistic_start); | |||
| 2057 | size_dw = le64_to_cpu(pda1_header->da2_statistic_size); | |||
| 2058 | da_dw = poffsets->da2_size / SIZE_OF_DWORD4; | |||
| 2059 | pda_offset = ptelemetry_buffer + poffsets->da2_start_offset; | |||
| 2060 | } else { | |||
| 2061 | nvme_show_error("Unsupported Data Area:[%d]", poffsets->data_area)nvme_show_message(1, "Unsupported Data Area:[%d]", poffsets-> data_area); | |||
| 2062 | return -1; | |||
| 2063 | } | |||
| 2064 | ||||
| 2065 | /* With no statistics, the start is never used */ | |||
| 2066 | if (!size_dw) { | |||
| 2067 | start_dw = 0; | |||
| 2068 | } else if (start_dw > da_dw || size_dw > da_dw - start_dw) { | |||
| 2069 | nvme_show_error("Invalid statistics bounds for Data Area %d",nvme_show_message(1, "Invalid statistics bounds for Data Area %d" , poffsets->data_area) | |||
| 2070 | poffsets->data_area)nvme_show_message(1, "Invalid statistics bounds for Data Area %d" , poffsets->data_area); | |||
| 2071 | return -1; | |||
| 2072 | } | |||
| 2073 | ||||
| 2074 | struct json_object *pstats_array = ((root != NULL((void*)0)) ? json_create_array()json_object_new_array() : NULL((void*)0)); | |||
| 2075 | ||||
| 2076 | snprintf(where, sizeof(where), "Data Area %d", poffsets->data_area); | |||
| 2077 | print_statistics(pda_offset + start_dw * SIZE_OF_DWORD4, size_dw * SIZE_OF_DWORD4, | |||
| 2078 | pstats_array, fp, false0, where); | |||
| 2079 | ||||
| 2080 | if (root != NULL((void*)0) && pstats_array != NULL((void*)0)) | |||
| 2081 | json_object_add_value_array(root,json_object_object_add(root, poffsets->data_area == 1 ? "Data Area 1 Statistics" : "Data Area 2 Statistics", pstats_array) | |||
| 2082 | poffsets->data_area == 1 ? STR_DA_1_STATS : STR_DA_2_STATS,json_object_object_add(root, poffsets->data_area == 1 ? "Data Area 1 Statistics" : "Data Area 2 Statistics", pstats_array) | |||
| 2083 | pstats_array)json_object_object_add(root, poffsets->data_area == 1 ? "Data Area 1 Statistics" : "Data Area 2 Statistics", pstats_array); | |||
| 2084 | ||||
| 2085 | return 0; | |||
| 2086 | } | |||
| 2087 | ||||
| 2088 | static void parse_log_page_header(struct json_object *pobject, FILE *fp) | |||
| 2089 | { | |||
| 2090 | if (ptelemetry_buffer[0] == NVME_LOG_LID_TELEMETRY_CTRL) | |||
| 2091 | generic_structure_parser(ptelemetry_buffer, | |||
| 2092 | controller_log_page_header, | |||
| 2093 | ARRAY_SIZE(controller_log_page_header)(sizeof(controller_log_page_header) / sizeof((controller_log_page_header )[0]) + 0), pobject, 0, fp); | |||
| 2094 | else | |||
| 2095 | generic_structure_parser(ptelemetry_buffer, | |||
| 2096 | host_log_page_header, | |||
| 2097 | ARRAY_SIZE(host_log_page_header)(sizeof(host_log_page_header) / sizeof((host_log_page_header) [0]) + 0), pobject, 0, fp); | |||
| 2098 | } | |||
| 2099 | ||||
| 2100 | int print_ocp_telemetry_normal(struct ocp_telemetry_parse_options *options) | |||
| 2101 | { | |||
| 2102 | int status = 0; | |||
| 2103 | char file_path[PATH_MAX4096]; | |||
| 2104 | FILE *fp = stdoutstdout; | |||
| 2105 | ||||
| 2106 | if (ptelemetry_buffer == NULL((void*)0)) { | |||
| 2107 | nvme_show_error("No telemetry data to parse.")nvme_show_message(1, "No telemetry data to parse."); | |||
| 2108 | return -1; | |||
| 2109 | } | |||
| 2110 | ||||
| 2111 | if (options->output_file != NULL((void*)0)) { | |||
| 2112 | snprintf(file_path, sizeof(file_path), "%s.%s", | |||
| 2113 | options->output_file, "txt"); | |||
| 2114 | fp = fopen(file_path, "w"); | |||
| 2115 | if (!fp) { | |||
| 2116 | nvme_show_error("Failed to open %s file.", file_path)nvme_show_message(1, "Failed to open %s file.", file_path); | |||
| 2117 | return -1; | |||
| 2118 | } | |||
| 2119 | } | |||
| 2120 | ||||
| 2121 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2122 | fprintf(fp, "%s\n", STR_LOG_PAGE_HEADER"Log Page Header"); | |||
| 2123 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2124 | parse_log_page_header(NULL((void*)0), fp); | |||
| 2125 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2126 | fprintf(fp, "%s\n", STR_REASON_IDENTIFIER"Reason Identifier"); | |||
| 2127 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2128 | __u8 *preason_identifier_offset = ptelemetry_buffer + | |||
| 2129 | offsetof(struct nvme_ocp_telemetry_host_initiated_header,__builtin_offsetof(struct nvme_ocp_telemetry_host_initiated_header , reason_id) | |||
| 2130 | reason_id)__builtin_offsetof(struct nvme_ocp_telemetry_host_initiated_header , reason_id); | |||
| 2131 | ||||
| 2132 | generic_structure_parser(preason_identifier_offset, reason_identifier, | |||
| 2133 | ARRAY_SIZE(reason_identifier)(sizeof(reason_identifier) / sizeof((reason_identifier)[0]) + 0), NULL((void*)0), 0, fp); | |||
| 2134 | ||||
| 2135 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2136 | fprintf(fp, "%s\n", STR_TELEMETRY_HOST_DATA_BLOCK_1"Telemetry Host-Initiated Data Block 1"); | |||
| 2137 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2138 | ||||
| 2139 | //Set DA to 1 and get offsets | |||
| 2140 | struct nvme_ocp_telemetry_offsets offsets = { 0 }; | |||
| 2141 | ||||
| 2142 | offsets.data_area = 1;// Default DA - DA1 | |||
| 2143 | ||||
| 2144 | struct nvme_ocp_telemetry_common_header *ptelemetry_common_header = | |||
| 2145 | (struct nvme_ocp_telemetry_common_header *) ptelemetry_buffer; | |||
| 2146 | ||||
| 2147 | get_telemetry_das_offset_and_size(ptelemetry_common_header, ptelemetry_buffer_size, | |||
| 2148 | &offsets); | |||
| 2149 | ||||
| 2150 | __u8 *pda1_header_offset = ptelemetry_buffer + | |||
| 2151 | offsets.da1_start_offset;//512 | |||
| 2152 | ||||
| 2153 | if (telemetry_da1_header_present(&offsets)) { | |||
| 2154 | generic_structure_parser( | |||
| 2155 | pda1_header_offset, ocp_header_in_da1, | |||
| 2156 | ARRAY_SIZE(ocp_header_in_da1)(sizeof(ocp_header_in_da1) / sizeof((ocp_header_in_da1)[0]) + 0), NULL((void*)0), 0, fp); | |||
| 2157 | ||||
| 2158 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2159 | fprintf(fp, "%s\n", STR_SMART_HEALTH_INFO"SMART / Health Information Log(LID-02h)"); | |||
| 2160 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2161 | __u8 *pda1_smart_offset = pda1_header_offset + | |||
| 2162 | offsetof(struct nvme_ocp_header_in_da1,__builtin_offsetof(struct nvme_ocp_header_in_da1, smart_health_info ) | |||
| 2163 | smart_health_info)__builtin_offsetof(struct nvme_ocp_header_in_da1, smart_health_info ); | |||
| 2164 | //512+512 =1024 | |||
| 2165 | ||||
| 2166 | generic_structure_parser(pda1_smart_offset, smart, | |||
| 2167 | ARRAY_SIZE(smart)(sizeof(smart) / sizeof((smart)[0]) + 0), NULL((void*)0), 0, fp); | |||
| 2168 | ||||
| 2169 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2170 | fprintf(fp, "%s\n", STR_SMART_HEALTH_INTO_EXTENDED"SMART / Health Information Extended(LID-C0h)"); | |||
| 2171 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2172 | __u8 *pda1_smart_ext_offset = pda1_header_offset + | |||
| 2173 | offsetof(struct nvme_ocp_header_in_da1,__builtin_offsetof(struct nvme_ocp_header_in_da1, smart_health_info_extended ) | |||
| 2174 | smart_health_info_extended)__builtin_offsetof(struct nvme_ocp_header_in_da1, smart_health_info_extended ); | |||
| 2175 | ||||
| 2176 | generic_structure_parser( | |||
| 2177 | pda1_smart_ext_offset, smart_extended, | |||
| 2178 | ARRAY_SIZE(smart_extended)(sizeof(smart_extended) / sizeof((smart_extended)[0]) + 0), NULL((void*)0), 0, fp); | |||
| 2179 | } | |||
| 2180 | ||||
| 2181 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2182 | fprintf(fp, "%s\n", STR_DA_1_STATS"Data Area 1 Statistics"); | |||
| 2183 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2184 | ||||
| 2185 | status = parse_statistics(NULL((void*)0), &offsets, fp); | |||
| 2186 | if (status != 0) { | |||
| 2187 | nvme_show_error("status: %d", status)nvme_show_message(1, "status: %d", status); | |||
| 2188 | status = -1; | |||
| 2189 | goto out; | |||
| 2190 | } | |||
| 2191 | ||||
| 2192 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2193 | fprintf(fp, "%s\n", STR_DA_1_EVENT_FIFO_INFO"Data Area 1 Event FIFO info"); | |||
| 2194 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2195 | status = parse_event_fifos(NULL((void*)0), &offsets, fp); | |||
| 2196 | if (status != 0) { | |||
| 2197 | status = -1; | |||
| 2198 | goto out; | |||
| 2199 | } | |||
| 2200 | ||||
| 2201 | //Set the DA to 2 | |||
| 2202 | if (options->data_area >= DATA_AREA_2) { | |||
| 2203 | offsets.data_area = 2; | |||
| 2204 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2205 | fprintf(fp, "%s\n", STR_DA_2_STATS"Data Area 2 Statistics"); | |||
| 2206 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2207 | status = parse_statistics(NULL((void*)0), &offsets, fp); | |||
| 2208 | if (status != 0) { | |||
| 2209 | nvme_show_error("status: %d", status)nvme_show_message(1, "status: %d", status); | |||
| 2210 | status = -1; | |||
| 2211 | goto out; | |||
| 2212 | } | |||
| 2213 | ||||
| 2214 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2215 | fprintf(fp, "%s\n", STR_DA_2_EVENT_FIFO_INFO"Data Area 2 Event FIFO info"); | |||
| 2216 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2217 | status = parse_event_fifos(NULL((void*)0), &offsets, fp); | |||
| 2218 | if (status != 0) { | |||
| 2219 | status = -1; | |||
| 2220 | goto out; | |||
| 2221 | } | |||
| 2222 | } | |||
| 2223 | ||||
| 2224 | fprintf(fp, STR_LINE"==============================================================================\n"); | |||
| 2225 | out: | |||
| 2226 | if (fp != stdoutstdout) | |||
| 2227 | fclose(fp); | |||
| 2228 | ||||
| 2229 | return status; | |||
| 2230 | } | |||
| 2231 | ||||
| 2232 | #ifdef CONFIG_JSONC | |||
| 2233 | int print_ocp_telemetry_json(struct ocp_telemetry_parse_options *options) | |||
| 2234 | { | |||
| 2235 | int status = 0; | |||
| 2236 | char file_path[PATH_MAX4096]; | |||
| 2237 | ||||
| 2238 | //create json objects | |||
| 2239 | struct json_object *root, *pheader, *preason_identifier, *da1_header, *smart_obj, | |||
| 2240 | *ext_smart_obj; | |||
| 2241 | ||||
| 2242 | root = json_create_object()json_object_new_object(); | |||
| 2243 | ||||
| 2244 | //Add data to root json object | |||
| 2245 | ||||
| 2246 | //"Log Page Header" | |||
| 2247 | pheader = json_create_object()json_object_new_object(); | |||
| 2248 | ||||
| 2249 | parse_log_page_header(pheader, NULL((void*)0)); | |||
| 2250 | json_object_add_value_object(root, STR_LOG_PAGE_HEADER, pheader)json_object_object_add(root, "Log Page Header", pheader); | |||
| 2251 | ||||
| 2252 | //"Reason Identifier" | |||
| 2253 | preason_identifier = json_create_object()json_object_new_object(); | |||
| 2254 | ||||
| 2255 | __u8 *preason_identifier_offset = ptelemetry_buffer + | |||
| 2256 | offsetof(struct nvme_ocp_telemetry_host_initiated_header, reason_id)__builtin_offsetof(struct nvme_ocp_telemetry_host_initiated_header , reason_id); | |||
| 2257 | ||||
| 2258 | generic_structure_parser(preason_identifier_offset, reason_identifier, | |||
| 2259 | ARRAY_SIZE(reason_identifier)(sizeof(reason_identifier) / sizeof((reason_identifier)[0]) + 0), preason_identifier, 0, NULL((void*)0)); | |||
| 2260 | json_object_add_value_object(pheader, STR_REASON_IDENTIFIER, preason_identifier)json_object_object_add(pheader, "Reason Identifier", preason_identifier ); | |||
| 2261 | ||||
| 2262 | struct nvme_ocp_telemetry_offsets offsets = { 0 }; | |||
| 2263 | ||||
| 2264 | //Set DA to 1 and get offsets | |||
| 2265 | offsets.data_area = 1; | |||
| 2266 | struct nvme_ocp_telemetry_common_header *ptelemetry_common_header = | |||
| 2267 | (struct nvme_ocp_telemetry_common_header *) ptelemetry_buffer; | |||
| 2268 | ||||
| 2269 | get_telemetry_das_offset_and_size(ptelemetry_common_header, ptelemetry_buffer_size, | |||
| 2270 | &offsets); | |||
| 2271 | ||||
| 2272 | //"Telemetry Host-Initiated Data Block 1" | |||
| 2273 | __u8 *pda1_header_offset = ptelemetry_buffer + offsets.da1_start_offset;//512 | |||
| 2274 | ||||
| 2275 | if (telemetry_da1_header_present(&offsets)) { | |||
| 2276 | da1_header = json_create_object()json_object_new_object(); | |||
| 2277 | ||||
| 2278 | generic_structure_parser( | |||
| 2279 | pda1_header_offset, ocp_header_in_da1, | |||
| 2280 | ARRAY_SIZE(ocp_header_in_da1)(sizeof(ocp_header_in_da1) / sizeof((ocp_header_in_da1)[0]) + 0), da1_header, 0, NULL((void*)0)); | |||
| 2281 | json_object_add_value_object(root,json_object_object_add(root, "Telemetry Host-Initiated Data Block 1" , da1_header) | |||
| 2282 | STR_TELEMETRY_HOST_DATA_BLOCK_1,json_object_object_add(root, "Telemetry Host-Initiated Data Block 1" , da1_header) | |||
| 2283 | da1_header)json_object_object_add(root, "Telemetry Host-Initiated Data Block 1" , da1_header); | |||
| 2284 | ||||
| 2285 | //"SMART / Health Information Log(LID-02h)" | |||
| 2286 | __u8 *pda1_smart_offset = pda1_header_offset + | |||
| 2287 | offsetof(struct nvme_ocp_header_in_da1,__builtin_offsetof(struct nvme_ocp_header_in_da1, smart_health_info ) | |||
| 2288 | smart_health_info)__builtin_offsetof(struct nvme_ocp_header_in_da1, smart_health_info ); | |||
| 2289 | smart_obj = json_create_object()json_object_new_object(); | |||
| 2290 | ||||
| 2291 | generic_structure_parser(pda1_smart_offset, smart, | |||
| 2292 | ARRAY_SIZE(smart)(sizeof(smart) / sizeof((smart)[0]) + 0), smart_obj, 0, | |||
| 2293 | NULL((void*)0)); | |||
| 2294 | json_object_add_value_object(da1_header, STR_SMART_HEALTH_INFO,json_object_object_add(da1_header, "SMART / Health Information Log(LID-02h)" , smart_obj) | |||
| 2295 | smart_obj)json_object_object_add(da1_header, "SMART / Health Information Log(LID-02h)" , smart_obj); | |||
| 2296 | ||||
| 2297 | //"SMART / Health Information Extended(LID-C0h)" | |||
| 2298 | __u8 *pda1_smart_ext_offset = pda1_header_offset + | |||
| 2299 | offsetof(struct nvme_ocp_header_in_da1,__builtin_offsetof(struct nvme_ocp_header_in_da1, smart_health_info_extended ) | |||
| 2300 | smart_health_info_extended)__builtin_offsetof(struct nvme_ocp_header_in_da1, smart_health_info_extended ); | |||
| 2301 | ext_smart_obj = json_create_object()json_object_new_object(); | |||
| 2302 | ||||
| 2303 | generic_structure_parser( | |||
| 2304 | pda1_smart_ext_offset, smart_extended, | |||
| 2305 | ARRAY_SIZE(smart_extended)(sizeof(smart_extended) / sizeof((smart_extended)[0]) + 0), ext_smart_obj, 0, NULL((void*)0)); | |||
| 2306 | json_object_add_value_object(da1_header,json_object_object_add(da1_header, "SMART / Health Information Extended(LID-C0h)" , ext_smart_obj) | |||
| 2307 | STR_SMART_HEALTH_INTO_EXTENDED,json_object_object_add(da1_header, "SMART / Health Information Extended(LID-C0h)" , ext_smart_obj) | |||
| 2308 | ext_smart_obj)json_object_object_add(da1_header, "SMART / Health Information Extended(LID-C0h)" , ext_smart_obj); | |||
| 2309 | } | |||
| 2310 | ||||
| 2311 | //Data Area 1 Statistics | |||
| 2312 | status = parse_statistics(root, &offsets, NULL((void*)0)); | |||
| 2313 | if (status != 0) { | |||
| 2314 | nvme_show_error("status: %d", status)nvme_show_message(1, "status: %d", status); | |||
| 2315 | status = -1; | |||
| 2316 | goto out; | |||
| 2317 | } | |||
| 2318 | ||||
| 2319 | //Data Area 1 Event FIFOs | |||
| 2320 | status = parse_event_fifos(root, &offsets, NULL((void*)0)); | |||
| 2321 | if (status != 0) { | |||
| 2322 | status = -1; | |||
| 2323 | goto out; | |||
| 2324 | } | |||
| 2325 | ||||
| 2326 | if (options->data_area >= DATA_AREA_2) { | |||
| 2327 | //Set the DA to 2 | |||
| 2328 | offsets.data_area = 2; | |||
| 2329 | //Data Area 2 Statistics | |||
| 2330 | status = parse_statistics(root, &offsets, NULL((void*)0)); | |||
| 2331 | if (status != 0) { | |||
| 2332 | nvme_show_error("status: %d", status)nvme_show_message(1, "status: %d", status); | |||
| 2333 | status = -1; | |||
| 2334 | goto out; | |||
| 2335 | } | |||
| 2336 | ||||
| 2337 | //Data Area 2 Event FIFOs | |||
| 2338 | status = parse_event_fifos(root, &offsets, NULL((void*)0)); | |||
| 2339 | if (status != 0) { | |||
| 2340 | status = -1; | |||
| 2341 | goto out; | |||
| 2342 | } | |||
| 2343 | } | |||
| 2344 | ||||
| 2345 | if (options->output_file != NULL((void*)0)) { | |||
| 2346 | const char *json_string = json_object_to_json_string(root); | |||
| 2347 | snprintf(file_path, sizeof(file_path), "%s.%s", | |||
| 2348 | options->output_file, "json"); | |||
| 2349 | FILE *fp = fopen(file_path, "w"); | |||
| 2350 | ||||
| 2351 | if (fp) { | |||
| 2352 | fputs(json_string, fp); | |||
| 2353 | fclose(fp); | |||
| 2354 | } else { | |||
| 2355 | nvme_show_error("Failed to open %s file.", file_path)nvme_show_message(1, "Failed to open %s file.", file_path); | |||
| 2356 | status = -1; | |||
| 2357 | } | |||
| 2358 | } else { | |||
| 2359 | //Print root json object | |||
| 2360 | json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 << 1) | (1 << 4))); | |||
| 2361 | nvme_show_result("\n")nvme_show_message(0, "\n"); | |||
| 2362 | } | |||
| 2363 | ||||
| 2364 | out: | |||
| 2365 | json_free_object(root)json_object_put(root); | |||
| 2366 | ||||
| 2367 | return status; | |||
| 2368 | } | |||
| 2369 | #endif /* CONFIG_JSONC */ |