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