Coverage Report

Created: 2026-10-16 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ipmi-fru-parser/frup.cpp
Line
Count
Source
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
0
#define TEXTSTR(a) #a
55
#define ASSERT(x)                                                              \
56
56.5k
    do                                                                         \
57
56.5k
    {                                                                          \
58
56.5k
        if (0 == (x))                                                          \
59
56.5k
        {                                                                      \
60
0
            fprintf(stderr,                                                    \
61
0
                    "Assertion failed: %s, "                                   \
62
0
                    "%d at \'%s\'\n",                                          \
63
0
                    __FILE__, __LINE__, TEXTSTR(a));                           \
64
0
            return -1;                                                         \
65
0
        }                                                                      \
66
56.5k
    } while (0)
67
68
142k
#define IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX 512
69
13.7k
#define IPMI_FRU_SENTINEL_VALUE 0xC1
70
37.1k
#define IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK 0xC0
71
37.1k
#define IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT 0x06
72
37.1k
#define IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK 0x3F
73
26.0k
#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
701
#define IPMI_FRU_AREA_CHASSIS_INFO 0x01
78
611
#define IPMI_FRU_AREA_BOARD_INFO 0x02
79
612
#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
611
#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
171
static size_t _to_time_str(uint32_t mfg_date_time, char* timestr, uint32_t len)
172
611
{
173
611
    struct tm tm;
174
611
    time_t t;
175
611
    size_t s;
176
177
611
    ASSERT(timestr);
178
611
    ASSERT(len);
179
180
611
    memset(&tm, '\0', sizeof(struct tm));
181
182
611
    t = mfg_date_time;
183
611
    gmtime_r(&t, &tm);
184
611
    s = strftime(timestr, len, "%F - %H:%M:%S UTC", &tm);
185
186
611
    return s;
187
611
}
188
189
/* private method to parse type/length */
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
13.0k
{
195
13.0k
    const uint8_t* areabufptr = static_cast<const uint8_t*>(areabuf);
196
13.0k
    uint8_t type_length;
197
13.0k
    uint8_t type_code;
198
199
13.0k
    ASSERT(areabuf);
200
13.0k
    ASSERT(areabuflen);
201
13.0k
    ASSERT(number_of_data_bytes);
202
203
13.0k
    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
13.0k
    ASSERT(type_length != IPMI_FRU_SENTINEL_VALUE);
218
219
13.0k
    type_code = (type_length & IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK) >>
220
13.0k
                IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT;
221
13.0k
    (*number_of_data_bytes) =
222
13.0k
        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
13.0k
    if (type_code == IPMI_FRU_TYPE_LENGTH_TYPE_CODE_LANGUAGE_CODE &&
228
3.37k
        (*number_of_data_bytes) == 0x01)
229
0
    {
230
0
        return (-1);
231
0
    }
232
233
13.0k
    if ((current_area_offset + 1 + (*number_of_data_bytes)) > areabuflen)
234
1.23k
    {
235
1.23k
        return (-1);
236
1.23k
    }
237
238
11.8k
    if (field)
239
11.8k
    {
240
11.8k
        memset(field->type_length_field, '\0',
241
11.8k
               IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX);
242
11.8k
        memcpy(field->type_length_field, &areabufptr[current_area_offset],
243
11.8k
               1 + (*number_of_data_bytes));
244
11.8k
        field->type_length_field_length = 1 + (*number_of_data_bytes);
245
11.8k
    }
246
247
11.8k
    return (0);
248
13.0k
}
249
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
701
{
257
701
    const uint8_t* areabufptr = static_cast<const uint8_t*>(areabuf);
258
701
    unsigned int area_offset = 0;
259
701
    unsigned int custom_fields_index = 0;
260
701
    uint8_t number_of_data_bytes;
261
701
    int rv = -1;
262
263
701
    if (!areabuf || !areabuflen)
264
0
    {
265
0
        return (-1);
266
0
    }
267
268
701
    if (chassis_part_number)
269
701
        memset(chassis_part_number, '\0', sizeof(ipmi_fru_field_t));
270
701
    if (chassis_serial_number)
271
701
        memset(chassis_serial_number, '\0', sizeof(ipmi_fru_field_t));
272
701
    if (chassis_custom_fields && chassis_custom_fields_len)
273
701
        memset(chassis_custom_fields, '\0',
274
701
               sizeof(ipmi_fru_field_t) * chassis_custom_fields_len);
275
276
701
    if (chassis_type)
277
701
        (*chassis_type) = areabufptr[area_offset];
278
701
    area_offset++;
279
280
701
    if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
281
2
        goto out;
282
283
699
    if (_parse_type_length(areabufptr, areabuflen, area_offset,
284
699
                           &number_of_data_bytes, chassis_part_number) < 0)
285
40
        goto cleanup;
286
659
    area_offset += 1; /* type/length byte */
287
659
    area_offset += number_of_data_bytes;
288
289
659
    if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
290
2
        goto out;
291
292
657
    if (_parse_type_length(areabufptr, areabuflen, area_offset,
293
657
                           &number_of_data_bytes, chassis_serial_number) < 0)
294
197
        goto cleanup;
295
460
    area_offset += 1; /* type/length byte */
296
460
    area_offset += number_of_data_bytes;
297
298
2.88k
    while (area_offset < areabuflen &&
299
2.88k
           areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE)
300
2.87k
    {
301
2.87k
        ipmi_fru_field_t* field_ptr = nullptr;
302
303
2.87k
        if (chassis_custom_fields && chassis_custom_fields_len)
304
2.87k
        {
305
2.87k
            if (custom_fields_index < chassis_custom_fields_len)
306
2.63k
                field_ptr = &chassis_custom_fields[custom_fields_index];
307
234
            else
308
234
            {
309
234
                goto cleanup;
310
234
            }
311
2.87k
        }
312
313
2.63k
        if (_parse_type_length(areabufptr, areabuflen, area_offset,
314
2.63k
                               &number_of_data_bytes, field_ptr) < 0)
315
215
            goto cleanup;
316
317
2.42k
        area_offset += 1; /* type/length byte */
318
2.42k
        area_offset += number_of_data_bytes;
319
2.42k
        custom_fields_index++;
320
2.42k
    }
321
322
15
out:
323
15
    rv = 0;
324
701
cleanup:
325
701
    return (rv);
326
15
}
327
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
611
{
335
611
    const uint8_t* areabufptr = static_cast<const uint8_t*>(areabuf);
336
611
    unsigned int area_offset = 0;
337
611
    unsigned int custom_fields_index = 0;
338
611
    uint8_t number_of_data_bytes;
339
611
    int rv = -1;
340
341
611
    if (!areabuf || !areabuflen)
342
0
    {
343
0
        return (-1);
344
0
    }
345
346
611
    if (board_manufacturer)
347
611
        memset(board_manufacturer, '\0', sizeof(ipmi_fru_field_t));
348
611
    if (board_product_name)
349
611
        memset(board_product_name, '\0', sizeof(ipmi_fru_field_t));
350
611
    if (board_serial_number)
351
611
        memset(board_serial_number, '\0', sizeof(ipmi_fru_field_t));
352
611
    if (board_part_number)
353
611
        memset(board_part_number, '\0', sizeof(ipmi_fru_field_t));
354
611
    if (board_fru_file_id)
355
611
        memset(board_fru_file_id, '\0', sizeof(ipmi_fru_field_t));
356
611
    if (board_custom_fields && board_custom_fields_len)
357
611
        memset(board_custom_fields, '\0',
358
611
               sizeof(ipmi_fru_field_t) * board_custom_fields_len);
359
360
611
    if (language_code)
361
0
        (*language_code) = areabufptr[area_offset];
362
611
    area_offset++;
363
364
611
    if (mfg_date_time)
365
611
    {
366
611
        unsigned int minutes = areabufptr[area_offset];
367
611
        area_offset++;
368
611
        minutes |= (areabufptr[area_offset] << 8);
369
611
        area_offset++;
370
611
        minutes |= (areabufptr[area_offset] << 16);
371
611
        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
611
        (*mfg_date_time) = fruEpochMinutes + static_cast<long>(minutes) * 60;
377
611
    }
378
0
    else
379
0
        area_offset += 3;
380
381
611
    if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
382
2
        goto out;
383
384
609
    if (_parse_type_length(areabufptr, areabuflen, area_offset,
385
609
                           &number_of_data_bytes, board_manufacturer) < 0)
386
70
        goto cleanup;
387
539
    area_offset += 1; /* type/length byte */
388
539
    area_offset += number_of_data_bytes;
389
390
539
    if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
391
3
        goto out;
392
393
536
    if (_parse_type_length(areabufptr, areabuflen, area_offset,
394
536
                           &number_of_data_bytes, board_product_name) < 0)
395
144
        goto cleanup;
396
392
    area_offset += 1; /* type/length byte */
397
392
    area_offset += number_of_data_bytes;
398
399
392
    if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
400
1
        goto out;
401
402
391
    if (_parse_type_length(areabufptr, areabuflen, area_offset,
403
391
                           &number_of_data_bytes, board_serial_number) < 0)
404
50
        goto cleanup;
405
341
    area_offset += 1; /* type/length byte */
406
341
    area_offset += number_of_data_bytes;
407
408
341
    if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
409
1
        goto out;
410
411
340
    if (_parse_type_length(areabufptr, areabuflen, area_offset,
412
340
                           &number_of_data_bytes, board_part_number) < 0)
413
26
        goto cleanup;
414
314
    area_offset += 1; /* type/length byte */
415
314
    area_offset += number_of_data_bytes;
416
417
314
    if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
418
1
        goto out;
419
420
313
    if (_parse_type_length(areabufptr, areabuflen, area_offset,
421
313
                           &number_of_data_bytes, board_fru_file_id) < 0)
422
30
        goto cleanup;
423
283
    area_offset += 1; /* type/length byte */
424
283
    area_offset += number_of_data_bytes;
425
426
2.11k
    while (area_offset < areabuflen &&
427
2.11k
           areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE)
428
2.10k
    {
429
2.10k
        ipmi_fru_field_t* field_ptr = nullptr;
430
431
2.10k
        if (board_custom_fields && board_custom_fields_len)
432
2.10k
        {
433
2.10k
            if (custom_fields_index < board_custom_fields_len)
434
1.90k
                field_ptr = &board_custom_fields[custom_fields_index];
435
194
            else
436
194
            {
437
194
                goto cleanup;
438
194
            }
439
2.10k
        }
440
441
1.90k
        if (_parse_type_length(areabufptr, areabuflen, area_offset,
442
1.90k
                               &number_of_data_bytes, field_ptr) < 0)
443
80
            goto cleanup;
444
445
1.82k
        area_offset += 1; /* type/length byte */
446
1.82k
        area_offset += number_of_data_bytes;
447
1.82k
        custom_fields_index++;
448
1.82k
    }
449
450
17
out:
451
17
    rv = 0;
452
611
cleanup:
453
611
    return (rv);
454
17
}
455
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
612
{
465
612
    const uint8_t* areabufptr = static_cast<const uint8_t*>(areabuf);
466
612
    unsigned int area_offset = 0;
467
612
    unsigned int custom_fields_index = 0;
468
612
    uint8_t number_of_data_bytes;
469
612
    int rv = -1;
470
471
612
    if (!areabuf || !areabuflen)
472
0
    {
473
0
        return (-1);
474
0
    }
475
476
612
    if (product_manufacturer_name)
477
612
        memset(product_manufacturer_name, '\0', sizeof(ipmi_fru_field_t));
478
612
    if (product_name)
479
612
        memset(product_name, '\0', sizeof(ipmi_fru_field_t));
480
612
    if (product_part_model_number)
481
612
        memset(product_part_model_number, '\0', sizeof(ipmi_fru_field_t));
482
612
    if (product_version)
483
612
        memset(product_version, '\0', sizeof(ipmi_fru_field_t));
484
612
    if (product_serial_number)
485
612
        memset(product_serial_number, '\0', sizeof(ipmi_fru_field_t));
486
612
    if (product_asset_tag)
487
612
        memset(product_asset_tag, '\0', sizeof(ipmi_fru_field_t));
488
612
    if (product_fru_file_id)
489
612
        memset(product_fru_file_id, '\0', sizeof(ipmi_fru_field_t));
490
612
    if (product_custom_fields && product_custom_fields_len)
491
612
        memset(product_custom_fields, '\0',
492
612
               sizeof(ipmi_fru_field_t) * product_custom_fields_len);
493
494
612
    if (language_code)
495
0
        (*language_code) = areabufptr[area_offset];
496
612
    area_offset++;
497
498
612
    if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
499
2
        goto out;
500
501
610
    if (_parse_type_length(areabufptr, areabuflen, area_offset,
502
610
                           &number_of_data_bytes, product_manufacturer_name) <
503
610
        0)
504
34
        goto cleanup;
505
576
    area_offset += 1; /* type/length byte */
506
576
    area_offset += number_of_data_bytes;
507
508
576
    if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
509
2
        goto out;
510
511
574
    if (_parse_type_length(areabufptr, areabuflen, area_offset,
512
574
                           &number_of_data_bytes, product_name) < 0)
513
82
        goto cleanup;
514
492
    area_offset += 1; /* type/length byte */
515
492
    area_offset += number_of_data_bytes;
516
517
492
    if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
518
13
        goto out;
519
520
479
    if (_parse_type_length(areabufptr, areabuflen, area_offset,
521
479
                           &number_of_data_bytes, product_part_model_number) <
522
479
        0)
523
44
        goto cleanup;
524
435
    area_offset += 1; /* type/length byte */
525
435
    area_offset += number_of_data_bytes;
526
527
435
    if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
528
2
        goto out;
529
530
433
    if (_parse_type_length(areabufptr, areabuflen, area_offset,
531
433
                           &number_of_data_bytes, product_version) < 0)
532
46
        goto cleanup;
533
387
    area_offset += 1; /* type/length byte */
534
387
    area_offset += number_of_data_bytes;
535
536
387
    if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
537
2
        goto out;
538
539
385
    if (_parse_type_length(areabufptr, areabuflen, area_offset,
540
385
                           &number_of_data_bytes, product_serial_number) < 0)
541
49
        goto cleanup;
542
336
    area_offset += 1; /* type/length byte */
543
336
    area_offset += number_of_data_bytes;
544
545
336
    if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
546
1
        goto out;
547
548
335
    if (_parse_type_length(areabufptr, areabuflen, area_offset,
549
335
                           &number_of_data_bytes, product_asset_tag) < 0)
550
39
        goto cleanup;
551
296
    area_offset += 1; /* type/length byte */
552
296
    area_offset += number_of_data_bytes;
553
554
296
    if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
555
2
        goto out;
556
557
294
    if (_parse_type_length(areabufptr, areabuflen, area_offset,
558
294
                           &number_of_data_bytes, product_fru_file_id) < 0)
559
22
        goto cleanup;
560
561
272
    area_offset += 1; /* type/length byte */
562
272
    area_offset += number_of_data_bytes;
563
564
2.03k
    while (area_offset < areabuflen &&
565
2.03k
           areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE)
566
2.02k
    {
567
2.02k
        ipmi_fru_field_t* field_ptr = nullptr;
568
569
2.02k
        if (product_custom_fields && product_custom_fields_len)
570
2.02k
        {
571
2.02k
            if (custom_fields_index < product_custom_fields_len)
572
1.83k
                field_ptr = &product_custom_fields[custom_fields_index];
573
193
            else
574
193
            {
575
193
                goto cleanup;
576
193
            }
577
2.02k
        }
578
579
1.83k
        if (_parse_type_length(areabufptr, areabuflen, area_offset,
580
1.83k
                               &number_of_data_bytes, field_ptr) < 0)
581
70
            goto cleanup;
582
583
1.76k
        area_offset += 1; /* type/length byte */
584
1.76k
        area_offset += number_of_data_bytes;
585
1.76k
        custom_fields_index++;
586
1.76k
    }
587
588
33
out:
589
33
    rv = 0;
590
612
cleanup:
591
612
    return (rv);
592
33
}
593
594
void _append_to_dict(uint8_t vpd_key_id, uint8_t* vpd_key_val,
595
                     IPMIFruInfo& info)
