/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 | } |