blob: d3471ffddf3e32b8420c33b8e375acc561d47d59 [file] [log] [blame]
Hariharasubramanian R44473092015-10-15 08:25:28 -05001/*
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 <stdio.h>
43#include <unistd.h>
44#include <string.h>
45#include <time.h>
46
47#define uint8_t unsigned char
48#define uint32_t unsigned int
49
50#define ASSERT(x) if(!(x)) return -1;
51
52
53#define IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX 512
54#define IPMI_FRU_SENTINEL_VALUE 0xC1
55#define IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK 0xC0
56#define IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT 0x06
57#define IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK 0x3F
58#define IPMI_FRU_TYPE_LENGTH_TYPE_CODE_LANGUAGE_CODE 0x03
59
60
61struct ipmi_fru_field
62{
63 uint8_t type_length_field[IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX];
64 /* store length of data stored in buffer */
65 unsigned int type_length_field_length;
66};
67
68typedef struct ipmi_fru_field ipmi_fru_field_t;
69
70static int
71_parse_type_length (const void *areabuf,
72 unsigned int areabuflen,
73 unsigned int current_area_offset,
74 uint8_t *number_of_data_bytes,
75 ipmi_fru_field_t *field)
76{
77 const uint8_t *areabufptr = (const uint8_t*) areabuf;
78 uint8_t type_length;
79 uint8_t type_code;
80
81 ASSERT (areabuf);
82 ASSERT (areabuflen);
83 ASSERT (number_of_data_bytes);
84
85 type_length = areabufptr[current_area_offset];
86
87 /* IPMI Workaround
88 *
89 * Dell P weredge R610
90 *
91 * My reading of the FRU Spec is that all non-custom fields are
92 * required to be listed by the vendor. However, on this
93 * motherboard, some areas list this, indicating that there is
94 * no more data to be parsed. So now, for "required" fields, I
95 * check to see if the type-length field is a sentinel before
96 * calling this function.
97 */
98
99 ASSERT (type_length != IPMI_FRU_SENTINEL_VALUE);
100
101 type_code = (type_length & IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK) >> IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT;
102 (*number_of_data_bytes) = type_length & IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK;
103
104 /* Special Case: This shouldn't be a length of 0x01 (see type/length
105 * byte format in FRU Information Storage Definition).
106 */
107 if (type_code == IPMI_FRU_TYPE_LENGTH_TYPE_CODE_LANGUAGE_CODE
108 && (*number_of_data_bytes) == 0x01)
109 {
110 return (-1);
111 }
112
113 if ((current_area_offset + 1 + (*number_of_data_bytes)) > areabuflen)
114 {
115 return (-1);
116 }
117
118 if (field)
119 {
120 memset (field->type_length_field,
121 '\0',
122 IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX);
123 memcpy (field->type_length_field,
124 &areabufptr[current_area_offset],
125 1 + (*number_of_data_bytes));
126 field->type_length_field_length = 1 + (*number_of_data_bytes);
127 }
128
129 return (0);
130}
131
132int
133ipmi_fru_chassis_info_area (const void *areabuf,
134 unsigned int areabuflen,
135 uint8_t *chassis_type,
136 ipmi_fru_field_t *chassis_part_number,
137 ipmi_fru_field_t *chassis_serial_number,
138 ipmi_fru_field_t *chassis_custom_fields,
139 unsigned int chassis_custom_fields_len)
140{
141 const uint8_t *areabufptr = (const uint8_t*) areabuf;
142 unsigned int area_offset = 0;
143 unsigned int custom_fields_index = 0;
144 uint8_t number_of_data_bytes;
145 int rv = -1;
146
147 if (!areabuf || !areabuflen)
148 {
149 return (-1);
150 }
151
152 if (chassis_part_number)
153 memset (chassis_part_number,
154 '\0',
155 sizeof (ipmi_fru_field_t));
156 if (chassis_serial_number)
157 memset (chassis_serial_number,
158 '\0',
159 sizeof (ipmi_fru_field_t));
160 if (chassis_custom_fields && chassis_custom_fields_len)
161 memset (chassis_custom_fields,
162 '\0',
163 sizeof (ipmi_fru_field_t) * chassis_custom_fields_len);
164
165 if (chassis_type)
166 (*chassis_type) = areabufptr[area_offset];
167 area_offset++;
168
169 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
170 goto out;
171
172 if (_parse_type_length (areabufptr,
173 areabuflen,
174 area_offset,
175 &number_of_data_bytes,
176 chassis_part_number) < 0)
177 goto cleanup;
178 area_offset += 1; /* type/length byte */
179 area_offset += number_of_data_bytes;
180
181 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
182 goto out;
183
184 if (_parse_type_length (areabufptr,
185 areabuflen,
186 area_offset,
187 &number_of_data_bytes,
188 chassis_serial_number) < 0)
189 goto cleanup;
190 area_offset += 1; /* type/length byte */
191 area_offset += number_of_data_bytes;
192
193 while (area_offset < areabuflen
194 && areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE)
195 {
196 ipmi_fru_field_t *field_ptr = NULL;
197
198 if (chassis_custom_fields && chassis_custom_fields_len)
199 {
200 if (custom_fields_index < chassis_custom_fields_len)
201 field_ptr = &chassis_custom_fields[custom_fields_index];
202 else
203 {
204 goto cleanup;
205 }
206 }
207
208 if (_parse_type_length (areabufptr,
209 areabuflen,
210 area_offset,
211 &number_of_data_bytes,
212 field_ptr) < 0)
213 goto cleanup;
214
215 area_offset += 1; /* type/length byte */
216 area_offset += number_of_data_bytes;
217 custom_fields_index++;
218 }
219
220
221 out:
222 rv = 0;
223 cleanup:
224 return (rv);
225}
226
227int
228ipmi_fru_board_info_area (const void *areabuf,
229 unsigned int areabuflen,
230 uint8_t *language_code,
231 uint32_t *mfg_date_time,
232 ipmi_fru_field_t *board_manufacturer,
233 ipmi_fru_field_t *board_product_name,
234 ipmi_fru_field_t *board_serial_number,
235 ipmi_fru_field_t *board_part_number,
236 ipmi_fru_field_t *board_fru_file_id,
237 ipmi_fru_field_t *board_custom_fields,
238 unsigned int board_custom_fields_len)
239{
240 const uint8_t *areabufptr = (const uint8_t*) areabuf;
241 uint32_t mfg_date_time_tmp = 0;
242 unsigned int area_offset = 0;
243 unsigned int custom_fields_index = 0;
244 uint8_t number_of_data_bytes;
245 int rv = -1;
246
247 if (!areabuf || !areabuflen)
248 {
249 return (-1);
250 }
251
252 if (board_manufacturer)
253 memset (board_manufacturer,
254 '\0',
255 sizeof (ipmi_fru_field_t));
256 if (board_product_name)
257 memset (board_product_name,
258 '\0',
259 sizeof (ipmi_fru_field_t));
260 if (board_serial_number)
261 memset (board_serial_number,
262 '\0',
263 sizeof (ipmi_fru_field_t));
264 if (board_part_number)
265 memset (board_part_number,
266 '\0',
267 sizeof (ipmi_fru_field_t));
268 if (board_fru_file_id)
269 memset (board_fru_file_id,
270 '\0',
271 sizeof (ipmi_fru_field_t));
272 if (board_custom_fields && board_custom_fields_len)
273 memset (board_custom_fields,
274 '\0',
275 sizeof (ipmi_fru_field_t) * board_custom_fields_len);
276
277 if (language_code)
278 (*language_code) = areabufptr[area_offset];
279 area_offset++;
280
281 if (mfg_date_time)
282 {
283 struct tm tm;
284 time_t t;
285
286 /* mfg_date_time is little endian - see spec */
287 mfg_date_time_tmp |= areabufptr[area_offset];
288 area_offset++;
289 mfg_date_time_tmp |= (areabufptr[area_offset] << 8);
290 area_offset++;
291 mfg_date_time_tmp |= (areabufptr[area_offset] << 16);
292 area_offset++;
293
294 /* mfg_date_time is in minutes, so multiple by 60 to get seconds */
295 mfg_date_time_tmp *= 60;
296
297 /* Posix says individual calls need not clear/set all portions of
298 * 'struct tm', thus passing 'struct tm' between functions could
299 * have issues. So we need to memset.
300 */
301 memset (&tm, '\0', sizeof(struct tm));
302
303 /* In FRU, epoch is 0:00 hrs 1/1/96
304 *
305 * So convert into ansi epoch
306 */
307
308 tm.tm_year = 96; /* years since 1900 */
309 tm.tm_mon = 0; /* months since January */
310 tm.tm_mday = 1; /* 1-31 */
311 tm.tm_hour = 0;
312 tm.tm_min = 0;
313 tm.tm_sec = 0;
314 tm.tm_isdst = -1;
315
316 if ((t = mktime (&tm)) == (time_t)-1)
317 {
318 goto cleanup;
319 }
320
321 mfg_date_time_tmp += (uint32_t)t;
322 (*mfg_date_time) = mfg_date_time_tmp;
323 }
324 else
325 area_offset += 3;
326
327 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
328 goto out;
329
330 if (_parse_type_length (areabufptr,
331 areabuflen,
332 area_offset,
333 &number_of_data_bytes,
334 board_manufacturer) < 0)
335 goto cleanup;
336 area_offset += 1; /* type/length byte */
337 area_offset += number_of_data_bytes;
338
339 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
340 goto out;
341
342 if (_parse_type_length (areabufptr,
343 areabuflen,
344 area_offset,
345 &number_of_data_bytes,
346 board_product_name) < 0)
347 goto cleanup;
348 area_offset += 1; /* type/length byte */
349 area_offset += number_of_data_bytes;
350
351 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
352 goto out;
353
354 if (_parse_type_length (areabufptr,
355 areabuflen,
356 area_offset,
357 &number_of_data_bytes,
358 board_serial_number) < 0)
359 goto cleanup;
360 area_offset += 1; /* type/length byte */
361 area_offset += number_of_data_bytes;
362
363 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
364 goto out;
365
366 if (_parse_type_length (
367 areabufptr,
368 areabuflen,
369 area_offset,
370 &number_of_data_bytes,
371 board_part_number) < 0)
372 goto cleanup;
373 area_offset += 1; /* type/length byte */
374 area_offset += number_of_data_bytes;
375
376 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
377 goto out;
378
379 if (_parse_type_length (areabufptr,
380 areabuflen,
381 area_offset,
382 &number_of_data_bytes,
383 board_fru_file_id) < 0)
384 goto cleanup;
385 area_offset += 1; /* type/length byte */
386 area_offset += number_of_data_bytes;
387
388 while (area_offset < areabuflen
389 && areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE)
390 {
391 ipmi_fru_field_t *field_ptr = NULL;
392
393 if (board_custom_fields && board_custom_fields_len)
394 {
395 if (custom_fields_index < board_custom_fields_len)
396 field_ptr = &board_custom_fields[custom_fields_index];
397 else
398 {
399 goto cleanup;
400 }
401 }
402
403 if (_parse_type_length (areabufptr,
404 areabuflen,
405 area_offset,
406 &number_of_data_bytes,
407 field_ptr) < 0)
408 goto cleanup;
409
410 area_offset += 1; /* type/length byte */
411 area_offset += number_of_data_bytes;
412 custom_fields_index++;
413 }
414
415 out:
416 rv = 0;
417 cleanup:
418 return (rv);
419}
420
421int
422ipmi_fru_product_info_area (const void *areabuf,
423 unsigned int areabuflen,
424 uint8_t *language_code,
425 ipmi_fru_field_t *product_manufacturer_name,
426 ipmi_fru_field_t *product_name,
427 ipmi_fru_field_t *product_part_model_number,
428 ipmi_fru_field_t *product_version,
429 ipmi_fru_field_t *product_serial_number,
430 ipmi_fru_field_t *product_asset_tag,
431 ipmi_fru_field_t *product_fru_file_id,
432 ipmi_fru_field_t *product_custom_fields,
433 unsigned int product_custom_fields_len)
434{
435 const uint8_t *areabufptr = (const uint8_t*) areabuf;
436 unsigned int area_offset = 0;
437 unsigned int custom_fields_index = 0;
438 uint8_t number_of_data_bytes;
439 int rv = -1;
440
441 if (!areabuf || !areabuflen)
442 {
443 return (-1);
444 }
445
446 if (product_manufacturer_name)
447 memset (product_manufacturer_name,
448 '\0',
449 sizeof (ipmi_fru_field_t));
450 if (product_name)
451 memset (product_name,
452 '\0',
453 sizeof (ipmi_fru_field_t));
454 if (product_part_model_number)
455 memset (product_part_model_number,
456 '\0',
457 sizeof (ipmi_fru_field_t));
458 if (product_version)
459 memset (product_version,
460 '\0',
461 sizeof (ipmi_fru_field_t));
462 if (product_serial_number)
463 memset (product_serial_number,
464 '\0',
465 sizeof (ipmi_fru_field_t));
466 if (product_asset_tag)
467 memset (product_asset_tag,
468 '\0',
469 sizeof (ipmi_fru_field_t));
470 if (product_fru_file_id)
471 memset (product_fru_file_id,
472 '\0',
473 sizeof (ipmi_fru_field_t));
474 if (product_custom_fields && product_custom_fields_len)
475 memset (product_custom_fields,
476 '\0',
477 sizeof (ipmi_fru_field_t) * product_custom_fields_len);
478
479 if (language_code)
480 (*language_code) = areabufptr[area_offset];
481 area_offset++;
482
483 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
484 goto out;
485
486 if (_parse_type_length (areabufptr,
487 areabuflen,
488 area_offset,
489 &number_of_data_bytes,
490 product_manufacturer_name) < 0)
491 goto cleanup;
492 area_offset += 1; /* type/length byte */
493 area_offset += number_of_data_bytes;
494
495 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
496 goto out;
497
498 if (_parse_type_length (areabufptr,
499 areabuflen,
500 area_offset,
501 &number_of_data_bytes,
502 product_name) < 0)
503 goto cleanup;
504 area_offset += 1; /* type/length byte */
505 area_offset += number_of_data_bytes;
506
507 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
508 goto out;
509
510 if (_parse_type_length (areabufptr,
511 areabuflen,
512 area_offset,
513 &number_of_data_bytes,
514 product_part_model_number) < 0)
515 goto cleanup;
516 area_offset += 1; /* type/length byte */
517 area_offset += number_of_data_bytes;
518
519 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
520 goto out;
521
522 if (_parse_type_length (areabufptr,
523 areabuflen,
524 area_offset,
525 &number_of_data_bytes,
526 product_version) < 0)
527 goto cleanup;
528 area_offset += 1; /* type/length byte */
529 area_offset += number_of_data_bytes;
530
531 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
532 goto out;
533
534 if (_parse_type_length (areabufptr,
535 areabuflen,
536 area_offset,
537 &number_of_data_bytes,
538 product_serial_number) < 0)
539 goto cleanup;
540 area_offset += 1; /* type/length byte */
541 area_offset += number_of_data_bytes;
542
543 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
544 goto out;
545
546 if (_parse_type_length (areabufptr,
547 areabuflen,
548 area_offset,
549 &number_of_data_bytes,
550 product_asset_tag) < 0)
551 goto cleanup;
552 area_offset += 1; /* type/length byte */
553 area_offset += number_of_data_bytes;
554
555 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE)
556 goto out;
557
558 if (_parse_type_length (areabufptr,
559 areabuflen,
560 area_offset,
561 &number_of_data_bytes,
562 product_fru_file_id) < 0)
563 goto cleanup;
564 area_offset += 1; /* type/length byte */
565 area_offset += number_of_data_bytes;
566
567 while (area_offset < areabuflen
568 && areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE)
569 {
570 ipmi_fru_field_t *field_ptr = NULL;
571
572 if (product_custom_fields && product_custom_fields_len)
573 {
574 if (custom_fields_index < product_custom_fields_len)
575 field_ptr = &product_custom_fields[custom_fields_index];
576 else
577 {
578 goto cleanup;
579 }
580 }
581
582 if (_parse_type_length (areabufptr,
583 areabuflen,
584 area_offset,
585 &number_of_data_bytes,
586 field_ptr) < 0)
587 goto cleanup;
588
589 area_offset += 1; /* type/length byte */
590 area_offset += number_of_data_bytes;
591 custom_fields_index++;
592 }
593
594
595 out:
596 rv = 0;
597 cleanup:
598 return (rv);
599}