xref: /openbmc/pldm/pldmtool/pldm_fru_cmd.cpp (revision a4a96162)
1 #include "pldm_fru_cmd.hpp"
2 
3 #include "pldm_cmd_helper.hpp"
4 
5 #include <endian.h>
6 
7 #include <functional>
8 #include <tuple>
9 
10 namespace pldmtool
11 {
12 
13 namespace fru
14 {
15 
16 namespace
17 {
18 
19 using namespace pldmtool::helper;
20 
21 std::vector<std::unique_ptr<CommandInterface>> commands;
22 
23 } // namespace
24 
25 class GetFruRecordTableMetadata : public CommandInterface
26 {
27   public:
28     ~GetFruRecordTableMetadata() = default;
29     GetFruRecordTableMetadata() = delete;
30     GetFruRecordTableMetadata(const GetFruRecordTableMetadata&) = delete;
31     GetFruRecordTableMetadata(GetFruRecordTableMetadata&&) = default;
32     GetFruRecordTableMetadata&
33         operator=(const GetFruRecordTableMetadata&) = delete;
34     GetFruRecordTableMetadata& operator=(GetFruRecordTableMetadata&&) = default;
35 
36     using CommandInterface::CommandInterface;
37 
38     std::pair<int, std::vector<uint8_t>> createRequestMsg() override
39     {
40         std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr));
41         auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
42 
43         auto rc = encode_get_fru_record_table_metadata_req(
44             instanceId, request, PLDM_GET_FRU_RECORD_TABLE_METADATA_REQ_BYTES);
45         return {rc, requestMsg};
46     }
47 
48     void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override
49     {
50         uint8_t cc = 0;
51         uint8_t fru_data_major_version, fru_data_minor_version;
52         uint32_t fru_table_maximum_size, fru_table_length;
53         uint16_t total_record_set_identifiers, total_table_records;
54         uint32_t checksum;
55 
56         auto rc = decode_get_fru_record_table_metadata_resp(
57             responsePtr, payloadLength, &cc, &fru_data_major_version,
58             &fru_data_minor_version, &fru_table_maximum_size, &fru_table_length,
59             &total_record_set_identifiers, &total_table_records, &checksum);
60         if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
61         {
62             std::cerr << "Response Message Error: "
63                       << "rc=" << rc << ",cc=" << (int)cc << std::endl;
64             return;
65         }
66         std::cout << "FRUDATAMajorVersion : "
67                   << static_cast<uint32_t>(fru_data_major_version) << std::endl;
68         std::cout << "FRUDATAMinorVersion : "
69                   << static_cast<uint32_t>(fru_data_minor_version) << std::endl;
70         std::cout << "FRUTableMaximumSize : " << fru_table_maximum_size
71                   << std::endl;
72         std::cout << "FRUTableLength : " << fru_table_length << std::endl;
73         std::cout << "Total number of Record Set Identifiers in table : "
74                   << total_record_set_identifiers << std::endl;
75         std::cout << "Total number of records in table :  "
76                   << total_table_records << std::endl;
77         std::cout << "FRU DATAStructureTableIntegrityChecksum :  " << checksum
78                   << std::endl;
79     }
80 };
81 
82 class FRUTablePrint
83 {
84   public:
85     explicit FRUTablePrint(const uint8_t* table, size_t table_size) :
86         table(table), table_size(table_size)
87     {}
88 
89     void print()
90     {
91         auto p = table;
92         while (!isTableEnd(p))
93         {
94             auto record =
95                 reinterpret_cast<const pldm_fru_record_data_format*>(p);
96             std::cout << "FRU Record Set Identifier: "
97                       << (int)le16toh(record->record_set_id) << std::endl;
98             std::cout << "FRU Record Type: "
99                       << typeToString(fruRecordTypes, record->record_type)
100                       << std::endl;
101             std::cout << "Number of FRU fields: " << (int)record->num_fru_fields
102                       << std::endl;
103             std::cout << "Encoding Type for FRU fields: "
104                       << typeToString(fruEncodingType, record->encoding_type)
105                       << std::endl;
106 
107             auto isGeneralRec = true;
108             if (record->record_type != PLDM_FRU_RECORD_TYPE_GENERAL)
109             {
110                 isGeneralRec = false;
111             }
112 
113             p += sizeof(pldm_fru_record_data_format) -
114                  sizeof(pldm_fru_record_tlv);
115             for (int i = 0; i < record->num_fru_fields; i++)
116             {
117                 auto tlv = reinterpret_cast<const pldm_fru_record_tlv*>(p);
118                 if (isGeneralRec)
119                 {
120                     fruFieldPrint(record->record_type, tlv->type, tlv->length,
121                                   tlv->value);
122                 }
123                 p += sizeof(pldm_fru_record_tlv) - 1 + tlv->length;
124             }
125         }
126     }
127 
128   private:
129     const uint8_t* table;
130     size_t table_size;
131 
132     bool isTableEnd(const uint8_t* p)
133     {
134         auto offset = p - table;
135         return (table_size - offset) <= 7;
136     }
137 
138     static inline const std::map<uint8_t, const char*> fruEncodingType{
139         {PLDM_FRU_ENCODING_UNSPECIFIED, "Unspecified"},
140         {PLDM_FRU_ENCODING_ASCII, "ASCII"},
141         {PLDM_FRU_ENCODING_UTF8, "UTF8"},
142         {PLDM_FRU_ENCODING_UTF16, "UTF16"},
143         {PLDM_FRU_ENCODING_UTF16LE, "UTF16LE"},
144         {PLDM_FRU_ENCODING_UTF16BE, "UTF16BE"}};
145 
146     static inline const std::map<uint8_t, const char*> fruRecordTypes{
147         {PLDM_FRU_RECORD_TYPE_GENERAL, "General"},
148         {PLDM_FRU_RECORD_TYPE_OEM, "OEM"}};
149 
150     std::string typeToString(std::map<uint8_t, const char*> typeMap,
151                              uint8_t type)
152     {
153         auto typeString = std::to_string(type);
154         try
155         {
156             return typeString + "(" + typeMap.at(type) + ")";
157         }
158         catch (const std::out_of_range& e)
159         {
160             return typeString;
161         }
162     }
163 
164     using FruFieldParser =
165         std::function<std::string(const uint8_t* value, uint8_t length)>;
166 
167     using FieldType = uint8_t;
168     using RecordType = uint8_t;
169     using FieldName = std::string;
170     using FruFieldTypes =
171         std::map<FieldType, std::tuple<FieldName, FruFieldParser>>;
172 
173     static std::string fruFieldParserString(const uint8_t* value,
174                                             uint8_t length)
175     {
176         return std::string(reinterpret_cast<const char*>(value), length);
177     }
178 
179     static std::string fruFieldParserTimestamp(const uint8_t*, uint8_t)
180     {
181         return std::string("TODO");
182     }
183 
184     static std::string fruFieldParserU32(const uint8_t* value, uint8_t length)
185     {
186         assert(length == 4);
187         uint32_t v;
188         std::memcpy(&v, value, length);
189         return std::to_string(le32toh(v));
190     }
191 
192     static inline const FruFieldTypes fruGeneralFieldTypes = {
193         {PLDM_FRU_FIELD_TYPE_CHASSIS, {"Chassis", fruFieldParserString}},
194         {PLDM_FRU_FIELD_TYPE_MODEL, {"Model", fruFieldParserString}},
195         {PLDM_FRU_FIELD_TYPE_PN, {"Part Number", fruFieldParserString}},
196         {PLDM_FRU_FIELD_TYPE_SN, {"Serial Number", fruFieldParserString}},
197         {PLDM_FRU_FIELD_TYPE_MANUFAC, {"Manufacturer", fruFieldParserString}},
198         {PLDM_FRU_FIELD_TYPE_MANUFAC_DATE,
199          {"Manufacture Date", fruFieldParserTimestamp}},
200         {PLDM_FRU_FIELD_TYPE_VENDOR, {"Vendor", fruFieldParserString}},
201         {PLDM_FRU_FIELD_TYPE_NAME, {"Name", fruFieldParserString}},
202         {PLDM_FRU_FIELD_TYPE_SKU, {"SKU", fruFieldParserString}},
203         {PLDM_FRU_FIELD_TYPE_VERSION, {"Version", fruFieldParserString}},
204         {PLDM_FRU_FIELD_TYPE_ASSET_TAG, {"Asset Tag", fruFieldParserString}},
205         {PLDM_FRU_FIELD_TYPE_DESC, {"Description", fruFieldParserString}},
206         {PLDM_FRU_FIELD_TYPE_EC_LVL,
207          {"Engineering Change Level", fruFieldParserString}},
208         {PLDM_FRU_FIELD_TYPE_OTHER,
209          {"Other Information", fruFieldParserString}},
210         {PLDM_FRU_FIELD_TYPE_IANA, {"Vendor IANA", fruFieldParserU32}},
211     };
212 
213     static inline const FruFieldTypes fruOEMFieldTypes = {
214         {1, {"Vendor IANA", fruFieldParserU32}},
215 
216     };
217 
218     static inline const std::map<RecordType, FruFieldTypes> fruFieldTypes{
219         {PLDM_FRU_RECORD_TYPE_GENERAL, fruGeneralFieldTypes},
220         {PLDM_FRU_RECORD_TYPE_OEM, fruOEMFieldTypes}};
221 
222     void fruFieldPrint(uint8_t recordType, uint8_t type, uint8_t length,
223                        const uint8_t* value)
224     {
225         auto& [typeString, parser] = fruFieldTypes.at(recordType).at(type);
226 
227         std::cout << "\tFRU Field Type: " << typeString << std::endl;
228         std::cout << "\tFRU Field Length: " << (int)(length) << std::endl;
229         std::cout << "\tFRU Field Value: " << parser(value, length)
230                   << std::endl;
231     }
232 };
233 
234 class GetFRURecordByOption : public CommandInterface
235 {
236   public:
237     ~GetFRURecordByOption() = default;
238     GetFRURecordByOption() = delete;
239     GetFRURecordByOption(const GetFRURecordByOption&) = delete;
240     GetFRURecordByOption(GetFruRecordTableMetadata&&) = delete;
241     GetFRURecordByOption& operator=(const GetFRURecordByOption&) = delete;
242     GetFRURecordByOption& operator=(GetFRURecordByOption&&) = delete;
243 
244     explicit GetFRURecordByOption(const char* type, const char* name,
245                                   CLI::App* app) :
246         CommandInterface(type, name, app)
247     {
248         app->add_option("-i, --identifier", recordSetIdentifier,
249                         "Record Set Identifier\n"
250                         "Possible values: {All record sets = 0, Specific "
251                         "record set = 1 – 65535}")
252             ->required();
253         app->add_option("-r, --record", recordType,
254                         "Record Type\n"
255                         "Possible values: {All record types = 0, Specific "
256                         "record types = 1 – 255}")
257             ->required();
258         app->add_option("-f, --field", fieldType,
259                         "Field Type\n"
260                         "Possible values: {All record field types = 0, "
261                         "Specific field types = 1 – 15}")
262             ->required();
263     }
264 
265     std::pair<int, std::vector<uint8_t>> createRequestMsg() override
266     {
267         if (fieldType != 0 && recordType == 0)
268         {
269             throw std::invalid_argument("if field type is non-zero, the record "
270                                         "type shall also be non-zero");
271         }
272 
273         auto payloadLength = sizeof(pldm_get_fru_record_by_option_req);
274 
275         std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) + payloadLength,
276                                         0);
277         auto reqMsg = reinterpret_cast<pldm_msg*>(requestMsg.data());
278 
279         auto rc = encode_get_fru_record_by_option_req(
280             instanceId, 0 /* DataTransferHandle */, 0 /* FRUTableHandle */,
281             recordSetIdentifier, recordType, fieldType, PLDM_GET_FIRSTPART,
282             reqMsg, payloadLength);
283 
284         return {rc, requestMsg};
285     }
286 
287     void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override
288     {
289         uint8_t cc;
290         uint32_t dataTransferHandle;
291         uint8_t transferFlag;
292         variable_field fruData;
293 
294         auto rc = decode_get_fru_record_by_option_resp(
295             responsePtr, payloadLength, &cc, &dataTransferHandle, &transferFlag,
296             &fruData);
297 
298         if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
299         {
300             std::cerr << "Response Message Error: "
301                       << "rc=" << rc << ",cc=" << (int)cc << std::endl;
302             return;
303         }
304 
305         FRUTablePrint tablePrint(fruData.ptr, fruData.length);
306         tablePrint.print();
307     }
308 
309   private:
310     uint16_t recordSetIdentifier;
311     uint8_t recordType;
312     uint8_t fieldType;
313 };
314 
315 class GetFruRecordTable : public CommandInterface
316 {
317   public:
318     ~GetFruRecordTable() = default;
319     GetFruRecordTable() = delete;
320     GetFruRecordTable(const GetFruRecordTable&) = delete;
321     GetFruRecordTable(GetFruRecordTable&&) = default;
322     GetFruRecordTable& operator=(const GetFruRecordTable&) = delete;
323     GetFruRecordTable& operator=(GetFruRecordTable&&) = default;
324 
325     using CommandInterface::CommandInterface;
326     std::pair<int, std::vector<uint8_t>> createRequestMsg() override
327     {
328         std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
329                                         PLDM_GET_FRU_RECORD_TABLE_REQ_BYTES);
330         auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
331 
332         auto rc = encode_get_fru_record_table_req(
333             instanceId, 0, PLDM_START_AND_END, request,
334             requestMsg.size() - sizeof(pldm_msg_hdr));
335         return {rc, requestMsg};
336     }
337     void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override
338     {
339         uint8_t cc = 0;
340         uint32_t next_data_transfer_handle = 0;
341         uint8_t transfer_flag = 0;
342         size_t fru_record_table_length = 0;
343         std::vector<uint8_t> fru_record_table_data(payloadLength);
344 
345         auto rc = decode_get_fru_record_table_resp(
346             responsePtr, payloadLength, &cc, &next_data_transfer_handle,
347             &transfer_flag, fru_record_table_data.data(),
348             &fru_record_table_length);
349 
350         if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
351         {
352             std::cerr << "Response Message Error: "
353                       << "rc=" << rc << ",cc=" << (int)cc << std::endl;
354             return;
355         }
356 
357         FRUTablePrint tablePrint(fru_record_table_data.data(),
358                                  fru_record_table_length);
359         tablePrint.print();
360     }
361 };
362 
363 void registerCommand(CLI::App& app)
364 {
365     auto fru = app.add_subcommand("fru", "FRU type command");
366     fru->require_subcommand(1);
367     auto getFruRecordTableMetadata = fru->add_subcommand(
368         "GetFruRecordTableMetadata", "get FRU record table metadata");
369     commands.push_back(std::make_unique<GetFruRecordTableMetadata>(
370         "fru", "GetFruRecordTableMetadata", getFruRecordTableMetadata));
371 
372     auto getFRURecordByOption =
373         fru->add_subcommand("GetFRURecordByOption", "get FRU Record By Option");
374     commands.push_back(std::make_unique<GetFRURecordByOption>(
375         "fru", "GetFRURecordByOption", getFRURecordByOption));
376 
377     auto getFruRecordTable =
378         fru->add_subcommand("GetFruRecordTable", "get FRU Record Table");
379     commands.push_back(std::make_unique<GetFruRecordTable>(
380         "fru", "GetFruRecordTable", getFruRecordTable));
381 }
382 
383 } // namespace fru
384 
385 } // namespace pldmtool
386