596
24.1k
{
597
24.1k
    int type_length = vpd_key_val[0];
598
24.1k
    int type_code = (type_length & IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK) >>
599
24.1k
                    IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT;
600
24.1k
    int vpd_val_len = type_length &
601
24.1k
                      IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK;
602
603
    /* Needed to convert each uint8_t byte to a ascii */
604
24.1k
    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
24.1k
    char bin_in_ascii_len = vpd_val_len * 2 + 3;
611
612
    /* Binary converted to ascii in array */
613
24.1k
    char* bin_in_ascii = static_cast<char*>(malloc(bin_in_ascii_len));
614
615
    /* For reading byte from the area */
616
24.1k
    int val = 0;
617
618
24.1k
    char* bin_copy = &(static_cast<char*>(bin_in_ascii)[2]);
619
620
24.1k
    switch (type_code)
621
24.1k
    {
622
17.6k
        case 0:
623
17.6k
            memset(bin_in_ascii, 0x0, bin_in_ascii_len);
624
625
            /* Offset 1 is where actual data starts */
626
56.6k
            for (val = 1; val <= vpd_val_len; val++)
627
39.0k
            {
628
                /* 2 bytes for data and 1 for terminating '\0' */
629
39.0k
                snprintf(bin_byte, 3, "%02x", vpd_key_val[val]);
630
631
39.0k
#pragma GCC diagnostic push
632
39.0k
#ifdef __clang__
633
39.0k
#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
39.0k
                strncat(bin_copy, bin_byte, 2);
639
39.0k
#pragma GCC diagnostic pop
640
39.0k
            }
641
642
            /* We need the data represented as 0x...... */
643
17.6k
            if (vpd_val_len > 0)
644
2.40k
            {
645
2.40k
                memcpy(bin_in_ascii, "0x", 2);
646
2.40k
            }
647
648
17.6k
            lg2::debug(
649
17.6k
                "_append_to_dict: VPD Key = [{KEY}] : Type Code = [BINARY] : Len = [{LEN}] : Val = [{VAL}]",
650
17.6k
                "KEY", vpd_key_names[vpd_key_id], "LEN", vpd_val_len, "VAL",
651
17.6k
                bin_in_ascii);
652
653
17.6k
            info[vpd_key_id] =
654
17.6k
                std::make_pair(vpd_key_names[vpd_key_id], bin_in_ascii);
655
17.6k
            break;
656
657
3.00k
        case 3:
658
3.00k
            lg2::debug(
659
3.00k
                "_append_to_dict: VPD Key = [{KEY}] : Type Code = [ASCII+Latin] : Len = [{LEN}] : Val = [{VAL}]",
660
3.00k
                "KEY", vpd_key_names[vpd_key_id], "LEN", vpd_val_len, "VAL",
661
3.00k
                &vpd_key_val[1]);
662
3.00k
            info[vpd_key_id] = std::make_pair(
663
3.00k
                vpd_key_names[vpd_key_id],
664
3.00k
                std::string(vpd_key_val + 1, vpd_key_val + 1 + type_length));
665
3.00k
            break;
666
24.1k
    }
667
668
24.1k
    if (bin_in_ascii)
669
23.1k
    {
670
23.1k
        free(bin_in_ascii);
671
23.1k
        bin_in_ascii = nullptr;
672
23.1k
    }
673
24.1k
}
674
675
int parse_fru_area(const uint8_t area, const void* msgbuf, const size_t len,
676
                   IPMIFruInfo& info)
