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