blob: 8009b3e36253362492f1bdf996c659bb235f0f95 [file] [log] [blame]
Hariharasubramanian R44473092015-10-15 08:25:28 -05001/*
2 * Copyright (C) 2003-2014 FreeIPMI Core Team
Hariharasubramanian Ra032c772015-10-20 07:28:19 -05003 *
Hariharasubramanian R44473092015-10-15 08:25:28 -05004 * 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/>.
Hariharasubramanian Ra032c772015-10-20 07:28:19 -050016 *
Hariharasubramanian R44473092015-10-15 08:25:28 -050017 */
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>
Matthew Barth619db932016-08-29 14:19:38 -050043#include <stdlib.h>
Hariharasubramanian R44473092015-10-15 08:25:28 -050044#include <unistd.h>
45#include <string.h>
46#include <time.h>
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -050047#include <systemd/sd-bus.h>
Hariharasubramanian Ra032c772015-10-20 07:28:19 -050048#include <ctype.h>
Ratan Gupta19c617b2017-02-10 15:39:40 +053049#include "frup.hpp"
Hariharasubramanian R44473092015-10-15 08:25:28 -050050
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -050051#define TEXTSTR(a) #a
52# define ASSERT(x) \
53do { \
54if (0 == (x)) { \
55fprintf(stderr, \
56"Assertion failed: %s, " \
57"%d at \'%s\'\n", \
58__FILE__, \
59__LINE__, \
60TEXTSTR(a)); \
61return -1; \
62} \
63} while (0)
Hariharasubramanian R44473092015-10-15 08:25:28 -050064
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
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -050072/* 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
Hariharasubramanian R44473092015-10-15 08:25:28 -050082
83struct 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
90typedef struct ipmi_fru_field ipmi_fru_field_t;
Hariharasubramanian Ra032c772015-10-20 07:28:19 -050091/*
92 * FRU Parser
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -050093 */
Hariharasubramanian R44473092015-10-15 08:25:28 -050094
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -050095typedef struct ipmi_fru_area_info
96{
97 uint8_t off;
98 uint8_t len;
99} ipmi_fru_area_info_t;
100
101typedef 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
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500111const char* vpd_key_names [] =
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500112{
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500113 "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,*/
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500125
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500126 "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,*/
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500140
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500141 "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,*/
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500156
157 "Key Names Table End" /*OPENBMC_VPD_KEY_MAX,*/
158};
159
160
161/*
162 * --------------------------------------------------------------------
163 *
164 * --------------------------------------------------------------------
165 */
Hariharasubramanian R4a0b6fb2015-10-31 22:45:48 -0500166
167static 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
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500185/* private method to parse type/length */
Hariharasubramanian R44473092015-10-15 08:25:28 -0500186static 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);
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500200
Hariharasubramanian R44473092015-10-15 08:25:28 -0500201 type_length = areabufptr[current_area_offset];
202
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500203 /* ipmi workaround
Hariharasubramanian R44473092015-10-15 08:25:28 -0500204 *
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500205 * dell p weredge r610
Hariharasubramanian R44473092015-10-15 08:25:28 -0500206 *
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500207 * my reading of the fru spec is that all non-custom fields are
208 * required to be listed by the vendor. however, on this
Hariharasubramanian R44473092015-10-15 08:25:28 -0500209 * motherboard, some areas list this, indicating that there is
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500210 * no more data to be parsed. so now, for "required" fields, i
Hariharasubramanian R44473092015-10-15 08:25:28 -0500211 * 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
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500220 /* special case: this shouldn't be a length of 0x01 (see type/length
221 * byte format in fru information storage definition).
Hariharasubramanian R44473092015-10-15 08:25:28 -0500222 */
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 }
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500244
Hariharasubramanian R44473092015-10-15 08:25:28 -0500245 return (0);
246}
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500247
Hariharasubramanian R44473092015-10-15 08:25:28 -0500248int
249ipmi_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
343int
344ipmi_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++;
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500409
Hariharasubramanian R44473092015-10-15 08:25:28 -0500410 /* mfg_date_time is in minutes, so multiple by 60 to get seconds */
411 mfg_date_time_tmp *= 60;
412
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500413 /* posix says individual calls need not clear/set all portions of
Hariharasubramanian R44473092015-10-15 08:25:28 -0500414 * 'struct tm', thus passing 'struct tm' between functions could
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500415 * have issues. so we need to memset.
Hariharasubramanian R44473092015-10-15 08:25:28 -0500416 */
417 memset (&tm, '\0', sizeof(struct tm));
418
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500419 /* in fru, epoch is 0:00 hrs 1/1/96
Hariharasubramanian R44473092015-10-15 08:25:28 -0500420 *
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500421 * so convert into ansi epoch
Hariharasubramanian R44473092015-10-15 08:25:28 -0500422 */
423
424 tm.tm_year = 96; /* years since 1900 */
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500425 tm.tm_mon = 0; /* months since january */
Hariharasubramanian R44473092015-10-15 08:25:28 -0500426 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
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500482 if (_parse_type_length (areabufptr,
Hariharasubramanian R44473092015-10-15 08:25:28 -0500483 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
536int
537ipmi_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;
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500679
Hariharasubramanian R44473092015-10-15 08:25:28 -0500680 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}
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500716
Deepak Kodihalli8a2a6752017-02-11 03:04:38 -0600717void _append_to_dict (uint8_t vpd_key_id,
718 uint8_t* vpd_key_val,
719 IPMIFruInfo& info)
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500720{
721 int type_length = vpd_key_val[0];
Deepak Kodihalli8a2a6752017-02-11 03:04:38 -0600722 int type_code = (type_length & IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK) >>
723 IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT;
724 int vpd_val_len =
725 type_length & IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK;
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500726 int sdr=0;
727
vishwa24afbb92016-06-10 06:08:56 -0500728 /* Needed to convert each uint8_t byte to a ascii */
729 char bin_byte[3] = {0};
730
731 /*
732 * Max number of characters needed to represent 1 unsigned byte in string
Gunnar Millsc19b8132018-06-14 08:56:17 -0500733 * is number of bytes multiplied by 2. Extra 3 for 0x and a ending '\0';
vishwa24afbb92016-06-10 06:08:56 -0500734 */
735 char bin_in_ascii_len = vpd_val_len * 2 + 3;
736
737 /* Binary converted to ascii in array */
738 char *bin_in_ascii = (char *)malloc(bin_in_ascii_len);
739
740 /* For reading byte from the area */
Ratan Guptacb0d4e52016-12-22 19:05:57 +0530741 int val = 0;
vishwa24afbb92016-06-10 06:08:56 -0500742
743 char *bin_copy = &((char *)bin_in_ascii)[2];
744
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500745 switch (type_code)
746 {
747 case 0:
vishwa24afbb92016-06-10 06:08:56 -0500748 memset(bin_in_ascii, 0x0, bin_in_ascii_len);
749
750 /* Offset 1 is where actual data starts */
751 for(val = 1; val <= vpd_val_len ; val++)
752 {
753 /* 2 bytes for data and 1 for terminating '\0' */
754 snprintf(bin_byte, 3, "%02x", vpd_key_val[val]);
755
756 /* Its a running string so strip off the '\0' */
757 strncat(bin_copy, bin_byte, 2);
758 }
759
760 /* We need the data represented as 0x...... */
761 if(vpd_val_len > 0)
762 {
763 strncpy(bin_in_ascii, "0x", 2);
764 }
Andrew Geissler4e411662017-06-01 07:26:39 -0500765#if IPMI_FRU_PARSER_DEBUG
Deepak Kodihalli8a2a6752017-02-11 03:04:38 -0600766 printf ("_append_to_dict: VPD Key = [%s] : Type Code = [BINARY] :"
767 " Len = [%d] : Val = [%s]\n",
vishwa24afbb92016-06-10 06:08:56 -0500768 vpd_key_names [vpd_key_id], vpd_val_len, bin_in_ascii);
Andrew Geissler4e411662017-06-01 07:26:39 -0500769#endif
Deepak Kodihalli8a2a6752017-02-11 03:04:38 -0600770 info[vpd_key_id] = std::make_pair(vpd_key_names[vpd_key_id],
771 bin_in_ascii);
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500772 break;
vishwa24afbb92016-06-10 06:08:56 -0500773
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500774 case 3:
Andrew Geissler4e411662017-06-01 07:26:39 -0500775#if IPMI_FRU_PARSER_DEBUG
Deepak Kodihalli8a2a6752017-02-11 03:04:38 -0600776 printf ("_append_to_dict: VPD Key = [%s] : Type Code=[ASCII+Latin]"
777 " : Len = [%d] : Val = [%s]\n",
778 vpd_key_names [vpd_key_id], vpd_val_len, &vpd_key_val[1]);
Andrew Geissler4e411662017-06-01 07:26:39 -0500779#endif
Deepak Kodihalli8a2a6752017-02-11 03:04:38 -0600780 info[vpd_key_id] = std::make_pair(
781 vpd_key_names[vpd_key_id],
782 std::string(vpd_key_val + 1,
783 vpd_key_val + 1 + type_length));
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500784 break;
785 }
786
vishwa24afbb92016-06-10 06:08:56 -0500787 if(bin_in_ascii)
788 {
789 free(bin_in_ascii);
790 bin_in_ascii = NULL;
791 }
792
793
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500794 if (sdr < 0)
795 {
796#if IPMI_FRU_PARSER_DEBUG
797 printf ("_append_to_dict : sd_bus_message_append Failed [ %d ] for [%s]\n", sdr, vpd_key_names[vpd_key_id]);
798#endif
799 }
800}
801
Ratan Guptacb0d4e52016-12-22 19:05:57 +0530802int parse_fru_area (const uint8_t area, const void* msgbuf,
803 const size_t len, IPMIFruInfo& info)
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500804{
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500805 int rv = -1;
806 int i = 0;
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500807
808 /* Chassis */
809 uint8_t chassis_type;
810 /* Board */
811 uint32_t mfg_date_time;
812 /* Product */
Matthew Barth619db932016-08-29 14:19:38 -0500813 //unsigned int product_custom_fields_len;
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500814
Matthew Barth619db932016-08-29 14:19:38 -0500815 //ipmi_fru_area_info_t fru_area_info [ IPMI_FRU_AREA_TYPE_MAX ];
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500816 ipmi_fru_field_t vpd_info [ OPENBMC_VPD_KEY_MAX ];
Hariharasubramanian R4a0b6fb2015-10-31 22:45:48 -0500817 char timestr [ OPENBMC_VPD_VAL_LEN ];
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500818
Matthew Barth619db932016-08-29 14:19:38 -0500819 //uint8_t* ipmi_fru_field_str=NULL;
820 //ipmi_fru_common_hdr_t* chdr = NULL;
821 //uint8_t* hdr = NULL;
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500822
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500823 ASSERT (msgbuf);
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500824
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500825 for (i=0; i<OPENBMC_VPD_KEY_MAX; i++)
826 {
Deepak Kodihalli8a2a6752017-02-11 03:04:38 -0600827 memset (vpd_info[i].type_length_field, '\0',
828 IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX);
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500829 vpd_info[i].type_length_field_length = 0;
830 }
831
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500832 switch (area)
833 {
834 case IPMI_FRU_AREA_CHASSIS_INFO:
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500835#if IPMI_FRU_PARSER_DEBUG
836 printf ("Chassis : Buf len = [%d]\n", len);
837#endif
838 ipmi_fru_chassis_info_area ((uint8_t*)msgbuf+2,
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500839 len,
840 &chassis_type,
841 &vpd_info [OPENBMC_VPD_KEY_CHASSIS_PART_NUM],
842 &vpd_info [OPENBMC_VPD_KEY_CHASSIS_SERIAL_NUM],
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500843 &vpd_info [OPENBMC_VPD_KEY_CHASSIS_CUSTOM1],
844 OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX);
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500845
846 /* Populate VPD Table */
847 for (i=1; i<=OPENBMC_VPD_KEY_CHASSIS_MAX; i++)
848 {
849 if (i==OPENBMC_VPD_KEY_CHASSIS_TYPE)
850 {
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500851#if IPMI_FRU_PARSER_DEBUG
Deepak Kodihalli8a2a6752017-02-11 03:04:38 -0600852 printf ("Chassis : Appending [%s] = [%d]\n",
853 vpd_key_names[i], chassis_type);
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500854#endif
Ratan Guptacb0d4e52016-12-22 19:05:57 +0530855 info[i] = std::make_pair(vpd_key_names[i],
856 std::to_string(chassis_type));
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500857 continue;
858 }
Ratan Gupta10b7ea72017-02-16 18:02:23 +0530859 _append_to_dict (i, vpd_info[i].type_length_field, info);
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500860 }
861 break;
862 case IPMI_FRU_AREA_BOARD_INFO:
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500863#if IPMI_FRU_PARSER_DEBUG
864 printf ("Board : Buf len = [%d]\n", len);
865#endif
866 ipmi_fru_board_info_area ((uint8_t*)msgbuf+2,
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500867 len,
868 NULL,
869 &mfg_date_time,
870 &vpd_info [OPENBMC_VPD_KEY_BOARD_MFR],
871 &vpd_info [OPENBMC_VPD_KEY_BOARD_NAME],
872 &vpd_info [OPENBMC_VPD_KEY_BOARD_SERIAL_NUM],
873 &vpd_info [OPENBMC_VPD_KEY_BOARD_PART_NUM],
874 &vpd_info [OPENBMC_VPD_KEY_BOARD_FRU_FILE_ID],
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500875 &vpd_info [OPENBMC_VPD_KEY_BOARD_CUSTOM1],
876 OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX);
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500877
878 /* Populate VPD Table */
Tom Joseph2796bf92017-08-22 18:16:33 +0530879 for (i=OPENBMC_VPD_KEY_BOARD_MFG_DATE;
880 i<=OPENBMC_VPD_KEY_BOARD_MAX; i++)
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500881 {
882 if (i==OPENBMC_VPD_KEY_BOARD_MFG_DATE)
883 {
Hariharasubramanian R4a0b6fb2015-10-31 22:45:48 -0500884 _to_time_str (mfg_date_time, timestr, OPENBMC_VPD_VAL_LEN);
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500885#if IPMI_FRU_PARSER_DEBUG
Tom Joseph2796bf92017-08-22 18:16:33 +0530886 printf ("Board : Appending [%s] = [%s]\n",
Deepak Kodihalli8a2a6752017-02-11 03:04:38 -0600887 vpd_key_names[i], timestr);
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500888#endif
Ratan Guptacb0d4e52016-12-22 19:05:57 +0530889 info[i] = std::make_pair(vpd_key_names[i],
890 std::string(timestr));
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500891 continue;
892 }
Deepak Kodihalli8a2a6752017-02-11 03:04:38 -0600893 _append_to_dict (i, vpd_info[i].type_length_field, info);
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500894
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500895 }
896 break;
897 case IPMI_FRU_AREA_PRODUCT_INFO:
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500898#if IPMI_FRU_PARSER_DEBUG
899 printf ("Product : Buf len = [%d]\n", len);
900#endif
901 ipmi_fru_product_info_area ((uint8_t*)msgbuf+2,
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500902 len,
903 NULL,
904 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_MFR],
905 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_NAME],
906 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_PART_MODEL_NUM],
907 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_VER],
908 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_SERIAL_NUM],
909 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_ASSET_TAG],
910 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_FRU_FILE_ID],
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500911 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_CUSTOM1],
912 OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX);
913
Deepak Kodihalli8a2a6752017-02-11 03:04:38 -0600914 for (i=OPENBMC_VPD_KEY_PRODUCT_MFR;
915 i<=OPENBMC_VPD_KEY_PRODUCT_MAX;
916 ++i)
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500917 {
Deepak Kodihalli8a2a6752017-02-11 03:04:38 -0600918 _append_to_dict (i, vpd_info[i].type_length_field, info);
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500919 }
920 break;
Matthew Barth619db932016-08-29 14:19:38 -0500921 default:
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500922 /* TODO: Parse Multi Rec / Internal use area */
923 break;
924 }
925
Hariharasubramanian Ra032c772015-10-20 07:28:19 -0500926#if IPMI_FRU_PARSER_DEBUG
927 printf ("parse_fru_area : Dictionary Packing Complete\n");
928#endif
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500929 rv = 0;
Hariharasubramanian Rc2d79462015-10-16 06:47:56 -0500930 return (rv);
931}