1 #include "pldm_fru_cmd.hpp" 2 3 #include "pldm_cmd_helper.hpp" 4 5 #ifdef OEM_IBM 6 #include "oem/ibm/libpldm/fru.h" 7 #endif 8 9 #include <endian.h> 10 11 #include <functional> 12 #include <tuple> 13 14 namespace pldmtool 15 { 16 17 namespace fru 18 { 19 20 namespace 21 { 22 23 using namespace pldmtool::helper; 24 25 std::vector<std::unique_ptr<CommandInterface>> commands; 26 27 } // namespace 28 29 class GetFruRecordTableMetadata : public CommandInterface 30 { 31 public: 32 ~GetFruRecordTableMetadata() = default; 33 GetFruRecordTableMetadata() = delete; 34 GetFruRecordTableMetadata(const GetFruRecordTableMetadata&) = delete; 35 GetFruRecordTableMetadata(GetFruRecordTableMetadata&&) = default; 36 GetFruRecordTableMetadata& 37 operator=(const GetFruRecordTableMetadata&) = delete; 38 GetFruRecordTableMetadata& operator=(GetFruRecordTableMetadata&&) = default; 39 40 using CommandInterface::CommandInterface; 41 42 std::pair<int, std::vector<uint8_t>> createRequestMsg() override 43 { 44 std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr)); 45 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 46 47 auto rc = encode_get_fru_record_table_metadata_req( 48 instanceId, request, PLDM_GET_FRU_RECORD_TABLE_METADATA_REQ_BYTES); 49 return {rc, requestMsg}; 50 } 51 52 void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override 53 { 54 uint8_t cc = 0; 55 uint8_t fru_data_major_version, fru_data_minor_version; 56 uint32_t fru_table_maximum_size, fru_table_length; 57 uint16_t total_record_set_identifiers, total_table_records; 58 uint32_t checksum; 59 60 auto rc = decode_get_fru_record_table_metadata_resp( 61 responsePtr, payloadLength, &cc, &fru_data_major_version, 62 &fru_data_minor_version, &fru_table_maximum_size, &fru_table_length, 63 &total_record_set_identifiers, &total_table_records, &checksum); 64 if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS) 65 { 66 std::cerr << "Response Message Error: " 67 << "rc=" << rc << ",cc=" << (int)cc << std::endl; 68 return; 69 } 70 ordered_json output; 71 output["FRUDATAMajorVersion"] = 72 static_cast<uint32_t>(fru_data_major_version); 73 output["FRUDATAMinorVersion"] = 74 static_cast<uint32_t>(fru_data_minor_version); 75 output["FRUTableMaximumSize"] = fru_table_maximum_size; 76 output["FRUTableLength"] = fru_table_length; 77 output["Total number of Record Set Identifiers in table"] = 78 total_record_set_identifiers; 79 output["Total number of records in table"] = total_table_records; 80 output["FRU DATAStructureTableIntegrityChecksum"] = checksum; 81 pldmtool::helper::DisplayInJson(output); 82 } 83 }; 84 85 class FRUTablePrint 86 { 87 public: 88 explicit FRUTablePrint(const uint8_t* table, size_t table_size) : 89 table(table), table_size(table_size) 90 {} 91 92 void print() 93 { 94 auto p = table; 95 ordered_json frutable; 96 ordered_json output; 97 while (!isTableEnd(p)) 98 { 99 auto record = 100 reinterpret_cast<const pldm_fru_record_data_format*>(p); 101 output["FRU Record Set Identifier"] = 102 (int)le16toh(record->record_set_id); 103 output["FRU Record Type"] = 104 typeToString(fruRecordTypes, record->record_type); 105 output["Number of FRU fields"] = (int)record->num_fru_fields; 106 output["Encoding Type for FRU fields"] = 107 typeToString(fruEncodingType, record->encoding_type); 108 109 p += sizeof(pldm_fru_record_data_format) - 110 sizeof(pldm_fru_record_tlv); 111 112 auto isIPZ = false; 113 std::map<uint8_t, std::string> FruFieldTypeMap; 114 std::string fruFieldValue; 115 116 ordered_json frudata; 117 ordered_json frufielddata; 118 frufielddata.emplace_back(output); 119 for (int i = 0; i < record->num_fru_fields; i++) 120 { 121 auto tlv = reinterpret_cast<const pldm_fru_record_tlv*>(p); 122 if (record->record_type == PLDM_FRU_RECORD_TYPE_GENERAL) 123 { 124 FruFieldTypeMap.insert(fruGeneralFieldTypes.begin(), 125 fruGeneralFieldTypes.end()); 126 if (tlv->type == PLDM_FRU_FIELD_TYPE_IANA) 127 { 128 fruFieldValue = 129 fruFieldParserU32(tlv->value, tlv->length); 130 } 131 else if (tlv->type == PLDM_FRU_FIELD_TYPE_MANUFAC_DATE) 132 { 133 fruFieldValue = 134 fruFieldParserTimestamp(tlv->value, tlv->length); 135 } 136 } 137 else 138 { 139 #ifdef OEM_IBM 140 if (tlv->type == PLDM_OEM_FRU_FIELD_TYPE_RT) 141 { 142 auto oemIPZValue = 143 fruFieldValuestring(tlv->value, tlv->length); 144 145 if (populateMaps.find(oemIPZValue) != 146 populateMaps.end()) 147 { 148 isIPZ = true; 149 const std::map<uint8_t, std::string> IPZTypes = 150 populateMaps.at(oemIPZValue); 151 FruFieldTypeMap.insert(IPZTypes.begin(), 152 IPZTypes.end()); 153 } 154 } 155 else 156 { 157 FruFieldTypeMap.insert(fruOemFieldTypes.begin(), 158 fruOemFieldTypes.end()); 159 } 160 if (tlv->type == PLDM_OEM_FRU_FIELD_TYPE_IANA) 161 { 162 fruFieldValue = 163 fruFieldParserU32(tlv->value, tlv->length); 164 } 165 #endif 166 } 167 168 if (isIPZ && (tlv->type != 2)) 169 { 170 fruFieldValue = fruFieldIPZParser(tlv->value, tlv->length); 171 } 172 else 173 { 174 fruFieldValue = 175 fruFieldValuestring(tlv->value, tlv->length); 176 } 177 178 frudata["FRU Field Type"] = 179 typeToString(FruFieldTypeMap, tlv->type); 180 frudata["FRU Field Length"] = (int)(tlv->length); 181 frudata["FRU Field Value"] = fruFieldValue; 182 frufielddata.emplace_back(frudata); 183 p += sizeof(pldm_fru_record_tlv) - 1 + tlv->length; 184 } 185 frutable.emplace_back(frufielddata); 186 } 187 pldmtool::helper::DisplayInJson(frutable); 188 } 189 190 private: 191 const uint8_t* table; 192 size_t table_size; 193 194 bool isTableEnd(const uint8_t* p) 195 { 196 auto offset = p - table; 197 return (table_size - offset) <= 7; 198 } 199 200 static inline const std::map<uint8_t, std::string> fruEncodingType{ 201 {PLDM_FRU_ENCODING_UNSPECIFIED, "Unspecified"}, 202 {PLDM_FRU_ENCODING_ASCII, "ASCII"}, 203 {PLDM_FRU_ENCODING_UTF8, "UTF8"}, 204 {PLDM_FRU_ENCODING_UTF16, "UTF16"}, 205 {PLDM_FRU_ENCODING_UTF16LE, "UTF16LE"}, 206 {PLDM_FRU_ENCODING_UTF16BE, "UTF16BE"}}; 207 208 static inline const std::map<uint8_t, std::string> fruGeneralFieldTypes{ 209 {PLDM_FRU_FIELD_TYPE_CHASSIS, "Chassis"}, 210 {PLDM_FRU_FIELD_TYPE_MODEL, "Model"}, 211 {PLDM_FRU_FIELD_TYPE_PN, "Part Number"}, 212 {PLDM_FRU_FIELD_TYPE_SN, "Serial Number"}, 213 {PLDM_FRU_FIELD_TYPE_MANUFAC, "Manufacturer"}, 214 {PLDM_FRU_FIELD_TYPE_MANUFAC_DATE, "Manufacture Date"}, 215 {PLDM_FRU_FIELD_TYPE_VENDOR, "Vendor"}, 216 {PLDM_FRU_FIELD_TYPE_NAME, "Name"}, 217 {PLDM_FRU_FIELD_TYPE_SKU, "SKU"}, 218 {PLDM_FRU_FIELD_TYPE_VERSION, "Version"}, 219 {PLDM_FRU_FIELD_TYPE_ASSET_TAG, "Asset Tag"}, 220 {PLDM_FRU_FIELD_TYPE_DESC, "Description"}, 221 {PLDM_FRU_FIELD_TYPE_EC_LVL, "Engineering Change Level"}, 222 {PLDM_FRU_FIELD_TYPE_OTHER, "Other Information"}, 223 {PLDM_FRU_FIELD_TYPE_IANA, "Vendor IANA"}}; 224 225 static inline const std::map<uint8_t, std::string> fruRecordTypes{ 226 {PLDM_FRU_RECORD_TYPE_GENERAL, "General"}, 227 {PLDM_FRU_RECORD_TYPE_OEM, "OEM"}}; 228 229 #ifdef OEM_IBM 230 static inline const std::map<uint8_t, std::string> fruOemFieldTypes{ 231 {PLDM_OEM_FRU_FIELD_TYPE_IANA, "Vendor IANA"}, 232 {PLDM_OEM_FRU_FIELD_TYPE_RT, "RT"}, 233 {PLDM_OEM_FRU_FIELD_TYPE_LOCATION_CODE, "Location Code"}}; 234 235 static inline const std::map<uint8_t, std::string> VINIFieldTypes{ 236 {2, "RT"}, {3, "B3"}, {4, "B4"}, {5, "B7"}, {6, "CC"}, {7, "CE"}, 237 {8, "CT"}, {9, "DR"}, {10, "FG"}, {11, "FN"}, {12, "HE"}, {13, "HW"}, 238 {14, "HX"}, {15, "PN"}, {16, "SN"}, {17, "TS"}, {18, "VZ"}}; 239 240 static inline const std::map<uint8_t, std::string> VSYSFieldTypes{ 241 {2, "RT"}, {3, "BR"}, {4, "DR"}, {5, "FV"}, {6, "ID"}, 242 {7, "MN"}, {8, "NN"}, {9, "RB"}, {10, "RG"}, {11, "SE"}, 243 {12, "SG"}, {13, "SU"}, {14, "TM"}, {15, "TN"}, {16, "WN"}}; 244 245 static inline const std::map<uint8_t, std::string> LXR0FieldTypes{ 246 {2, "RT"}, {3, "LX"}, {4, "VZ"}}; 247 248 static inline const std::map<uint8_t, std::string> VW10FieldTypes{ 249 {2, "RT"}, {3, "DR"}, {4, "GD"}}; 250 251 static inline const std::map<uint8_t, std::string> VR10FieldTypes{ 252 {2, "RT"}, {3, "DC"}, {4, "DR"}, {5, "FL"}, {6, "WA"}}; 253 254 static inline const std::map<std::string, 255 const std::map<uint8_t, std::string>> 256 populateMaps{{"VINI", VINIFieldTypes}, 257 {"VSYS", VSYSFieldTypes}, 258 {"LXR0", LXR0FieldTypes}, 259 {"VWX10", VW10FieldTypes}, 260 {"VR10", VR10FieldTypes}}; 261 #endif 262 263 std::string typeToString(std::map<uint8_t, std::string> typeMap, 264 uint8_t type) 265 { 266 auto typeString = std::to_string(type); 267 try 268 { 269 return std::string(typeMap.at(type)) + "(" + typeString + ")"; 270 } 271 catch (const std::out_of_range& e) 272 { 273 return typeString; 274 } 275 } 276 277 std::string fruFieldValuestring(const uint8_t* value, uint8_t length) 278 { 279 return std::string(reinterpret_cast<const char*>(value), length); 280 } 281 282 static std::string fruFieldParserU32(const uint8_t* value, uint8_t length) 283 { 284 assert(length == 4); 285 uint32_t v; 286 std::memcpy(&v, value, length); 287 return std::to_string(le32toh(v)); 288 } 289 290 static std::string fruFieldParserTimestamp(const uint8_t*, uint8_t) 291 { 292 return std::string("TODO"); 293 } 294 295 static std::string fruFieldIPZParser(const uint8_t* value, uint8_t length) 296 { 297 std::ostringstream tempStream; 298 for (int i = 0; i < int(length); ++i) 299 { 300 tempStream << "0x" << std::setfill('0') << std::setw(2) << std::hex 301 << (unsigned)value[i] << " "; 302 } 303 return tempStream.str(); 304 } 305 }; 306 307 class GetFRURecordByOption : public CommandInterface 308 { 309 public: 310 ~GetFRURecordByOption() = default; 311 GetFRURecordByOption() = delete; 312 GetFRURecordByOption(const GetFRURecordByOption&) = delete; 313 GetFRURecordByOption(GetFruRecordTableMetadata&&) = delete; 314 GetFRURecordByOption& operator=(const GetFRURecordByOption&) = delete; 315 GetFRURecordByOption& operator=(GetFRURecordByOption&&) = delete; 316 317 explicit GetFRURecordByOption(const char* type, const char* name, 318 CLI::App* app) : 319 CommandInterface(type, name, app) 320 { 321 app->add_option("-i, --identifier", recordSetIdentifier, 322 "Record Set Identifier\n" 323 "Possible values: {All record sets = 0, Specific " 324 "record set = 1 – 65535}") 325 ->required(); 326 app->add_option("-r, --record", recordType, 327 "Record Type\n" 328 "Possible values: {All record types = 0, Specific " 329 "record types = 1 – 255}") 330 ->required(); 331 app->add_option("-f, --field", fieldType, 332 "Field Type\n" 333 "Possible values: {All record field types = 0, " 334 "Specific field types = 1 – 15}") 335 ->required(); 336 } 337 338 std::pair<int, std::vector<uint8_t>> createRequestMsg() override 339 { 340 if (fieldType != 0 && recordType == 0) 341 { 342 throw std::invalid_argument("if field type is non-zero, the record " 343 "type shall also be non-zero"); 344 } 345 if (recordType == 254 && (fieldType > 2 && fieldType < 254)) 346 { 347 throw std::invalid_argument( 348 "GetFRURecordByOption is not supported for recordType : 254 " 349 "and fieldType > 2"); 350 } 351 352 auto payloadLength = sizeof(pldm_get_fru_record_by_option_req); 353 354 std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) + payloadLength, 355 0); 356 auto reqMsg = reinterpret_cast<pldm_msg*>(requestMsg.data()); 357 358 auto rc = encode_get_fru_record_by_option_req( 359 instanceId, 0 /* DataTransferHandle */, 0 /* FRUTableHandle */, 360 recordSetIdentifier, recordType, fieldType, PLDM_GET_FIRSTPART, 361 reqMsg, payloadLength); 362 363 return {rc, requestMsg}; 364 } 365 366 void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override 367 { 368 uint8_t cc; 369 uint32_t dataTransferHandle; 370 uint8_t transferFlag; 371 variable_field fruData; 372 373 auto rc = decode_get_fru_record_by_option_resp( 374 responsePtr, payloadLength, &cc, &dataTransferHandle, &transferFlag, 375 &fruData); 376 377 if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS) 378 { 379 std::cerr << "Response Message Error: " 380 << "rc=" << rc << ",cc=" << (int)cc << std::endl; 381 return; 382 } 383 384 FRUTablePrint tablePrint(fruData.ptr, fruData.length); 385 tablePrint.print(); 386 } 387 388 private: 389 uint16_t recordSetIdentifier; 390 uint8_t recordType; 391 uint8_t fieldType; 392 }; 393 394 class GetFruRecordTable : public CommandInterface 395 { 396 public: 397 ~GetFruRecordTable() = default; 398 GetFruRecordTable() = delete; 399 GetFruRecordTable(const GetFruRecordTable&) = delete; 400 GetFruRecordTable(GetFruRecordTable&&) = default; 401 GetFruRecordTable& operator=(const GetFruRecordTable&) = delete; 402 GetFruRecordTable& operator=(GetFruRecordTable&&) = default; 403 404 using CommandInterface::CommandInterface; 405 std::pair<int, std::vector<uint8_t>> createRequestMsg() override 406 { 407 std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) + 408 PLDM_GET_FRU_RECORD_TABLE_REQ_BYTES); 409 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 410 411 auto rc = encode_get_fru_record_table_req( 412 instanceId, 0, PLDM_START_AND_END, request, 413 requestMsg.size() - sizeof(pldm_msg_hdr)); 414 return {rc, requestMsg}; 415 } 416 void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override 417 { 418 uint8_t cc = 0; 419 uint32_t next_data_transfer_handle = 0; 420 uint8_t transfer_flag = 0; 421 size_t fru_record_table_length = 0; 422 std::vector<uint8_t> fru_record_table_data(payloadLength); 423 424 auto rc = decode_get_fru_record_table_resp( 425 responsePtr, payloadLength, &cc, &next_data_transfer_handle, 426 &transfer_flag, fru_record_table_data.data(), 427 &fru_record_table_length); 428 429 if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS) 430 { 431 std::cerr << "Response Message Error: " 432 << "rc=" << rc << ",cc=" << (int)cc << std::endl; 433 return; 434 } 435 436 FRUTablePrint tablePrint(fru_record_table_data.data(), 437 fru_record_table_length); 438 tablePrint.print(); 439 } 440 }; 441 442 void registerCommand(CLI::App& app) 443 { 444 auto fru = app.add_subcommand("fru", "FRU type command"); 445 fru->require_subcommand(1); 446 auto getFruRecordTableMetadata = fru->add_subcommand( 447 "GetFruRecordTableMetadata", "get FRU record table metadata"); 448 commands.push_back(std::make_unique<GetFruRecordTableMetadata>( 449 "fru", "GetFruRecordTableMetadata", getFruRecordTableMetadata)); 450 451 auto getFRURecordByOption = 452 fru->add_subcommand("GetFRURecordByOption", "get FRU Record By Option"); 453 commands.push_back(std::make_unique<GetFRURecordByOption>( 454 "fru", "GetFRURecordByOption", getFRURecordByOption)); 455 456 auto getFruRecordTable = 457 fru->add_subcommand("GetFruRecordTable", "get FRU Record Table"); 458 commands.push_back(std::make_unique<GetFruRecordTable>( 459 "fru", "GetFruRecordTable", getFruRecordTable)); 460 } 461 462 } // namespace fru 463 464 } // namespace pldmtool 465