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