xref: /openbmc/ipmi-fru-parser/frup.cpp (revision 5b1725d3e0a44a238912367a1af437ba6934d6ac)
1 /*
2  * Copyright (C) 2003-2014 FreeIPMI Core Team
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  *
17  */
18 /*****************************************************************************\
19  *  Copyright (C) 2007-2014 Lawrence Livermore National Security, LLC.
20  *  Copyright (C) 2007 The Regents of the University of California.
21  *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
22  *  Written by Albert Chu <chu11@llnl.gov>
23  *  UCRL-CODE-232183
24  *
25  *  This file is part of Ipmi-fru, a tool used for retrieving
26  *  motherboard field replaceable unit (FRU) information. For details,
27  *  see http://www.llnl.gov/linux/.
28  *
29  *  Ipmi-fru is free software; you can redistribute it and/or modify
30  *  it under the terms of the GNU General Public License as published by the
31  *  Free Software Foundation; either version 3 of the License, or (at your
32  *  option) any later version.
33  *
34  *  Ipmi-fru is distributed in the hope that it will be useful, but
35  *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
36  *  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
37  *  for more details.
38  *
39  *  You should have received a copy of the GNU General Public License along
40  *  with Ipmi-fru.  If not, see <http://www.gnu.org/licenses/>.
41 \*****************************************************************************/
42 #include "frup.hpp"
43 
44 #include <ctype.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <systemd/sd-bus.h>
49 #include <time.h>
50 #include <unistd.h>
51 
52 #include <phosphor-logging/lg2.hpp>
53 
54 #define TEXTSTR(a) #a
55 #define ASSERT(x)                                                              \
56     do                                                                         \
57     {                                                                          \
58         if (0 == (x))                                                          \
59         {                                                                      \
60             fprintf(stderr,                                                    \
61                     "Assertion failed: %s, "                                   \
62                     "%d at \'%s\'\n",                                          \
63                     __FILE__, __LINE__, TEXTSTR(a));                           \
64             return -1;                                                         \
65         }                                                                      \
66     } while (0)
67 
68 #define IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX 512
69 #define IPMI_FRU_SENTINEL_VALUE 0xC1
70 #define IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK 0xC0
71 #define IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT 0x06
72 #define IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK 0x3F
73 #define IPMI_FRU_TYPE_LENGTH_TYPE_CODE_LANGUAGE_CODE 0x03
74 
75 /* OpenBMC defines for Parser */
76 #define IPMI_FRU_AREA_INTERNAL_USE 0x00
77 #define IPMI_FRU_AREA_CHASSIS_INFO 0x01
78 #define IPMI_FRU_AREA_BOARD_INFO 0x02
79 #define IPMI_FRU_AREA_PRODUCT_INFO 0x03
80 #define IPMI_FRU_AREA_MULTI_RECORD 0x04
81 #define IPMI_FRU_AREA_TYPE_MAX 0x05
82 
83 #define OPENBMC_VPD_KEY_LEN 64
84 #define OPENBMC_VPD_VAL_LEN 512
85 
86 constexpr long fruEpochMinutes = 820454400;
87 
88 struct ipmi_fru_field
89 {
90     uint8_t type_length_field[IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX];
91     /* store length of data stored in buffer */
92     unsigned int type_length_field_length;
93 };
94 
95 typedef struct ipmi_fru_field ipmi_fru_field_t;
96 /*
97  * FRU Parser
98  */
99 
100 typedef struct ipmi_fru_area_info
101 {
102     uint8_t off;
103     uint8_t len;
104 } ipmi_fru_area_info_t;
105 
106 typedef struct ipmi_fru_common_hdr
107 {
108     uint8_t fmtver;
109     uint8_t internal;
110     uint8_t chassis;
111     uint8_t board;
112     uint8_t product;
113     uint8_t multirec;
114 } __attribute__((packed)) ipmi_fru_common_hdr_t;
115 
116 const char* vpd_key_names[] = {
117     "Key Names Table Start",
118     "Type",           /*OPENBMC_VPD_KEY_CHASSIS_TYPE*/
119     "Part Number",    /*OPENBMC_VPD_KEY_CHASSIS_PART_NUM,*/
120     "Serial Number",  /*OPENBMC_VPD_KEY_CHASSIS_SERIAL_NUM,*/
121     "Custom Field 1", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM1,*/
122     "Custom Field 2", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM2,*/
123     "Custom Field 3", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM3,*/
124     "Custom Field 4", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM4,*/
125     "Custom Field 5", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM5,*/
126     "Custom Field 6", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM6,*/
127     "Custom Field 7", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM7,*/
128     "Custom Field 8", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM8,*/
129 
130     "Mfg Date",
131     /* OPENBMC_VPD_KEY_BOARD_MFG_DATE, */ /* not a type/len */
132     "Manufacturer",                       /* OPENBMC_VPD_KEY_BOARD_MFR, */
133     "Name",                               /* OPENBMC_VPD_KEY_BOARD_NAME, */
134     "Serial Number",      /* OPENBMC_VPD_KEY_BOARD_SERIAL_NUM, */
135     "Part Number",        /* OPENBMC_VPD_KEY_BOARD_PART_NUM, */
136     "FRU File ID",        /* OPENBMC_VPD_KEY_BOARD_FRU_FILE_ID, */
137     "Custom Field 1",     /*OPENBMC_VPD_KEY_BOARD_CUSTOM1,*/
138     "Custom Field 2",     /*OPENBMC_VPD_KEY_BOARD_CUSTOM2,*/
139     "Custom Field 3",     /*OPENBMC_VPD_KEY_BOARD_CUSTOM3,*/
140     "Custom Field 4",     /*OPENBMC_VPD_KEY_BOARD_CUSTOM4,*/
141     "Custom Field 5",     /*OPENBMC_VPD_KEY_BOARD_CUSTOM5,*/
142     "Custom Field 6",     /*OPENBMC_VPD_KEY_BOARD_CUSTOM6,*/
143     "Custom Field 7",     /*OPENBMC_VPD_KEY_BOARD_CUSTOM7,*/
144     "Custom Field 8",     /*OPENBMC_VPD_KEY_BOARD_CUSTOM8,*/
145 
146     "Manufacturer",       /* OPENBMC_VPD_KEY_PRODUCT_MFR, */
147     "Name",               /* OPENBMC_VPD_KEY_PRODUCT_NAME, */
148     "Model Number",       /* OPENBMC_VPD_KEY_PRODUCT_PART_MODEL_NUM, */
149     "Version",            /* OPENBMC_VPD_KEY_PRODUCT_VER, */
150     "Serial Number",      /* OPENBMC_VPD_KEY_PRODUCT_SERIAL_NUM, */
151     "Asset Tag",          /* OPENBMC_VPD_KEY_PRODUCT_ASSET_TAG, */
152     "FRU File ID",        /* OPENBMC_VPD_KEY_PRODUCT_FRU_FILE_ID, */
153     "Custom Field 1",     /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM1,*/
154     "Custom Field 2",     /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM2,*/
155     "Custom Field 3",     /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM3,*/
156     "Custom Field 4",     /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM4,*/
157     "Custom Field 5",     /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM5,*/
158     "Custom Field 6",     /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM6,*/
159     "Custom Field 7",     /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM7,*/
160     "Custom Field 8",     /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM8,*/
161 
162     "Key Names Table End" /*OPENBMC_VPD_KEY_MAX,*/
163 };
164 
165 /*
166  * --------------------------------------------------------------------
167  *
168  * --------------------------------------------------------------------
169  */
170 
_to_time_str(uint32_t mfg_date_time,char * timestr,uint32_t len)171 static size_t _to_time_str(uint32_t mfg_date_time, char* timestr, uint32_t len)
172 {
173     struct tm tm;
174     time_t t;
175     size_t s;
176 
177     ASSERT(timestr);
178     ASSERT(len);
179 
180     memset(&tm, '\0', sizeof(struct tm));
181 
182     t = mfg_date_time;
183     gmtime_r(&t, &tm);
184     s = strftime(timestr, len, "%F - %H:%M:%S UTC", &tm);
185 
186     return s;
187 }
188 
189 /* private method to parse type/length */
_parse_type_length(const void * areabuf,unsigned int areabuflen,unsigned int current_area_offset,uint8_t * number_of_data_bytes,ipmi_fru_field_t * field)190 static int _parse_type_length(const void* areabuf, unsigned int areabuflen,
191                               unsigned int current_area_offset,
192                               uint8_t* number_of_data_bytes,
193                               ipmi_fru_field_t* field)
194 {
195     const uint8_t* areabufptr = static_cast<const uint8_t*>(areabuf);
196     uint8_t type_length;
197     uint8_t type_code;
198 
199     ASSERT(areabuf);
200     ASSERT(areabuflen);
201     ASSERT(number_of_data_bytes);
202 
203     type_length = areabufptr[current_area_offset];
204 
205     /* ipmi workaround
206      *
207      * dell p weredge r610
208      *
209      * my reading of the fru spec is that all non-custom fields are
210      * required to be listed by the vendor.  however, on this
211      * motherboard, some areas list this, indicating that there is
212      * no more data to be parsed.  so now, for "required" fields, i
213      * check to see if the type-length field is a sentinel before
214      * calling this function.
215      */
216 
217     ASSERT(type_length != IPMI_FRU_SENTINEL_VALUE);
218 
219     type_code = (type_length & IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK) >>
220                 IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT;
221     (*number_of_data_bytes) =
222         type_length & IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK;
223 
224     /* special case: this shouldn't be a length of 0x01 (see type/length
225      * byte format in fru information storage definition).
226      */
227     if (type_code == IPMI_FRU_TYPE_LENGTH_TYPE_CODE_LANGUAGE_CODE &&
228         (*number_of_data_bytes) == 0x01)
229     {
230         return (-1);
231     }
232 
233     if ((current_area_offset + 1 + (*number_of_data_bytes)) > areabuflen)
234     {
235         return (-1);
236     }
237 
238     if (field)
239     {
240         memset(field->type_length_field, '\0',
241                IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX);
242         memcpy(field->type_length_field, &areabufptr[current_area_offset],
243                1 + (*number_of_data_bytes));
244         field->type_length_field_length = 1 + (*number_of_data_bytes);
245     }
246 
247     return (0);
248 }
249 
ipmi_fru_chassis_info_area(const void * areabuf,unsigned int areabuflen,uint8_t * chassis_type,ipmi_fru_field_t * chassis_part_number,ipmi_fru_field_t * chassis_serial_number,ipmi_fru_field_t * chassis_custom_fields,unsigned int chassis_custom_fields_len)250 int ipmi_fru_chassis_info_area(
251     const void* areabuf, unsigned int areabuflen, uint8_t* chassis_type,
252     ipmi_fru_field_t* chassis_part_number,
253     ipmi_fru_field_t* chassis_serial_number,
254     ipmi_fru_field_t* chassis_custom_fields,
255     unsigned int chassis_custom_fields_len)
256 {
257     const uint8_t* areabufptr = static_cast<const uint8_t*>(areabuf);
258     unsigned int area_offset = 0;
259     unsigned int custom_fields_index = 0;
260     uint8_t number_of_data_bytes;
261     int rv = -1;
262 
263     if (!areabuf || !areabuflen)
264     {
265         return (-1);
266     }
267 
268     if (chassis_part_number)
269         memset(chassis_part_number, '\0', sizeof(ipmi_fru_field_t));
270     if (chassis_serial_number)
271         memset(chassis_serial_number, '\0', sizeof(ipmi_fru_field_t));
272     if (chassis_custom_fields && chassis_custom_fields_len)
273         memset(chassis_custom_fields, '\0',
274                sizeof(ipmi_fru_field_t) * chassis_custom_fields_len);
275 
276     if (chassis_type)
277         (*chassis_type) = areabufptr[area_offset];
278     area_offset++;
279 
280     if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
281         goto out;
282 
283     if (_parse_type_length(areabufptr, areabuflen, area_offset,
284                            &number_of_data_bytes, chassis_part_number) < 0)
285         goto cleanup;
286     area_offset += 1; /* type/length byte */
287     area_offset += number_of_data_bytes;
288 
289     if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
290         goto out;
291 
292     if (_parse_type_length(areabufptr, areabuflen, area_offset,
293                            &number_of_data_bytes, chassis_serial_number) < 0)
294         goto cleanup;
295     area_offset += 1; /* type/length byte */
296     area_offset += number_of_data_bytes;
297 
298     while (area_offset < areabuflen &&
299            areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE)
300     {
301         ipmi_fru_field_t* field_ptr = nullptr;
302 
303         if (chassis_custom_fields && chassis_custom_fields_len)
304         {
305             if (custom_fields_index < chassis_custom_fields_len)
306                 field_ptr = &chassis_custom_fields[custom_fields_index];
307             else
308             {
309                 goto cleanup;
310             }
311         }
312 
313         if (_parse_type_length(areabufptr, areabuflen, area_offset,
314                                &number_of_data_bytes, field_ptr) < 0)
315             goto cleanup;
316 
317         area_offset += 1; /* type/length byte */
318         area_offset += number_of_data_bytes;
319         custom_fields_index++;
320     }
321 
322 out:
323     rv = 0;
324 cleanup:
325     return (rv);
326 }
327 
ipmi_fru_board_info_area(const void * areabuf,unsigned int areabuflen,uint8_t * language_code,uint32_t * mfg_date_time,ipmi_fru_field_t * board_manufacturer,ipmi_fru_field_t * board_product_name,ipmi_fru_field_t * board_serial_number,ipmi_fru_field_t * board_part_number,ipmi_fru_field_t * board_fru_file_id,ipmi_fru_field_t * board_custom_fields,unsigned int board_custom_fields_len)328 int ipmi_fru_board_info_area(
329     const void* areabuf, unsigned int areabuflen, uint8_t* language_code,
330     uint32_t* mfg_date_time, ipmi_fru_field_t* board_manufacturer,
331     ipmi_fru_field_t* board_product_name, ipmi_fru_field_t* board_serial_number,
332     ipmi_fru_field_t* board_part_number, ipmi_fru_field_t* board_fru_file_id,
333     ipmi_fru_field_t* board_custom_fields, unsigned int board_custom_fields_len)
334 {
335     const uint8_t* areabufptr = static_cast<const uint8_t*>(areabuf);
336     unsigned int area_offset = 0;
337     unsigned int custom_fields_index = 0;
338     uint8_t number_of_data_bytes;
339     int rv = -1;
340 
341     if (!areabuf || !areabuflen)
342     {
343         return (-1);
344     }
345 
346     if (board_manufacturer)
347         memset(board_manufacturer, '\0', sizeof(ipmi_fru_field_t));
348     if (board_product_name)
349         memset(board_product_name, '\0', sizeof(ipmi_fru_field_t));
350     if (board_serial_number)
351         memset(board_serial_number, '\0', sizeof(ipmi_fru_field_t));
352     if (board_part_number)
353         memset(board_part_number, '\0', sizeof(ipmi_fru_field_t));
354     if (board_fru_file_id)
355         memset(board_fru_file_id, '\0', sizeof(ipmi_fru_field_t));
356     if (board_custom_fields && board_custom_fields_len)
357         memset(board_custom_fields, '\0',
358                sizeof(ipmi_fru_field_t) * board_custom_fields_len);
359 
360     if (language_code)
361         (*language_code) = areabufptr[area_offset];
362     area_offset++;
363 
364     if (mfg_date_time)
365     {
366         unsigned int minutes = areabufptr[area_offset];
367         area_offset++;
368         minutes |= (areabufptr[area_offset] << 8);
369         area_offset++;
370         minutes |= (areabufptr[area_offset] << 16);
371         area_offset++;
372 
373         /* In fru, epoch is 0:00 hrs 1/1/96 == 820454400
374          * Remove it directly and remove the time conversion.
375          */
376         (*mfg_date_time) = fruEpochMinutes + static_cast<long>(minutes) * 60;
377     }
378     else
379         area_offset += 3;
380 
381     if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
382         goto out;
383 
384     if (_parse_type_length(areabufptr, areabuflen, area_offset,
385                            &number_of_data_bytes, board_manufacturer) < 0)
386         goto cleanup;
387     area_offset += 1; /* type/length byte */
388     area_offset += number_of_data_bytes;
389 
390     if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
391         goto out;
392 
393     if (_parse_type_length(areabufptr, areabuflen, area_offset,
394                            &number_of_data_bytes, board_product_name) < 0)
395         goto cleanup;
396     area_offset += 1; /* type/length byte */
397     area_offset += number_of_data_bytes;
398 
399     if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
400         goto out;
401 
402     if (_parse_type_length(areabufptr, areabuflen, area_offset,
403                            &number_of_data_bytes, board_serial_number) < 0)
404         goto cleanup;
405     area_offset += 1; /* type/length byte */
406     area_offset += number_of_data_bytes;
407 
408     if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
409         goto out;
410 
411     if (_parse_type_length(areabufptr, areabuflen, area_offset,
412                            &number_of_data_bytes, board_part_number) < 0)
413         goto cleanup;
414     area_offset += 1; /* type/length byte */
415     area_offset += number_of_data_bytes;
416 
417     if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
418         goto out;
419 
420     if (_parse_type_length(areabufptr, areabuflen, area_offset,
421                            &number_of_data_bytes, board_fru_file_id) < 0)
422         goto cleanup;
423     area_offset += 1; /* type/length byte */
424     area_offset += number_of_data_bytes;
425 
426     while (area_offset < areabuflen &&
427            areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE)
428     {
429         ipmi_fru_field_t* field_ptr = nullptr;
430 
431         if (board_custom_fields && board_custom_fields_len)
432         {
433             if (custom_fields_index < board_custom_fields_len)
434                 field_ptr = &board_custom_fields[custom_fields_index];
435             else
436             {
437                 goto cleanup;
438             }
439         }
440 
441         if (_parse_type_length(areabufptr, areabuflen, area_offset,
442                                &number_of_data_bytes, field_ptr) < 0)
443             goto cleanup;
444 
445         area_offset += 1; /* type/length byte */
446         area_offset += number_of_data_bytes;
447         custom_fields_index++;
448     }
449 
450 out:
451     rv = 0;
452 cleanup:
453     return (rv);
454 }
455 
ipmi_fru_product_info_area(const void * areabuf,unsigned int areabuflen,uint8_t * language_code,ipmi_fru_field_t * product_manufacturer_name,ipmi_fru_field_t * product_name,ipmi_fru_field_t * product_part_model_number,ipmi_fru_field_t * product_version,ipmi_fru_field_t * product_serial_number,ipmi_fru_field_t * product_asset_tag,ipmi_fru_field_t * product_fru_file_id,ipmi_fru_field_t * product_custom_fields,unsigned int product_custom_fields_len)456 int ipmi_fru_product_info_area(
457     const void* areabuf, unsigned int areabuflen, uint8_t* language_code,
458     ipmi_fru_field_t* product_manufacturer_name, ipmi_fru_field_t* product_name,
459     ipmi_fru_field_t* product_part_model_number,
460     ipmi_fru_field_t* product_version, ipmi_fru_field_t* product_serial_number,
461     ipmi_fru_field_t* product_asset_tag, ipmi_fru_field_t* product_fru_file_id,
462     ipmi_fru_field_t* product_custom_fields,
463     unsigned int product_custom_fields_len)
464 {
465     const uint8_t* areabufptr = static_cast<const uint8_t*>(areabuf);
466     unsigned int area_offset = 0;
467     unsigned int custom_fields_index = 0;
468     uint8_t number_of_data_bytes;
469     int rv = -1;
470 
471     if (!areabuf || !areabuflen)
472     {
473         return (-1);
474     }
475 
476     if (product_manufacturer_name)
477         memset(product_manufacturer_name, '\0', sizeof(ipmi_fru_field_t));
478     if (product_name)
479         memset(product_name, '\0', sizeof(ipmi_fru_field_t));
480     if (product_part_model_number)
481         memset(product_part_model_number, '\0', sizeof(ipmi_fru_field_t));
482     if (product_version)
483         memset(product_version, '\0', sizeof(ipmi_fru_field_t));
484     if (product_serial_number)
485         memset(product_serial_number, '\0', sizeof(ipmi_fru_field_t));
486     if (product_asset_tag)
487         memset(product_asset_tag, '\0', sizeof(ipmi_fru_field_t));
488     if (product_fru_file_id)
489         memset(product_fru_file_id, '\0', sizeof(ipmi_fru_field_t));
490     if (product_custom_fields && product_custom_fields_len)
491         memset(product_custom_fields, '\0',
492                sizeof(ipmi_fru_field_t) * product_custom_fields_len);
493 
494     if (language_code)
495         (*language_code) = areabufptr[area_offset];
496     area_offset++;
497 
498     if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
499         goto out;
500 
501     if (_parse_type_length(areabufptr, areabuflen, area_offset,
502                            &number_of_data_bytes, product_manufacturer_name) <
503         0)
504         goto cleanup;
505     area_offset += 1; /* type/length byte */
506     area_offset += number_of_data_bytes;
507 
508     if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
509         goto out;
510 
511     if (_parse_type_length(areabufptr, areabuflen, area_offset,
512                            &number_of_data_bytes, product_name) < 0)
513         goto cleanup;
514     area_offset += 1; /* type/length byte */
515     area_offset += number_of_data_bytes;
516 
517     if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
518         goto out;
519 
520     if (_parse_type_length(areabufptr, areabuflen, area_offset,
521                            &number_of_data_bytes, product_part_model_number) <
522         0)
523         goto cleanup;
524     area_offset += 1; /* type/length byte */
525     area_offset += number_of_data_bytes;
526 
527     if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
528         goto out;
529 
530     if (_parse_type_length(areabufptr, areabuflen, area_offset,
531                            &number_of_data_bytes, product_version) < 0)
532         goto cleanup;
533     area_offset += 1; /* type/length byte */
534     area_offset += number_of_data_bytes;
535 
536     if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
537         goto out;
538 
539     if (_parse_type_length(areabufptr, areabuflen, area_offset,
540                            &number_of_data_bytes, product_serial_number) < 0)
541         goto cleanup;
542     area_offset += 1; /* type/length byte */
543     area_offset += number_of_data_bytes;
544 
545     if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
546         goto out;
547 
548     if (_parse_type_length(areabufptr, areabuflen, area_offset,
549                            &number_of_data_bytes, product_asset_tag) < 0)
550         goto cleanup;
551     area_offset += 1; /* type/length byte */
552     area_offset += number_of_data_bytes;
553 
554     if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
555         goto out;
556 
557     if (_parse_type_length(areabufptr, areabuflen, area_offset,
558                            &number_of_data_bytes, product_fru_file_id) < 0)
559         goto cleanup;
560 
561     area_offset += 1; /* type/length byte */
562     area_offset += number_of_data_bytes;
563 
564     while (area_offset < areabuflen &&
565            areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE)
566     {
567         ipmi_fru_field_t* field_ptr = nullptr;
568 
569         if (product_custom_fields && product_custom_fields_len)
570         {
571             if (custom_fields_index < product_custom_fields_len)
572                 field_ptr = &product_custom_fields[custom_fields_index];
573             else
574             {
575                 goto cleanup;
576             }
577         }
578 
579         if (_parse_type_length(areabufptr, areabuflen, area_offset,
580                                &number_of_data_bytes, field_ptr) < 0)
581             goto cleanup;
582 
583         area_offset += 1; /* type/length byte */
584         area_offset += number_of_data_bytes;
585         custom_fields_index++;
586     }
587 
588 out:
589     rv = 0;
590 cleanup:
591     return (rv);
592 }
593 
_append_to_dict(uint8_t vpd_key_id,uint8_t * vpd_key_val,IPMIFruInfo & info)594 void _append_to_dict(uint8_t vpd_key_id, uint8_t* vpd_key_val,
595                      IPMIFruInfo& info)
596 {
597     int type_length = vpd_key_val[0];
598     int type_code = (type_length & IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK) >>
599                     IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT;
600     int vpd_val_len = type_length &
601                       IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK;
602 
603     /* Needed to convert each uint8_t byte to a ascii */
604     char bin_byte[3] = {0};
605 
606     /*
607      * Max number of characters needed to represent 1 unsigned byte in string
608      * is number of bytes multiplied by 2. Extra 3 for 0x and a ending '\0';
609      */
610     char bin_in_ascii_len = vpd_val_len * 2 + 3;
611 
612     /* Binary converted to ascii in array */
613     char* bin_in_ascii = static_cast<char*>(malloc(bin_in_ascii_len));
614 
615     /* For reading byte from the area */
616     int val = 0;
617 
618     char* bin_copy = &(static_cast<char*>(bin_in_ascii)[2]);
619 
620     switch (type_code)
621     {
622         case 0:
623             memset(bin_in_ascii, 0x0, bin_in_ascii_len);
624 
625             /* Offset 1 is where actual data starts */
626             for (val = 1; val <= vpd_val_len; val++)
627             {
628                 /* 2 bytes for data and 1 for terminating '\0' */
629                 snprintf(bin_byte, 3, "%02x", vpd_key_val[val]);
630 
631 #pragma GCC diagnostic push
632 #ifdef __clang__
633 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
634 #else
635 #pragma GCC diagnostic ignored "-Wstringop-truncation"
636 #endif
637                 /* Its a running string so strip off the '\0' */
638                 strncat(bin_copy, bin_byte, 2);
639 #pragma GCC diagnostic pop
640             }
641 
642             /* We need the data represented as 0x...... */
643             if (vpd_val_len > 0)
644             {
645                 memcpy(bin_in_ascii, "0x", 2);
646             }
647 
648             lg2::debug(
649                 "_append_to_dict: VPD Key = [{KEY}] : Type Code = [BINARY] : Len = [{LEN}] : Val = [{VAL}]",
650                 "KEY", vpd_key_names[vpd_key_id], "LEN", vpd_val_len, "VAL",
651                 bin_in_ascii);
652 
653             info[vpd_key_id] =
654                 std::make_pair(vpd_key_names[vpd_key_id], bin_in_ascii);
655             break;
656 
657         case 3:
658             lg2::debug(
659                 "_append_to_dict: VPD Key = [{KEY}] : Type Code = [ASCII+Latin] : Len = [{LEN}] : Val = [{VAL}]",
660                 "KEY", vpd_key_names[vpd_key_id], "LEN", vpd_val_len, "VAL",
661                 &vpd_key_val[1]);
662             info[vpd_key_id] = std::make_pair(
663                 vpd_key_names[vpd_key_id],
664                 std::string(vpd_key_val + 1, vpd_key_val + 1 + type_length));
665             break;
666     }
667 
668     if (bin_in_ascii)
669     {
670         free(bin_in_ascii);
671         bin_in_ascii = nullptr;
672     }
673 }
674 
parse_fru_area(const uint8_t area,const void * msgbuf,const size_t len,IPMIFruInfo & info)675 int parse_fru_area(const uint8_t area, const void* msgbuf, const size_t len,
676                    IPMIFruInfo& info)
677 {
678     int rv = -1;
679     int i = 0;
680 
681     /* Chassis */
682     uint8_t chassis_type;
683     /* Board */
684     uint32_t mfg_date_time;
685     /* Product */
686     // unsigned int product_custom_fields_len;
687 
688     // ipmi_fru_area_info_t fru_area_info [ IPMI_FRU_AREA_TYPE_MAX ];
689     ipmi_fru_field_t vpd_info[OPENBMC_VPD_KEY_MAX];
690     char timestr[OPENBMC_VPD_VAL_LEN];
691 
692     // uint8_t* ipmi_fru_field_str=NULL;
693     // ipmi_fru_common_hdr_t* chdr = NULL;
694     // uint8_t* hdr = NULL;
695 
696     ASSERT(msgbuf);
697 
698     for (i = 0; i < OPENBMC_VPD_KEY_MAX; i++)
699     {
700         memset(vpd_info[i].type_length_field, '\0',
701                IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX);
702         vpd_info[i].type_length_field_length = 0;
703     }
704 
705     switch (area)
706     {
707         case IPMI_FRU_AREA_CHASSIS_INFO:
708             lg2::debug("Chassis : Buf len = [{LEN}]", "LEN", len);
709             ipmi_fru_chassis_info_area(
710                 static_cast<const uint8_t*>(msgbuf) + 2, len, &chassis_type,
711                 &vpd_info[OPENBMC_VPD_KEY_CHASSIS_PART_NUM],
712                 &vpd_info[OPENBMC_VPD_KEY_CHASSIS_SERIAL_NUM],
713                 &vpd_info[OPENBMC_VPD_KEY_CHASSIS_CUSTOM1],
714                 OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX);
715 
716             /* Populate VPD Table */
717             for (i = 1; i <= OPENBMC_VPD_KEY_CHASSIS_MAX; i++)
718             {
719                 if (i == OPENBMC_VPD_KEY_CHASSIS_TYPE)
720                 {
721                     lg2::debug("Chassis : Appending [{KEY}] = [{TYPE}]", "KEY",
722                                vpd_key_names[i], "TYPE", chassis_type);
723                     info[i] = std::make_pair(vpd_key_names[i],
724                                              std::to_string(chassis_type));
725                     continue;
726                 }
727                 _append_to_dict(i, vpd_info[i].type_length_field, info);
728             }
729             break;
730         case IPMI_FRU_AREA_BOARD_INFO:
731             lg2::debug("Board : Buf len = [{LEN}]", "LEN", len);
732             ipmi_fru_board_info_area(
733                 static_cast<const uint8_t*>(msgbuf) + 2, len, nullptr,
734                 &mfg_date_time, &vpd_info[OPENBMC_VPD_KEY_BOARD_MFR],
735                 &vpd_info[OPENBMC_VPD_KEY_BOARD_NAME],
736                 &vpd_info[OPENBMC_VPD_KEY_BOARD_SERIAL_NUM],
737                 &vpd_info[OPENBMC_VPD_KEY_BOARD_PART_NUM],
738                 &vpd_info[OPENBMC_VPD_KEY_BOARD_FRU_FILE_ID],
739                 &vpd_info[OPENBMC_VPD_KEY_BOARD_CUSTOM1],
740                 OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX);
741 
742             /* Populate VPD Table */
743             for (i = OPENBMC_VPD_KEY_BOARD_MFG_DATE;
744                  i <= OPENBMC_VPD_KEY_BOARD_MAX; i++)
745             {
746                 if (i == OPENBMC_VPD_KEY_BOARD_MFG_DATE)
747                 {
748                     _to_time_str(mfg_date_time, timestr, OPENBMC_VPD_VAL_LEN);
749                     lg2::debug("Board : Appending [{KEY}] = [{VAL}]", "KEY",
750                                vpd_key_names[i], "VAL", timestr);
751                     info[i] =
752                         std::make_pair(vpd_key_names[i], std::string(timestr));
753                     continue;
754                 }
755                 _append_to_dict(i, vpd_info[i].type_length_field, info);
756             }
757             break;
758         case IPMI_FRU_AREA_PRODUCT_INFO:
759             lg2::debug("Product : Buf len = [{LEN}]", "LEN", len);
760             ipmi_fru_product_info_area(
761                 static_cast<const uint8_t*>(msgbuf) + 2, len, nullptr,
762                 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_MFR],
763                 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_NAME],
764                 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_PART_MODEL_NUM],
765                 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_VER],
766                 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_SERIAL_NUM],
767                 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_ASSET_TAG],
768                 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_FRU_FILE_ID],
769                 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_CUSTOM1],
770                 OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX);
771 
772             for (i = OPENBMC_VPD_KEY_PRODUCT_MFR;
773                  i <= OPENBMC_VPD_KEY_PRODUCT_MAX; ++i)
774             {
775                 _append_to_dict(i, vpd_info[i].type_length_field, info);
776             }
777             break;
778         default:
779             /* TODO: Parse Multi Rec / Internal use area */
780             break;
781     }
782 
783     lg2::debug("parse_fru_area : Dictionary Packing Complete");
784     rv = 0;
785     return (rv);
786 }
787