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