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 #define TEXTSTR(a) #a 53 #define ASSERT(x) \ 54 do \ 55 { \ 56 if (0 == (x)) \ 57 { \ 58 fprintf(stderr, \ 59 "Assertion failed: %s, " \ 60 "%d at \'%s\'\n", \ 61 __FILE__, __LINE__, TEXTSTR(a)); \ 62 return -1; \ 63 } \ 64 } while (0) 65 66 #define IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX 512 67 #define IPMI_FRU_SENTINEL_VALUE 0xC1 68 #define IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK 0xC0 69 #define IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT 0x06 70 #define IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK 0x3F 71 #define IPMI_FRU_TYPE_LENGTH_TYPE_CODE_LANGUAGE_CODE 0x03 72 73 /* OpenBMC defines for Parser */ 74 #define IPMI_FRU_AREA_INTERNAL_USE 0x00 75 #define IPMI_FRU_AREA_CHASSIS_INFO 0x01 76 #define IPMI_FRU_AREA_BOARD_INFO 0x02 77 #define IPMI_FRU_AREA_PRODUCT_INFO 0x03 78 #define IPMI_FRU_AREA_MULTI_RECORD 0x04 79 #define IPMI_FRU_AREA_TYPE_MAX 0x05 80 81 #define OPENBMC_VPD_KEY_LEN 64 82 #define OPENBMC_VPD_VAL_LEN 512 83 84 constexpr long fruEpochMinutes = 820454400; 85 86 struct ipmi_fru_field 87 { 88 uint8_t type_length_field[IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX]; 89 /* store length of data stored in buffer */ 90 unsigned int type_length_field_length; 91 }; 92 93 typedef struct ipmi_fru_field ipmi_fru_field_t; 94 /* 95 * FRU Parser 96 */ 97 98 typedef struct ipmi_fru_area_info 99 { 100 uint8_t off; 101 uint8_t len; 102 } ipmi_fru_area_info_t; 103 104 typedef struct ipmi_fru_common_hdr 105 { 106 uint8_t fmtver; 107 uint8_t internal; 108 uint8_t chassis; 109 uint8_t board; 110 uint8_t product; 111 uint8_t multirec; 112 } __attribute__((packed)) ipmi_fru_common_hdr_t; 113 114 const char* vpd_key_names[] = { 115 "Key Names Table Start", 116 "Type", /*OPENBMC_VPD_KEY_CHASSIS_TYPE*/ 117 "Part Number", /*OPENBMC_VPD_KEY_CHASSIS_PART_NUM,*/ 118 "Serial Number", /*OPENBMC_VPD_KEY_CHASSIS_SERIAL_NUM,*/ 119 "Custom Field 1", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM1,*/ 120 "Custom Field 2", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM2,*/ 121 "Custom Field 3", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM3,*/ 122 "Custom Field 4", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM4,*/ 123 "Custom Field 5", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM5,*/ 124 "Custom Field 6", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM6,*/ 125 "Custom Field 7", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM7,*/ 126 "Custom Field 8", /*OPENBMC_VPD_KEY_CHASSIS_CUSTOM8,*/ 127 128 "Mfg Date", 129 /* OPENBMC_VPD_KEY_BOARD_MFG_DATE, */ /* not a type/len */ 130 "Manufacturer", /* OPENBMC_VPD_KEY_BOARD_MFR, */ 131 "Name", /* OPENBMC_VPD_KEY_BOARD_NAME, */ 132 "Serial Number", /* OPENBMC_VPD_KEY_BOARD_SERIAL_NUM, */ 133 "Part Number", /* OPENBMC_VPD_KEY_BOARD_PART_NUM, */ 134 "FRU File ID", /* OPENBMC_VPD_KEY_BOARD_FRU_FILE_ID, */ 135 "Custom Field 1", /*OPENBMC_VPD_KEY_BOARD_CUSTOM1,*/ 136 "Custom Field 2", /*OPENBMC_VPD_KEY_BOARD_CUSTOM2,*/ 137 "Custom Field 3", /*OPENBMC_VPD_KEY_BOARD_CUSTOM3,*/ 138 "Custom Field 4", /*OPENBMC_VPD_KEY_BOARD_CUSTOM4,*/ 139 "Custom Field 5", /*OPENBMC_VPD_KEY_BOARD_CUSTOM5,*/ 140 "Custom Field 6", /*OPENBMC_VPD_KEY_BOARD_CUSTOM6,*/ 141 "Custom Field 7", /*OPENBMC_VPD_KEY_BOARD_CUSTOM7,*/ 142 "Custom Field 8", /*OPENBMC_VPD_KEY_BOARD_CUSTOM8,*/ 143 144 "Manufacturer", /* OPENBMC_VPD_KEY_PRODUCT_MFR, */ 145 "Name", /* OPENBMC_VPD_KEY_PRODUCT_NAME, */ 146 "Model Number", /* OPENBMC_VPD_KEY_PRODUCT_PART_MODEL_NUM, */ 147 "Version", /* OPENBMC_VPD_KEY_PRODUCT_VER, */ 148 "Serial Number", /* OPENBMC_VPD_KEY_PRODUCT_SERIAL_NUM, */ 149 "Asset Tag", /* OPENBMC_VPD_KEY_PRODUCT_ASSET_TAG, */ 150 "FRU File ID", /* OPENBMC_VPD_KEY_PRODUCT_FRU_FILE_ID, */ 151 "Custom Field 1", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM1,*/ 152 "Custom Field 2", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM2,*/ 153 "Custom Field 3", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM3,*/ 154 "Custom Field 4", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM4,*/ 155 "Custom Field 5", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM5,*/ 156 "Custom Field 6", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM6,*/ 157 "Custom Field 7", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM7,*/ 158 "Custom Field 8", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM8,*/ 159 160 "Key Names Table End" /*OPENBMC_VPD_KEY_MAX,*/ 161 }; 162 163 /* 164 * -------------------------------------------------------------------- 165 * 166 * -------------------------------------------------------------------- 167 */ 168 169 static size_t _to_time_str(uint32_t mfg_date_time, char* timestr, uint32_t len) 170 { 171 struct tm tm; 172 time_t t; 173 size_t s; 174 175 ASSERT(timestr); 176 ASSERT(len); 177 178 memset(&tm, '\0', sizeof(struct tm)); 179 180 t = mfg_date_time; 181 gmtime_r(&t, &tm); 182 s = strftime(timestr, len, "%F - %H:%M:%S UTC", &tm); 183 184 return s; 185 } 186 187 /* private method to parse type/length */ 188 static int _parse_type_length(const void* areabuf, 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) >> 218 IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT; 219 (*number_of_data_bytes) = type_length & 220 IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK; 221 222 /* special case: this shouldn't be a length of 0x01 (see type/length 223 * byte format in fru information storage definition). 224 */ 225 if (type_code == IPMI_FRU_TYPE_LENGTH_TYPE_CODE_LANGUAGE_CODE && 226 (*number_of_data_bytes) == 0x01) 227 { 228 return (-1); 229 } 230 231 if ((current_area_offset + 1 + (*number_of_data_bytes)) > areabuflen) 232 { 233 return (-1); 234 } 235 236 if (field) 237 { 238 memset(field->type_length_field, '\0', 239 IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX); 240 memcpy(field->type_length_field, &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 ipmi_fru_chassis_info_area(const void* areabuf, unsigned int areabuflen, 249 uint8_t* chassis_type, 250 ipmi_fru_field_t* chassis_part_number, 251 ipmi_fru_field_t* chassis_serial_number, 252 ipmi_fru_field_t* chassis_custom_fields, 253 unsigned int chassis_custom_fields_len) 254 { 255 const uint8_t* areabufptr = (const uint8_t*)areabuf; 256 unsigned int area_offset = 0; 257 unsigned int custom_fields_index = 0; 258 uint8_t number_of_data_bytes; 259 int rv = -1; 260 261 if (!areabuf || !areabuflen) 262 { 263 return (-1); 264 } 265 266 if (chassis_part_number) 267 memset(chassis_part_number, '\0', sizeof(ipmi_fru_field_t)); 268 if (chassis_serial_number) 269 memset(chassis_serial_number, '\0', sizeof(ipmi_fru_field_t)); 270 if (chassis_custom_fields && chassis_custom_fields_len) 271 memset(chassis_custom_fields, '\0', 272 sizeof(ipmi_fru_field_t) * chassis_custom_fields_len); 273 274 if (chassis_type) 275 (*chassis_type) = areabufptr[area_offset]; 276 area_offset++; 277 278 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 279 goto out; 280 281 if (_parse_type_length(areabufptr, areabuflen, area_offset, 282 &number_of_data_bytes, chassis_part_number) < 0) 283 goto cleanup; 284 area_offset += 1; /* type/length byte */ 285 area_offset += number_of_data_bytes; 286 287 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 288 goto out; 289 290 if (_parse_type_length(areabufptr, areabuflen, area_offset, 291 &number_of_data_bytes, chassis_serial_number) < 0) 292 goto cleanup; 293 area_offset += 1; /* type/length byte */ 294 area_offset += number_of_data_bytes; 295 296 while (area_offset < areabuflen && 297 areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE) 298 { 299 ipmi_fru_field_t* field_ptr = NULL; 300 301 if (chassis_custom_fields && chassis_custom_fields_len) 302 { 303 if (custom_fields_index < chassis_custom_fields_len) 304 field_ptr = &chassis_custom_fields[custom_fields_index]; 305 else 306 { 307 goto cleanup; 308 } 309 } 310 311 if (_parse_type_length(areabufptr, areabuflen, area_offset, 312 &number_of_data_bytes, field_ptr) < 0) 313 goto cleanup; 314 315 area_offset += 1; /* type/length byte */ 316 area_offset += number_of_data_bytes; 317 custom_fields_index++; 318 } 319 320 out: 321 rv = 0; 322 cleanup: 323 return (rv); 324 } 325 326 int ipmi_fru_board_info_area( 327 const void* areabuf, unsigned int areabuflen, uint8_t* language_code, 328 uint32_t* mfg_date_time, ipmi_fru_field_t* board_manufacturer, 329 ipmi_fru_field_t* board_product_name, ipmi_fru_field_t* board_serial_number, 330 ipmi_fru_field_t* board_part_number, ipmi_fru_field_t* board_fru_file_id, 331 ipmi_fru_field_t* board_custom_fields, unsigned int board_custom_fields_len) 332 { 333 const uint8_t* areabufptr = (const uint8_t*)areabuf; 334 unsigned int area_offset = 0; 335 unsigned int custom_fields_index = 0; 336 uint8_t number_of_data_bytes; 337 int rv = -1; 338 339 if (!areabuf || !areabuflen) 340 { 341 return (-1); 342 } 343 344 if (board_manufacturer) 345 memset(board_manufacturer, '\0', sizeof(ipmi_fru_field_t)); 346 if (board_product_name) 347 memset(board_product_name, '\0', sizeof(ipmi_fru_field_t)); 348 if (board_serial_number) 349 memset(board_serial_number, '\0', sizeof(ipmi_fru_field_t)); 350 if (board_part_number) 351 memset(board_part_number, '\0', sizeof(ipmi_fru_field_t)); 352 if (board_fru_file_id) 353 memset(board_fru_file_id, '\0', sizeof(ipmi_fru_field_t)); 354 if (board_custom_fields && board_custom_fields_len) 355 memset(board_custom_fields, '\0', 356 sizeof(ipmi_fru_field_t) * board_custom_fields_len); 357 358 if (language_code) 359 (*language_code) = areabufptr[area_offset]; 360 area_offset++; 361 362 if (mfg_date_time) 363 { 364 unsigned int minutes = areabufptr[area_offset]; 365 area_offset++; 366 minutes |= (areabufptr[area_offset] << 8); 367 area_offset++; 368 minutes |= (areabufptr[area_offset] << 16); 369 area_offset++; 370 371 /* In fru, epoch is 0:00 hrs 1/1/96 == 820454400 372 * Remove it directly and remove the time conversion. 373 */ 374 (*mfg_date_time) = fruEpochMinutes + static_cast<long>(minutes) * 60; 375 } 376 else 377 area_offset += 3; 378 379 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 380 goto out; 381 382 if (_parse_type_length(areabufptr, areabuflen, area_offset, 383 &number_of_data_bytes, board_manufacturer) < 0) 384 goto cleanup; 385 area_offset += 1; /* type/length byte */ 386 area_offset += number_of_data_bytes; 387 388 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 389 goto out; 390 391 if (_parse_type_length(areabufptr, areabuflen, area_offset, 392 &number_of_data_bytes, board_product_name) < 0) 393 goto cleanup; 394 area_offset += 1; /* type/length byte */ 395 area_offset += number_of_data_bytes; 396 397 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 398 goto out; 399 400 if (_parse_type_length(areabufptr, areabuflen, area_offset, 401 &number_of_data_bytes, board_serial_number) < 0) 402 goto cleanup; 403 area_offset += 1; /* type/length byte */ 404 area_offset += number_of_data_bytes; 405 406 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 407 goto out; 408 409 if (_parse_type_length(areabufptr, areabuflen, area_offset, 410 &number_of_data_bytes, board_part_number) < 0) 411 goto cleanup; 412 area_offset += 1; /* type/length byte */ 413 area_offset += number_of_data_bytes; 414 415 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 416 goto out; 417 418 if (_parse_type_length(areabufptr, areabuflen, area_offset, 419 &number_of_data_bytes, board_fru_file_id) < 0) 420 goto cleanup; 421 area_offset += 1; /* type/length byte */ 422 area_offset += number_of_data_bytes; 423 424 while (area_offset < areabuflen && 425 areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE) 426 { 427 ipmi_fru_field_t* field_ptr = NULL; 428 429 if (board_custom_fields && board_custom_fields_len) 430 { 431 if (custom_fields_index < board_custom_fields_len) 432 field_ptr = &board_custom_fields[custom_fields_index]; 433 else 434 { 435 goto cleanup; 436 } 437 } 438 439 if (_parse_type_length(areabufptr, areabuflen, area_offset, 440 &number_of_data_bytes, field_ptr) < 0) 441 goto cleanup; 442 443 area_offset += 1; /* type/length byte */ 444 area_offset += number_of_data_bytes; 445 custom_fields_index++; 446 } 447 448 out: 449 rv = 0; 450 cleanup: 451 return (rv); 452 } 453 454 int ipmi_fru_product_info_area( 455 const void* areabuf, unsigned int areabuflen, uint8_t* language_code, 456 ipmi_fru_field_t* product_manufacturer_name, ipmi_fru_field_t* product_name, 457 ipmi_fru_field_t* product_part_model_number, 458 ipmi_fru_field_t* product_version, ipmi_fru_field_t* product_serial_number, 459 ipmi_fru_field_t* product_asset_tag, ipmi_fru_field_t* product_fru_file_id, 460 ipmi_fru_field_t* product_custom_fields, 461 unsigned int product_custom_fields_len) 462 { 463 const uint8_t* areabufptr = (const uint8_t*)areabuf; 464 unsigned int area_offset = 0; 465 unsigned int custom_fields_index = 0; 466 uint8_t number_of_data_bytes; 467 int rv = -1; 468 469 if (!areabuf || !areabuflen) 470 { 471 return (-1); 472 } 473 474 if (product_manufacturer_name) 475 memset(product_manufacturer_name, '\0', sizeof(ipmi_fru_field_t)); 476 if (product_name) 477 memset(product_name, '\0', sizeof(ipmi_fru_field_t)); 478 if (product_part_model_number) 479 memset(product_part_model_number, '\0', sizeof(ipmi_fru_field_t)); 480 if (product_version) 481 memset(product_version, '\0', sizeof(ipmi_fru_field_t)); 482 if (product_serial_number) 483 memset(product_serial_number, '\0', sizeof(ipmi_fru_field_t)); 484 if (product_asset_tag) 485 memset(product_asset_tag, '\0', sizeof(ipmi_fru_field_t)); 486 if (product_fru_file_id) 487 memset(product_fru_file_id, '\0', sizeof(ipmi_fru_field_t)); 488 if (product_custom_fields && product_custom_fields_len) 489 memset(product_custom_fields, '\0', 490 sizeof(ipmi_fru_field_t) * product_custom_fields_len); 491 492 if (language_code) 493 (*language_code) = areabufptr[area_offset]; 494 area_offset++; 495 496 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 497 goto out; 498 499 if (_parse_type_length(areabufptr, areabuflen, area_offset, 500 &number_of_data_bytes, 501 product_manufacturer_name) < 0) 502 goto cleanup; 503 area_offset += 1; /* type/length byte */ 504 area_offset += number_of_data_bytes; 505 506 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 507 goto out; 508 509 if (_parse_type_length(areabufptr, areabuflen, area_offset, 510 &number_of_data_bytes, product_name) < 0) 511 goto cleanup; 512 area_offset += 1; /* type/length byte */ 513 area_offset += number_of_data_bytes; 514 515 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 516 goto out; 517 518 if (_parse_type_length(areabufptr, areabuflen, area_offset, 519 &number_of_data_bytes, 520 product_part_model_number) < 0) 521 goto cleanup; 522 area_offset += 1; /* type/length byte */ 523 area_offset += number_of_data_bytes; 524 525 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 526 goto out; 527 528 if (_parse_type_length(areabufptr, areabuflen, area_offset, 529 &number_of_data_bytes, product_version) < 0) 530 goto cleanup; 531 area_offset += 1; /* type/length byte */ 532 area_offset += number_of_data_bytes; 533 534 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 535 goto out; 536 537 if (_parse_type_length(areabufptr, areabuflen, area_offset, 538 &number_of_data_bytes, 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, areabuflen, area_offset, 547 &number_of_data_bytes, product_asset_tag) < 0) 548 goto cleanup; 549 area_offset += 1; /* type/length byte */ 550 area_offset += number_of_data_bytes; 551 552 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 553 goto out; 554 555 if (_parse_type_length(areabufptr, areabuflen, area_offset, 556 &number_of_data_bytes, product_fru_file_id) < 0) 557 goto cleanup; 558 559 area_offset += 1; /* type/length byte */ 560 area_offset += number_of_data_bytes; 561 562 while (area_offset < areabuflen && 563 areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE) 564 { 565 ipmi_fru_field_t* field_ptr = NULL; 566 567 if (product_custom_fields && product_custom_fields_len) 568 { 569 if (custom_fields_index < product_custom_fields_len) 570 field_ptr = &product_custom_fields[custom_fields_index]; 571 else 572 { 573 goto cleanup; 574 } 575 } 576 577 if (_parse_type_length(areabufptr, areabuflen, area_offset, 578 &number_of_data_bytes, field_ptr) < 0) 579 goto cleanup; 580 581 area_offset += 1; /* type/length byte */ 582 area_offset += number_of_data_bytes; 583 custom_fields_index++; 584 } 585 586 out: 587 rv = 0; 588 cleanup: 589 return (rv); 590 } 591 592 void _append_to_dict(uint8_t vpd_key_id, uint8_t* vpd_key_val, 593 IPMIFruInfo& info) 594 { 595 int type_length = vpd_key_val[0]; 596 int type_code = (type_length & IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK) >> 597 IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT; 598 int vpd_val_len = type_length & 599 IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK; 600 601 /* Needed to convert each uint8_t byte to a ascii */ 602 char bin_byte[3] = {0}; 603 604 /* 605 * Max number of characters needed to represent 1 unsigned byte in string 606 * is number of bytes multiplied by 2. Extra 3 for 0x and a ending '\0'; 607 */ 608 char bin_in_ascii_len = vpd_val_len * 2 + 3; 609 610 /* Binary converted to ascii in array */ 611 char* bin_in_ascii = (char*)malloc(bin_in_ascii_len); 612 613 /* For reading byte from the area */ 614 int val = 0; 615 616 char* bin_copy = &((char*)bin_in_ascii)[2]; 617 618 switch (type_code) 619 { 620 case 0: 621 memset(bin_in_ascii, 0x0, bin_in_ascii_len); 622 623 /* Offset 1 is where actual data starts */ 624 for (val = 1; val <= vpd_val_len; val++) 625 { 626 /* 2 bytes for data and 1 for terminating '\0' */ 627 snprintf(bin_byte, 3, "%02x", vpd_key_val[val]); 628 629 #pragma GCC diagnostic push 630 #pragma GCC diagnostic ignored "-Wstringop-truncation" 631 /* Its a running string so strip off the '\0' */ 632 strncat(bin_copy, bin_byte, 2); 633 #pragma GCC diagnostic pop 634 } 635 636 /* We need the data represented as 0x...... */ 637 if (vpd_val_len > 0) 638 { 639 memcpy(bin_in_ascii, "0x", 2); 640 } 641 #if IPMI_FRU_PARSER_DEBUG 642 printf("_append_to_dict: VPD Key = [%s] : Type Code = [BINARY] :" 643 " Len = [%d] : Val = [%s]\n", 644 vpd_key_names[vpd_key_id], vpd_val_len, bin_in_ascii); 645 #endif 646 info[vpd_key_id] = std::make_pair(vpd_key_names[vpd_key_id], 647 bin_in_ascii); 648 break; 649 650 case 3: 651 #if IPMI_FRU_PARSER_DEBUG 652 printf("_append_to_dict: VPD Key = [%s] : Type Code=[ASCII+Latin]" 653 " : Len = [%d] : Val = [%s]\n", 654 vpd_key_names[vpd_key_id], vpd_val_len, &vpd_key_val[1]); 655 #endif 656 info[vpd_key_id] = std::make_pair( 657 vpd_key_names[vpd_key_id], 658 std::string(vpd_key_val + 1, vpd_key_val + 1 + type_length)); 659 break; 660 } 661 662 if (bin_in_ascii) 663 { 664 free(bin_in_ascii); 665 bin_in_ascii = NULL; 666 } 667 } 668 669 int parse_fru_area(const uint8_t area, const void* msgbuf, const size_t len, 670 IPMIFruInfo& info) 671 { 672 int rv = -1; 673 int i = 0; 674 675 /* Chassis */ 676 uint8_t chassis_type; 677 /* Board */ 678 uint32_t mfg_date_time; 679 /* Product */ 680 // unsigned int product_custom_fields_len; 681 682 // ipmi_fru_area_info_t fru_area_info [ IPMI_FRU_AREA_TYPE_MAX ]; 683 ipmi_fru_field_t vpd_info[OPENBMC_VPD_KEY_MAX]; 684 char timestr[OPENBMC_VPD_VAL_LEN]; 685 686 // uint8_t* ipmi_fru_field_str=NULL; 687 // ipmi_fru_common_hdr_t* chdr = NULL; 688 // uint8_t* hdr = NULL; 689 690 ASSERT(msgbuf); 691 692 for (i = 0; i < OPENBMC_VPD_KEY_MAX; i++) 693 { 694 memset(vpd_info[i].type_length_field, '\0', 695 IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX); 696 vpd_info[i].type_length_field_length = 0; 697 } 698 699 switch (area) 700 { 701 case IPMI_FRU_AREA_CHASSIS_INFO: 702 #if IPMI_FRU_PARSER_DEBUG 703 printf("Chassis : Buf len = [%d]\n", len); 704 #endif 705 ipmi_fru_chassis_info_area( 706 (uint8_t*)msgbuf + 2, len, &chassis_type, 707 &vpd_info[OPENBMC_VPD_KEY_CHASSIS_PART_NUM], 708 &vpd_info[OPENBMC_VPD_KEY_CHASSIS_SERIAL_NUM], 709 &vpd_info[OPENBMC_VPD_KEY_CHASSIS_CUSTOM1], 710 OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX); 711 712 /* Populate VPD Table */ 713 for (i = 1; i <= OPENBMC_VPD_KEY_CHASSIS_MAX; i++) 714 { 715 if (i == OPENBMC_VPD_KEY_CHASSIS_TYPE) 716 { 717 #if IPMI_FRU_PARSER_DEBUG 718 printf("Chassis : Appending [%s] = [%d]\n", 719 vpd_key_names[i], chassis_type); 720 #endif 721 info[i] = std::make_pair(vpd_key_names[i], 722 std::to_string(chassis_type)); 723 continue; 724 } 725 _append_to_dict(i, vpd_info[i].type_length_field, info); 726 } 727 break; 728 case IPMI_FRU_AREA_BOARD_INFO: 729 #if IPMI_FRU_PARSER_DEBUG 730 printf("Board : Buf len = [%d]\n", len); 731 #endif 732 ipmi_fru_board_info_area( 733 (uint8_t*)msgbuf + 2, len, NULL, &mfg_date_time, 734 &vpd_info[OPENBMC_VPD_KEY_BOARD_MFR], 735 &vpd_info[OPENBMC_VPD_KEY_BOARD_NAME], 736 &vpd_info[OPENBMC_VPD_KEY_BOARD_SERIAL_NUM], 737 &vpd_info[OPENBMC_VPD_KEY_BOARD_PART_NUM], 738 &vpd_info[OPENBMC_VPD_KEY_BOARD_FRU_FILE_ID], 739 &vpd_info[OPENBMC_VPD_KEY_BOARD_CUSTOM1], 740 OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX); 741 742 /* Populate VPD Table */ 743 for (i = OPENBMC_VPD_KEY_BOARD_MFG_DATE; 744 i <= OPENBMC_VPD_KEY_BOARD_MAX; i++) 745 { 746 if (i == OPENBMC_VPD_KEY_BOARD_MFG_DATE) 747 { 748 _to_time_str(mfg_date_time, timestr, OPENBMC_VPD_VAL_LEN); 749 #if IPMI_FRU_PARSER_DEBUG 750 printf("Board : Appending [%s] = [%s]\n", vpd_key_names[i], 751 timestr); 752 #endif 753 info[i] = std::make_pair(vpd_key_names[i], 754 std::string(timestr)); 755 continue; 756 } 757 _append_to_dict(i, vpd_info[i].type_length_field, info); 758 } 759 break; 760 case IPMI_FRU_AREA_PRODUCT_INFO: 761 #if IPMI_FRU_PARSER_DEBUG 762 printf("Product : Buf len = [%d]\n", len); 763 #endif 764 ipmi_fru_product_info_area( 765 (uint8_t*)msgbuf + 2, len, NULL, 766 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_MFR], 767 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_NAME], 768 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_PART_MODEL_NUM], 769 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_VER], 770 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_SERIAL_NUM], 771 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_ASSET_TAG], 772 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_FRU_FILE_ID], 773 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_CUSTOM1], 774 OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX); 775 776 for (i = OPENBMC_VPD_KEY_PRODUCT_MFR; 777 i <= OPENBMC_VPD_KEY_PRODUCT_MAX; ++i) 778 { 779 _append_to_dict(i, vpd_info[i].type_length_field, info); 780 } 781 break; 782 default: 783 /* TODO: Parse Multi Rec / Internal use area */ 784 break; 785 } 786 787 #if IPMI_FRU_PARSER_DEBUG 788 printf("parse_fru_area : Dictionary Packing Complete\n"); 789 #endif 790 rv = 0; 791 return (rv); 792 } 793