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 #define TEXTSTR(a) #a
53 #define ASSERT(x) \
54 do \
55 { \
56 if (0 == (x)) \
57 { \
58 fprintf(stderr, \
59 "Assertion failed: %s, " \
60 "%d at \'%s\'\n", \
61 __FILE__, __LINE__, TEXTSTR(a)); \
62 return -1; \
63 } \
64 } while (0)
65
66 #define IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX 512
67 #define IPMI_FRU_SENTINEL_VALUE 0xC1
68 #define IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK 0xC0
69 #define IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT 0x06
70 #define IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK 0x3F
71 #define IPMI_FRU_TYPE_LENGTH_TYPE_CODE_LANGUAGE_CODE 0x03
72
73 /* OpenBMC defines for Parser */
74 #define IPMI_FRU_AREA_INTERNAL_USE 0x00
75 #define IPMI_FRU_AREA_CHASSIS_INFO 0x01
76 #define IPMI_FRU_AREA_BOARD_INFO 0x02
77 #define IPMI_FRU_AREA_PRODUCT_INFO 0x03
78 #define IPMI_FRU_AREA_MULTI_RECORD 0x04
79 #define IPMI_FRU_AREA_TYPE_MAX 0x05
80
81 #define OPENBMC_VPD_KEY_LEN 64
82 #define OPENBMC_VPD_VAL_LEN 512
83
84 constexpr long fruEpochMinutes = 820454400;
85
86 struct ipmi_fru_field
87 {
88 uint8_t type_length_field[IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX];
89 /* store length of data stored in buffer */
90 unsigned int type_length_field_length;
91 };
92
93 typedef struct ipmi_fru_field ipmi_fru_field_t;
94 /*
95 * FRU Parser
96 */
97
98 typedef struct ipmi_fru_area_info
99 {
100 uint8_t off;
101 uint8_t len;
102 } ipmi_fru_area_info_t;
103
104 typedef struct ipmi_fru_common_hdr
105 {
106 uint8_t fmtver;
107 uint8_t internal;
108 uint8_t chassis;
109 uint8_t board;
110 uint8_t product;
111 uint8_t multirec;
112 } __attribute__((packed)) ipmi_fru_common_hdr_t;
113
114 const char* vpd_key_names[] = {
115 "Key Names Table Start",
116 "Type", /*OPENBMC_VPD_KEY_CHASSIS_TYPE*/
117 "Part Number", /*OPENBMC_VPD_KEY_CHASSIS_PART_NUM,*/
118 "Serial Number", /*OPENBMC_VPD_KEY_CHASSIS_SERIAL_NUM,*/
119 "Custom Field 1", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM1,*/
120 "Custom Field 2", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM2,*/
121 "Custom Field 3", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM3,*/
122 "Custom Field 4", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM4,*/
123 "Custom Field 5", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM5,*/
124 "Custom Field 6", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM6,*/
125 "Custom Field 7", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM7,*/
126 "Custom Field 8", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM8,*/
127
128 "Mfg Date",
129 /* OPENBMC_VPD_KEY_BOARD_MFG_DATE, */ /* not a type/len */
130 "Manufacturer", /* OPENBMC_VPD_KEY_BOARD_MFR, */
131 "Name", /* OPENBMC_VPD_KEY_BOARD_NAME, */
132 "Serial Number", /* OPENBMC_VPD_KEY_BOARD_SERIAL_NUM, */
133 "Part Number", /* OPENBMC_VPD_KEY_BOARD_PART_NUM, */
134 "FRU File ID", /* OPENBMC_VPD_KEY_BOARD_FRU_FILE_ID, */
135 "Custom Field 1", /*OPENBMC_VPD_KEY_BOARD_CUSTOM1,*/
136 "Custom Field 2", /*OPENBMC_VPD_KEY_BOARD_CUSTOM2,*/
137 "Custom Field 3", /*OPENBMC_VPD_KEY_BOARD_CUSTOM3,*/
138 "Custom Field 4", /*OPENBMC_VPD_KEY_BOARD_CUSTOM4,*/
139 "Custom Field 5", /*OPENBMC_VPD_KEY_BOARD_CUSTOM5,*/
140 "Custom Field 6", /*OPENBMC_VPD_KEY_BOARD_CUSTOM6,*/
141 "Custom Field 7", /*OPENBMC_VPD_KEY_BOARD_CUSTOM7,*/
142 "Custom Field 8", /*OPENBMC_VPD_KEY_BOARD_CUSTOM8,*/
143
144 "Manufacturer", /* OPENBMC_VPD_KEY_PRODUCT_MFR, */
145 "Name", /* OPENBMC_VPD_KEY_PRODUCT_NAME, */
146 "Model Number", /* OPENBMC_VPD_KEY_PRODUCT_PART_MODEL_NUM, */
147 "Version", /* OPENBMC_VPD_KEY_PRODUCT_VER, */
148 "Serial Number", /* OPENBMC_VPD_KEY_PRODUCT_SERIAL_NUM, */
149 "Asset Tag", /* OPENBMC_VPD_KEY_PRODUCT_ASSET_TAG, */
150 "FRU File ID", /* OPENBMC_VPD_KEY_PRODUCT_FRU_FILE_ID, */
151 "Custom Field 1", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM1,*/
152 "Custom Field 2", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM2,*/
153 "Custom Field 3", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM3,*/
154 "Custom Field 4", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM4,*/
155 "Custom Field 5", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM5,*/
156 "Custom Field 6", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM6,*/
157 "Custom Field 7", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM7,*/
158 "Custom Field 8", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM8,*/
159
160 "Key Names Table End" /*OPENBMC_VPD_KEY_MAX,*/
161 };
162
163 /*
164 * --------------------------------------------------------------------
165 *
166 * --------------------------------------------------------------------
167 */
168
_to_time_str(uint32_t mfg_date_time,char * timestr,uint32_t len)169 static size_t _to_time_str(uint32_t mfg_date_time, char* timestr, uint32_t len)
170 {
171 struct tm tm;
172 time_t t;
173 size_t s;
174
175 ASSERT(timestr);
176 ASSERT(len);
177
178 memset(&tm, '\0', sizeof(struct tm));
179
180 t = mfg_date_time;
181 gmtime_r(&t, &tm);
182 s = strftime(timestr, len, "%F - %H:%M:%S UTC", &tm);
183
184 return s;
185 }
186
187 /* 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)188 static int _parse_type_length(const void* areabuf, 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) >>
218 IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT;
219 (*number_of_data_bytes) =
220 type_length & IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK;
221
222 /* special case: this shouldn't be a length of 0x01 (see type/length
223 * byte format in fru information storage definition).
224 */
225 if (type_code == IPMI_FRU_TYPE_LENGTH_TYPE_CODE_LANGUAGE_CODE &&
226 (*number_of_data_bytes) == 0x01)
227 {
228 return (-1);
229 }
230
231 if ((current_area_offset + 1 + (*number_of_data_bytes)) > areabuflen)
232 {
233 return (-1);
234 }
235
236 if (field)
237 {
238 memset(field->type_length_field, '\0',
239 IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX);
240 memcpy(field->type_length_field, &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
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)248 int ipmi_fru_chassis_info_area(
249 const void* areabuf, unsigned int areabuflen, uint8_t* chassis_type,
250 ipmi_fru_field_t* chassis_part_number,
251 ipmi_fru_field_t* chassis_serial_number,
252 ipmi_fru_field_t* chassis_custom_fields,
253 unsigned int chassis_custom_fields_len)
254 {
255 const uint8_t* areabufptr = (const uint8_t*)areabuf;
256 unsigned int area_offset = 0;
257 unsigned int custom_fields_index = 0;
258 uint8_t number_of_data_bytes;
259 int rv = -1;
260
261 if (!areabuf || !areabuflen)
262 {
263 return (-1);
264 }
265
266 if (chassis_part_number)
267 memset(chassis_part_number, '\0', sizeof(ipmi_fru_field_t));
268 if (chassis_serial_number)
269 memset(chassis_serial_number, '\0', sizeof(ipmi_fru_field_t));
270 if (chassis_custom_fields && chassis_custom_fields_len)
271 memset(chassis_custom_fields, '\0',
272 sizeof(ipmi_fru_field_t) * chassis_custom_fields_len);
273
274 if (chassis_type)
275 (*chassis_type) = areabufptr[area_offset];
276 area_offset++;
277
278 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
279 goto out;
280
281 if (_parse_type_length(areabufptr, areabuflen, area_offset,
282 &number_of_data_bytes, chassis_part_number) < 0)
283 goto cleanup;
284 area_offset += 1; /* type/length byte */
285 area_offset += number_of_data_bytes;
286
287 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
288 goto out;
289
290 if (_parse_type_length(areabufptr, areabuflen, area_offset,
291 &number_of_data_bytes, chassis_serial_number) < 0)
292 goto cleanup;
293 area_offset += 1; /* type/length byte */
294 area_offset += number_of_data_bytes;
295
296 while (area_offset < areabuflen &&
297 areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE)
298 {
299 ipmi_fru_field_t* field_ptr = NULL;
300
301 if (chassis_custom_fields && chassis_custom_fields_len)
302 {
303 if (custom_fields_index < chassis_custom_fields_len)
304 field_ptr = &chassis_custom_fields[custom_fields_index];
305 else
306 {
307 goto cleanup;
308 }
309 }
310
311 if (_parse_type_length(areabufptr, areabuflen, area_offset,
312 &number_of_data_bytes, field_ptr) < 0)
313 goto cleanup;
314
315 area_offset += 1; /* type/length byte */
316 area_offset += number_of_data_bytes;
317 custom_fields_index++;
318 }
319
320 out:
321 rv = 0;
322 cleanup:
323 return (rv);
324 }
325
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)326 int ipmi_fru_board_info_area(
327 const void* areabuf, unsigned int areabuflen, uint8_t* language_code,
328 uint32_t* mfg_date_time, ipmi_fru_field_t* board_manufacturer,
329 ipmi_fru_field_t* board_product_name, ipmi_fru_field_t* board_serial_number,
330 ipmi_fru_field_t* board_part_number, ipmi_fru_field_t* board_fru_file_id,
331 ipmi_fru_field_t* board_custom_fields, unsigned int board_custom_fields_len)
332 {
333 const uint8_t* areabufptr = (const uint8_t*)areabuf;
334 unsigned int area_offset = 0;
335 unsigned int custom_fields_index = 0;
336 uint8_t number_of_data_bytes;
337 int rv = -1;
338
339 if (!areabuf || !areabuflen)
340 {
341 return (-1);
342 }
343
344 if (board_manufacturer)
345 memset(board_manufacturer, '\0', sizeof(ipmi_fru_field_t));
346 if (board_product_name)
347 memset(board_product_name, '\0', sizeof(ipmi_fru_field_t));
348 if (board_serial_number)
349 memset(board_serial_number, '\0', sizeof(ipmi_fru_field_t));
350 if (board_part_number)
351 memset(board_part_number, '\0', sizeof(ipmi_fru_field_t));
352 if (board_fru_file_id)
353 memset(board_fru_file_id, '\0', sizeof(ipmi_fru_field_t));
354 if (board_custom_fields && board_custom_fields_len)
355 memset(board_custom_fields, '\0',
356 sizeof(ipmi_fru_field_t) * board_custom_fields_len);
357
358 if (language_code)
359 (*language_code) = areabufptr[area_offset];
360 area_offset++;
361
362 if (mfg_date_time)
363 {
364 unsigned int minutes = areabufptr[area_offset];
365 area_offset++;
366 minutes |= (areabufptr[area_offset] << 8);
367 area_offset++;
368 minutes |= (areabufptr[area_offset] << 16);
369 area_offset++;
370
371 /* In fru, epoch is 0:00 hrs 1/1/96 == 820454400
372 * Remove it directly and remove the time conversion.
373 */
374 (*mfg_date_time) = fruEpochMinutes + static_cast<long>(minutes) * 60;
375 }
376 else
377 area_offset += 3;
378
379 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
380 goto out;
381
382 if (_parse_type_length(areabufptr, areabuflen, area_offset,
383 &number_of_data_bytes, board_manufacturer) < 0)
384 goto cleanup;
385 area_offset += 1; /* type/length byte */
386 area_offset += number_of_data_bytes;
387
388 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
389 goto out;
390
391 if (_parse_type_length(areabufptr, areabuflen, area_offset,
392 &number_of_data_bytes, board_product_name) < 0)
393 goto cleanup;
394 area_offset += 1; /* type/length byte */
395 area_offset += number_of_data_bytes;
396
397 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
398 goto out;
399
400 if (_parse_type_length(areabufptr, areabuflen, area_offset,
401 &number_of_data_bytes, board_serial_number) < 0)
402 goto cleanup;
403 area_offset += 1; /* type/length byte */
404 area_offset += number_of_data_bytes;
405
406 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
407 goto out;
408
409 if (_parse_type_length(areabufptr, areabuflen, area_offset,
410 &number_of_data_bytes, board_part_number) < 0)
411 goto cleanup;
412 area_offset += 1; /* type/length byte */
413 area_offset += number_of_data_bytes;
414
415 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
416 goto out;
417
418 if (_parse_type_length(areabufptr, areabuflen, area_offset,
419 &number_of_data_bytes, board_fru_file_id) < 0)
420 goto cleanup;
421 area_offset += 1; /* type/length byte */
422 area_offset += number_of_data_bytes;
423
424 while (area_offset < areabuflen &&
425 areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE)
426 {
427 ipmi_fru_field_t* field_ptr = NULL;
428
429 if (board_custom_fields && board_custom_fields_len)
430 {
431 if (custom_fields_index < board_custom_fields_len)
432 field_ptr = &board_custom_fields[custom_fields_index];
433 else
434 {
435 goto cleanup;
436 }
437 }
438
439 if (_parse_type_length(areabufptr, areabuflen, area_offset,
440 &number_of_data_bytes, field_ptr) < 0)
441 goto cleanup;
442
443 area_offset += 1; /* type/length byte */
444 area_offset += number_of_data_bytes;
445 custom_fields_index++;
446 }
447
448 out:
449 rv = 0;
450 cleanup:
451 return (rv);
452 }
453
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)454 int ipmi_fru_product_info_area(
455 const void* areabuf, unsigned int areabuflen, uint8_t* language_code,
456 ipmi_fru_field_t* product_manufacturer_name, ipmi_fru_field_t* product_name,
457 ipmi_fru_field_t* product_part_model_number,
458 ipmi_fru_field_t* product_version, ipmi_fru_field_t* product_serial_number,
459 ipmi_fru_field_t* product_asset_tag, ipmi_fru_field_t* product_fru_file_id,
460 ipmi_fru_field_t* product_custom_fields,
461 unsigned int product_custom_fields_len)
462 {
463 const uint8_t* areabufptr = (const uint8_t*)areabuf;
464 unsigned int area_offset = 0;
465 unsigned int custom_fields_index = 0;
466 uint8_t number_of_data_bytes;
467 int rv = -1;
468
469 if (!areabuf || !areabuflen)
470 {
471 return (-1);
472 }
473
474 if (product_manufacturer_name)
475 memset(product_manufacturer_name, '\0', sizeof(ipmi_fru_field_t));
476 if (product_name)
477 memset(product_name, '\0', sizeof(ipmi_fru_field_t));
478 if (product_part_model_number)
479 memset(product_part_model_number, '\0', sizeof(ipmi_fru_field_t));
480 if (product_version)
481 memset(product_version, '\0', sizeof(ipmi_fru_field_t));
482 if (product_serial_number)
483 memset(product_serial_number, '\0', sizeof(ipmi_fru_field_t));
484 if (product_asset_tag)
485 memset(product_asset_tag, '\0', sizeof(ipmi_fru_field_t));
486 if (product_fru_file_id)
487 memset(product_fru_file_id, '\0', sizeof(ipmi_fru_field_t));
488 if (product_custom_fields && product_custom_fields_len)
489 memset(product_custom_fields, '\0',
490 sizeof(ipmi_fru_field_t) * product_custom_fields_len);
491
492 if (language_code)
493 (*language_code) = areabufptr[area_offset];
494 area_offset++;
495
496 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
497 goto out;
498
499 if (_parse_type_length(areabufptr, areabuflen, area_offset,
500 &number_of_data_bytes, product_manufacturer_name) <
501 0)
502 goto cleanup;
503 area_offset += 1; /* type/length byte */
504 area_offset += number_of_data_bytes;
505
506 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
507 goto out;
508
509 if (_parse_type_length(areabufptr, areabuflen, area_offset,
510 &number_of_data_bytes, product_name) < 0)
511 goto cleanup;
512 area_offset += 1; /* type/length byte */
513 area_offset += number_of_data_bytes;
514
515 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
516 goto out;
517
518 if (_parse_type_length(areabufptr, areabuflen, area_offset,
519 &number_of_data_bytes, product_part_model_number) <
520 0)
521 goto cleanup;
522 area_offset += 1; /* type/length byte */
523 area_offset += number_of_data_bytes;
524
525 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
526 goto out;
527
528 if (_parse_type_length(areabufptr, areabuflen, area_offset,
529 &number_of_data_bytes, product_version) < 0)
530 goto cleanup;
531 area_offset += 1; /* type/length byte */
532 area_offset += number_of_data_bytes;
533
534 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
535 goto out;
536
537 if (_parse_type_length(areabufptr, areabuflen, area_offset,
538 &number_of_data_bytes, product_serial_number) < 0)
539 goto cleanup;
540 area_offset += 1; /* type/length byte */
541 area_offset += number_of_data_bytes;
542
543 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
544 goto out;
545
546 if (_parse_type_length(areabufptr, areabuflen, area_offset,
547 &number_of_data_bytes, product_asset_tag) < 0)
548 goto cleanup;
549 area_offset += 1; /* type/length byte */
550 area_offset += number_of_data_bytes;
551
552 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
553 goto out;
554
555 if (_parse_type_length(areabufptr, areabuflen, area_offset,
556 &number_of_data_bytes, product_fru_file_id) < 0)
557 goto cleanup;
558
559 area_offset += 1; /* type/length byte */
560 area_offset += number_of_data_bytes;
561
562 while (area_offset < areabuflen &&
563 areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE)
564 {
565 ipmi_fru_field_t* field_ptr = NULL;
566
567 if (product_custom_fields && product_custom_fields_len)
568 {
569 if (custom_fields_index < product_custom_fields_len)
570 field_ptr = &product_custom_fields[custom_fields_index];
571 else
572 {
573 goto cleanup;
574 }
575 }
576
577 if (_parse_type_length(areabufptr, areabuflen, area_offset,
578 &number_of_data_bytes, field_ptr) < 0)
579 goto cleanup;
580
581 area_offset += 1; /* type/length byte */
582 area_offset += number_of_data_bytes;
583 custom_fields_index++;
584 }
585
586 out:
587 rv = 0;
588 cleanup:
589 return (rv);
590 }
591
_append_to_dict(uint8_t vpd_key_id,uint8_t * vpd_key_val,IPMIFruInfo & info)592 void _append_to_dict(uint8_t vpd_key_id, uint8_t* vpd_key_val,
593 IPMIFruInfo& info)
594 {
595 int type_length = vpd_key_val[0];
596 int type_code = (type_length & IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK) >>
597 IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT;
598 int vpd_val_len = type_length &
599 IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK;
600
601 /* Needed to convert each uint8_t byte to a ascii */
602 char bin_byte[3] = {0};
603
604 /*
605 * Max number of characters needed to represent 1 unsigned byte in string
606 * is number of bytes multiplied by 2. Extra 3 for 0x and a ending '\0';
607 */
608 char bin_in_ascii_len = vpd_val_len * 2 + 3;
609
610 /* Binary converted to ascii in array */
611 char* bin_in_ascii = (char*)malloc(bin_in_ascii_len);
612
613 /* For reading byte from the area */
614 int val = 0;
615
616 char* bin_copy = &((char*)bin_in_ascii)[2];
617
618 switch (type_code)
619 {
620 case 0:
621 memset(bin_in_ascii, 0x0, bin_in_ascii_len);
622
623 /* Offset 1 is where actual data starts */
624 for (val = 1; val <= vpd_val_len; val++)
625 {
626 /* 2 bytes for data and 1 for terminating '\0' */
627 snprintf(bin_byte, 3, "%02x", vpd_key_val[val]);
628
629 #pragma GCC diagnostic push
630 #pragma GCC diagnostic ignored "-Wstringop-truncation"
631 /* Its a running string so strip off the '\0' */
632 strncat(bin_copy, bin_byte, 2);
633 #pragma GCC diagnostic pop
634 }
635
636 /* We need the data represented as 0x...... */
637 if (vpd_val_len > 0)
638 {
639 memcpy(bin_in_ascii, "0x", 2);
640 }
641 #if IPMI_FRU_PARSER_DEBUG
642 printf("_append_to_dict: VPD Key = [%s] : Type Code = [BINARY] :"
643 " Len = [%d] : Val = [%s]\n",
644 vpd_key_names[vpd_key_id], vpd_val_len, bin_in_ascii);
645 #endif
646 info[vpd_key_id] =
647 std::make_pair(vpd_key_names[vpd_key_id], bin_in_ascii);
648 break;
649
650 case 3:
651 #if IPMI_FRU_PARSER_DEBUG
652 printf("_append_to_dict: VPD Key = [%s] : Type Code=[ASCII+Latin]"
653 " : Len = [%d] : Val = [%s]\n",
654 vpd_key_names[vpd_key_id], vpd_val_len, &vpd_key_val[1]);
655 #endif
656 info[vpd_key_id] = std::make_pair(
657 vpd_key_names[vpd_key_id],
658 std::string(vpd_key_val + 1, vpd_key_val + 1 + type_length));
659 break;
660 }
661
662 if (bin_in_ascii)
663 {
664 free(bin_in_ascii);
665 bin_in_ascii = NULL;
666 }
667 }
668
parse_fru_area(const uint8_t area,const void * msgbuf,const size_t len,IPMIFruInfo & info)669 int parse_fru_area(const uint8_t area, const void* msgbuf, const size_t len,
670 IPMIFruInfo& info)
671 {
672 int rv = -1;
673 int i = 0;
674
675 /* Chassis */
676 uint8_t chassis_type;
677 /* Board */
678 uint32_t mfg_date_time;
679 /* Product */
680 // unsigned int product_custom_fields_len;
681
682 // ipmi_fru_area_info_t fru_area_info [ IPMI_FRU_AREA_TYPE_MAX ];
683 ipmi_fru_field_t vpd_info[OPENBMC_VPD_KEY_MAX];
684 char timestr[OPENBMC_VPD_VAL_LEN];
685
686 // uint8_t* ipmi_fru_field_str=NULL;
687 // ipmi_fru_common_hdr_t* chdr = NULL;
688 // uint8_t* hdr = NULL;
689
690 ASSERT(msgbuf);
691
692 for (i = 0; i < OPENBMC_VPD_KEY_MAX; i++)
693 {
694 memset(vpd_info[i].type_length_field, '\0',
695 IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX);
696 vpd_info[i].type_length_field_length = 0;
697 }
698
699 switch (area)
700 {
701 case IPMI_FRU_AREA_CHASSIS_INFO:
702 #if IPMI_FRU_PARSER_DEBUG
703 printf("Chassis : Buf len = [%d]\n", len);
704 #endif
705 ipmi_fru_chassis_info_area(
706 (uint8_t*)msgbuf + 2, len, &chassis_type,
707 &vpd_info[OPENBMC_VPD_KEY_CHASSIS_PART_NUM],
708 &vpd_info[OPENBMC_VPD_KEY_CHASSIS_SERIAL_NUM],
709 &vpd_info[OPENBMC_VPD_KEY_CHASSIS_CUSTOM1],
710 OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX);
711
712 /* Populate VPD Table */
713 for (i = 1; i <= OPENBMC_VPD_KEY_CHASSIS_MAX; i++)
714 {
715 if (i == OPENBMC_VPD_KEY_CHASSIS_TYPE)
716 {
717 #if IPMI_FRU_PARSER_DEBUG
718 printf("Chassis : Appending [%s] = [%d]\n",
719 vpd_key_names[i], chassis_type);
720 #endif
721 info[i] = std::make_pair(vpd_key_names[i],
722 std::to_string(chassis_type));
723 continue;
724 }
725 _append_to_dict(i, vpd_info[i].type_length_field, info);
726 }
727 break;
728 case IPMI_FRU_AREA_BOARD_INFO:
729 #if IPMI_FRU_PARSER_DEBUG
730 printf("Board : Buf len = [%d]\n", len);
731 #endif
732 ipmi_fru_board_info_area(
733 (uint8_t*)msgbuf + 2, len, NULL, &mfg_date_time,
734 &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 #if IPMI_FRU_PARSER_DEBUG
750 printf("Board : Appending [%s] = [%s]\n", vpd_key_names[i],
751 timestr);
752 #endif
753 info[i] =
754 std::make_pair(vpd_key_names[i], std::string(timestr));
755 continue;
756 }
757 _append_to_dict(i, vpd_info[i].type_length_field, info);
758 }
759 break;
760 case IPMI_FRU_AREA_PRODUCT_INFO:
761 #if IPMI_FRU_PARSER_DEBUG
762 printf("Product : Buf len = [%d]\n", len);
763 #endif
764 ipmi_fru_product_info_area(
765 (uint8_t*)msgbuf + 2, len, NULL,
766 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_MFR],
767 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_NAME],
768 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_PART_MODEL_NUM],
769 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_VER],
770 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_SERIAL_NUM],
771 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_ASSET_TAG],
772 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_FRU_FILE_ID],
773 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_CUSTOM1],
774 OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX);
775
776 for (i = OPENBMC_VPD_KEY_PRODUCT_MFR;
777 i <= OPENBMC_VPD_KEY_PRODUCT_MAX; ++i)
778 {
779 _append_to_dict(i, vpd_info[i].type_length_field, info);
780 }
781 break;
782 default:
783 /* TODO: Parse Multi Rec / Internal use area */
784 break;
785 }
786
787 #if IPMI_FRU_PARSER_DEBUG
788 printf("parse_fru_area : Dictionary Packing Complete\n");
789 #endif
790 rv = 0;
791 return (rv);
792 }
793