diff --git a/plugins/solidigm/solidigm-nvme.h b/plugins/solidigm/solidigm-nvme.h index 01a967b696..1928abe51a 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.12" +#define SOLIDIGM_PLUGIN_VERSION "1.13" 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 9d1b4e2ae3..56bfe33b0c 100644 --- a/plugins/solidigm/solidigm-telemetry/config.c +++ b/plugins/solidigm/solidigm-telemetry/config.c @@ -27,26 +27,34 @@ static bool config_get_by_version(const struct json_object *obj, int version_maj if (!json_object_object_get_ex(obj, str_key, &major_obj)) return false; - if (!json_object_object_get_ex(major_obj, str_subkey, value)) + if (!json_object_object_get_ex(major_obj, str_subkey, value)) return false; return value != NULL; } -bool solidigm_config_get_struct_by_token_version(const struct json_object *config, int token_id, +bool sldm_config_get_struct_by_key_version(const struct json_object *config, char *key, int version_major, int version_minor, struct json_object **value) { struct json_object *token = NULL; - char str_key[MAX_16BIT_NUM_AS_STRING_SIZE]; - - snprintf(str_key, sizeof(str_key), "%d", token_id); - if (!json_object_object_get_ex(config, str_key, &token)) + if (!json_object_object_get_ex(config, key, &token)) return false; - if (!config_get_by_version(token, version_major, version_minor, value)) + if (!config_get_by_version(token, version_major, version_minor, value)) return false; return value != NULL; } +bool solidigm_config_get_struct_by_token_version(const struct json_object *config, int token_id, + int version_major, int version_minor, + struct json_object **value) +{ + char str_key[MAX_16BIT_NUM_AS_STRING_SIZE]; + + snprintf(str_key, sizeof(str_key), "%d", token_id); + return sldm_config_get_struct_by_key_version(config, str_key, + version_major, version_minor, value); +} + const char *solidigm_config_get_nlog_obj_name(const struct json_object *config, uint32_t token) { struct json_object *nlog_names = NULL; diff --git a/plugins/solidigm/solidigm-telemetry/config.h b/plugins/solidigm/solidigm-telemetry/config.h index 4e56ba3b5c..b12cee2236 100644 --- a/plugins/solidigm/solidigm-telemetry/config.h +++ b/plugins/solidigm/solidigm-telemetry/config.h @@ -9,6 +9,9 @@ #define STR_HEX32_SIZE sizeof("0x00000000") +bool sldm_config_get_struct_by_key_version(const struct json_object *config, char *key, + int version_major, int version_minor, + struct json_object **value); bool solidigm_config_get_struct_by_token_version(const struct json_object *obj, int key, int subkey, int subsubkey, diff --git a/plugins/solidigm/solidigm-telemetry/data-area.c b/plugins/solidigm/solidigm-telemetry/data-area.c index b30aac421a..b9d0495ee0 100644 --- a/plugins/solidigm/solidigm-telemetry/data-area.c +++ b/plugins/solidigm/solidigm-telemetry/data-area.c @@ -11,15 +11,55 @@ #include "data-area.h" #include "config.h" #include "nlog.h" +#include "skht.h" #include -#define SIGNED_INT_PREFIX "int" -#define BITS_IN_BYTE 8 +#define SIGNED_int_PREFIX "int" +#define SIGNED_INT_PREFIX "INT" #define MAX_WARNING_SIZE 1024 #define MAX_ARRAY_RANK 16 #define NLOG_HEADER_ID 101 +static bool uint8_array_to_string(const struct telemetry_log *tl, + uint64_t offset_bit, uint32_t size_bit, + uint32_t array_size, + struct json_object **str_obj) +{ + uint32_t offset_byte = (uint32_t)offset_bit / NUM_BITS_IN_BYTE; + + if (size_bit != 8) { + *str_obj = json_object_new_string( + "Error: Only UINT8 arrays can be converted to strings"); + return false; + } + + if (offset_byte > (tl->log_size - array_size)) { + char err_msg[MAX_WARNING_SIZE]; + + snprintf(err_msg, MAX_WARNING_SIZE, + "String offset greater than binary size (%u > %zu).", + offset_byte, tl->log_size); + *str_obj = json_object_new_string(err_msg); + return false; + } + + // Get direct pointer to the UINT8 array in the telemetry log + const uint8_t *data_ptr = (const uint8_t *)tl->log + offset_byte; + + // Calculate actual string length (stopping at null terminator if found) + size_t actual_length = 0; + + for (actual_length = 0; actual_length < array_size; actual_length++) { + if (data_ptr[actual_length] == '\0') + break; + } + + // Create JSON string directly from the data without intermediate buffer + *str_obj = json_object_new_string_len((const char *)data_ptr, actual_length); + + return true; +} static void reverse_string(char *buff, size_t len) { @@ -54,23 +94,23 @@ static bool telemetry_log_get_value(const struct telemetry_log *tl, return false; } - additional_size_byte = (size_bit - 1) ? (size_bit - 1) / BITS_IN_BYTE : 0; - offset_byte = (uint32_t)offset_bit / BITS_IN_BYTE; + additional_size_byte = (size_bit - 1) ? (size_bit - 1) / NUM_BITS_IN_BYTE : 0; + offset_byte = (uint32_t)offset_bit / NUM_BITS_IN_BYTE; if (offset_byte > (tl->log_size - additional_size_byte)) { char err_msg[MAX_WARNING_SIZE]; snprintf(err_msg, MAX_WARNING_SIZE, - "Value offset greater than binary size (%u > %zu).", - offset_byte, tl->log_size); + "Value offset greater than binary size (%u + %u > %zu).", + offset_byte, additional_size_byte, tl->log_size); *val_obj = json_object_new_string(err_msg); return false; } - offset_bit_from_byte = (uint32_t) (offset_bit - ((uint64_t)offset_byte * BITS_IN_BYTE)); + offset_bit_from_byte = (uint32_t) (offset_bit - ((uint64_t)offset_byte * NUM_BITS_IN_BYTE)); - if ((size_bit + offset_bit_from_byte) > (sizeof(uint64_t) * BITS_IN_BYTE)) { + if ((size_bit + offset_bit_from_byte) > (sizeof(uint64_t) * NUM_BITS_IN_BYTE)) { char err_msg[MAX_WARNING_SIZE]; snprintf(err_msg, MAX_WARNING_SIZE, @@ -96,17 +136,19 @@ static bool telemetry_log_get_value(const struct telemetry_log *tl, return true; } -static int telemetry_log_structure_parse(const struct telemetry_log *tl, - struct json_object *struct_def, - uint64_t parent_offset_bit, - struct json_object *output, - struct json_object *metadata) +int sldm_telemetry_structure_parse(const struct telemetry_log *tl, + struct json_object *struct_def, + uint64_t parent_offset_bit, + struct json_object *output, + struct json_object *metadata) { struct json_object *obj_arraySizeArray = NULL; struct json_object *obj = NULL; struct json_object *obj_memberList; struct json_object *major_dimension = NULL; struct json_object *sub_output; + struct json_object *obj_arraySizeIndicator = NULL; + bool force_array = false; bool is_enumeration = false; bool has_member_list; const char *type = ""; @@ -115,7 +157,7 @@ static int telemetry_log_structure_parse(const struct telemetry_log *tl, uint64_t offset_bit; uint32_t size_bit; uint64_t linear_array_pos_bit; - uint32_t array_size_dimension[MAX_ARRAY_RANK]; + uint32_t array_size_dimension[MAX_ARRAY_RANK] = { 0 }; if (!json_object_object_get_ex(struct_def, "name", &obj)) { SOLIDIGM_LOG_WARNING("Warning: Structure definition missing property 'name': %s", @@ -186,6 +228,26 @@ static int telemetry_log_structure_parse(const struct telemetry_log *tl, array_size_dimension[i] = json_object_get_int(dimension); major_dimension = dimension; } + + // Check for arraySizeIndicator property to support dynamic array sizes + if (json_object_object_get_ex(struct_def, "arraySizeIndicator", &obj_arraySizeIndicator)) { + const char *indicator_name = json_object_get_string(obj_arraySizeIndicator); + + force_array = true; // Force array output even if size is 1 + + // Look for the indicator property in the parent object (output) + if (indicator_name && output) { + struct json_object *parent_prop = NULL; + + if (json_object_object_get_ex(output, indicator_name, &parent_prop)) { + // Get the dynamic array size from the parent property + uint32_t dynamic_size = json_object_get_int(parent_prop); + + array_size_dimension[0] = dynamic_size; + } + } + } + if (array_rank > 1) { uint32_t linear_pos_per_index = array_size_dimension[0]; uint32_t prev_index_offset_bit = 0; @@ -214,8 +276,8 @@ static int telemetry_log_structure_parse(const struct telemetry_log *tl, offset = parent_offset_bit + prev_index_offset_bit; json_object_array_add(dimension_output, sub_array); - telemetry_log_structure_parse(tl, struct_def, - offset, sub_array, NULL); + sldm_telemetry_structure_parse(tl, struct_def, + offset, sub_array, NULL); prev_index_offset_bit += linear_pos_per_index * size_bit; } @@ -228,7 +290,23 @@ static int telemetry_log_structure_parse(const struct telemetry_log *tl, linear_array_pos_bit = 0; sub_output = output; - if (array_size_dimension[0] > 1) { + if (array_size_dimension[0] > 1 || force_array) { + // Check if this is a UINT8 array that should be treated as a string + if (strcmp(type, "UINT8") == 0 && !force_array) { + // Handle UINT8 arrays as strings + struct json_object *str_obj = NULL; + uint64_t offset = parent_offset_bit + offset_bit; + + if (uint8_array_to_string(tl, offset, size_bit, + array_size_dimension[0], &str_obj)) { + json_object_object_add(output, name, str_obj); + return 0; + } + + // If string conversion failed, fall back to normal array processing + json_object_put(str_obj); + } + sub_output = json_create_array(); if (json_object_get_type(output) == json_type_array) json_object_array_add(output, sub_output); @@ -238,13 +316,16 @@ static int telemetry_log_structure_parse(const struct telemetry_log *tl, for (uint32_t j = 0; j < array_size_dimension[0]; j++) { if (is_enumeration || !has_member_list) { - bool is_signed = !strncmp(type, SIGNED_INT_PREFIX, sizeof(SIGNED_INT_PREFIX)-1); + bool is_signed = !strncmp(type, SIGNED_int_PREFIX, + sizeof(SIGNED_int_PREFIX)-1) || + !strncmp(type, SIGNED_INT_PREFIX, + sizeof(SIGNED_INT_PREFIX)-1); struct json_object *val_obj; uint64_t offset; offset = parent_offset_bit + offset_bit + linear_array_pos_bit; if (telemetry_log_get_value(tl, offset, size_bit, is_signed, &val_obj)) { - if (array_size_dimension[0] > 1) + if (array_size_dimension[0] > 1 || force_array) json_object_array_put_idx(sub_output, j, val_obj); else json_object_object_add(sub_output, name, val_obj); @@ -256,10 +337,10 @@ static int telemetry_log_structure_parse(const struct telemetry_log *tl, json_free_object(val_obj); } } else { - struct json_object *sub_sub_output = json_create_object(); + struct json_object *sub_sub_output = json_object_new_object(); int num_members; - if (array_size_dimension[0] > 1) + if (array_size_dimension[0] > 1 || force_array) json_object_array_put_idx(sub_output, j, sub_sub_output); else json_object_add_value_object(sub_output, name, sub_sub_output); @@ -270,8 +351,8 @@ static int telemetry_log_structure_parse(const struct telemetry_log *tl, uint64_t offset; offset = parent_offset_bit + offset_bit + linear_array_pos_bit; - telemetry_log_structure_parse(tl, member, offset, - sub_sub_output, NULL); + sldm_telemetry_structure_parse(tl, member, offset, + sub_sub_output, NULL); } } linear_array_pos_bit += size_bit; @@ -393,9 +474,13 @@ static void telemetry_log_data_area_toc_parse(const struct telemetry_log *tl, bool has_struct; const char *nlog_name = NULL; uint32_t header_offset = sizeof(const struct telemetry_object_header); + struct json_object *tele_obj_item; + struct json_object *parsed_struct; + struct json_object *obj_hasTelemObjHdr = NULL; + uint64_t object_file_offset; if ((char *)&toc->items[i] > - (((char *)toc) + da_size - sizeof(const struct toc_item))) { + (((char *)toc) + da_size - sizeof(const struct toc_item))) { SOLIDIGM_LOG_WARNING( "Warning: Data Area %d, Table of Contents item %d crossed Data Area size.", da, i); @@ -409,7 +494,7 @@ static void telemetry_log_data_area_toc_parse(const struct telemetry_log *tl, continue; } - toc_item = json_create_object(); + toc_item = json_object_new_object(); json_object_array_add(toc_array, toc_item); json_object_add_value_uint(toc_item, "dataArea", da); json_object_add_value_uint(toc_item, "dataAreaIndex", i); @@ -443,16 +528,14 @@ static void telemetry_log_data_area_toc_parse(const struct telemetry_log *tl, header->versionMinor, &structure_definition); } - struct json_object *tele_obj_item = json_create_object(); + tele_obj_item = json_object_new_object(); json_object_array_add(tele_obj_array, tele_obj_item); json_object_get(toc_item); json_object_add_value_object(tele_obj_item, "metadata", toc_item); - struct json_object *parsed_struct = json_create_object(); + parsed_struct = json_object_new_object(); json_object_add_value_object(tele_obj_item, "objectData", parsed_struct); - struct json_object *obj_hasTelemObjHdr = NULL; - uint64_t object_file_offset; if (json_object_object_get_ex(structure_definition, "hasTelemObjHdr", @@ -464,8 +547,8 @@ static void telemetry_log_data_area_toc_parse(const struct telemetry_log *tl, } object_file_offset = ((uint64_t)da_offset) + obj_offset + header_offset; if (has_struct) { - telemetry_log_structure_parse(tl, structure_definition, - BITS_IN_BYTE * object_file_offset, + sldm_telemetry_structure_parse(tl, structure_definition, + NUM_BITS_IN_BYTE * object_file_offset, parsed_struct, toc_item); } // NLOGs have different parser from other Telemetry objects @@ -478,7 +561,7 @@ static void telemetry_log_data_area_toc_parse(const struct telemetry_log *tl, if (json_object_object_get_ex(structure_definition, "sizeBit", &header_sizeBits)) header_offset = json_object_get_int(header_sizeBits) / - BITS_IN_BYTE; + NUM_BITS_IN_BYTE; // Overwrite nlogName with correct type if (json_object_object_get_ex(parsed_struct, "nlogSelect", &header_nlogSelect) && @@ -510,17 +593,18 @@ void solidigm_telemetry_log_da1_check_ocp(struct telemetry_log *tl) const uint64_t ocp_telemetry_uuid[] = {0xBC73719D87E64EFA, 0xBA560A9C3043424C}; const uint64_t *log_uuid = (uint64_t *) &tl->log->data_area[16]; - tl->is_ocp = tl->log_size >= (&tl->log->data_area[32] - (uint8_t *) tl->log) && + tl->is_ocp = tl->log_size >= (size_t)(&tl->log->data_area[32] - (uint8_t *) tl->log) && log_uuid[0] == ocp_telemetry_uuid[0] && log_uuid[1] == ocp_telemetry_uuid[1]; } int solidigm_telemetry_log_data_areas_parse(struct telemetry_log *tl, enum nvme_telemetry_da last_da) { - struct json_object *tele_obj_array = json_create_array(); - struct json_object *toc_array = json_create_array(); + struct json_object *tele_obj_array = NULL; + struct json_object *toc_array = NULL; solidigm_telemetry_log_da1_check_ocp(tl); + sldm_telemetry_da2_check_skhT(tl); solidigm_telemetry_log_header_parse(tl); solidigm_telemetry_log_cod_parse(tl); if (tl->configuration) { @@ -529,6 +613,17 @@ 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 (last_da >= NVME_TELEMETRY_DA_2) + sldm_telemetry_skhT_parse(tl); + if (last_da >= NVME_TELEMETRY_DA_3) + sldm_telemetry_sktT_segment_parse(tl, toc_array, + tele_obj_array); + return 0; + } + + tele_obj_array = json_create_array(); + toc_array = json_create_array(); json_object_add_value_array(tl->root, "tableOfContents", toc_array); json_object_add_value_array(tl->root, "telemetryObjects", tele_obj_array); diff --git a/plugins/solidigm/solidigm-telemetry/data-area.h b/plugins/solidigm/solidigm-telemetry/data-area.h index 5ae7efcdf7..00e1d2cf9e 100644 --- a/plugins/solidigm/solidigm-telemetry/data-area.h +++ b/plugins/solidigm/solidigm-telemetry/data-area.h @@ -4,8 +4,20 @@ * * Author: leonardo.da.cunha@solidigm.com */ +#ifndef __SOLIDIGM_DATA_AREA_H__ +#define __SOLIDIGM_DATA_AREA_H__ + #include "telemetry-log.h" +#define NUM_BITS_IN_BYTE 8 + int solidigm_telemetry_log_data_areas_parse(struct telemetry_log *tl, enum nvme_telemetry_da last_da); void solidigm_telemetry_log_da1_check_ocp(struct telemetry_log *tl); +int sldm_telemetry_structure_parse(const struct telemetry_log *tl, + struct json_object *struct_def, + uint64_t parent_offset_bit, + struct json_object *output, + struct json_object *metadata); + +#endif /* __SOLIDIGM_DATA_AREA_H__ */ diff --git a/plugins/solidigm/solidigm-telemetry/meson.build b/plugins/solidigm/solidigm-telemetry/meson.build index 96273b706d..c769a26b59 100644 --- a/plugins/solidigm/solidigm-telemetry/meson.build +++ b/plugins/solidigm/solidigm-telemetry/meson.build @@ -4,4 +4,6 @@ sources += [ 'plugins/solidigm/solidigm-telemetry/config.c', 'plugins/solidigm/solidigm-telemetry/data-area.c', 'plugins/solidigm/solidigm-telemetry/nlog.c', + 'plugins/solidigm/solidigm-telemetry/tracker.c', + 'plugins/solidigm/solidigm-telemetry/skht.c', ] diff --git a/plugins/solidigm/solidigm-telemetry/skht.c b/plugins/solidigm/solidigm-telemetry/skht.c new file mode 100644 index 0000000000..ba526ccb6a --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/skht.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (c) 2025 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#include "common.h" +#include "telemetry-log.h" +#include "config.h" +#include "tracker.h" +#include "skht.h" +#include "util/json.h" +#include "data-area.h" + +void sldm_telemetry_da2_check_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; + } +} + +void sldm_telemetry_skhT_parse(struct telemetry_log *tl) +{ + uint16_t da2_offset = (tl->log->dalb1 + 1) * NVME_LOG_TELEM_BLOCK_SIZE; + 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; + + // Basic validation + if (tl->log_size < da2_offset) { + SOLIDIGM_LOG_WARNING("Warning: Data Area 2 offset is beyond telemetry log size."); + return; + } + + // Use only the dynamic structure from configuration + has_struct = sldm_config_get_struct_by_key_version(tl->configuration, + "HynixHeader", SKT_VER_MAJOR, + SKT_VER_MINOR, &hynix_header_def); + + if (!has_struct || !hynix_header_def) { + SOLIDIGM_LOG_WARNING( + "Warning: HynixHeader structure definition not found in configuration"); + return; + } + + // Parse HynixHeader directly into tl->root + if (sldm_telemetry_structure_parse(tl, hynix_header_def, da2_offset * NUM_BITS_IN_BYTE, + tl->root, NULL) != 0) { + SOLIDIGM_LOG_WARNING("Failed to parse HynixHeader structure"); + return; + } + + // Now add BuildInfo parsing + has_build_info = sldm_config_get_struct_by_key_version(tl->configuration, + "BuildInfo", SKT_VER_MAJOR, + SKT_VER_MINOR, &build_info_def); + + if (!has_build_info || !build_info_def) { + SOLIDIGM_LOG_WARNING( + "Warning: BuildInfo structure definition not found in configuration"); + return; + } + + // Get HynixHeader size from structure definition + if (json_object_object_get_ex(hynix_header_def, "sizeBit", &size_bit_obj)) { + hynix_header_size_bits = json_object_get_int(size_bit_obj); + // Convert bits to bytes and add to offset + build_info_offset += (hynix_header_size_bits / NUM_BITS_IN_BYTE); + } else { + SOLIDIGM_LOG_WARNING( + "Warning: sizeBit not found in HynixHeader structure definition"); + return; + } + + // Parse BuildInfo directly into tl->root + 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; + } +} + +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; + struct json_object *segment_header_definition = NULL; + struct json_object *segment_header_obj = NULL; + struct json_object *num_segments_obj = NULL; + struct json_object *descriptors_obj = NULL; + uint32_t num_segments = 0; + + // Basic validation + if (tl->log_size < da3_offset) { + SOLIDIGM_LOG_WARNING("Warning: Data Area 3 offset is beyond telemetry log size."); + return; + } + + // Get the SegmentHeader structure definition from configuration + if (!sldm_config_get_struct_by_key_version(tl->configuration, + "SegmentHeader", SKT_VER_MAJOR, SKT_VER_MINOR, + &segment_header_definition)) { + SOLIDIGM_LOG_WARNING( + "Warning: SegmentHeader structure definition not found in configuration."); + return; + } + + // Parse the segment header using the dynamic structure definition + if (sldm_telemetry_structure_parse(tl, segment_header_definition, + da3_offset * NUM_BITS_IN_BYTE, + tl->root, NULL) != 0 || + !json_object_object_get_ex(tl->root, "SegmentHeader", &segment_header_obj)) { + SOLIDIGM_LOG_WARNING("Warning: Dynamic parsing of SegmentHeader failed"); + return; + } + + // Get the number of segments from the parsed JSON object + if (!json_object_object_get_ex(segment_header_obj, "nNumSegment", &num_segments_obj)) { + SOLIDIGM_LOG_WARNING( + "Warning: nNumSegment field not found in parsed SegmentHeader"); + return; + } + + num_segments = json_object_get_int(num_segments_obj); + + // Get the descriptors array from the parsed JSON object + if (!json_object_object_get_ex(segment_header_obj, "Descriptors", &descriptors_obj)) { + SOLIDIGM_LOG_WARNING( + "Warning: Descriptors array not found in parsed SegmentHeader"); + return; + } + + for (uint32_t i = 0; i < num_segments; i++) { + struct json_object *descriptor = json_object_new_object(); + struct json_object *descriptor_obj = NULL; + struct json_object *offset_obj = NULL; + struct json_object *size_obj = NULL; + struct json_object *description_obj = NULL; + uint32_t offset = 0; + uint32_t size = 0; + const char *description_str = NULL; + + // Get segment info from the parsed JSON structure + descriptor_obj = json_object_array_get_idx(descriptors_obj, i); + if (json_object_array_length(descriptors_obj) <= i || !descriptor_obj) { + SOLIDIGM_LOG_WARNING( + "Warning: Segment %d not found in descriptors array", i); + json_object_put(descriptor); + continue; + } + + if (json_object_object_get_ex(descriptor_obj, "nOffset", &offset_obj)) + offset = json_object_get_int(offset_obj); + else { + SOLIDIGM_LOG_WARNING("Warning: nOffset not found for segment %d", i); + json_object_put(descriptor); + continue; + } + + if (json_object_object_get_ex(descriptor_obj, "nSize", &size_obj)) + size = json_object_get_int(size_obj); + else { + SOLIDIGM_LOG_WARNING("Warning: nSize not found for segment %d", i); + json_object_put(descriptor); + continue; + } + + if (json_object_object_get_ex(descriptor_obj, "nDescription", &description_obj)) + description_str = json_object_get_string(description_obj); + + // check if descriptions starts with "TRACKER_DATA" + if (description_str && strncmp(description_str, "TRACKER_DATA", + sizeof("TRACKER_DATA") - 1) == 0) { + struct json_object *tracker_obj = json_object_new_object(); + + json_object_object_add(tl->root, description_str, tracker_obj); + // parse the tracker data + sldm_tracker_parse(tl, NVME_LOG_TELEM_BLOCK_SIZE + offset, + size, tracker_obj); + } + if (description_str && 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)); + } + } +} diff --git a/plugins/solidigm/solidigm-telemetry/skht.h b/plugins/solidigm/solidigm-telemetry/skht.h new file mode 100644 index 0000000000..d14e0e7fba --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/skht.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2025 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ +#ifndef __SOLIDIGM_SKHT_H__ +#define __SOLIDIGM_SKHT_H__ + +#include "telemetry-log.h" +#include "util/json.h" + +#define SKT_VER_MAJOR 47837 +#define SKT_VER_MINOR 49374 + +void sldm_telemetry_da2_check_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); + +void sldm_telemetry_skhT_parse(struct telemetry_log *tl); + +#endif /* __SOLIDIGM_SKHT_H__ */ diff --git a/plugins/solidigm/solidigm-telemetry/telemetry-log.h b/plugins/solidigm/solidigm-telemetry/telemetry-log.h index 5edd487f77..f09a81b9be 100644 --- a/plugins/solidigm/solidigm-telemetry/telemetry-log.h +++ b/plugins/solidigm/solidigm-telemetry/telemetry-log.h @@ -28,6 +28,7 @@ struct telemetry_log { struct json_object *root; struct json_object *configuration; bool is_ocp; + bool is_skhT; }; #endif /* _SOLIDIGM_TELEMETRY_LOG_H */ diff --git a/plugins/solidigm/solidigm-telemetry/tracker.c b/plugins/solidigm/solidigm-telemetry/tracker.c new file mode 100644 index 0000000000..92999ce86b --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/tracker.c @@ -0,0 +1,506 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (c) 2025 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ +#include "common.h" +#include "telemetry-log.h" +#include "util/json.h" +#include "config.h" +#include "data-area.h" +#include "skht.h" + +#define TRACKER_CHUNK_SIZE 4096 +#define MAX_ARGS 31 + +#define ABLIST_BUFFER_SIGNATURE 0xab15ab15 // Valid ABLIST + +/* + * All structure information is derived from the JSON configuration file. + * No static structure definitions are used. + * The dynamic parsing relies entirely on the structure definitions + * from the JSON configuration file. + * + * The expected JSON structure format from telemetry_log_structure_parse is: + * { "struct_name": { "field1": value1, "field2": value2, ... } } + */ + +// Get string for tracker level +static const char *get_level_string(uint32_t level) +{ + switch (level) { + case 0: return "DEBUG"; + case 1: return "INFO"; + case 2: return "ERROR"; + case 3: return "CRITICAL"; + default: return "UNKNOWN"; + } +} + +// Get tracker entry description from JSON config +static struct json_object *get_tracker_entry_info(struct json_object *config, uint32_t tracker_id) +{ + struct json_object *tracker_obj = NULL; + struct json_object *entries_obj = NULL; + struct json_object *entry_obj = NULL; + char tracker_id_str[16]; + + if (!config) + return NULL; + + // Convert tracker_id to string for JSON key lookup + snprintf(tracker_id_str, sizeof(tracker_id_str), "%u", tracker_id); + + // Navigate through JSON hierarchy + if (!json_object_object_get_ex(config, "Tracker", &tracker_obj)) + return NULL; + + if (!json_object_object_get_ex(tracker_obj, "TrackerEntry", &entries_obj)) + return NULL; + + if (!json_object_object_get_ex(entries_obj, tracker_id_str, &entry_obj)) + return NULL; + + return entry_obj; +} + +// Get argument description from tracker entry +static const char *get_arg_description(struct json_object *entry_obj, int arg_idx) +{ + char key_name[16]; + struct json_object *desc_obj = NULL; + + // Format key name for argument description + snprintf(key_name, sizeof(key_name), "descArg%d", arg_idx); + + if (json_object_object_get_ex(entry_obj, key_name, &desc_obj)) + return json_object_get_string(desc_obj); + + return NULL; +} + +static uint32_t get_struct_size_from_config(struct json_object *struct_def) +{ + struct json_object *size_bit_obj = NULL; + uint32_t size_bits = 0; + + if (!struct_def) { + SOLIDIGM_LOG_WARNING("Cannot get size from NULL structure definition"); + return 0; + } + + // Get the sizeBit property from the structure definition + if (json_object_object_get_ex(struct_def, "sizeBit", &size_bit_obj)) { + size_bits = (uint32_t)json_object_get_uint64(size_bit_obj); + // Convert from bits to bytes (divide by 8) + return size_bits / 8; + } + + SOLIDIGM_LOG_WARNING("Structure definition missing 'sizeBit' property"); + return 0; +} + +static void parse_tracker_chunk_json(const struct telemetry_log *tl, uint32_t chunk_offset, + int chunk_number, struct json_object *entries_array) +{ + struct json_object *ctx_obj = NULL; + struct json_object *ablist_context_def = NULL; + struct json_object *ablist_entry_def = NULL; + struct json_object *tracker_entry_def = NULL; + uint32_t offset = 0; + uint32_t signature = 0; + uint32_t entry_count = 0; + uint32_t data_start_offset = 0; + struct json_object *metadata_obj = NULL; + struct json_object *config = tl->configuration; + + // Get structure definitions from the config - all are required for dynamic parsing + if (!sldm_config_get_struct_by_key_version(config, + "ablist_context_t", SKT_VER_MAJOR, SKT_VER_MINOR, &ablist_context_def) || + !sldm_config_get_struct_by_key_version(config, + "ablist_entry_t", SKT_VER_MAJOR, SKT_VER_MINOR, &ablist_entry_def) || + !sldm_config_get_struct_by_key_version(config, + "tracker_entry_t", SKT_VER_MAJOR, SKT_VER_MINOR, &tracker_entry_def)) { + + SOLIDIGM_LOG_WARNING( + "Required structure definitions missing from config"); + return; + } + + // Parse the ablist_context_t structure to get context information + ctx_obj = json_object_new_object(); + metadata_obj = json_object_new_object(); + + if (sldm_telemetry_structure_parse(tl, ablist_context_def, chunk_offset * 8, + ctx_obj, metadata_obj) != 0) { + SOLIDIGM_LOG_WARNING("Failed to parse ablist_context_t using dynamic parsing"); + json_object_put(ctx_obj); + json_object_put(metadata_obj); + return; + } + + // Extract fields from the nested object structure + struct json_object *ablist_context_obj = NULL; + + if (json_object_object_get_ex(ctx_obj, "ablist_context_t", &ablist_context_obj)) { + // Extract fields from the inner object + struct json_object *sig_obj = NULL; + struct json_object *start_offset_obj = NULL; + struct json_object *count_obj = NULL; + + if (json_object_object_get_ex(ablist_context_obj, "signature", &sig_obj)) + signature = (uint32_t) json_object_get_int64(sig_obj); + else + SOLIDIGM_LOG_WARNING( + "Failed to find 'signature' field in ablist_context_t"); + + if (json_object_object_get_ex(ablist_context_obj, "data_start_offset", + &start_offset_obj)) + data_start_offset = json_object_get_int(start_offset_obj); + else + SOLIDIGM_LOG_WARNING( + "Failed to find 'data_start_offset' field in ablist_context_t"); + + if (json_object_object_get_ex(ablist_context_obj, "entry_count", &count_obj)) + entry_count = json_object_get_int(count_obj); + else + SOLIDIGM_LOG_WARNING( + "Failed to find 'entry_count' field in ablist_context_t"); + } else { + SOLIDIGM_LOG_WARNING("Nested 'ablist_context_t' object not found in parsed data"); + json_object_put(ctx_obj); + json_object_put(metadata_obj); + return; + } + + json_object_put(ctx_obj); + json_object_put(metadata_obj); + + // Validate chunk signature + if (signature != ABLIST_BUFFER_SIGNATURE) + return; + + offset = data_start_offset; + + // Process all entries in the chunk + while (offset < TRACKER_CHUNK_SIZE && entry_count > 0) { + uint32_t next_entry_index = 0; + struct json_object *entry_json = json_object_new_object(); + struct json_object *entry_metadata = json_object_new_object(); + struct json_object *header_obj = NULL; + struct json_object *data_obj = NULL; + uint32_t hash = 0; + struct json_object *tracker_entry_obj = NULL; + struct json_object *ablist_entry_obj = NULL; + struct json_object *next_obj = NULL; + struct json_object *name_obj = NULL; + struct json_object *file_obj = NULL; + struct json_object *line_obj = NULL; + struct json_object *hash_obj = NULL; + struct json_object *time_obj = NULL; + struct json_object *arm_id_obj = NULL; + struct json_object *level_obj = NULL; + struct json_object *group_obj = NULL; + struct json_object *arg_count_obj = NULL; + struct json_object *args_array = NULL; + struct json_object *arg_obj; + uint32_t arg_count = 0; + // Get the size of ablist_entry_t from configuration + uint32_t ablist_entry_size = get_struct_size_from_config(ablist_entry_def); + + if (ablist_entry_size == 0) { + SOLIDIGM_LOG_WARNING( + "Failed to get size of ablist_entry_t from configuration"); + json_object_put(entry_json); + json_object_put(entry_metadata); + return; + } + + // Parse entry header using ablist_entry_t structure + header_obj = json_object_new_object(); + if (sldm_telemetry_structure_parse(tl, ablist_entry_def, + chunk_offset * 8 + offset * 8, header_obj, NULL) != 0) { + SOLIDIGM_LOG_WARNING( + "Failed to parse ablist_entry_t at offset %u", offset); + json_object_put(header_obj); + json_object_put(entry_json); + json_object_put(entry_metadata); + + // Skip to next entry if we know where it is, otherwise break + if (next_entry_index == 0 || next_entry_index >= TRACKER_CHUNK_SIZE) + break; + + offset = next_entry_index; + entry_count--; + continue; + } + + // Parse entry data using tracker_entry_t structure + data_obj = json_object_new_object(); + if (sldm_telemetry_structure_parse(tl, tracker_entry_def, + chunk_offset * 8 + (offset + ablist_entry_size) * 8, + data_obj, entry_metadata) != 0) { + SOLIDIGM_LOG_WARNING( + "Failed to parse tracker_entry_t at offset %u", + offset + ablist_entry_size); + json_object_put(header_obj); + json_object_put(data_obj); + json_object_put(entry_json); + json_object_put(entry_metadata); + + // Skip to next entry if we know where it is, otherwise break + if (next_entry_index == 0 || next_entry_index >= TRACKER_CHUNK_SIZE) + break; + + offset = next_entry_index; + entry_count--; + continue; + } + + // Get the inner object from header_obj + if (!json_object_object_get_ex(header_obj, + "ablist_entry_t", &ablist_entry_obj)) { + SOLIDIGM_LOG_WARNING( + "Failed to find ablist_entry_t in parsed header data"); + json_object_put(header_obj); + json_object_put(data_obj); + json_object_put(entry_json); + json_object_put(entry_metadata); + + // Skip to next entry if we know where it is, otherwise break + if (next_entry_index == 0 || next_entry_index >= TRACKER_CHUNK_SIZE) + break; + + offset = next_entry_index; + entry_count--; + continue; + } + + // Get next_entry_index from ablist_entry_obj + if (!json_object_object_get_ex(ablist_entry_obj, + "next_entry_index", &next_obj)) { + SOLIDIGM_LOG_WARNING( + "Failed to find 'next_entry_index' in ablist_entry_t"); + // Continue with next_entry_index = 0 (will likely break) + } else { + next_entry_index = json_object_get_int(next_obj); + } + + // Get the inner object from data_obj - only supporting nested + // structure format + if (!json_object_object_get_ex(data_obj, + "tracker_entry_t", &tracker_entry_obj)) { + SOLIDIGM_LOG_WARNING( + "Failed to find tracker_entry_t in parsed data"); + json_object_put(header_obj); + json_object_put(data_obj); + json_object_put(entry_json); + json_object_put(entry_metadata); + + // Skip to next entry if we know where it is, otherwise break + if (next_entry_index == 0 || next_entry_index >= TRACKER_CHUNK_SIZE) + break; + + offset = next_entry_index; + entry_count--; + continue; + } + + // Get hash value from tracker_entry_obj + if (!json_object_object_get_ex(tracker_entry_obj, + "hash", &hash_obj)) { + SOLIDIGM_LOG_WARNING( + "Failed to find 'hash' in tracker_entry_t"); + json_object_put(header_obj); + json_object_put(data_obj); + json_object_put(entry_json); + json_object_put(entry_metadata); + + // Skip to next entry if we know where it is, otherwise break + if (next_entry_index == 0 || next_entry_index >= TRACKER_CHUNK_SIZE) + break; + + offset = next_entry_index; + entry_count--; + continue; + } + + hash = json_object_get_int(hash_obj); + + // Get tracker entry info from config + struct json_object *entry_info = get_tracker_entry_info(config, hash); + + // Handle case where no entry info is found + if (!entry_info) { + // Clean up the entry JSON since we won't be adding it to the array + json_object_put(entry_json); + + // Skip to next entry + if (next_entry_index == 0 || next_entry_index >= TRACKER_CHUNK_SIZE) + break; + + offset = next_entry_index; + entry_count--; + continue; + } + + // Get tracker entry info from config + if (json_object_object_get_ex(entry_info, "idName", &name_obj)) { + json_object_object_add(entry_json, "name", name_obj); + json_object_get(name_obj); // Increase ref count + } + if (json_object_object_get_ex(entry_info, "file", &file_obj)) { + json_object_object_add(entry_json, "file", file_obj); + json_object_get(file_obj); // Increase ref count + } + if (json_object_object_get_ex(entry_info, "line", &line_obj)) { + json_object_object_add(entry_json, "line", line_obj); + json_object_get(line_obj); // Increase ref count + } + + // We need to get tracker_entry_obj again for fields + if (!json_object_object_get_ex(data_obj, + "tracker_entry_t", &tracker_entry_obj)) { + // This shouldn't happen as we already checked, but let's be safe + json_object_put(entry_json); + json_object_put(header_obj); + json_object_put(data_obj); + json_object_put(entry_metadata); + + // Skip to next entry + if (next_entry_index == 0 || next_entry_index >= TRACKER_CHUNK_SIZE) + break; + + offset = next_entry_index; + entry_count--; + continue; + } + + if (json_object_object_get_ex(tracker_entry_obj, "time", + &time_obj)) { + json_object_add_value_uint64(entry_json, "time", + json_object_get_uint64(time_obj)); + } + + if (json_object_object_get_ex(tracker_entry_obj, "arm_id", + &arm_id_obj)) { + json_object_add_value_uint64(entry_json, "arm_id", + json_object_get_int(arm_id_obj)); + } + + // Re-get the hash obj to add the tracker_id to the JSON + if (json_object_object_get_ex(tracker_entry_obj, "hash", + &hash_obj)) { + json_object_add_value_uint64(entry_json, "hash", + json_object_get_int(hash_obj)); + } + + if (json_object_object_get_ex(tracker_entry_obj, "level", + &level_obj)) { + int level = json_object_get_int(level_obj); + + json_object_add_value_uint64(entry_json, "level", level); + json_object_object_add(entry_json, "level_name", + json_object_new_string(get_level_string(level))); + + } + + if (json_object_object_get_ex(tracker_entry_obj, "group", + &group_obj)) { + json_object_add_value_uint64(entry_json, "group", + json_object_get_int(group_obj)); + } + + if (json_object_object_get_ex(tracker_entry_obj, "arg_count", + &arg_count_obj)) { + arg_count = json_object_get_int(arg_count_obj); + json_object_add_value_uint(entry_json, "arg_count", arg_count); + } + + // Add arguments from dynamically parsed data + arg_obj = json_object_new_object(); + + // Get args array + if (!json_object_object_get_ex(tracker_entry_obj, + "args", &args_array) || !args_array) { + // No args or couldn't find args array, add empty arguments object + json_object_object_add(entry_json, "arguments", arg_obj); + json_object_array_add(entries_array, entry_json); + + // Clean up allocated objects + json_object_put(header_obj); + json_object_put(data_obj); + json_object_put(entry_metadata); + + // Skip to next entry + if (next_entry_index == 0 || next_entry_index >= TRACKER_CHUNK_SIZE) + break; + + offset = next_entry_index; + entry_count--; + continue; + } + + // Process array items if args_array is valid + int array_len = json_object_array_length(args_array); + + for (uint32_t i = 0; i < arg_count && i < MAX_ARGS + && i < (uint32_t)array_len; i++) { + char arg_key[16] = {0}; + const char *arg_desc = entry_info ? + get_arg_description(entry_info, i) + : NULL; + struct json_object *arg_val = + json_object_array_get_idx(args_array, i); + + if (!arg_desc) { + snprintf(arg_key, sizeof(arg_key), + "arg%d", i); + arg_desc = arg_key; + } + + if (arg_val) { + uint32_t arg_value = + json_object_get_int(arg_val); + + json_object_add_value_uint64(arg_obj, + arg_desc, arg_value); + } + } + + // Add arguments and entry to output + json_object_object_add(entry_json, "arguments", arg_obj); + json_object_array_add(entries_array, entry_json); + // Clean up any dynamically allocated objects + json_object_put(header_obj); + json_object_put(data_obj); + json_object_put(entry_metadata); + + // Break if we can't continue + if (next_entry_index == 0 || next_entry_index >= TRACKER_CHUNK_SIZE) + break; + + // Move to next entry + offset = next_entry_index; + entry_count--; + } +} + +void sldm_tracker_parse(struct telemetry_log *tl, uint32_t offset, uint32_t size, + struct json_object *tracker_obj) +{ + uint32_t chunks = size / TRACKER_CHUNK_SIZE; + struct json_object *entries_array = json_object_new_array(); + + json_object_add_value_uint(tracker_obj, "offset", offset); + json_object_add_value_uint(tracker_obj, "size", size); + json_object_add_value_uint(tracker_obj, "chunks", chunks); + + for (uint32_t i = 0; i < chunks; i++) { + parse_tracker_chunk_json(tl, offset + (i * TRACKER_CHUNK_SIZE), + i, entries_array); + } + + json_object_object_add(tracker_obj, "entries", entries_array); +} diff --git a/plugins/solidigm/solidigm-telemetry/tracker.h b/plugins/solidigm/solidigm-telemetry/tracker.h new file mode 100644 index 0000000000..f53ea13992 --- /dev/null +++ b/plugins/solidigm/solidigm-telemetry/tracker.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2025 Solidigm. + * + * Author: leonardo.da.cunha@solidigm.com + */ + +#ifndef SOLIDIGM_TELEMETRY_TRACKER_H +#define SOLIDIGM_TELEMETRY_TRACKER_H + +#include "telemetry-log.h" + +void sldm_tracker_parse(struct telemetry_log *tl, uint32_t offset, + uint32_t size, struct json_object *tracker_obj); +#endif /* SOLIDIGM_TELEMETRY_TRACKER_H */