diff --git a/plugins/solidigm/solidigm-nvme.h b/plugins/solidigm/solidigm-nvme.h index 052fe99c62..b9a72cfcb0 100644 --- a/plugins/solidigm/solidigm-nvme.h +++ b/plugins/solidigm/solidigm-nvme.h @@ -13,7 +13,7 @@ #include "cmd.h" -#define SOLIDIGM_PLUGIN_VERSION "1.21" +#define SOLIDIGM_PLUGIN_VERSION "1.22" PLUGIN(NAME("solidigm", "Solidigm vendor specific extensions", SOLIDIGM_PLUGIN_VERSION), COMMAND_LIST( diff --git a/plugins/solidigm/solidigm-telemetry/config.c b/plugins/solidigm/solidigm-telemetry/config.c index c66c04a7d9..ac2fe56cd5 100644 --- a/plugins/solidigm/solidigm-telemetry/config.c +++ b/plugins/solidigm/solidigm-telemetry/config.c @@ -43,6 +43,10 @@ static bool config_get_by_version(const struct json_object *obj, if (json_object_object_get_ex(major_obj, "*", value)) return value != NULL; + /* Try alternative minor version wildcard */ + if (json_object_object_get_ex(major_obj, "49374", value)) + return value != NULL; + SOLIDIGM_LOG_WARNING( "Warning: Object %s version major %d found but minor %d not found\n", key, version_major, version_minor); @@ -57,6 +61,25 @@ static bool config_get_by_version(const struct json_object *obj, /* Try wildcard minor version */ if (json_object_object_get_ex(major_obj, "*", value)) return value != NULL; + + /* Try alternative minor version wildcard */ + if (json_object_object_get_ex(major_obj, "49374", value)) + return value != NULL; + } + + /* Try alternative major version wildcard */ + if (json_object_object_get_ex(obj, "47837", &major_obj)) { + /* Try exact minor version match */ + if (json_object_object_get_ex(major_obj, str_subkey, value)) + return value != NULL; + + /* Try wildcard minor version */ + if (json_object_object_get_ex(major_obj, "*", value)) + return value != NULL; + + /* Try alternative minor version wildcard */ + if (json_object_object_get_ex(major_obj, "49374", value)) + return value != NULL; } return false; diff --git a/plugins/solidigm/solidigm-telemetry/data-area.c b/plugins/solidigm/solidigm-telemetry/data-area.c index bf5a4a1499..189d8e8d03 100644 --- a/plugins/solidigm/solidigm-telemetry/data-area.c +++ b/plugins/solidigm/solidigm-telemetry/data-area.c @@ -361,7 +361,9 @@ static int telemetry_log_data_area_get_offset(const struct telemetry_log *tl, *offset = offset_blocks * NVME_LOG_TELEM_BLOCK_SIZE; last = (last_block + 1) * NVME_LOG_TELEM_BLOCK_SIZE; *size = last - *offset; - if ((*offset > tl->log_size) || (last > tl->log_size) || (last <= *offset)) { + if (last < *offset) + return -1; /* DA is absent or empty, skip silently */ + if ((*offset > tl->log_size) || (last > tl->log_size)) { SOLIDIGM_LOG_WARNING("Warning: Data Area %d don't fit this Telemetry log.", da); return -1; } @@ -610,7 +612,8 @@ int solidigm_telemetry_log_data_areas_parse(struct telemetry_log *tl, struct json_object *toc_array = NULL; solidigm_telemetry_log_da1_check_ocp(tl); - sldm_telemetry_da2_check_skhT(tl); + sldm_telemetry_check_for_skhT(tl); + // if TELEMETRY_CONFIG_META available copy it to the output for better // context in the output data if (tl->configuration) { @@ -631,7 +634,7 @@ int solidigm_telemetry_log_data_areas_parse(struct telemetry_log *tl, if (tl->is_ocp) first_da = NVME_TELEMETRY_DA_3; - if (tl->is_skhT) { + if (tl->skhT_offset) { if (last_da >= NVME_TELEMETRY_DA_2) sldm_telemetry_skhT_parse(tl); if (last_da >= NVME_TELEMETRY_DA_3) diff --git a/plugins/solidigm/solidigm-telemetry/skht.c b/plugins/solidigm/solidigm-telemetry/skht.c index 44e3b708e6..a27d1c7f1a 100644 --- a/plugins/solidigm/solidigm-telemetry/skht.c +++ b/plugins/solidigm/solidigm-telemetry/skht.c @@ -14,36 +14,80 @@ #include "data-area.h" #include "debug-info.h" -void sldm_telemetry_da2_check_skhT(struct telemetry_log *tl) +static char *json_byte_array_to_string(struct json_object *array) +{ + size_t len = json_object_array_length(array); + char *str = malloc(len + 1); + + if (!str) + return NULL; + for (size_t i = 0; i < len; i++) + str[i] = (char)json_object_get_int( + json_object_array_get_idx(array, i)); + str[len] = '\0'; + return str; +} + +void sldm_telemetry_check_for_skhT(struct telemetry_log *tl) { const uint32_t expected_signature = 0x54686B73; // "skhT" in little-endian - uint16_t da2_offset = (tl->log->dalb1 + 1) * NVME_LOG_TELEM_BLOCK_SIZE; - uint32_t signature = 0; - - // Basic bounds checking - if (tl->log_size >= (da2_offset + sizeof(uint32_t))) { - // Read the first 4 bytes as the signature - memcpy(&signature, (char *)tl->log + da2_offset, sizeof(uint32_t)); - tl->is_skhT = (signature == expected_signature); - } else { - tl->is_skhT = false; + const uint32_t da2_offset = + ((uint32_t)(tl->log->dalb1 + 1) * NVME_LOG_TELEM_BLOCK_SIZE); + const uint32_t da3_offset = + ((uint32_t)(tl->log->dalb2 + 1) * NVME_LOG_TELEM_BLOCK_SIZE); + + tl->skhT_offset = 0; + if (!tl->is_ocp) { + // Basic bounds checking + if (tl->log_size >= (da2_offset + sizeof(uint32_t))) { + // Read the first 4 bytes as the signature + uint32_t signature = + *(uint32_t *)((char *)tl->log + da2_offset); + + if (signature == expected_signature) + tl->skhT_offset = da2_offset; + } + return; + } + // For OCP drives, the signature is located after the segment headers + // in Data Area 3 + if (tl->log_size >= (da3_offset + sizeof(uint32_t))) { + uint32_t num_segments = + *(uint32_t *)((char *)tl->log + da3_offset); + // check if num_segments is greater than 0 and less than a + // reasonable limit to avoid overflow + if (num_segments == 0 || num_segments > 1000) + return; + uint16_t segment_header_size = ((num_segments - 1) / 10 + 1) * + NVME_LOG_TELEM_BLOCK_SIZE; + + if (tl->log_size >= + (da3_offset + segment_header_size + sizeof(uint32_t))) { + uint32_t signature = *(uint32_t *)((char *)tl->log + + da3_offset + segment_header_size); + + if (signature == expected_signature) + tl->skhT_offset = + da3_offset + segment_header_size; + } } } void sldm_telemetry_skhT_parse(struct telemetry_log *tl) { - uint16_t da2_offset = (tl->log->dalb1 + 1) * NVME_LOG_TELEM_BLOCK_SIZE; + uint32_t header_offset = tl->skhT_offset; struct json_object *hynix_header_def = NULL; bool has_struct = false; struct json_object *build_info_def = NULL; bool has_build_info = false; struct json_object *size_bit_obj = NULL; uint32_t hynix_header_size_bits = 0; - uint16_t build_info_offset = da2_offset; + uint32_t build_info_offset = header_offset; // Basic validation - if (tl->log_size < da2_offset) { - SOLIDIGM_LOG_WARNING("Warning: Data Area 2 offset is beyond telemetry log size."); + if (tl->log_size < header_offset) { + SOLIDIGM_LOG_WARNING( + "Warning: skhT header offset is beyond telemetry log size."); return; } @@ -59,7 +103,8 @@ void sldm_telemetry_skhT_parse(struct telemetry_log *tl) } // Parse HynixHeader directly into tl->root - if (sldm_telemetry_structure_parse(tl, hynix_header_def, da2_offset * NUM_BITS_IN_BYTE, + if (sldm_telemetry_structure_parse(tl, hynix_header_def, + header_offset * NUM_BITS_IN_BYTE, tl->root, NULL) != 0) { SOLIDIGM_LOG_WARNING("Failed to parse HynixHeader structure"); return; @@ -88,7 +133,8 @@ void sldm_telemetry_skhT_parse(struct telemetry_log *tl) } // Parse BuildInfo directly into tl->root - if (sldm_telemetry_structure_parse(tl, build_info_def, build_info_offset * NUM_BITS_IN_BYTE, + if (sldm_telemetry_structure_parse(tl, build_info_def, + build_info_offset * NUM_BITS_IN_BYTE, tl->root, NULL) != 0) { SOLIDIGM_LOG_WARNING("Failed to parse BuildInfo structure"); return; @@ -99,7 +145,7 @@ void sldm_telemetry_sktT_segment_parse(struct telemetry_log *tl, struct json_object *toc_array, struct json_object *tele_obj_array) { - uint16_t da3_offset = (tl->log->dalb2 + 1) * NVME_LOG_TELEM_BLOCK_SIZE; + uint32_t da3_offset = (tl->log->dalb2 + 1) * NVME_LOG_TELEM_BLOCK_SIZE; struct json_object *segment_header_definition = NULL; struct json_object *segment_header_obj = NULL; struct json_object *num_segments_obj = NULL; @@ -154,7 +200,7 @@ void sldm_telemetry_sktT_segment_parse(struct telemetry_log *tl, struct json_object *description_obj = NULL; uint32_t offset = 0; uint32_t size = 0; - const char *description_str = NULL; + char *description_str = NULL; // Get segment info from the parsed JSON structure descriptor_obj = json_object_array_get_idx(descriptors_obj, i); @@ -181,11 +227,54 @@ void sldm_telemetry_sktT_segment_parse(struct telemetry_log *tl, continue; } - if (json_object_object_get_ex(descriptor_obj, "nDescription", &description_obj)) - description_str = json_object_get_string(description_obj); + // Get the description for this segment + if (json_object_object_get_ex(descriptor_obj, + "nDescription", + &description_obj)) { + if (json_object_get_type(description_obj) == + json_type_array) { + description_str = + json_byte_array_to_string( + description_obj); + /* replace json array with string in output + * for better readability + */ + json_object_object_add(descriptor_obj, + "nDescription", + json_object_new_string( + description_str)); + } + } else { + SOLIDIGM_LOG_WARNING( + "nDescription not found for segment %d", i); + json_object_put(descriptor); + continue; + } + + struct json_object *type_name_obj = NULL; + + if (json_object_object_get_ex(descriptor_obj, "nTypeName", + &type_name_obj) && + json_object_get_type(type_name_obj) == json_type_array) { + char *type_name_alloc = + json_byte_array_to_string(type_name_obj); + + json_object_object_add(descriptor_obj, "nTypeName", + json_object_new_string( + type_name_alloc)); + free(type_name_alloc); + } + + if (description_str == 0) { + SOLIDIGM_LOG_WARNING( + "Warning: nDescription is NULL for segment %d", + i); + json_object_put(descriptor); + continue; + } // check if descriptions starts with "TRACKER_DATA" - if (description_str && strncmp(description_str, "TRACKER_DATA", + if (strncmp(description_str, "TRACKER_DATA", sizeof("TRACKER_DATA") - 1) == 0) { struct json_object *tracker_obj = json_object_new_object(); @@ -194,14 +283,14 @@ void sldm_telemetry_sktT_segment_parse(struct telemetry_log *tl, sldm_tracker_parse(tl, NVME_LOG_TELEM_BLOCK_SIZE + offset, size, tracker_obj); } - if (description_str && strncmp(description_str, "UART_LOG_INFO", + if (strncmp(description_str, "UART_LOG_INFO", sizeof("UART_LOG_INFO") - 1) == 0) { json_object_object_add(tl->root, description_str, json_object_new_string( (char *)tl->log + NVME_LOG_TELEM_BLOCK_SIZE + offset)); } - if (description_str && strncmp(description_str, "DEBUG_INFO", + if (strncmp(description_str, "DEBUG_INFO", sizeof("DEBUG_INFO") - 1) == 0) { struct json_object *debug_info_obj = json_object_new_object(); @@ -209,5 +298,6 @@ void sldm_telemetry_sktT_segment_parse(struct telemetry_log *tl, sldm_debug_info_parse(tl, NVME_LOG_TELEM_BLOCK_SIZE + offset, size, debug_info_obj); } + free(description_str); } } diff --git a/plugins/solidigm/solidigm-telemetry/skht.h b/plugins/solidigm/solidigm-telemetry/skht.h index d14e0e7fba..98f79316bd 100644 --- a/plugins/solidigm/solidigm-telemetry/skht.h +++ b/plugins/solidigm/solidigm-telemetry/skht.h @@ -13,7 +13,7 @@ #define SKT_VER_MAJOR 47837 #define SKT_VER_MINOR 49374 -void sldm_telemetry_da2_check_skhT(struct telemetry_log *tl); +void sldm_telemetry_check_for_skhT(struct telemetry_log *tl); void sldm_telemetry_sktT_segment_parse(struct telemetry_log *tl, struct json_object *toc_array, struct json_object *tele_obj_array); diff --git a/plugins/solidigm/solidigm-telemetry/telemetry-log.h b/plugins/solidigm/solidigm-telemetry/telemetry-log.h index 79136b42ce..8442e4a927 100644 --- a/plugins/solidigm/solidigm-telemetry/telemetry-log.h +++ b/plugins/solidigm/solidigm-telemetry/telemetry-log.h @@ -27,7 +27,7 @@ struct telemetry_log { struct json_object *root; struct json_object *configuration; bool is_ocp; - bool is_skhT; + uint32_t skhT_offset; }; #endif /* _SOLIDIGM_TELEMETRY_LOG_H */