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