xref: /openbmc/pldm/pldmtool/pldm_fru_cmd.cpp (revision 96a3a048)
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