677
3.17k
{
678
3.17k
    int rv = -1;
679
3.17k
    int i = 0;
680
681
    /* Chassis */
682
3.17k
    uint8_t chassis_type;
683
    /* Board */
684
3.17k
    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
3.17k
    ipmi_fru_field_t vpd_info[OPENBMC_VPD_KEY_MAX];
690
3.17k
    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
3.17k
    ASSERT(msgbuf);
697
698
133k
    for (i = 0; i < OPENBMC_VPD_KEY_MAX; i++)
699
130k
    {
700
130k
        memset(vpd_info[i].type_length_field, '\0',
701
130k
               IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX);
702
130k
        vpd_info[i].type_length_field_length = 0;
703
130k
    }
704
705
3.17k
    switch (area)
706
3.17k
    {
707
701
        case IPMI_FRU_AREA_CHASSIS_INFO:
708
701
            lg2::debug("Chassis : Buf len = [{LEN}]", "LEN", len);
709
701
            ipmi_fru_chassis_info_area(
710
701
                static_cast<const uint8_t*>(msgbuf) + 2, len, &chassis_type,
711
701
                &vpd_info[OPENBMC_VPD_KEY_CHASSIS_PART_NUM],
712
701
                &vpd_info[OPENBMC_VPD_KEY_CHASSIS_SERIAL_NUM],
713
701
                &vpd_info[OPENBMC_VPD_KEY_CHASSIS_CUSTOM1],
714
701
                OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX);
715
716
            /* Populate VPD Table */
717
8.41k
            for (i = 1; i <= OPENBMC_VPD_KEY_CHASSIS_MAX; i++)
718
7.71k
            {
719
7.71k
                if (i == OPENBMC_VPD_KEY_CHASSIS_TYPE)
720
701
                {
721
701
                    lg2::debug("Chassis : Appending [{KEY}] = [{TYPE}]", "KEY",
722
701
                               vpd_key_names[i], "TYPE", chassis_type);
723
701
                    info[i] = std::make_pair(vpd_key_names[i],
724
701
                                             std::to_string(chassis_type));
725
701
                    continue;
726
701
                }
727
7.01k
                _append_to_dict(i, vpd_info[i].type_length_field, info);
728
7.01k
            }
729
701
            break;
730
611
        case IPMI_FRU_AREA_BOARD_INFO:
731
611
            lg2::debug("Board : Buf len = [{LEN}]", "LEN", len);
732
611
            ipmi_fru_board_info_area(
733
611
                static_cast<const uint8_t*>(msgbuf) + 2, len, nullptr,
734
611
                &mfg_date_time, &vpd_info[OPENBMC_VPD_KEY_BOARD_MFR],
735
611
                &vpd_info[OPENBMC_VPD_KEY_BOARD_NAME],
736
611
                &vpd_info[OPENBMC_VPD_KEY_BOARD_SERIAL_NUM],
737
611
                &vpd_info[OPENBMC_VPD_KEY_BOARD_PART_NUM],
738
611
                &vpd_info[OPENBMC_VPD_KEY_BOARD_FRU_FILE_ID],
739
611
                &vpd_info[OPENBMC_VPD_KEY_BOARD_CUSTOM1],
740
611
                OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX);
741
742
            /* Populate VPD Table */
743
611
            for (i = OPENBMC_VPD_KEY_BOARD_MFG_DATE;
744
9.16k
                 i <= OPENBMC_VPD_KEY_BOARD_MAX; i++)
745
8.55k
            {
746
8.55k
                if (i == OPENBMC_VPD_KEY_BOARD_MFG_DATE)
747
611
                {
748
611
                    _to_time_str(mfg_date_time, timestr, OPENBMC_VPD_VAL_LEN);
749
611
                    lg2::debug("Board : Appending [{KEY}] = [{VAL}]", "KEY",
750
611
                               vpd_key_names[i], "VAL", timestr);
751
611
                    info[i] =
752
611
                        std::make_pair(vpd_key_names[i], std::string(timestr));
753
611
                    continue;
754
611
                }
755
7.94k
                _append_to_dict(i, vpd_info[i].type_length_field, info);
756
7.94k
            }
757
611
            break;
758
612
        case IPMI_FRU_AREA_PRODUCT_INFO:
759
612
            lg2::debug("Product : Buf len = [{LEN}]", "LEN", len);
760
612
            ipmi_fru_product_info_area(
761
612
                static_cast<const uint8_t*>(msgbuf) + 2, len, nullptr,
762
612
                &vpd_info[OPENBMC_VPD_KEY_PRODUCT_MFR],
763
612
                &vpd_info[OPENBMC_VPD_KEY_PRODUCT_NAME],
764
612
                &vpd_info[OPENBMC_VPD_KEY_PRODUCT_PART_MODEL_NUM],
765
612
                &vpd_info[OPENBMC_VPD_KEY_PRODUCT_VER],
766
612
                &vpd_info[OPENBMC_VPD_KEY_PRODUCT_SERIAL_NUM],
767
612
                &vpd_info[OPENBMC_VPD_KEY_PRODUCT_ASSET_TAG],
768
612
                &vpd_info[OPENBMC_VPD_KEY_PRODUCT_FRU_FILE_ID],
769
612
                &vpd_info[OPENBMC_VPD_KEY_PRODUCT_CUSTOM1],
770
612
                OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX);
771
772
612
            for (i = OPENBMC_VPD_KEY_PRODUCT_MFR;
773
9.79k
                 i <= OPENBMC_VPD_KEY_PRODUCT_MAX; ++i)
774
9.18k
            {
775
9.18k
                _append_to_dict(i, vpd_info[i].type_length_field, info);
776
9.18k
            }
777
612
            break;
778
1.25k
        default:
779
            /* TODO: Parse Multi Rec / Internal use area */
780
1.25k
            break;
781
3.17k
    }
782
783
3.17k
    lg2::debug("parse_fru_area : Dictionary Packing Complete");
784
3.17k
    rv = 0;
785
3.17k
    return (rv);
786
3.17k
}