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 <stdio.h> 43 #include <stdlib.h> 44 #include <unistd.h> 45 #include <string.h> 46 #include <time.h> 47 #include <systemd/sd-bus.h> 48 #include <ctype.h> 49 #include "frup.hpp" 50 51 #define TEXTSTR(a) #a 52 # define ASSERT(x) \ 53 do { \ 54 if (0 == (x)) { \ 55 fprintf(stderr, \ 56 "Assertion failed: %s, " \ 57 "%d at \'%s\'\n", \ 58 __FILE__, \ 59 __LINE__, \ 60 TEXTSTR(a)); \ 61 return -1; \ 62 } \ 63 } while (0) 64 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 72 /* 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 82 83 struct 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 90 typedef struct ipmi_fru_field ipmi_fru_field_t; 91 /* 92 * FRU Parser 93 */ 94 95 typedef struct ipmi_fru_area_info 96 { 97 uint8_t off; 98 uint8_t len; 99 } ipmi_fru_area_info_t; 100 101 typedef 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 111 const char* vpd_key_names [] = 112 { 113 "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,*/ 125 126 "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,*/ 140 141 "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,*/ 156 157 "Key Names Table End" /*OPENBMC_VPD_KEY_MAX,*/ 158 }; 159 160 161 /* 162 * -------------------------------------------------------------------- 163 * 164 * -------------------------------------------------------------------- 165 */ 166 167 static 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 185 /* private method to parse type/length */ 186 static 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); 200 201 type_length = areabufptr[current_area_offset]; 202 203 /* ipmi workaround 204 * 205 * dell p weredge r610 206 * 207 * my reading of the fru spec is that all non-custom fields are 208 * required to be listed by the vendor. however, on this 209 * motherboard, some areas list this, indicating that there is 210 * no more data to be parsed. so now, for "required" fields, i 211 * check to see if the type-length field is a sentinel before 212 * calling this function. 213 */ 214 215 ASSERT (type_length != IPMI_FRU_SENTINEL_VALUE); 216 217 type_code = (type_length & IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK) >> IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT; 218 (*number_of_data_bytes) = type_length & IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK; 219 220 /* special case: this shouldn't be a length of 0x01 (see type/length 221 * byte format in fru information storage definition). 222 */ 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 } 244 245 return (0); 246 } 247 248 int 249 ipmi_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 343 int 344 ipmi_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++; 409 410 /* mfg_date_time is in minutes, so multiple by 60 to get seconds */ 411 mfg_date_time_tmp *= 60; 412 413 /* posix says individual calls need not clear/set all portions of 414 * 'struct tm', thus passing 'struct tm' between functions could 415 * have issues. so we need to memset. 416 */ 417 memset (&tm, '\0', sizeof(struct tm)); 418 419 /* in fru, epoch is 0:00 hrs 1/1/96 420 * 421 * so convert into ansi epoch 422 */ 423 424 tm.tm_year = 96; /* years since 1900 */ 425 tm.tm_mon = 0; /* months since january */ 426 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 482 if (_parse_type_length (areabufptr, 483 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 536 int 537 ipmi_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; 679 680 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 } 716 717 void _append_to_dict (uint8_t vpd_key_id, uint8_t* vpd_key_val, sd_bus_message* vpdtbl) 718 { 719 int type_length = vpd_key_val[0]; 720 int type_code = (type_length & IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK) >> IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT; 721 int vpd_val_len = type_length & IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK; 722 int sdr=0; 723 724 /* Needed to convert each uint8_t byte to a ascii */ 725 char bin_byte[3] = {0}; 726 727 /* 728 * Max number of characters needed to represent 1 unsigned byte in string 729 * is number of bytes multipled by 2. Extra 3 for 0x and a ending '\0'; 730 */ 731 char bin_in_ascii_len = vpd_val_len * 2 + 3; 732 733 /* Binary converted to ascii in array */ 734 char *bin_in_ascii = (char *)malloc(bin_in_ascii_len); 735 736 /* For reading byte from the area */ 737 int val = 0; 738 739 char *bin_copy = &((char *)bin_in_ascii)[2]; 740 741 switch (type_code) 742 { 743 case 0: 744 memset(bin_in_ascii, 0x0, bin_in_ascii_len); 745 746 /* Offset 1 is where actual data starts */ 747 for(val = 1; val <= vpd_val_len ; val++) 748 { 749 /* 2 bytes for data and 1 for terminating '\0' */ 750 snprintf(bin_byte, 3, "%02x", vpd_key_val[val]); 751 752 /* Its a running string so strip off the '\0' */ 753 strncat(bin_copy, bin_byte, 2); 754 } 755 756 /* We need the data represented as 0x...... */ 757 if(vpd_val_len > 0) 758 { 759 strncpy(bin_in_ascii, "0x", 2); 760 } 761 762 printf ("_append_to_dict: VPD Key = [%s] : Type Code = [BINARY] : Len = [%d] : Val = [%s]\n", 763 vpd_key_names [vpd_key_id], vpd_val_len, bin_in_ascii); 764 sdr = sd_bus_message_append (vpdtbl, "{sv}", vpd_key_names[vpd_key_id], "s", bin_in_ascii); 765 break; 766 767 case 3: 768 printf ("_append_to_dict: VPD Key = [%s] : Type Code = [ASCII+Latin] : Len = [%d] : Val = [%s]\n", vpd_key_names [vpd_key_id], vpd_val_len, &vpd_key_val[1]); 769 sdr = sd_bus_message_append (vpdtbl, "{sv}", vpd_key_names[vpd_key_id], "s", &vpd_key_val[1]); 770 break; 771 } 772 773 if(bin_in_ascii) 774 { 775 free(bin_in_ascii); 776 bin_in_ascii = NULL; 777 } 778 779 780 if (sdr < 0) 781 { 782 #if IPMI_FRU_PARSER_DEBUG 783 printf ("_append_to_dict : sd_bus_message_append Failed [ %d ] for [%s]\n", sdr, vpd_key_names[vpd_key_id]); 784 #endif 785 } 786 } 787 788 int 789 parse_fru (const void* msgbuf, sd_bus_message* vpdtbl) 790 { 791 int rv = -1; 792 int i = 0; 793 ipmi_fru_area_info_t fru_area_info [ IPMI_FRU_AREA_TYPE_MAX ]; 794 ipmi_fru_common_hdr_t* chdr = NULL; 795 uint8_t* hdr = NULL; 796 char timestr [ OPENBMC_VPD_VAL_LEN ]; 797 798 799 ipmi_fru_field_t vpd_info [ OPENBMC_VPD_KEY_MAX ]; 800 //uint8_t* ipmi_fru_field_str; 801 802 /* Chassis */ 803 uint8_t chassis_type; 804 805 /* Board */ 806 uint32_t mfg_date_time; 807 808 /* Product */ 809 //unsigned int product_custom_fields_len; 810 811 ASSERT (msgbuf); 812 ASSERT (vpdtbl); 813 814 for (i=0; i<OPENBMC_VPD_KEY_MAX; i++) 815 { 816 memset (vpd_info[i].type_length_field, '\0', IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX); 817 vpd_info[i].type_length_field_length = 0; 818 } 819 820 for (i=0; i<IPMI_FRU_AREA_TYPE_MAX; i++) 821 { 822 fru_area_info [ i ].off = 0; 823 fru_area_info [ i ].len = 0; 824 } 825 826 chdr = (ipmi_fru_common_hdr_t*) msgbuf; 827 hdr = (uint8_t*) msgbuf; 828 829 fru_area_info [ IPMI_FRU_AREA_INTERNAL_USE ].off = chdr->internal; 830 fru_area_info [ IPMI_FRU_AREA_CHASSIS_INFO ].off = chdr->chassis; 831 fru_area_info [ IPMI_FRU_AREA_BOARD_INFO ].off = chdr->board; 832 fru_area_info [ IPMI_FRU_AREA_PRODUCT_INFO ].off = chdr->product; 833 fru_area_info [ IPMI_FRU_AREA_MULTI_RECORD ].off = chdr->multirec; 834 835 if (chdr->internal) 836 { 837 fru_area_info [ IPMI_FRU_AREA_INTERNAL_USE ].len = 8*(*(hdr+8*chdr->internal+1)); 838 839 /* TODO: Parse internal use area */ 840 } 841 842 if (chdr->chassis) 843 { 844 fru_area_info [ IPMI_FRU_AREA_CHASSIS_INFO ].len = 8*(*(hdr+8*chdr->chassis+1)); 845 ipmi_fru_chassis_info_area (hdr+8*chdr->chassis+2, 846 fru_area_info [ IPMI_FRU_AREA_CHASSIS_INFO ].len, 847 &chassis_type, 848 &vpd_info [OPENBMC_VPD_KEY_CHASSIS_PART_NUM], 849 &vpd_info [OPENBMC_VPD_KEY_CHASSIS_SERIAL_NUM], 850 &vpd_info [OPENBMC_VPD_KEY_CHASSIS_CUSTOM1], 851 OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX); 852 } 853 854 if (chdr->board) 855 { 856 fru_area_info [ IPMI_FRU_AREA_BOARD_INFO ].len = 8*(*(hdr+8*chdr->board+1)); 857 ipmi_fru_board_info_area (hdr+8*chdr->board+2, 858 fru_area_info [ IPMI_FRU_AREA_BOARD_INFO ].len, 859 NULL, 860 &mfg_date_time, 861 &vpd_info [OPENBMC_VPD_KEY_BOARD_MFR], 862 &vpd_info [OPENBMC_VPD_KEY_BOARD_NAME], 863 &vpd_info [OPENBMC_VPD_KEY_BOARD_SERIAL_NUM], 864 &vpd_info [OPENBMC_VPD_KEY_BOARD_PART_NUM], 865 &vpd_info [OPENBMC_VPD_KEY_BOARD_FRU_FILE_ID], 866 &vpd_info [OPENBMC_VPD_KEY_BOARD_CUSTOM1], 867 OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX); 868 } 869 870 if (chdr->product) 871 { 872 fru_area_info [ IPMI_FRU_AREA_PRODUCT_INFO ].len = 8*(*(hdr+8*chdr->product+1)); 873 ipmi_fru_product_info_area (hdr+8*chdr->product+2, 874 fru_area_info [ IPMI_FRU_AREA_PRODUCT_INFO ].len, 875 NULL, 876 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_MFR], 877 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_NAME], 878 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_PART_MODEL_NUM], 879 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_VER], 880 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_SERIAL_NUM], 881 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_ASSET_TAG], 882 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_FRU_FILE_ID], 883 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_CUSTOM1], 884 OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX); 885 } 886 887 if (chdr->multirec) 888 { 889 fru_area_info [ IPMI_FRU_AREA_MULTI_RECORD ].len = 8*(*(hdr+8*chdr->multirec+1)); 890 /* TODO: Parse multi record area */ 891 } 892 893 for (i=0; i<IPMI_FRU_AREA_TYPE_MAX; i++) 894 { 895 #if IPMI_FRU_PARSER_DEBUG 896 printf ("IPMI_FRU_AREA_TYPE=[%d] : Offset=[%d] : Len=[%d]\n", i, fru_area_info [i].off, fru_area_info[i].len); 897 #endif 898 } 899 900 /* Populate VPD Table */ 901 for (i=1; i<OPENBMC_VPD_KEY_MAX; i++) 902 { 903 if (i==OPENBMC_VPD_KEY_CHASSIS_TYPE) 904 { 905 sd_bus_message_append (vpdtbl, "{sv}", vpd_key_names[i], "y", chassis_type); 906 #if IPMI_FRU_PARSER_DEBUG 907 printf ("[%s] = [%d]\n", vpd_key_names[i], chassis_type); 908 #endif 909 continue; 910 } 911 912 if (i==OPENBMC_VPD_KEY_BOARD_MFG_DATE) 913 { 914 _to_time_str (mfg_date_time, timestr, OPENBMC_VPD_VAL_LEN); 915 sd_bus_message_append (vpdtbl, "{sv}", vpd_key_names[i], "s", timestr); 916 #if IPMI_FRU_PARSER_DEBUG 917 printf ("[%s] = [%d]\n", vpd_key_names[i], mfg_date_time); 918 #endif 919 continue; 920 } 921 922 /* Append TypeLen Field to Dictionary */ 923 _append_to_dict (i, vpd_info[i].type_length_field, vpdtbl); 924 925 /*ipmi_fru_field_str = (unsigned char*) &(vpd_info[i].type_length_field) + 1;*/ 926 /*sd_bus_message_append (vpdtbl, "{sv}", vpd_key_names[i], "s", ipmi_fru_field_str); */ 927 } 928 rv = 0; 929 return (rv); 930 } 931 932 int parse_fru_area (const uint8_t area, const void* msgbuf, 933 const size_t len, IPMIFruInfo& info) 934 { 935 int rv = -1; 936 int i = 0; 937 938 /* Chassis */ 939 uint8_t chassis_type; 940 /* Board */ 941 uint32_t mfg_date_time; 942 /* Product */ 943 //unsigned int product_custom_fields_len; 944 945 //ipmi_fru_area_info_t fru_area_info [ IPMI_FRU_AREA_TYPE_MAX ]; 946 ipmi_fru_field_t vpd_info [ OPENBMC_VPD_KEY_MAX ]; 947 char timestr [ OPENBMC_VPD_VAL_LEN ]; 948 949 //uint8_t* ipmi_fru_field_str=NULL; 950 //ipmi_fru_common_hdr_t* chdr = NULL; 951 //uint8_t* hdr = NULL; 952 953 ASSERT (msgbuf); 954 955 for (i=0; i<OPENBMC_VPD_KEY_MAX; i++) 956 { 957 memset (vpd_info[i].type_length_field, '\0', IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX); 958 vpd_info[i].type_length_field_length = 0; 959 } 960 961 switch (area) 962 { 963 case IPMI_FRU_AREA_CHASSIS_INFO: 964 #if IPMI_FRU_PARSER_DEBUG 965 printf ("Chassis : Buf len = [%d]\n", len); 966 #endif 967 ipmi_fru_chassis_info_area ((uint8_t*)msgbuf+2, 968 len, 969 &chassis_type, 970 &vpd_info [OPENBMC_VPD_KEY_CHASSIS_PART_NUM], 971 &vpd_info [OPENBMC_VPD_KEY_CHASSIS_SERIAL_NUM], 972 &vpd_info [OPENBMC_VPD_KEY_CHASSIS_CUSTOM1], 973 OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX); 974 975 /* Populate VPD Table */ 976 for (i=1; i<=OPENBMC_VPD_KEY_CHASSIS_MAX; i++) 977 { 978 if (i==OPENBMC_VPD_KEY_CHASSIS_TYPE) 979 { 980 #if IPMI_FRU_PARSER_DEBUG 981 printf ("Chassis : Appending [%s] = [%d]\n", vpd_key_names[i], chassis_type); 982 #endif 983 info[i] = std::make_pair(vpd_key_names[i], 984 std::to_string(chassis_type)); 985 continue; 986 } 987 info[i] = std::make_pair(vpd_key_names[i], 988 std::string(reinterpret_cast<char*> 989 (vpd_info[i].type_length_field))); 990 991 } 992 break; 993 case IPMI_FRU_AREA_BOARD_INFO: 994 #if IPMI_FRU_PARSER_DEBUG 995 printf ("Board : Buf len = [%d]\n", len); 996 #endif 997 ipmi_fru_board_info_area ((uint8_t*)msgbuf+2, 998 len, 999 NULL, 1000 &mfg_date_time, 1001 &vpd_info [OPENBMC_VPD_KEY_BOARD_MFR], 1002 &vpd_info [OPENBMC_VPD_KEY_BOARD_NAME], 1003 &vpd_info [OPENBMC_VPD_KEY_BOARD_SERIAL_NUM], 1004 &vpd_info [OPENBMC_VPD_KEY_BOARD_PART_NUM], 1005 &vpd_info [OPENBMC_VPD_KEY_BOARD_FRU_FILE_ID], 1006 &vpd_info [OPENBMC_VPD_KEY_BOARD_CUSTOM1], 1007 OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX); 1008 1009 /* Populate VPD Table */ 1010 for (i=OPENBMC_VPD_KEY_BOARD_MFR; i<=OPENBMC_VPD_KEY_BOARD_MAX; i++) 1011 { 1012 if (i==OPENBMC_VPD_KEY_BOARD_MFG_DATE) 1013 { 1014 _to_time_str (mfg_date_time, timestr, OPENBMC_VPD_VAL_LEN); 1015 #if IPMI_FRU_PARSER_DEBUG 1016 printf ("Board : Appending [%s] = [%d]\n", vpd_key_names[i], timestr); 1017 #endif 1018 info[i] = std::make_pair(vpd_key_names[i], 1019 std::string(timestr)); 1020 continue; 1021 } 1022 info[i] = std::make_pair(vpd_key_names[i], 1023 std::string(reinterpret_cast<char*> 1024 (vpd_info[i].type_length_field))); 1025 1026 } 1027 break; 1028 case IPMI_FRU_AREA_PRODUCT_INFO: 1029 #if IPMI_FRU_PARSER_DEBUG 1030 printf ("Product : Buf len = [%d]\n", len); 1031 #endif 1032 ipmi_fru_product_info_area ((uint8_t*)msgbuf+2, 1033 len, 1034 NULL, 1035 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_MFR], 1036 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_NAME], 1037 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_PART_MODEL_NUM], 1038 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_VER], 1039 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_SERIAL_NUM], 1040 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_ASSET_TAG], 1041 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_FRU_FILE_ID], 1042 &vpd_info [OPENBMC_VPD_KEY_PRODUCT_CUSTOM1], 1043 OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX); 1044 1045 for (i=OPENBMC_VPD_KEY_PRODUCT_MFR; i<=OPENBMC_VPD_KEY_PRODUCT_MAX; i++) 1046 { 1047 info[i] = std::make_pair(vpd_key_names[i], 1048 std::string(reinterpret_cast<char*> 1049 (vpd_info[i].type_length_field))); 1050 } 1051 break; 1052 default: 1053 /* TODO: Parse Multi Rec / Internal use area */ 1054 break; 1055 } 1056 1057 #if IPMI_FRU_PARSER_DEBUG 1058 printf ("parse_fru_area : Dictionary Packing Complete\n"); 1059 #endif 1060 rv = 0; 1061 return (rv); 1062 } 1063