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 struct ipmi_fru_field 85 { 86 uint8_t type_length_field[IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX]; 87 /* store length of data stored in buffer */ 88 unsigned int type_length_field_length; 89 }; 90 91 typedef struct ipmi_fru_field ipmi_fru_field_t; 92 /* 93 * FRU Parser 94 */ 95 96 typedef struct ipmi_fru_area_info 97 { 98 uint8_t off; 99 uint8_t len; 100 } ipmi_fru_area_info_t; 101 102 typedef struct ipmi_fru_common_hdr 103 { 104 uint8_t fmtver; 105 uint8_t internal; 106 uint8_t chassis; 107 uint8_t board; 108 uint8_t product; 109 uint8_t multirec; 110 } __attribute__((packed)) ipmi_fru_common_hdr_t; 111 112 const char* vpd_key_names[] = { 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", 127 /* OPENBMC_VPD_KEY_BOARD_MFG_DATE, */ /* not a type/len */ 128 "Manufacturer", /* OPENBMC_VPD_KEY_BOARD_MFR, */ 129 "Name", /* OPENBMC_VPD_KEY_BOARD_NAME, */ 130 "Serial Number", /* OPENBMC_VPD_KEY_BOARD_SERIAL_NUM, */ 131 "Part Number", /* OPENBMC_VPD_KEY_BOARD_PART_NUM, */ 132 "FRU File ID", /* OPENBMC_VPD_KEY_BOARD_FRU_FILE_ID, */ 133 "Custom Field 1", /*OPENBMC_VPD_KEY_BOARD_CUSTOM1,*/ 134 "Custom Field 2", /*OPENBMC_VPD_KEY_BOARD_CUSTOM2,*/ 135 "Custom Field 3", /*OPENBMC_VPD_KEY_BOARD_CUSTOM3,*/ 136 "Custom Field 4", /*OPENBMC_VPD_KEY_BOARD_CUSTOM4,*/ 137 "Custom Field 5", /*OPENBMC_VPD_KEY_BOARD_CUSTOM5,*/ 138 "Custom Field 6", /*OPENBMC_VPD_KEY_BOARD_CUSTOM6,*/ 139 "Custom Field 7", /*OPENBMC_VPD_KEY_BOARD_CUSTOM7,*/ 140 "Custom Field 8", /*OPENBMC_VPD_KEY_BOARD_CUSTOM8,*/ 141 142 "Manufacturer", /* OPENBMC_VPD_KEY_PRODUCT_MFR, */ 143 "Name", /* OPENBMC_VPD_KEY_PRODUCT_NAME, */ 144 "Model Number", /* OPENBMC_VPD_KEY_PRODUCT_PART_MODEL_NUM, */ 145 "Version", /* OPENBMC_VPD_KEY_PRODUCT_VER, */ 146 "Serial Number", /* OPENBMC_VPD_KEY_PRODUCT_SERIAL_NUM, */ 147 "Asset Tag", /* OPENBMC_VPD_KEY_PRODUCT_ASSET_TAG, */ 148 "FRU File ID", /* OPENBMC_VPD_KEY_PRODUCT_FRU_FILE_ID, */ 149 "Custom Field 1", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM1,*/ 150 "Custom Field 2", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM2,*/ 151 "Custom Field 3", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM3,*/ 152 "Custom Field 4", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM4,*/ 153 "Custom Field 5", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM5,*/ 154 "Custom Field 6", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM6,*/ 155 "Custom Field 7", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM7,*/ 156 "Custom Field 8", /*OPENBMC_VPD_KEY_PRODUCT_CUSTOM8,*/ 157 158 "Key Names Table End" /*OPENBMC_VPD_KEY_MAX,*/ 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 _parse_type_length(const void* areabuf, unsigned int areabuflen, 187 unsigned int current_area_offset, 188 uint8_t* number_of_data_bytes, 189 ipmi_fru_field_t* field) 190 { 191 const uint8_t* areabufptr = (const uint8_t*)areabuf; 192 uint8_t type_length; 193 uint8_t type_code; 194 195 ASSERT(areabuf); 196 ASSERT(areabuflen); 197 ASSERT(number_of_data_bytes); 198 199 type_length = areabufptr[current_area_offset]; 200 201 /* ipmi workaround 202 * 203 * dell p weredge r610 204 * 205 * my reading of the fru spec is that all non-custom fields are 206 * required to be listed by the vendor. however, on this 207 * motherboard, some areas list this, indicating that there is 208 * no more data to be parsed. so now, for "required" fields, i 209 * check to see if the type-length field is a sentinel before 210 * calling this function. 211 */ 212 213 ASSERT(type_length != IPMI_FRU_SENTINEL_VALUE); 214 215 type_code = (type_length & IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK) >> 216 IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT; 217 (*number_of_data_bytes) = 218 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, '\0', 237 IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX); 238 memcpy(field->type_length_field, &areabufptr[current_area_offset], 239 1 + (*number_of_data_bytes)); 240 field->type_length_field_length = 1 + (*number_of_data_bytes); 241 } 242 243 return (0); 244 } 245 246 int ipmi_fru_chassis_info_area(const void* areabuf, unsigned int areabuflen, 247 uint8_t* chassis_type, 248 ipmi_fru_field_t* chassis_part_number, 249 ipmi_fru_field_t* chassis_serial_number, 250 ipmi_fru_field_t* chassis_custom_fields, 251 unsigned int chassis_custom_fields_len) 252 { 253 const uint8_t* areabufptr = (const uint8_t*)areabuf; 254 unsigned int area_offset = 0; 255 unsigned int custom_fields_index = 0; 256 uint8_t number_of_data_bytes; 257 int rv = -1; 258 259 if (!areabuf || !areabuflen) 260 { 261 return (-1); 262 } 263 264 if (chassis_part_number) 265 memset(chassis_part_number, '\0', sizeof(ipmi_fru_field_t)); 266 if (chassis_serial_number) 267 memset(chassis_serial_number, '\0', sizeof(ipmi_fru_field_t)); 268 if (chassis_custom_fields && chassis_custom_fields_len) 269 memset(chassis_custom_fields, '\0', 270 sizeof(ipmi_fru_field_t) * chassis_custom_fields_len); 271 272 if (chassis_type) 273 (*chassis_type) = areabufptr[area_offset]; 274 area_offset++; 275 276 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 277 goto out; 278 279 if (_parse_type_length(areabufptr, areabuflen, area_offset, 280 &number_of_data_bytes, chassis_part_number) < 0) 281 goto cleanup; 282 area_offset += 1; /* type/length byte */ 283 area_offset += number_of_data_bytes; 284 285 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 286 goto out; 287 288 if (_parse_type_length(areabufptr, areabuflen, area_offset, 289 &number_of_data_bytes, chassis_serial_number) < 0) 290 goto cleanup; 291 area_offset += 1; /* type/length byte */ 292 area_offset += number_of_data_bytes; 293 294 while (area_offset < areabuflen && 295 areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE) 296 { 297 ipmi_fru_field_t* field_ptr = NULL; 298 299 if (chassis_custom_fields && chassis_custom_fields_len) 300 { 301 if (custom_fields_index < chassis_custom_fields_len) 302 field_ptr = &chassis_custom_fields[custom_fields_index]; 303 else 304 { 305 goto cleanup; 306 } 307 } 308 309 if (_parse_type_length(areabufptr, areabuflen, area_offset, 310 &number_of_data_bytes, field_ptr) < 0) 311 goto cleanup; 312 313 area_offset += 1; /* type/length byte */ 314 area_offset += number_of_data_bytes; 315 custom_fields_index++; 316 } 317 318 out: 319 rv = 0; 320 cleanup: 321 return (rv); 322 } 323 324 int ipmi_fru_board_info_area( 325 const void* areabuf, unsigned int areabuflen, uint8_t* language_code, 326 uint32_t* mfg_date_time, ipmi_fru_field_t* board_manufacturer, 327 ipmi_fru_field_t* board_product_name, ipmi_fru_field_t* board_serial_number, 328 ipmi_fru_field_t* board_part_number, ipmi_fru_field_t* board_fru_file_id, 329 ipmi_fru_field_t* board_custom_fields, unsigned int board_custom_fields_len) 330 { 331 const uint8_t* areabufptr = (const uint8_t*)areabuf; 332 uint32_t mfg_date_time_tmp = 0; 333 unsigned int area_offset = 0; 334 unsigned int custom_fields_index = 0; 335 uint8_t number_of_data_bytes; 336 int rv = -1; 337 338 if (!areabuf || !areabuflen) 339 { 340 return (-1); 341 } 342 343 if (board_manufacturer) 344 memset(board_manufacturer, '\0', sizeof(ipmi_fru_field_t)); 345 if (board_product_name) 346 memset(board_product_name, '\0', sizeof(ipmi_fru_field_t)); 347 if (board_serial_number) 348 memset(board_serial_number, '\0', sizeof(ipmi_fru_field_t)); 349 if (board_part_number) 350 memset(board_part_number, '\0', sizeof(ipmi_fru_field_t)); 351 if (board_fru_file_id) 352 memset(board_fru_file_id, '\0', sizeof(ipmi_fru_field_t)); 353 if (board_custom_fields && board_custom_fields_len) 354 memset(board_custom_fields, '\0', 355 sizeof(ipmi_fru_field_t) * board_custom_fields_len); 356 357 if (language_code) 358 (*language_code) = areabufptr[area_offset]; 359 area_offset++; 360 361 if (mfg_date_time) 362 { 363 struct tm tm; 364 time_t t; 365 366 /* mfg_date_time is little endian - see spec */ 367 mfg_date_time_tmp |= areabufptr[area_offset]; 368 area_offset++; 369 mfg_date_time_tmp |= (areabufptr[area_offset] << 8); 370 area_offset++; 371 mfg_date_time_tmp |= (areabufptr[area_offset] << 16); 372 area_offset++; 373 374 /* mfg_date_time is in minutes, so multiple by 60 to get seconds */ 375 mfg_date_time_tmp *= 60; 376 377 /* posix says individual calls need not clear/set all portions of 378 * 'struct tm', thus passing 'struct tm' between functions could 379 * have issues. so we need to memset. 380 */ 381 memset(&tm, '\0', sizeof(struct tm)); 382 383 /* in fru, epoch is 0:00 hrs 1/1/96 384 * 385 * so convert into ansi epoch 386 */ 387 388 tm.tm_year = 96; /* years since 1900 */ 389 tm.tm_mon = 0; /* months since january */ 390 tm.tm_mday = 1; /* 1-31 */ 391 tm.tm_hour = 0; 392 tm.tm_min = 0; 393 tm.tm_sec = 0; 394 tm.tm_isdst = -1; 395 396 if ((t = mktime(&tm)) == (time_t)-1) 397 { 398 goto cleanup; 399 } 400 401 mfg_date_time_tmp += (uint32_t)t; 402 (*mfg_date_time) = mfg_date_time_tmp; 403 } 404 else 405 area_offset += 3; 406 407 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 408 goto out; 409 410 if (_parse_type_length(areabufptr, areabuflen, area_offset, 411 &number_of_data_bytes, board_manufacturer) < 0) 412 goto cleanup; 413 area_offset += 1; /* type/length byte */ 414 area_offset += number_of_data_bytes; 415 416 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 417 goto out; 418 419 if (_parse_type_length(areabufptr, areabuflen, area_offset, 420 &number_of_data_bytes, board_product_name) < 0) 421 goto cleanup; 422 area_offset += 1; /* type/length byte */ 423 area_offset += number_of_data_bytes; 424 425 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 426 goto out; 427 428 if (_parse_type_length(areabufptr, areabuflen, area_offset, 429 &number_of_data_bytes, board_serial_number) < 0) 430 goto cleanup; 431 area_offset += 1; /* type/length byte */ 432 area_offset += number_of_data_bytes; 433 434 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 435 goto out; 436 437 if (_parse_type_length(areabufptr, areabuflen, area_offset, 438 &number_of_data_bytes, board_part_number) < 0) 439 goto cleanup; 440 area_offset += 1; /* type/length byte */ 441 area_offset += number_of_data_bytes; 442 443 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 444 goto out; 445 446 if (_parse_type_length(areabufptr, areabuflen, area_offset, 447 &number_of_data_bytes, board_fru_file_id) < 0) 448 goto cleanup; 449 area_offset += 1; /* type/length byte */ 450 area_offset += number_of_data_bytes; 451 452 while (area_offset < areabuflen && 453 areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE) 454 { 455 ipmi_fru_field_t* field_ptr = NULL; 456 457 if (board_custom_fields && board_custom_fields_len) 458 { 459 if (custom_fields_index < board_custom_fields_len) 460 field_ptr = &board_custom_fields[custom_fields_index]; 461 else 462 { 463 goto cleanup; 464 } 465 } 466 467 if (_parse_type_length(areabufptr, areabuflen, area_offset, 468 &number_of_data_bytes, field_ptr) < 0) 469 goto cleanup; 470 471 area_offset += 1; /* type/length byte */ 472 area_offset += number_of_data_bytes; 473 custom_fields_index++; 474 } 475 476 out: 477 rv = 0; 478 cleanup: 479 return (rv); 480 } 481 482 int ipmi_fru_product_info_area( 483 const void* areabuf, unsigned int areabuflen, uint8_t* language_code, 484 ipmi_fru_field_t* product_manufacturer_name, ipmi_fru_field_t* product_name, 485 ipmi_fru_field_t* product_part_model_number, 486 ipmi_fru_field_t* product_version, ipmi_fru_field_t* product_serial_number, 487 ipmi_fru_field_t* product_asset_tag, ipmi_fru_field_t* product_fru_file_id, 488 ipmi_fru_field_t* product_custom_fields, 489 unsigned int product_custom_fields_len) 490 { 491 const uint8_t* areabufptr = (const uint8_t*)areabuf; 492 unsigned int area_offset = 0; 493 unsigned int custom_fields_index = 0; 494 uint8_t number_of_data_bytes; 495 int rv = -1; 496 497 if (!areabuf || !areabuflen) 498 { 499 return (-1); 500 } 501 502 if (product_manufacturer_name) 503 memset(product_manufacturer_name, '\0', sizeof(ipmi_fru_field_t)); 504 if (product_name) 505 memset(product_name, '\0', sizeof(ipmi_fru_field_t)); 506 if (product_part_model_number) 507 memset(product_part_model_number, '\0', sizeof(ipmi_fru_field_t)); 508 if (product_version) 509 memset(product_version, '\0', sizeof(ipmi_fru_field_t)); 510 if (product_serial_number) 511 memset(product_serial_number, '\0', sizeof(ipmi_fru_field_t)); 512 if (product_asset_tag) 513 memset(product_asset_tag, '\0', sizeof(ipmi_fru_field_t)); 514 if (product_fru_file_id) 515 memset(product_fru_file_id, '\0', sizeof(ipmi_fru_field_t)); 516 if (product_custom_fields && product_custom_fields_len) 517 memset(product_custom_fields, '\0', 518 sizeof(ipmi_fru_field_t) * product_custom_fields_len); 519 520 if (language_code) 521 (*language_code) = areabufptr[area_offset]; 522 area_offset++; 523 524 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 525 goto out; 526 527 if (_parse_type_length(areabufptr, areabuflen, area_offset, 528 &number_of_data_bytes, 529 product_manufacturer_name) < 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_name) < 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, 548 product_part_model_number) < 0) 549 goto cleanup; 550 area_offset += 1; /* type/length byte */ 551 area_offset += number_of_data_bytes; 552 553 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 554 goto out; 555 556 if (_parse_type_length(areabufptr, areabuflen, area_offset, 557 &number_of_data_bytes, product_version) < 0) 558 goto cleanup; 559 area_offset += 1; /* type/length byte */ 560 area_offset += number_of_data_bytes; 561 562 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 563 goto out; 564 565 if (_parse_type_length(areabufptr, areabuflen, area_offset, 566 &number_of_data_bytes, product_serial_number) < 0) 567 goto cleanup; 568 area_offset += 1; /* type/length byte */ 569 area_offset += number_of_data_bytes; 570 571 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 572 goto out; 573 574 if (_parse_type_length(areabufptr, areabuflen, area_offset, 575 &number_of_data_bytes, product_asset_tag) < 0) 576 goto cleanup; 577 area_offset += 1; /* type/length byte */ 578 area_offset += number_of_data_bytes; 579 580 if (areabufptr[area_offset] == IPMI_FRU_SENTINEL_VALUE) 581 goto out; 582 583 if (_parse_type_length(areabufptr, areabuflen, area_offset, 584 &number_of_data_bytes, product_fru_file_id) < 0) 585 goto cleanup; 586 587 area_offset += 1; /* type/length byte */ 588 area_offset += number_of_data_bytes; 589 590 while (area_offset < areabuflen && 591 areabufptr[area_offset] != IPMI_FRU_SENTINEL_VALUE) 592 { 593 ipmi_fru_field_t* field_ptr = NULL; 594 595 if (product_custom_fields && product_custom_fields_len) 596 { 597 if (custom_fields_index < product_custom_fields_len) 598 field_ptr = &product_custom_fields[custom_fields_index]; 599 else 600 { 601 goto cleanup; 602 } 603 } 604 605 if (_parse_type_length(areabufptr, areabuflen, area_offset, 606 &number_of_data_bytes, field_ptr) < 0) 607 goto cleanup; 608 609 area_offset += 1; /* type/length byte */ 610 area_offset += number_of_data_bytes; 611 custom_fields_index++; 612 } 613 614 out: 615 rv = 0; 616 cleanup: 617 return (rv); 618 } 619 620 void _append_to_dict(uint8_t vpd_key_id, uint8_t* vpd_key_val, 621 IPMIFruInfo& info) 622 { 623 int type_length = vpd_key_val[0]; 624 int type_code = (type_length & IPMI_FRU_TYPE_LENGTH_TYPE_CODE_MASK) >> 625 IPMI_FRU_TYPE_LENGTH_TYPE_CODE_SHIFT; 626 int vpd_val_len = 627 type_length & IPMI_FRU_TYPE_LENGTH_NUMBER_OF_DATA_BYTES_MASK; 628 629 /* Needed to convert each uint8_t byte to a ascii */ 630 char bin_byte[3] = {0}; 631 632 /* 633 * Max number of characters needed to represent 1 unsigned byte in string 634 * is number of bytes multiplied by 2. Extra 3 for 0x and a ending '\0'; 635 */ 636 char bin_in_ascii_len = vpd_val_len * 2 + 3; 637 638 /* Binary converted to ascii in array */ 639 char* bin_in_ascii = (char*)malloc(bin_in_ascii_len); 640 641 /* For reading byte from the area */ 642 int val = 0; 643 644 char* bin_copy = &((char*)bin_in_ascii)[2]; 645 646 switch (type_code) 647 { 648 case 0: 649 memset(bin_in_ascii, 0x0, bin_in_ascii_len); 650 651 /* Offset 1 is where actual data starts */ 652 for (val = 1; val <= vpd_val_len; val++) 653 { 654 /* 2 bytes for data and 1 for terminating '\0' */ 655 snprintf(bin_byte, 3, "%02x", vpd_key_val[val]); 656 657 /* Its a running string so strip off the '\0' */ 658 strncat(bin_copy, bin_byte, 2); 659 } 660 661 /* We need the data represented as 0x...... */ 662 if (vpd_val_len > 0) 663 { 664 memcpy(bin_in_ascii, "0x", 2); 665 } 666 #if IPMI_FRU_PARSER_DEBUG 667 printf("_append_to_dict: VPD Key = [%s] : Type Code = [BINARY] :" 668 " Len = [%d] : Val = [%s]\n", 669 vpd_key_names[vpd_key_id], vpd_val_len, bin_in_ascii); 670 #endif 671 info[vpd_key_id] = 672 std::make_pair(vpd_key_names[vpd_key_id], bin_in_ascii); 673 break; 674 675 case 3: 676 #if IPMI_FRU_PARSER_DEBUG 677 printf("_append_to_dict: VPD Key = [%s] : Type Code=[ASCII+Latin]" 678 " : Len = [%d] : Val = [%s]\n", 679 vpd_key_names[vpd_key_id], vpd_val_len, &vpd_key_val[1]); 680 #endif 681 info[vpd_key_id] = std::make_pair( 682 vpd_key_names[vpd_key_id], 683 std::string(vpd_key_val + 1, vpd_key_val + 1 + type_length)); 684 break; 685 } 686 687 if (bin_in_ascii) 688 { 689 free(bin_in_ascii); 690 bin_in_ascii = NULL; 691 } 692 } 693 694 int parse_fru_area(const uint8_t area, const void* msgbuf, const size_t len, 695 IPMIFruInfo& info) 696 { 697 int rv = -1; 698 int i = 0; 699 700 /* Chassis */ 701 uint8_t chassis_type; 702 /* Board */ 703 uint32_t mfg_date_time; 704 /* Product */ 705 // unsigned int product_custom_fields_len; 706 707 // ipmi_fru_area_info_t fru_area_info [ IPMI_FRU_AREA_TYPE_MAX ]; 708 ipmi_fru_field_t vpd_info[OPENBMC_VPD_KEY_MAX]; 709 char timestr[OPENBMC_VPD_VAL_LEN]; 710 711 // uint8_t* ipmi_fru_field_str=NULL; 712 // ipmi_fru_common_hdr_t* chdr = NULL; 713 // uint8_t* hdr = NULL; 714 715 ASSERT(msgbuf); 716 717 for (i = 0; i < OPENBMC_VPD_KEY_MAX; i++) 718 { 719 memset(vpd_info[i].type_length_field, '\0', 720 IPMI_FRU_AREA_TYPE_LENGTH_FIELD_MAX); 721 vpd_info[i].type_length_field_length = 0; 722 } 723 724 switch (area) 725 { 726 case IPMI_FRU_AREA_CHASSIS_INFO: 727 #if IPMI_FRU_PARSER_DEBUG 728 printf("Chassis : Buf len = [%d]\n", len); 729 #endif 730 ipmi_fru_chassis_info_area( 731 (uint8_t*)msgbuf + 2, len, &chassis_type, 732 &vpd_info[OPENBMC_VPD_KEY_CHASSIS_PART_NUM], 733 &vpd_info[OPENBMC_VPD_KEY_CHASSIS_SERIAL_NUM], 734 &vpd_info[OPENBMC_VPD_KEY_CHASSIS_CUSTOM1], 735 OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX); 736 737 /* Populate VPD Table */ 738 for (i = 1; i <= OPENBMC_VPD_KEY_CHASSIS_MAX; i++) 739 { 740 if (i == OPENBMC_VPD_KEY_CHASSIS_TYPE) 741 { 742 #if IPMI_FRU_PARSER_DEBUG 743 printf("Chassis : Appending [%s] = [%d]\n", 744 vpd_key_names[i], chassis_type); 745 #endif 746 info[i] = std::make_pair(vpd_key_names[i], 747 std::to_string(chassis_type)); 748 continue; 749 } 750 _append_to_dict(i, vpd_info[i].type_length_field, info); 751 } 752 break; 753 case IPMI_FRU_AREA_BOARD_INFO: 754 #if IPMI_FRU_PARSER_DEBUG 755 printf("Board : Buf len = [%d]\n", len); 756 #endif 757 ipmi_fru_board_info_area( 758 (uint8_t*)msgbuf + 2, len, NULL, &mfg_date_time, 759 &vpd_info[OPENBMC_VPD_KEY_BOARD_MFR], 760 &vpd_info[OPENBMC_VPD_KEY_BOARD_NAME], 761 &vpd_info[OPENBMC_VPD_KEY_BOARD_SERIAL_NUM], 762 &vpd_info[OPENBMC_VPD_KEY_BOARD_PART_NUM], 763 &vpd_info[OPENBMC_VPD_KEY_BOARD_FRU_FILE_ID], 764 &vpd_info[OPENBMC_VPD_KEY_BOARD_CUSTOM1], 765 OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX); 766 767 /* Populate VPD Table */ 768 for (i = OPENBMC_VPD_KEY_BOARD_MFG_DATE; 769 i <= OPENBMC_VPD_KEY_BOARD_MAX; i++) 770 { 771 if (i == OPENBMC_VPD_KEY_BOARD_MFG_DATE) 772 { 773 _to_time_str(mfg_date_time, timestr, OPENBMC_VPD_VAL_LEN); 774 #if IPMI_FRU_PARSER_DEBUG 775 printf("Board : Appending [%s] = [%s]\n", vpd_key_names[i], 776 timestr); 777 #endif 778 info[i] = 779 std::make_pair(vpd_key_names[i], std::string(timestr)); 780 continue; 781 } 782 _append_to_dict(i, vpd_info[i].type_length_field, info); 783 } 784 break; 785 case IPMI_FRU_AREA_PRODUCT_INFO: 786 #if IPMI_FRU_PARSER_DEBUG 787 printf("Product : Buf len = [%d]\n", len); 788 #endif 789 ipmi_fru_product_info_area( 790 (uint8_t*)msgbuf + 2, len, NULL, 791 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_MFR], 792 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_NAME], 793 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_PART_MODEL_NUM], 794 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_VER], 795 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_SERIAL_NUM], 796 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_ASSET_TAG], 797 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_FRU_FILE_ID], 798 &vpd_info[OPENBMC_VPD_KEY_PRODUCT_CUSTOM1], 799 OPENBMC_VPD_KEY_CUSTOM_FIELDS_MAX); 800 801 for (i = OPENBMC_VPD_KEY_PRODUCT_MFR; 802 i <= OPENBMC_VPD_KEY_PRODUCT_MAX; ++i) 803 { 804 _append_to_dict(i, vpd_info[i].type_length_field, info); 805 } 806 break; 807 default: 808 /* TODO: Parse Multi Rec / Internal use area */ 809 break; 810 } 811 812 #if IPMI_FRU_PARSER_DEBUG 813 printf("parse_fru_area : Dictionary Packing Complete\n"); 814 #endif 815 rv = 0; 816 return (rv); 817 } 818