xref: /openbmc/ipmi-fru-parser/frup.cpp (revision 6eed4032)
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 <stdio.h>
43 #include <stdlib.h>
44 #include <unistd.h>
45 #include <string.h>
46 #include <time.h>
47 #include <systemd/sd-bus.h>
48 #include <ctype.h>
49 #include "frup.hpp"
50 
51 #define TEXTSTR(a) #a
52 # define ASSERT(x) \
53 do { \
54 if (0 == (x)) { \
55 fprintf(stderr, \
56 "Assertion failed: %s, " \
57 "%d at \'%s\'\n", \
58 __FILE__, \
59 __LINE__, \
60 TEXTSTR(a)); \
61 return -1; \
62 } \
63 } while (0)
64 
65 #define IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX            512
66 #define IPMI_FRU_SENTINEL_VALUE                        0xC1
67 #define IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK            0xC0
68 #define IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT           0x06
69 #define IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK 0x3F
70 #define IPMI_FRU_TYPE_LENGTH_TYPE_CODE_LANGUAGE_CODE   0x03
71 
72 /* OpenBMC defines for Parser */
73 #define IPMI_FRU_AREA_INTERNAL_USE                     0x00
74 #define IPMI_FRU_AREA_CHASSIS_INFO                     0x01
75 #define IPMI_FRU_AREA_BOARD_INFO                       0x02
76 #define IPMI_FRU_AREA_PRODUCT_INFO                     0x03
77 #define IPMI_FRU_AREA_MULTI_RECORD                     0x04
78 #define IPMI_FRU_AREA_TYPE_MAX                         0x05
79 
80 #define OPENBMC_VPD_KEY_LEN                            64
81 #define OPENBMC_VPD_VAL_LEN                            512
82 
83 struct ipmi_fru_field
84 {
85   uint8_t type_length_field[IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX];
86   /* store length of data stored in buffer */
87   unsigned int type_length_field_length;
88 };
89 
90 typedef struct ipmi_fru_field ipmi_fru_field_t;
91 /*
92  * FRU Parser
93  */
94 
95 typedef struct ipmi_fru_area_info
96 {
97     uint8_t off;
98     uint8_t len;
99 } ipmi_fru_area_info_t;
100 
101 typedef struct ipmi_fru_common_hdr
102 {
103     uint8_t fmtver;
104     uint8_t internal;
105     uint8_t chassis;
106     uint8_t board;
107     uint8_t product;
108     uint8_t multirec;
109 } __attribute__((packed)) ipmi_fru_common_hdr_t;
110 
111 const char* vpd_key_names [] =
112 {
113   "Key Names Table Start",
114   "Type", /*OPENBMC_VPD_KEY_CHASSIS_TYPE*/
115   "Part Number", /*OPENBMC_VPD_KEY_CHASSIS_PART_NUM,*/
116   "Serial Number", /*OPENBMC_VPD_KEY_CHASSIS_SERIAL_NUM,*/
117   "Custom Field 1", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM1,*/
118   "Custom Field 2", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM2,*/
119   "Custom Field 3", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM3,*/
120   "Custom Field 4", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM4,*/
121   "Custom Field 5", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM5,*/
122   "Custom Field 6", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM6,*/
123   "Custom Field 7", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM7,*/
124   "Custom Field 8", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM8,*/
125 
126   "Mfg Date", /* OPENBMC_VPD_KEY_BOARD_MFG_DATE, */ /* not a type/len */
127   "Manufacturer", /* OPENBMC_VPD_KEY_BOARD_MFR, */
128   "Name", /* OPENBMC_VPD_KEY_BOARD_NAME, */
129   "Serial Number", /* OPENBMC_VPD_KEY_BOARD_SERIAL_NUM, */
130   "Part Number", /* OPENBMC_VPD_KEY_BOARD_PART_NUM, */
131   "FRU File ID", /* OPENBMC_VPD_KEY_BOARD_FRU_FILE_ID, */
132   "Custom Field 1", /*OPENBMC_VPD_KEY_BOARD_CUSTOM1,*/
133   "Custom Field 2", /*OPENBMC_VPD_KEY_BOARD_CUSTOM2,*/
134   "Custom Field 3", /*OPENBMC_VPD_KEY_BOARD_CUSTOM3,*/
135   "Custom Field 4", /*OPENBMC_VPD_KEY_BOARD_CUSTOM4,*/
136   "Custom Field 5", /*OPENBMC_VPD_KEY_BOARD_CUSTOM5,*/
137   "Custom Field 6", /*OPENBMC_VPD_KEY_BOARD_CUSTOM6,*/
138   "Custom Field 7", /*OPENBMC_VPD_KEY_BOARD_CUSTOM7,*/
139   "Custom Field 8", /*OPENBMC_VPD_KEY_BOARD_CUSTOM8,*/
140 
141   "Manufacturer", /* OPENBMC_VPD_KEY_PRODUCT_MFR, */
142   "Name", /* OPENBMC_VPD_KEY_PRODUCT_NAME, */
143   "Model Number", /* OPENBMC_VPD_KEY_PRODUCT_PART_MODEL_NUM, */
144   "Version", /* OPENBMC_VPD_KEY_PRODUCT_VER, */
145   "Serial Number", /* OPENBMC_VPD_KEY_PRODUCT_SERIAL_NUM, */
146   "Asset Tag", /* OPENBMC_VPD_KEY_PRODUCT_ASSET_TAG, */
147   "FRU File ID", /* OPENBMC_VPD_KEY_PRODUCT_FRU_FILE_ID, */
148   "Custom Field 1", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM1,*/
149   "Custom Field 2", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM2,*/
150   "Custom Field 3", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM3,*/
151   "Custom Field 4", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM4,*/
152   "Custom Field 5", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM5,*/
153   "Custom Field 6", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM6,*/
154   "Custom Field 7", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM7,*/
155   "Custom Field 8", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM8,*/
156 
157   "Key Names Table End" /*OPENBMC_VPD_KEY_MAX,*/
158 };
159 
160 
161 /*
162  * --------------------------------------------------------------------
163  *
164  * --------------------------------------------------------------------
165  */
166 
167 static size_t _to_time_str (uint32_t mfg_date_time, char* timestr, uint32_t len)
168 {
169     struct tm tm;
170     time_t t;
171     size_t s;
172 
173     ASSERT (timestr);
174     ASSERT (len);
175 
176     memset (&tm, '\0', sizeof (struct tm));
177 
178     t = mfg_date_time;
179     gmtime_r (&t, &tm);
180     s = strftime (timestr, len, "%F - %H:%M:%S", &tm);
181 
182     return s;
183 }
184 
185 /* private method to parse type/length */
186 static int
187 _parse_type_length (const void *areabuf,
188                     unsigned int areabuflen,
189                     unsigned int current_area_offset,
190                     uint8_t *number_of_data_bytes,
191                     ipmi_fru_field_t *field)
192 {
193   const uint8_t *areabufptr = (const uint8_t*) areabuf;
194   uint8_t type_length;
195   uint8_t type_code;
196 
197   ASSERT (areabuf);
198   ASSERT (areabuflen);
199   ASSERT (number_of_data_bytes);
200 
201   type_length = areabufptr[current_area_offset];
202 
203   /* ipmi workaround
204    *
205    * dell p weredge r610
206    *
207    * my reading of the fru spec is that all non-custom fields are
208    * required to be listed by the vendor.  however, on this
209    * motherboard, some areas list this, indicating that there is
210    * no more data to be parsed.  so now, for "required" fields, i
211    * check to see if the type-length field is a sentinel before
212    * calling this function.
213    */
214 
215   ASSERT (type_length != IPMI_FRU_SENTINEL_VALUE);
216 
217   type_code = (type_length & IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK) >> IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT;
218   (*number_of_data_bytes) = type_length & IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK;
219 
220   /* special case: this shouldn't be a length of 0x01 (see type/length
221    * byte format in fru information storage definition).
222    */
223   if (type_code == IPMI_FRU_TYPE_LENGTH_TYPE_CODE_LANGUAGE_CODE
224       && (*number_of_data_bytes) == 0x01)
225     {
226       return (-1);
227     }
228 
229   if ((current_area_offset + 1 + (*number_of_data_bytes)) > areabuflen)
230     {
231       return (-1);
232     }
233 
234   if (field)
235     {
236       memset (field->type_length_field,
237               '\0',
238               IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX);
239       memcpy (field->type_length_field,
240               &areabufptr[current_area_offset],
241               1 + (*number_of_data_bytes));
242       field->type_length_field_length = 1 + (*number_of_data_bytes);
243     }
244 
245   return (0);
246 }
247 
248 int
249 ipmi_fru_chassis_info_area (const void *areabuf,
250 			    unsigned int areabuflen,
251 			    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 = (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,
270             '\0',
271             sizeof (ipmi_fru_field_t));
272   if (chassis_serial_number)
273     memset (chassis_serial_number,
274             '\0',
275             sizeof (ipmi_fru_field_t));
276   if (chassis_custom_fields && chassis_custom_fields_len)
277     memset (chassis_custom_fields,
278             '\0',
279             sizeof (ipmi_fru_field_t) * chassis_custom_fields_len);
280 
281   if (chassis_type)
282     (*chassis_type) = areabufptr[area_offset];
283   area_offset++;
284 
285   if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
286     goto out;
287 
288   if (_parse_type_length (areabufptr,
289                           areabuflen,
290                           area_offset,
291                           &number_of_data_bytes,
292                           chassis_part_number) < 0)
293     goto cleanup;
294   area_offset += 1;          /* type/length byte */
295   area_offset += number_of_data_bytes;
296 
297   if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
298     goto out;
299 
300   if (_parse_type_length (areabufptr,
301                           areabuflen,
302                           area_offset,
303                           &number_of_data_bytes,
304                           chassis_serial_number) < 0)
305     goto cleanup;
306   area_offset += 1;          /* type/length byte */
307   area_offset += number_of_data_bytes;
308 
309   while (area_offset < areabuflen
310          && areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE)
311     {
312       ipmi_fru_field_t *field_ptr = NULL;
313 
314       if (chassis_custom_fields && chassis_custom_fields_len)
315         {
316           if (custom_fields_index < chassis_custom_fields_len)
317             field_ptr = &chassis_custom_fields[custom_fields_index];
318           else
319             {
320               goto cleanup;
321             }
322         }
323 
324       if (_parse_type_length (areabufptr,
325                               areabuflen,
326                               area_offset,
327                               &number_of_data_bytes,
328                               field_ptr) < 0)
329         goto cleanup;
330 
331       area_offset += 1;          /* type/length byte */
332       area_offset += number_of_data_bytes;
333       custom_fields_index++;
334     }
335 
336 
337  out:
338   rv = 0;
339  cleanup:
340   return (rv);
341 }
342 
343 int
344 ipmi_fru_board_info_area (const void *areabuf,
345 			  unsigned int areabuflen,
346 			  uint8_t *language_code,
347 			  uint32_t *mfg_date_time,
348 			  ipmi_fru_field_t *board_manufacturer,
349 			  ipmi_fru_field_t *board_product_name,
350 			  ipmi_fru_field_t *board_serial_number,
351 			  ipmi_fru_field_t *board_part_number,
352 			  ipmi_fru_field_t *board_fru_file_id,
353 			  ipmi_fru_field_t *board_custom_fields,
354 			  unsigned int board_custom_fields_len)
355 {
356   const uint8_t *areabufptr = (const uint8_t*) areabuf;
357   uint32_t mfg_date_time_tmp = 0;
358   unsigned int area_offset = 0;
359   unsigned int custom_fields_index = 0;
360   uint8_t number_of_data_bytes;
361   int rv = -1;
362 
363   if (!areabuf || !areabuflen)
364     {
365       return (-1);
366     }
367 
368   if (board_manufacturer)
369     memset (board_manufacturer,
370             '\0',
371             sizeof (ipmi_fru_field_t));
372   if (board_product_name)
373     memset (board_product_name,
374             '\0',
375             sizeof (ipmi_fru_field_t));
376   if (board_serial_number)
377     memset (board_serial_number,
378             '\0',
379             sizeof (ipmi_fru_field_t));
380   if (board_part_number)
381     memset (board_part_number,
382             '\0',
383             sizeof (ipmi_fru_field_t));
384   if (board_fru_file_id)
385     memset (board_fru_file_id,
386             '\0',
387             sizeof (ipmi_fru_field_t));
388   if (board_custom_fields && board_custom_fields_len)
389     memset (board_custom_fields,
390             '\0',
391             sizeof (ipmi_fru_field_t) * board_custom_fields_len);
392 
393   if (language_code)
394     (*language_code) = areabufptr[area_offset];
395   area_offset++;
396 
397   if (mfg_date_time)
398     {
399       struct tm tm;
400       time_t t;
401 
402       /* mfg_date_time is little endian - see spec */
403       mfg_date_time_tmp |= areabufptr[area_offset];
404       area_offset++;
405       mfg_date_time_tmp |= (areabufptr[area_offset] << 8);
406       area_offset++;
407       mfg_date_time_tmp |= (areabufptr[area_offset] << 16);
408       area_offset++;
409 
410       /* mfg_date_time is in minutes, so multiple by 60 to get seconds */
411       mfg_date_time_tmp *= 60;
412 
413       /* posix says individual calls need not clear/set all portions of
414        * 'struct tm', thus passing 'struct tm' between functions could
415        * have issues.  so we need to memset.
416        */
417       memset (&tm, '\0', sizeof(struct tm));
418 
419       /* in fru, epoch is 0:00 hrs 1/1/96
420        *
421        * so convert into ansi epoch
422        */
423 
424       tm.tm_year = 96;          /* years since 1900 */
425       tm.tm_mon = 0;            /* months since january */
426       tm.tm_mday = 1;           /* 1-31 */
427       tm.tm_hour = 0;
428       tm.tm_min = 0;
429       tm.tm_sec = 0;
430       tm.tm_isdst = -1;
431 
432       if ((t = mktime (&tm)) == (time_t)-1)
433         {
434           goto cleanup;
435         }
436 
437       mfg_date_time_tmp += (uint32_t)t;
438       (*mfg_date_time) = mfg_date_time_tmp;
439     }
440   else
441     area_offset += 3;
442 
443   if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
444     goto out;
445 
446   if (_parse_type_length (areabufptr,
447                           areabuflen,
448                           area_offset,
449                           &number_of_data_bytes,
450                           board_manufacturer) < 0)
451     goto cleanup;
452   area_offset += 1;          /* type/length byte */
453   area_offset += number_of_data_bytes;
454 
455   if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
456     goto out;
457 
458   if (_parse_type_length (areabufptr,
459                           areabuflen,
460                           area_offset,
461                           &number_of_data_bytes,
462                           board_product_name) < 0)
463     goto cleanup;
464   area_offset += 1;          /* type/length byte */
465   area_offset += number_of_data_bytes;
466 
467   if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
468     goto out;
469 
470   if (_parse_type_length (areabufptr,
471                           areabuflen,
472                           area_offset,
473                           &number_of_data_bytes,
474                           board_serial_number) < 0)
475     goto cleanup;
476   area_offset += 1;          /* type/length byte */
477   area_offset += number_of_data_bytes;
478 
479   if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
480     goto out;
481 
482   if (_parse_type_length (areabufptr,
483                           areabuflen,
484                           area_offset,
485                           &number_of_data_bytes,
486                           board_part_number) < 0)
487     goto cleanup;
488   area_offset += 1;          /* type/length byte */
489   area_offset += number_of_data_bytes;
490 
491   if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
492     goto out;
493 
494   if (_parse_type_length (areabufptr,
495                           areabuflen,
496                           area_offset,
497                           &number_of_data_bytes,
498                           board_fru_file_id) < 0)
499     goto cleanup;
500   area_offset += 1;          /* type/length byte */
501   area_offset += number_of_data_bytes;
502 
503   while (area_offset < areabuflen
504          && areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE)
505     {
506       ipmi_fru_field_t *field_ptr = NULL;
507 
508       if (board_custom_fields && board_custom_fields_len)
509         {
510           if (custom_fields_index < board_custom_fields_len)
511             field_ptr = &board_custom_fields[custom_fields_index];
512           else
513             {
514               goto cleanup;
515             }
516         }
517 
518       if (_parse_type_length (areabufptr,
519                               areabuflen,
520                               area_offset,
521                               &number_of_data_bytes,
522                               field_ptr) < 0)
523         goto cleanup;
524 
525       area_offset += 1;          /* type/length byte */
526       area_offset += number_of_data_bytes;
527       custom_fields_index++;
528     }
529 
530  out:
531   rv = 0;
532  cleanup:
533   return (rv);
534 }
535 
536 int
537 ipmi_fru_product_info_area (const void *areabuf,
538 			    unsigned int areabuflen,
539 			    uint8_t *language_code,
540 			    ipmi_fru_field_t *product_manufacturer_name,
541 			    ipmi_fru_field_t *product_name,
542 			    ipmi_fru_field_t *product_part_model_number,
543 			    ipmi_fru_field_t *product_version,
544 			    ipmi_fru_field_t *product_serial_number,
545 			    ipmi_fru_field_t *product_asset_tag,
546 			    ipmi_fru_field_t *product_fru_file_id,
547 			    ipmi_fru_field_t *product_custom_fields,
548 			    unsigned int product_custom_fields_len)
549 {
550   const uint8_t *areabufptr = (const uint8_t*) areabuf;
551   unsigned int area_offset = 0;
552   unsigned int custom_fields_index = 0;
553   uint8_t number_of_data_bytes;
554   int rv = -1;
555 
556   if (!areabuf || !areabuflen)
557     {
558       return (-1);
559     }
560 
561   if (product_manufacturer_name)
562     memset (product_manufacturer_name,
563             '\0',
564             sizeof (ipmi_fru_field_t));
565   if (product_name)
566     memset (product_name,
567             '\0',
568             sizeof (ipmi_fru_field_t));
569   if (product_part_model_number)
570     memset (product_part_model_number,
571             '\0',
572             sizeof (ipmi_fru_field_t));
573   if (product_version)
574     memset (product_version,
575             '\0',
576             sizeof (ipmi_fru_field_t));
577   if (product_serial_number)
578     memset (product_serial_number,
579             '\0',
580             sizeof (ipmi_fru_field_t));
581   if (product_asset_tag)
582     memset (product_asset_tag,
583             '\0',
584             sizeof (ipmi_fru_field_t));
585   if (product_fru_file_id)
586     memset (product_fru_file_id,
587             '\0',
588             sizeof (ipmi_fru_field_t));
589   if (product_custom_fields && product_custom_fields_len)
590     memset (product_custom_fields,
591             '\0',
592             sizeof (ipmi_fru_field_t) * product_custom_fields_len);
593 
594   if (language_code)
595     (*language_code) = areabufptr[area_offset];
596   area_offset++;
597 
598   if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
599     goto out;
600 
601   if (_parse_type_length (areabufptr,
602                           areabuflen,
603                           area_offset,
604                           &number_of_data_bytes,
605                           product_manufacturer_name) < 0)
606     goto cleanup;
607   area_offset += 1;          /* type/length byte */
608   area_offset += number_of_data_bytes;
609 
610   if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
611     goto out;
612 
613   if (_parse_type_length (areabufptr,
614                           areabuflen,
615                           area_offset,
616                           &number_of_data_bytes,
617                           product_name) < 0)
618     goto cleanup;
619   area_offset += 1;          /* type/length byte */
620   area_offset += number_of_data_bytes;
621 
622   if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
623     goto out;
624 
625   if (_parse_type_length (areabufptr,
626                           areabuflen,
627                           area_offset,
628                           &number_of_data_bytes,
629                           product_part_model_number) < 0)
630     goto cleanup;
631   area_offset += 1;          /* type/length byte */
632   area_offset += number_of_data_bytes;
633 
634   if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
635     goto out;
636 
637   if (_parse_type_length (areabufptr,
638                           areabuflen,
639                           area_offset,
640                           &number_of_data_bytes,
641                           product_version) < 0)
642     goto cleanup;
643   area_offset += 1;          /* type/length byte */
644   area_offset += number_of_data_bytes;
645 
646   if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
647     goto out;
648 
649   if (_parse_type_length (areabufptr,
650                           areabuflen,
651                           area_offset,
652                           &number_of_data_bytes,
653                           product_serial_number) < 0)
654     goto cleanup;
655   area_offset += 1;          /* type/length byte */
656   area_offset += number_of_data_bytes;
657 
658   if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
659     goto out;
660 
661   if (_parse_type_length (areabufptr,
662                           areabuflen,
663                           area_offset,
664                           &number_of_data_bytes,
665                           product_asset_tag) < 0)
666     goto cleanup;
667   area_offset += 1;          /* type/length byte */
668   area_offset += number_of_data_bytes;
669 
670   if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
671     goto out;
672 
673   if (_parse_type_length (areabufptr,
674                           areabuflen,
675                           area_offset,
676                           &number_of_data_bytes,
677                           product_fru_file_id) < 0)
678     goto cleanup;
679 
680   area_offset += 1;          /* type/length byte */
681   area_offset += number_of_data_bytes;
682 
683   while (area_offset < areabuflen
684          && areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE)
685     {
686       ipmi_fru_field_t *field_ptr = NULL;
687 
688       if (product_custom_fields && product_custom_fields_len)
689         {
690           if (custom_fields_index < product_custom_fields_len)
691             field_ptr = &product_custom_fields[custom_fields_index];
692           else
693             {
694               goto cleanup;
695             }
696         }
697 
698       if (_parse_type_length (areabufptr,
699                               areabuflen,
700                               area_offset,
701                               &number_of_data_bytes,
702                               field_ptr) < 0)
703         goto cleanup;
704 
705       area_offset += 1;          /* type/length byte */
706       area_offset += number_of_data_bytes;
707       custom_fields_index++;
708     }
709 
710 
711  out:
712   rv = 0;
713  cleanup:
714   return (rv);
715 }
716 
717 void _append_to_dict (uint8_t vpd_key_id, uint8_t* vpd_key_val, sd_bus_message* vpdtbl)
718 {
719     int type_length = vpd_key_val[0];
720     int type_code = (type_length & IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK) >> IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT;
721     int vpd_val_len = type_length & IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK;
722     int sdr=0;
723 
724     /* Needed to convert each uint8_t byte to a ascii */
725     char bin_byte[3] = {0};
726 
727     /*
728      * Max number of characters needed to represent 1 unsigned byte in string
729      * is number of bytes multipled by 2. Extra 3 for 0x and a ending '\0';
730      */
731     char bin_in_ascii_len = vpd_val_len * 2 + 3;
732 
733     /* Binary converted to ascii in array */
734     char *bin_in_ascii = (char *)malloc(bin_in_ascii_len);
735 
736     /* For reading byte from the area */
737     int val = 0;
738 
739     char *bin_copy = &((char *)bin_in_ascii)[2];
740 
741     switch (type_code)
742     {
743         case 0:
744             memset(bin_in_ascii, 0x0, bin_in_ascii_len);
745 
746             /* Offset 1 is where actual data starts */
747             for(val = 1; val <= vpd_val_len ; val++)
748             {
749                 /* 2 bytes for data and 1 for terminating '\0' */
750                 snprintf(bin_byte, 3, "%02x", vpd_key_val[val]);
751 
752                 /* Its a running string so strip off the '\0' */
753                 strncat(bin_copy, bin_byte, 2);
754             }
755 
756             /* We need the data represented as 0x...... */
757             if(vpd_val_len > 0)
758             {
759                 strncpy(bin_in_ascii, "0x", 2);
760             }
761 
762             printf ("_append_to_dict: VPD Key = [%s] : Type Code = [BINARY] : Len = [%d] : Val = [%s]\n",
763                     vpd_key_names [vpd_key_id], vpd_val_len, bin_in_ascii);
764             sdr = sd_bus_message_append (vpdtbl, "{sv}", vpd_key_names[vpd_key_id], "s", bin_in_ascii);
765             break;
766 
767         case 3:
768             printf ("_append_to_dict: VPD Key = [%s] : Type Code = [ASCII+Latin] : Len = [%d] : Val = [%s]\n", vpd_key_names [vpd_key_id], vpd_val_len, &vpd_key_val[1]);
769             sdr = sd_bus_message_append (vpdtbl, "{sv}", vpd_key_names[vpd_key_id], "s", &vpd_key_val[1]);
770             break;
771     }
772 
773     if(bin_in_ascii)
774     {
775         free(bin_in_ascii);
776         bin_in_ascii = NULL;
777     }
778 
779 
780     if (sdr < 0)
781     {
782 #if IPMI_FRU_PARSER_DEBUG
783         printf ("_append_to_dict : sd_bus_message_append Failed [ %d ] for [%s]\n", sdr, vpd_key_names[vpd_key_id]);
784 #endif
785     }
786 }
787 
788 int
789 parse_fru (const void* msgbuf, sd_bus_message* vpdtbl)
790 {
791   int rv = -1;
792   int i = 0;
793   ipmi_fru_area_info_t fru_area_info [ IPMI_FRU_AREA_TYPE_MAX ];
794   ipmi_fru_common_hdr_t* chdr = NULL;
795   uint8_t* hdr = NULL;
796   char timestr [ OPENBMC_VPD_VAL_LEN ];
797 
798 
799   ipmi_fru_field_t vpd_info [ OPENBMC_VPD_KEY_MAX ];
800   //uint8_t* ipmi_fru_field_str;
801 
802   /* Chassis */
803   uint8_t chassis_type;
804 
805   /* Board */
806   uint32_t mfg_date_time;
807 
808   /* Product */
809   //unsigned int product_custom_fields_len;
810 
811   ASSERT (msgbuf);
812   ASSERT (vpdtbl);
813 
814   for (i=0; i<OPENBMC_VPD_KEY_MAX; i++)
815   {
816     memset (vpd_info[i].type_length_field, '\0', IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX);
817     vpd_info[i].type_length_field_length = 0;
818   }
819 
820   for (i=0; i<IPMI_FRU_AREA_TYPE_MAX; i++)
821   {
822     fru_area_info [ i ].off = 0;
823     fru_area_info [ i ].len = 0;
824   }
825 
826   chdr = (ipmi_fru_common_hdr_t*) msgbuf;
827   hdr  = (uint8_t*) msgbuf;
828 
829   fru_area_info [ IPMI_FRU_AREA_INTERNAL_USE ].off = chdr->internal;
830   fru_area_info [ IPMI_FRU_AREA_CHASSIS_INFO ].off = chdr->chassis;
831   fru_area_info [ IPMI_FRU_AREA_BOARD_INFO   ].off = chdr->board;
832   fru_area_info [ IPMI_FRU_AREA_PRODUCT_INFO ].off = chdr->product;
833   fru_area_info [ IPMI_FRU_AREA_MULTI_RECORD ].off = chdr->multirec;
834 
835   if (chdr->internal)
836   {
837     fru_area_info [ IPMI_FRU_AREA_INTERNAL_USE ].len =  8*(*(hdr+8*chdr->internal+1));
838 
839     /* TODO: Parse internal use area */
840   }
841 
842   if (chdr->chassis)
843   {
844     fru_area_info [ IPMI_FRU_AREA_CHASSIS_INFO ].len = 8*(*(hdr+8*chdr->chassis+1));
845     ipmi_fru_chassis_info_area (hdr+8*chdr->chassis+2,
846         fru_area_info [ IPMI_FRU_AREA_CHASSIS_INFO ].len,
847         &chassis_type,
848         &vpd_info [OPENBMC_VPD_KEY_CHASSIS_PART_NUM],
849         &vpd_info [OPENBMC_VPD_KEY_CHASSIS_SERIAL_NUM],
850         &vpd_info [OPENBMC_VPD_KEY_CHASSIS_CUSTOM1],
851         OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX);
852   }
853 
854   if (chdr->board)
855   {
856     fru_area_info [ IPMI_FRU_AREA_BOARD_INFO ].len = 8*(*(hdr+8*chdr->board+1));
857     ipmi_fru_board_info_area (hdr+8*chdr->board+2,
858         fru_area_info [ IPMI_FRU_AREA_BOARD_INFO ].len,
859         NULL,
860         &mfg_date_time,
861         &vpd_info [OPENBMC_VPD_KEY_BOARD_MFR],
862         &vpd_info [OPENBMC_VPD_KEY_BOARD_NAME],
863         &vpd_info [OPENBMC_VPD_KEY_BOARD_SERIAL_NUM],
864         &vpd_info [OPENBMC_VPD_KEY_BOARD_PART_NUM],
865         &vpd_info [OPENBMC_VPD_KEY_BOARD_FRU_FILE_ID],
866         &vpd_info [OPENBMC_VPD_KEY_BOARD_CUSTOM1],
867         OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX);
868   }
869 
870   if (chdr->product)
871   {
872     fru_area_info [ IPMI_FRU_AREA_PRODUCT_INFO ].len = 8*(*(hdr+8*chdr->product+1));
873     ipmi_fru_product_info_area (hdr+8*chdr->product+2,
874         fru_area_info [ IPMI_FRU_AREA_PRODUCT_INFO ].len,
875         NULL,
876         &vpd_info [OPENBMC_VPD_KEY_PRODUCT_MFR],
877         &vpd_info [OPENBMC_VPD_KEY_PRODUCT_NAME],
878         &vpd_info [OPENBMC_VPD_KEY_PRODUCT_PART_MODEL_NUM],
879         &vpd_info [OPENBMC_VPD_KEY_PRODUCT_VER],
880         &vpd_info [OPENBMC_VPD_KEY_PRODUCT_SERIAL_NUM],
881         &vpd_info [OPENBMC_VPD_KEY_PRODUCT_ASSET_TAG],
882         &vpd_info [OPENBMC_VPD_KEY_PRODUCT_FRU_FILE_ID],
883         &vpd_info [OPENBMC_VPD_KEY_PRODUCT_CUSTOM1],
884         OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX);
885   }
886 
887   if (chdr->multirec)
888   {
889     fru_area_info [ IPMI_FRU_AREA_MULTI_RECORD ].len = 8*(*(hdr+8*chdr->multirec+1));
890     /* TODO: Parse multi record area */
891   }
892 
893   for (i=0; i<IPMI_FRU_AREA_TYPE_MAX; i++)
894   {
895 #if IPMI_FRU_PARSER_DEBUG
896     printf ("IPMI_FRU_AREA_TYPE=[%d] : Offset=[%d] : Len=[%d]\n", i, fru_area_info [i].off, fru_area_info[i].len);
897 #endif
898   }
899 
900   /* Populate VPD Table */
901   for (i=1; i<OPENBMC_VPD_KEY_MAX; i++)
902   {
903     if (i==OPENBMC_VPD_KEY_CHASSIS_TYPE)
904     {
905         sd_bus_message_append (vpdtbl, "{sv}", vpd_key_names[i], "y", chassis_type);
906 #if IPMI_FRU_PARSER_DEBUG
907         printf ("[%s] = [%d]\n", vpd_key_names[i], chassis_type);
908 #endif
909         continue;
910     }
911 
912     if (i==OPENBMC_VPD_KEY_BOARD_MFG_DATE)
913     {
914         _to_time_str (mfg_date_time, timestr, OPENBMC_VPD_VAL_LEN);
915         sd_bus_message_append (vpdtbl, "{sv}", vpd_key_names[i], "s", timestr);
916 #if IPMI_FRU_PARSER_DEBUG
917         printf ("[%s] = [%d]\n", vpd_key_names[i], mfg_date_time);
918 #endif
919         continue;
920     }
921 
922     /* Append TypeLen Field to Dictionary */
923     _append_to_dict (i, vpd_info[i].type_length_field, vpdtbl);
924 
925     /*ipmi_fru_field_str = (unsigned char*) &(vpd_info[i].type_length_field) + 1;*/
926     /*sd_bus_message_append (vpdtbl, "{sv}", vpd_key_names[i], "s", ipmi_fru_field_str); */
927   }
928   rv = 0;
929   return (rv);
930 }
931 
932 int parse_fru_area (const uint8_t area, const void* msgbuf,
933                     const size_t len, IPMIFruInfo& info)
934 {
935   int rv = -1;
936   int i = 0;
937 
938   /* Chassis */
939   uint8_t chassis_type;
940   /* Board */
941   uint32_t mfg_date_time;
942   /* Product */
943   //unsigned int product_custom_fields_len;
944 
945   //ipmi_fru_area_info_t fru_area_info [ IPMI_FRU_AREA_TYPE_MAX ];
946   ipmi_fru_field_t vpd_info [ OPENBMC_VPD_KEY_MAX ];
947   char timestr [ OPENBMC_VPD_VAL_LEN ];
948 
949   //uint8_t* ipmi_fru_field_str=NULL;
950   //ipmi_fru_common_hdr_t* chdr = NULL;
951   //uint8_t* hdr = NULL;
952 
953   ASSERT (msgbuf);
954 
955   for (i=0; i<OPENBMC_VPD_KEY_MAX; i++)
956   {
957     memset (vpd_info[i].type_length_field, '\0', IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX);
958     vpd_info[i].type_length_field_length = 0;
959   }
960 
961   switch (area)
962   {
963     case IPMI_FRU_AREA_CHASSIS_INFO:
964 #if IPMI_FRU_PARSER_DEBUG
965           printf ("Chassis : Buf len = [%d]\n", len);
966 #endif
967         ipmi_fru_chassis_info_area ((uint8_t*)msgbuf+2,
968             len,
969             &chassis_type,
970             &vpd_info [OPENBMC_VPD_KEY_CHASSIS_PART_NUM],
971             &vpd_info [OPENBMC_VPD_KEY_CHASSIS_SERIAL_NUM],
972             &vpd_info [OPENBMC_VPD_KEY_CHASSIS_CUSTOM1],
973             OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX);
974 
975           /* Populate VPD Table */
976           for (i=1; i<=OPENBMC_VPD_KEY_CHASSIS_MAX; i++)
977           {
978             if (i==OPENBMC_VPD_KEY_CHASSIS_TYPE)
979             {
980 #if IPMI_FRU_PARSER_DEBUG
981                 printf ("Chassis : Appending [%s] = [%d]\n", vpd_key_names[i], chassis_type);
982 #endif
983                 info[i] = std::make_pair(vpd_key_names[i],
984                                          std::to_string(chassis_type));
985                 continue;
986             }
987             info[i] = std::make_pair(vpd_key_names[i],
988                       std::string(reinterpret_cast<char*>
989                                  (vpd_info[i].type_length_field)));
990 
991           }
992         break;
993     case IPMI_FRU_AREA_BOARD_INFO:
994 #if IPMI_FRU_PARSER_DEBUG
995             printf ("Board : Buf len = [%d]\n", len);
996 #endif
997             ipmi_fru_board_info_area ((uint8_t*)msgbuf+2,
998                 len,
999                 NULL,
1000                 &mfg_date_time,
1001                 &vpd_info [OPENBMC_VPD_KEY_BOARD_MFR],
1002                 &vpd_info [OPENBMC_VPD_KEY_BOARD_NAME],
1003                 &vpd_info [OPENBMC_VPD_KEY_BOARD_SERIAL_NUM],
1004                 &vpd_info [OPENBMC_VPD_KEY_BOARD_PART_NUM],
1005                 &vpd_info [OPENBMC_VPD_KEY_BOARD_FRU_FILE_ID],
1006                 &vpd_info [OPENBMC_VPD_KEY_BOARD_CUSTOM1],
1007                 OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX);
1008 
1009           /* Populate VPD Table */
1010             for (i=OPENBMC_VPD_KEY_BOARD_MFR; i<=OPENBMC_VPD_KEY_BOARD_MAX; i++)
1011             {
1012                 if (i==OPENBMC_VPD_KEY_BOARD_MFG_DATE)
1013                 {
1014                     _to_time_str (mfg_date_time, timestr, OPENBMC_VPD_VAL_LEN);
1015 #if IPMI_FRU_PARSER_DEBUG
1016                     printf ("Board : Appending [%s] = [%d]\n", vpd_key_names[i], timestr);
1017 #endif
1018                     info[i] = std::make_pair(vpd_key_names[i],
1019                                              std::string(timestr));
1020                     continue;
1021                 }
1022                 info[i] = std::make_pair(vpd_key_names[i],
1023                           std::string(reinterpret_cast<char*>
1024                                      (vpd_info[i].type_length_field)));
1025 
1026             }
1027             break;
1028     case IPMI_FRU_AREA_PRODUCT_INFO:
1029 #if IPMI_FRU_PARSER_DEBUG
1030             printf ("Product : Buf len = [%d]\n", len);
1031 #endif
1032             ipmi_fru_product_info_area ((uint8_t*)msgbuf+2,
1033                 len,
1034                 NULL,
1035                 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_MFR],
1036                 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_NAME],
1037                 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_PART_MODEL_NUM],
1038                 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_VER],
1039                 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_SERIAL_NUM],
1040                 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_ASSET_TAG],
1041                 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_FRU_FILE_ID],
1042                 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_CUSTOM1],
1043                 OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX);
1044 
1045             for (i=OPENBMC_VPD_KEY_PRODUCT_MFR; i<=OPENBMC_VPD_KEY_PRODUCT_MAX; i++)
1046             {
1047                 info[i] = std::make_pair(vpd_key_names[i],
1048                           std::string(reinterpret_cast<char*>
1049                                      (vpd_info[i].type_length_field)));
1050             }
1051             break;
1052     default:
1053     /* TODO: Parse Multi Rec / Internal use area */
1054     break;
1055   }
1056 
1057 #if IPMI_FRU_PARSER_DEBUG
1058     printf ("parse_fru_area : Dictionary Packing Complete\n");
1059 #endif
1060   rv = 0;
1061   return (rv);
1062 }
1063