1 #include <exception> 2 #include <vector> 3 #include <stdlib.h> 4 #include <dlfcn.h> 5 #include <errno.h> 6 #include <stdio.h> 7 #include <systemd/sd-bus.h> 8 #include <unistd.h> 9 #include <host-ipmid/ipmid-api.h> 10 #include <iostream> 11 #include <memory> 12 #include <algorithm> 13 #include <fstream> 14 #include <sstream> 15 #include <mapper.h> 16 #include "frup.hpp" 17 #include "fru-area.hpp" 18 #include <sdbusplus/server.hpp> 19 20 // OpenBMC System Manager dbus framework 21 const char *sys_object_name = "/org/openbmc/managers/System"; 22 const char *sys_intf_name = "org.openbmc.managers.System"; 23 24 extern const FruMap frus; 25 26 using Property = std::string; 27 using Value = sdbusplus::message::variant<bool, int64_t, std::string>; 28 // Association between property and its value 29 using PropertyMap = std::map<Property, Value>; 30 31 using Interface = std::string; 32 // Association between interface and the dbus property 33 using InterfaceMap = std::map<Interface, PropertyMap>; 34 35 using Object = sdbusplus::message::object_path; 36 37 // Association between object and the interfaces. 38 using ObjectMap = std::map<Object, InterfaceMap>; 39 40 //---------------------------------------------------------------- 41 // Constructor 42 //---------------------------------------------------------------- 43 ipmi_fru::ipmi_fru(const uint8_t fruid, const ipmi_fru_area_type type, 44 sd_bus *bus_type, bool bmc_fru) 45 { 46 iv_fruid = fruid; 47 iv_type = type; 48 iv_bmc_fru = bmc_fru; 49 iv_bus_type = bus_type; 50 iv_valid = false; 51 iv_data = NULL; 52 iv_present = false; 53 54 if(iv_type == IPMI_FRU_AREA_INTERNAL_USE) 55 { 56 iv_name = "INTERNAL_"; 57 } 58 else if(iv_type == IPMI_FRU_AREA_CHASSIS_INFO) 59 { 60 iv_name = "CHASSIS_"; 61 } 62 else if(iv_type == IPMI_FRU_AREA_BOARD_INFO) 63 { 64 iv_name = "BOARD_"; 65 } 66 else if(iv_type == IPMI_FRU_AREA_PRODUCT_INFO) 67 { 68 iv_name = "PRODUCT_"; 69 } 70 else if(iv_type == IPMI_FRU_AREA_MULTI_RECORD) 71 { 72 iv_name = "MULTI_"; 73 } 74 else 75 { 76 iv_name = IPMI_FRU_AREA_TYPE_MAX; 77 fprintf(stderr, "ERROR: Invalid Area type :[%d]\n",iv_type); 78 } 79 } 80 81 //----------------------------------------------------- 82 // For a FRU area type, accepts the data and updates 83 // area specific data. 84 //----------------------------------------------------- 85 void ipmi_fru::set_data(const uint8_t *data, const size_t len) 86 { 87 iv_len = len; 88 iv_data = new uint8_t[len]; 89 memcpy(iv_data, data, len); 90 } 91 92 //----------------------------------------------------- 93 // Sets the dbus parameters 94 //----------------------------------------------------- 95 void ipmi_fru::update_dbus_paths(const char *bus_name, 96 const char *obj_path, const char *intf_name) 97 { 98 iv_bus_name = bus_name; 99 iv_obj_path = obj_path; 100 iv_intf_name = intf_name; 101 } 102 103 //------------------- 104 // Destructor 105 //------------------- 106 ipmi_fru::~ipmi_fru() 107 { 108 sd_bus_error bus_error = SD_BUS_ERROR_NULL; 109 sd_bus_message *response = NULL; 110 int rc = 0; 111 112 if(iv_data != NULL) 113 { 114 delete [] iv_data; 115 iv_data = NULL; 116 } 117 118 // If we have not been successful in doing some updates and we are a BMC 119 // fru, then need to set the fault bits. 120 bool valid_dbus = !(iv_bus_name.empty()) && 121 !(iv_obj_path.empty()) && 122 !(iv_intf_name.empty()); 123 124 // Based on bmc_fru, success in updating the FRU inventory we need to set 125 // some special bits. 126 if(iv_bmc_fru && valid_dbus) 127 { 128 // Set the Fault bit if we did not successfully process the fru 129 const char *fault_bit = iv_valid ? "False" : "True"; 130 131 rc = sd_bus_call_method(iv_bus_type, // On the System Bus 132 iv_bus_name.c_str(), // Service to contact 133 iv_obj_path.c_str(), // Object path 134 iv_intf_name.c_str(), // Interface name 135 "setFault", // Method to be called 136 &bus_error, // object to return error 137 &response, // Response message on success 138 "s", // input message (string) 139 fault_bit); // First argument to setFault 140 141 if(rc <0) 142 { 143 fprintf(stderr,"Failed to set Fault bit, value:[%s] for fruid:[%d], path:[%s]\n", 144 fault_bit, iv_fruid, iv_obj_path.c_str()); 145 } 146 else 147 { 148 printf("Fault bit set to :[%s] for fruid:[%d], Path:[%s]\n", 149 fault_bit, iv_fruid,iv_obj_path.c_str()); 150 } 151 152 sd_bus_error_free(&bus_error); 153 sd_bus_message_unref(response); 154 155 // Set the Present bits 156 const char *present_bit = iv_present ? "True" : "False"; 157 158 rc = sd_bus_call_method(iv_bus_type, // On the System Bus 159 iv_bus_name.c_str(), // Service to contact 160 iv_obj_path.c_str(), // Object path 161 iv_intf_name.c_str(), // Interface name 162 "setPresent", // Method to be called 163 &bus_error, // object to return error 164 &response, // Response message on success 165 "s", // input message (string) 166 present_bit); // First argument to setPresent 167 if(rc < 0) 168 { 169 fprintf(stderr,"Failed to set Present bit for fruid:[%d], path:[%s]\n", 170 iv_fruid, iv_obj_path.c_str()); 171 } 172 else 173 { 174 printf("Present bit set to :[%s] for fruid:[%d], Path[%s]:\n", 175 present_bit, iv_fruid, iv_obj_path.c_str()); 176 } 177 178 sd_bus_error_free(&bus_error); 179 sd_bus_message_unref(response); 180 } 181 } 182 183 // Sets up the sd_bus structures for the given fru type 184 int ipmi_fru::setup_sd_bus_paths(void) 185 { 186 // Need this to get respective DBUS objects 187 sd_bus_error bus_error = SD_BUS_ERROR_NULL; 188 sd_bus_message *response = NULL; 189 int rc = 0; 190 191 // What we need is BOARD_1, PRODUCT_1, CHASSIS_1 etc.. 192 char *inv_bus_name = NULL; 193 const char *inv_obj_path = NULL, 194 *inv_intf_name = NULL; 195 char fru_area_name[16] = {0}; 196 char *sys_bus_name = NULL; 197 sprintf(fru_area_name,"%s%d",iv_name.c_str(), iv_fruid); 198 199 #ifdef __IPMI_DEBUG__ 200 printf("Getting sd_bus for :[%s]\n",fru_area_name); 201 #endif 202 203 rc = mapper_get_service(iv_bus_type, sys_object_name, &sys_bus_name); 204 if(rc < 0) 205 { 206 fprintf(stderr, "Failed to get system manager service:[%s]\n", 207 strerror(-rc)); 208 goto exit; 209 } 210 211 // We want to call a method "getObjectFromId" on System Bus that is 212 // made available over OpenBmc system services. 213 214 rc = sd_bus_call_method(iv_bus_type, // On the System Bus 215 sys_bus_name, // Service to contact 216 sys_object_name, // Object path 217 sys_intf_name, // Interface name 218 "getObjectFromId", // Method to be called 219 &bus_error, // object to return error 220 &response, // Response message on success 221 "ss", // input message (string,string) 222 "FRU_STR", // First argument to getObjectFromId 223 fru_area_name); // Second Argument 224 if(rc < 0) 225 { 226 fprintf(stderr, "Failed to resolve fruid:[%d] to dbus: [%s]\n", iv_fruid, bus_error.message); 227 } 228 else 229 { 230 // Method getObjectFromId returns 2 parameters and all are strings, namely 231 // object_path and interface name for accessing that particular 232 // FRU over Inventory SDBUS manager. 'ss' here mentions that format. 233 rc = sd_bus_message_read(response, "(ss)", &inv_obj_path, &inv_intf_name); 234 if(rc < 0) 235 { 236 fprintf(stderr, "Failed to parse response message:[%s]\n", strerror(-rc)); 237 } 238 else 239 { 240 rc = mapper_get_service(iv_bus_type, inv_obj_path, &inv_bus_name); 241 if(rc < 0) 242 { 243 fprintf(stderr, "Failed to get inventory item service:[%s]\n", 244 strerror(-rc)); 245 goto exit; 246 } 247 // Update the paths in the area object 248 update_dbus_paths(inv_bus_name, inv_obj_path, inv_intf_name); 249 } 250 } 251 252 exit: 253 #ifdef __IPMI_DEBUG__ 254 printf("fru_area=[%s], inv_bus_name=[%s], inv_obj_path=[%s], inv_intf_name=[%s]\n", 255 fru_area_name, inv_bus_name, inv_obj_path, inv_intf_name); 256 #endif 257 258 free(sys_bus_name); 259 free(inv_bus_name); 260 sd_bus_error_free(&bus_error); 261 sd_bus_message_unref(response); 262 263 return rc; 264 } 265 266 //------------------------------------------------ 267 // Takes the pointer to stream of bytes and length 268 // and returns the 8 bit checksum 269 // This algo is per IPMI V2.0 spec 270 //------------------------------------------------- 271 unsigned char calculate_crc(const unsigned char *data, size_t len) 272 { 273 char crc = 0; 274 size_t byte = 0; 275 276 for(byte = 0; byte < len; byte++) 277 { 278 crc += *data++; 279 } 280 281 return(-crc); 282 } 283 284 //--------------------------------------------------------------------- 285 // Accepts a fru area offset in commom hdr and tells which area it is. 286 //--------------------------------------------------------------------- 287 ipmi_fru_area_type get_fru_area_type(uint8_t area_offset) 288 { 289 ipmi_fru_area_type type = IPMI_FRU_AREA_TYPE_MAX; 290 291 switch(area_offset) 292 { 293 case IPMI_FRU_INTERNAL_OFFSET: 294 type = IPMI_FRU_AREA_INTERNAL_USE; 295 break; 296 297 case IPMI_FRU_CHASSIS_OFFSET: 298 type = IPMI_FRU_AREA_CHASSIS_INFO; 299 break; 300 301 case IPMI_FRU_BOARD_OFFSET: 302 type = IPMI_FRU_AREA_BOARD_INFO; 303 break; 304 305 case IPMI_FRU_PRODUCT_OFFSET: 306 type = IPMI_FRU_AREA_PRODUCT_INFO; 307 break; 308 309 case IPMI_FRU_MULTI_OFFSET: 310 type = IPMI_FRU_AREA_MULTI_RECORD; 311 break; 312 313 default: 314 type = IPMI_FRU_AREA_TYPE_MAX; 315 } 316 317 return type; 318 } 319 320 ///----------------------------------------------- 321 // Validates the data for crc and mandatory fields 322 ///----------------------------------------------- 323 int verify_fru_data(const uint8_t *data, const size_t len) 324 { 325 uint8_t checksum = 0; 326 int rc = -1; 327 328 // Validate for first byte to always have a value of [1] 329 if(data[0] != IPMI_FRU_HDR_BYTE_ZERO) 330 { 331 fprintf(stderr, "Invalid entry:[%d] in byte-0\n",data[0]); 332 return rc; 333 } 334 #ifdef __IPMI_DEBUG__ 335 else 336 { 337 printf("SUCCESS: Validated [0x%X] in entry_1 of fru_data\n",data[0]); 338 } 339 #endif 340 341 // See if the calculated CRC matches with the embedded one. 342 // CRC to be calculated on all except the last one that is CRC itself. 343 checksum = calculate_crc(data, len - 1); 344 if(checksum != data[len-1]) 345 { 346 #ifdef __IPMI_DEBUG__ 347 fprintf(stderr, "Checksum mismatch." 348 " Calculated:[0x%X], Embedded:[0x%X]\n", 349 checksum, data[len]); 350 #endif 351 return rc; 352 } 353 #ifdef __IPMI_DEBUG__ 354 else 355 { 356 printf("SUCCESS: Checksum matches:[0x%X]\n",checksum); 357 } 358 #endif 359 360 return EXIT_SUCCESS; 361 } 362 363 //------------------------------------------------------------------------ 364 // Gets the value of the key from the fru dictionary of the given section. 365 // FRU dictionary is parsed fru data for all the sections. 366 //------------------------------------------------------------------------ 367 368 std::string getFRUValue(const std::string& section, 369 const std::string& key, 370 const std::string& delimiter, 371 IPMIFruInfo& fruData) 372 { 373 374 auto minIndexValue = 0; 375 auto maxIndexValue = 0; 376 std::string fruValue = ""; 377 if (section == "Board") 378 { 379 minIndexValue = OPENBMC_VPD_KEY_BOARD_MFG_DATE; 380 maxIndexValue = OPENBMC_VPD_KEY_BOARD_MAX; 381 } 382 else if (section == "Product") 383 { 384 minIndexValue = OPENBMC_VPD_KEY_PRODUCT_MFR; 385 maxIndexValue = OPENBMC_VPD_KEY_PRODUCT_MAX; 386 387 } 388 else if (section == "Chassis") 389 { 390 minIndexValue = OPENBMC_VPD_KEY_CHASSIS_TYPE; 391 maxIndexValue = OPENBMC_VPD_KEY_CHASSIS_MAX; 392 } 393 394 auto first = fruData.cbegin() + minIndexValue; 395 auto last = first + (maxIndexValue - minIndexValue) + 1; 396 397 auto itr = std::find_if(first, last, 398 [&key](auto& e){ return key == e.first; }); 399 400 if (itr != last) 401 { 402 fruValue = itr->second; 403 } 404 405 //if the key is custom property then the value could be in two formats. 406 //1) custom field 2 = "value". 407 //2) custom field 2 = "key:value". 408 //if delimeter length = 0 i.e custom field 2 = "value" 409 410 constexpr auto customProp = "Custom Field"; 411 if (key.find(customProp) != std::string::npos) 412 { 413 if (delimiter.length() > 0) 414 { 415 size_t delimiterpos = fruValue.find(delimiter); 416 if (delimiterpos != std::string::npos) 417 fruValue = fruValue.substr(delimiterpos + 1); 418 } 419 } 420 return fruValue; 421 422 } 423 //Get the inventory service from the mapper. 424 auto getService(sdbusplus::bus::bus& bus, 425 const std::string& intf, 426 const std::string& path) 427 { 428 auto mapperCall = bus.new_method_call( 429 "xyz.openbmc_project.ObjectMapper", 430 "/xyz/openbmc_project/ObjectMapper", 431 "xyz.openbmc_project.ObjectMapper", 432 "GetObject"); 433 434 mapperCall.append(path); 435 mapperCall.append(std::vector<std::string>({intf})); 436 437 auto mapperResponseMsg = bus.call(mapperCall); 438 if (mapperResponseMsg.is_method_error()) 439 { 440 throw std::runtime_error("ERROR in mapper call"); 441 } 442 443 std::map<std::string, std::vector<std::string>> mapperResponse; 444 mapperResponseMsg.read(mapperResponse); 445 446 if (mapperResponse.begin() == mapperResponse.end()) 447 { 448 throw std::runtime_error("ERROR in reading the mapper response"); 449 } 450 451 return mapperResponse.begin()->first; 452 } 453 454 //------------------------------------------------------------------------ 455 // Takes FRU data, invokes Parser for each fru record area and updates 456 // Inventory 457 //------------------------------------------------------------------------ 458 int ipmi_update_inventory(fru_area_vec_t& area_vec) 459 { 460 // Generic error reporter 461 int rc = 0; 462 uint8_t fruid = 0; 463 IPMIFruInfo fruData; 464 465 // For each FRU area, extract the needed data , get it parsed and update 466 // the Inventory. 467 for (const auto& fruArea : area_vec) 468 { 469 fruid = fruArea->get_fruid(); 470 // Fill the container with information 471 rc = parse_fru_area((fruArea)->get_type(), (void*)(fruArea)->get_data(), 472 (fruArea)->get_len(), fruData); 473 if (rc < 0) 474 { 475 std::cerr << "ERROR parsing FRU records\n"; 476 return rc; 477 } 478 } // END walking the vector of areas and updating 479 480 // For each Fru we have the list of instances which needs to be updated. 481 // Each instance object implements certain interfaces. 482 // Each Interface is having Dbus properties. 483 // Each Dbus Property would be having metaData(eg section,VpdPropertyName). 484 485 // Here we are just printing the object,interface and the properties. 486 // which needs to be called with the new inventory manager implementation. 487 auto bus = sdbusplus::bus::new_default(); 488 using namespace std::string_literals; 489 static const auto intf = "xyz.openbmc_project.Inventory.Manager"s; 490 static const auto path = "/xyz/openbmc_project/Inventory"s; 491 std::string service; 492 try 493 { 494 service = getService(bus,intf,path); 495 } 496 catch (const std::runtime_error& e) 497 { 498 std::cerr << e.what() << "\n"; 499 return -1; 500 } 501 502 auto iter = frus.find(fruid); 503 if (iter == frus.end()) 504 { 505 std::cerr << "ERROR Unable to get the fru info for FRU=" 506 << static_cast<int>(fruid) << "\n"; 507 return -1; 508 } 509 510 auto& instanceList = iter->second; 511 if (instanceList.size() <= 0) 512 { 513 std::cout << "Object List empty for this FRU=" 514 << static_cast<int>(fruid) << "\n"; 515 } 516 517 ObjectMap objects; 518 for (auto& instance : instanceList) 519 { 520 InterfaceMap interfaces; 521 522 for (auto& interfaceList : instance.second) 523 { 524 PropertyMap props;//store all the properties 525 for (auto& properties : interfaceList.second) 526 { 527 std::string section, property, delimiter, value; 528 for (auto& info : properties.second) 529 { 530 if (info.first == "IPMIFruSection") 531 { 532 section = std::move(info.second); 533 } 534 if (info.first == "IPMIFruProperty") 535 { 536 property = std::move(info.second); 537 } 538 if (info.first == "IPMIFruValueDelimiter") 539 { 540 //Read the delimeter as ascii value 541 //convert it into char 542 if( info.second.length() > 0 ) 543 { 544 char dlm = ' '; 545 rc = sscanf(info.second.c_str(),"%hhd",&dlm); 546 if (rc > 0) 547 { 548 delimiter = std::string(1,dlm); 549 } 550 } 551 } 552 553 } 554 555 if (!section.empty() && !property.empty()) 556 { 557 value = getFRUValue(section, property, delimiter, fruData); 558 } 559 props.emplace(std::move(properties.first), std::move(value)); 560 } 561 interfaces.emplace(std::move(interfaceList.first), std::move(props)); 562 } 563 564 //Call the inventory manager 565 sdbusplus::message::object_path path = instance.first; 566 objects.emplace(path,interfaces); 567 } 568 569 auto pimMsg = bus.new_method_call( 570 service.c_str(), 571 path.c_str(), 572 intf.c_str(), 573 "Notify"); 574 pimMsg.append(std::move(objects)); 575 auto inventoryMgrResponseMsg = bus.call(pimMsg); 576 if (inventoryMgrResponseMsg.is_method_error()) 577 { 578 std::cerr << "Error in notify call\n"; 579 return -1; 580 } 581 return rc; 582 } 583 584 ///---------------------------------------------------- 585 // Checks if a particular fru area is populated or not 586 ///---------------------------------------------------- 587 bool remove_invalid_area(const std::unique_ptr<ipmi_fru> &fru_area) 588 { 589 // Filter the ones that are empty 590 if(!(fru_area->get_len())) 591 { 592 return true; 593 } 594 return false; 595 } 596 597 ///---------------------------------------------------------------------------------- 598 // Populates various FRU areas 599 // @prereq : This must be called only after validating common header. 600 ///---------------------------------------------------------------------------------- 601 int ipmi_populate_fru_areas(uint8_t *fru_data, const size_t data_len, 602 fru_area_vec_t & fru_area_vec) 603 { 604 size_t area_offset = 0; 605 int rc = -1; 606 607 // Now walk the common header and see if the file size has atleast the last 608 // offset mentioned by the common_hdr. If the file size is less than the 609 // offset of any if the fru areas mentioned in the common header, then we do 610 // not have a complete file. 611 for(uint8_t fru_entry = IPMI_FRU_INTERNAL_OFFSET; 612 fru_entry < (sizeof(struct common_header) -2); fru_entry++) 613 { 614 rc = -1; 615 // Actual offset in the payload is the offset mentioned in common header 616 // multipled by 8. Common header is always the first 8 bytes. 617 area_offset = fru_data[fru_entry] * IPMI_EIGHT_BYTES; 618 if(area_offset && (data_len < (area_offset + 2))) 619 { 620 // Our file size is less than what it needs to be. +2 because we are 621 // using area len that is at 2 byte off area_offset 622 fprintf(stderr, "fru file is incomplete. Size:[%zd]\n",data_len); 623 return rc; 624 } 625 else if(area_offset) 626 { 627 // Read 2 bytes to know the actual size of area. 628 uint8_t area_hdr[2] = {0}; 629 memcpy(area_hdr, &((uint8_t *)fru_data)[area_offset], sizeof(area_hdr)); 630 631 // Size of this area will be the 2nd byte in the fru area header. 632 size_t area_len = area_hdr[1] * IPMI_EIGHT_BYTES; 633 uint8_t area_data[area_len] = {0}; 634 635 printf("fru data size:[%zd], area offset:[%zd], area_size:[%zd]\n", 636 data_len, area_offset, area_len); 637 638 // See if we really have that much buffer. We have area offset amd 639 // from there, the actual len. 640 if(data_len < (area_len + area_offset)) 641 { 642 fprintf(stderr, "Incomplete Fru file.. Size:[%zd]\n",data_len); 643 return rc; 644 } 645 646 // Save off the data. 647 memcpy(area_data, &((uint8_t *)fru_data)[area_offset], area_len); 648 649 // Validate the crc 650 rc = verify_fru_data(area_data, area_len); 651 if(rc < 0) 652 { 653 fprintf(stderr, "Error validating fru area. offset:[%zd]\n",area_offset); 654 return rc; 655 } 656 else 657 { 658 printf("Successfully verified area checksum. offset:[%zd]\n",area_offset); 659 } 660 661 // We already have a vector that is passed to us containing all 662 // of the fields populated. Update the data portion now. 663 for(auto& iter : fru_area_vec) 664 { 665 if((iter)->get_type() == get_fru_area_type(fru_entry)) 666 { 667 (iter)->set_data(area_data, area_len); 668 } 669 } 670 } // If we have fru data present 671 } // Walk common_hdr 672 673 // Not all the fields will be populated in a fru data. Mostly all cases will 674 // not have more than 2 or 3. 675 fru_area_vec.erase(std::remove_if(fru_area_vec.begin(), fru_area_vec.end(), 676 remove_invalid_area), fru_area_vec.end()); 677 678 return EXIT_SUCCESS; 679 } 680 681 ///--------------------------------------------------------- 682 // Validates the fru data per ipmi common header constructs. 683 // Returns with updated common_hdr and also file_size 684 //---------------------------------------------------------- 685 int ipmi_validate_common_hdr(const uint8_t *fru_data, const size_t data_len) 686 { 687 int rc = -1; 688 689 uint8_t common_hdr[sizeof(struct common_header)] = {0}; 690 if(data_len >= sizeof(common_hdr)) 691 { 692 memcpy(common_hdr, fru_data, sizeof(common_hdr)); 693 } 694 else 695 { 696 fprintf(stderr, "Incomplete fru data file. Size:[%zd]\n", data_len); 697 return rc; 698 } 699 700 // Verify the crc and size 701 rc = verify_fru_data(common_hdr, sizeof(common_hdr)); 702 if(rc < 0) 703 { 704 fprintf(stderr, "Failed to validate common header\n"); 705 return rc; 706 } 707 708 return EXIT_SUCCESS; 709 } 710 711 //------------------------------------------------------------ 712 // Cleanup routine 713 //------------------------------------------------------------ 714 int cleanup_error(FILE *fru_fp, fru_area_vec_t & fru_area_vec) 715 { 716 if(fru_fp != NULL) 717 { 718 fclose(fru_fp); 719 fru_fp = NULL; 720 } 721 722 if(!(fru_area_vec.empty())) 723 { 724 fru_area_vec.clear(); 725 } 726 727 return -1; 728 } 729 730 731 ///----------------------------------------------------- 732 // Get the fru area names defined in BMC for a given @fruid. 733 //---------------------------------------------------- 734 int get_defined_fru_area(sd_bus *bus_type, const uint8_t fruid, 735 std::vector<std::string> &defined_fru_area) 736 { 737 // Need this to get respective DBUS objects 738 sd_bus_error bus_error = SD_BUS_ERROR_NULL; 739 sd_bus_message *response = NULL; 740 int rc = 0; 741 const char *areas = NULL; 742 char *sys_bus_name = NULL; 743 744 #ifdef __IPMI_DEBUG__ 745 printf("Getting fru areas defined in Skeleton for :[%d]\n", fruid); 746 #endif 747 748 rc = mapper_get_service(bus_type, sys_object_name, &sys_bus_name); 749 if(rc < 0) 750 { 751 fprintf(stderr, "Failed to get system manager service:[%s]\n", 752 strerror(-rc)); 753 goto exit; 754 } 755 756 // We want to call a method "getFRUArea" on System Bus that is 757 // made available over OpenBmc system services. 758 rc = sd_bus_call_method(bus_type, // On the System Bus 759 sys_bus_name, // Service to contact 760 sys_object_name, // Object path 761 sys_intf_name, // Interface name 762 "getFRUArea", // Method to be called 763 &bus_error, // object to return error 764 &response, // Response message on success 765 "y", // input message (integer) 766 fruid); // Argument 767 768 if(rc < 0) 769 { 770 fprintf(stderr, "Failed to get fru area for fruid:[%d] to dbus: [%s]\n", 771 fruid, bus_error.message); 772 } 773 else 774 { 775 // if several fru area names are defined, the names are combined to 776 // a string seperated by ',' 777 rc = sd_bus_message_read(response, "s", &areas); 778 if(rc < 0) 779 { 780 fprintf(stderr, "Failed to parse response message from getFRUArea:[%s]\n", 781 strerror(-rc)); 782 } 783 else 784 { 785 #ifdef __IPMI_DEBUG__ 786 printf("get defined fru area: id: %d, areas: %s\n", fruid, areas); 787 #endif 788 std::string area_name; 789 std::stringstream ss(areas); 790 // fru area names string is seperated by ',', parse it into tokens 791 while (std::getline(ss, area_name, ',')) 792 { 793 if (!area_name.empty()) 794 defined_fru_area.emplace_back(area_name); 795 } 796 } 797 } 798 799 exit: 800 801 free(sys_bus_name); 802 sd_bus_error_free(&bus_error); 803 sd_bus_message_unref(response); 804 805 return rc; 806 } 807 808 809 ///----------------------------------------------------- 810 // Accepts the filename and validates per IPMI FRU spec 811 //---------------------------------------------------- 812 int ipmi_validate_fru_area(const uint8_t fruid, const char *fru_file_name, 813 sd_bus *bus_type, const bool bmc_fru) 814 { 815 size_t data_len = 0; 816 size_t bytes_read = 0; 817 int rc = -1; 818 819 // Vector that holds individual IPMI FRU AREAs. Although MULTI and INTERNAL 820 // are not used, keeping it here for completeness. 821 fru_area_vec_t fru_area_vec; 822 std::vector<std::string> defined_fru_area; 823 824 // BMC defines fru areas that should be present in Skeleton 825 rc = get_defined_fru_area(bus_type, fruid, defined_fru_area); 826 if(rc < 0) 827 { 828 fprintf(stderr, "ERROR: cannot get defined fru area\n"); 829 return rc; 830 } 831 for(uint8_t fru_entry = IPMI_FRU_INTERNAL_OFFSET; 832 fru_entry < (sizeof(struct common_header) -2); fru_entry++) 833 { 834 // Create an object and push onto a vector. 835 std::unique_ptr<ipmi_fru> fru_area = std::make_unique<ipmi_fru> 836 (fruid, get_fru_area_type(fru_entry), bus_type, bmc_fru); 837 838 // Physically being present 839 bool present = access(fru_file_name, F_OK) == 0; 840 fru_area->set_present(present); 841 842 // Only setup dbus path for areas defined in BMC. 843 // Otherwise Skeleton will report 'not found' error 844 std::string fru_area_name = fru_area->get_name() + std::to_string(fruid); 845 auto iter = std::find(defined_fru_area.begin(), defined_fru_area.end(), 846 fru_area_name); 847 if (iter != defined_fru_area.end()) 848 { 849 fru_area->setup_sd_bus_paths(); 850 } 851 fru_area_vec.emplace_back(std::move(fru_area)); 852 } 853 854 FILE *fru_fp = fopen(fru_file_name,"rb"); 855 if(fru_fp == NULL) 856 { 857 fprintf(stderr, "ERROR: opening:[%s]\n",fru_file_name); 858 perror("Error:"); 859 return cleanup_error(fru_fp, fru_area_vec); 860 } 861 862 // Get the size of the file to see if it meets minimum requirement 863 if(fseek(fru_fp, 0, SEEK_END)) 864 { 865 perror("Error:"); 866 return cleanup_error(fru_fp, fru_area_vec); 867 } 868 869 // Allocate a buffer to hold entire file content 870 data_len = ftell(fru_fp); 871 uint8_t fru_data[data_len] = {0}; 872 873 rewind(fru_fp); 874 bytes_read = fread(fru_data, data_len, 1, fru_fp); 875 if(bytes_read != 1) 876 { 877 fprintf(stderr, "Failed reading fru data. Bytes_read=[%zd]\n",bytes_read); 878 perror("Error:"); 879 return cleanup_error(fru_fp, fru_area_vec); 880 } 881 882 // We are done reading. 883 fclose(fru_fp); 884 fru_fp = NULL; 885 886 rc = ipmi_validate_common_hdr(fru_data, data_len); 887 if(rc < 0) 888 { 889 return cleanup_error(fru_fp, fru_area_vec); 890 } 891 892 // Now that we validated the common header, populate various fru sections if we have them here. 893 rc = ipmi_populate_fru_areas(fru_data, data_len, fru_area_vec); 894 if(rc < 0) 895 { 896 fprintf(stderr,"Populating FRU areas failed for:[%d]\n",fruid); 897 return cleanup_error(fru_fp, fru_area_vec); 898 } 899 else 900 { 901 printf("SUCCESS: Populated FRU areas for:[%s]\n",fru_file_name); 902 } 903 904 #ifdef __IPMI_DEBUG__ 905 for(auto& iter : fru_area_vec) 906 { 907 printf("FRU ID : [%d]\n",(iter)->get_fruid()); 908 printf("AREA NAME : [%s]\n",(iter)->get_name()); 909 printf("TYPE : [%d]\n",(iter)->get_type()); 910 printf("LEN : [%d]\n",(iter)->get_len()); 911 printf("BUS NAME : [%s]\n", (iter)->get_bus_name()); 912 printf("OBJ PATH : [%s]\n", (iter)->get_obj_path()); 913 printf("INTF NAME :[%s]\n", (iter)->get_intf_name()); 914 } 915 #endif 916 917 // If the vector is populated with everything, then go ahead and update the 918 // inventory. 919 if(!(fru_area_vec.empty())) 920 { 921 922 #ifdef __IPMI_DEBUG__ 923 printf("\n SIZE of vector is : [%d] \n",fru_area_vec.size()); 924 #endif 925 rc = ipmi_update_inventory(fru_area_vec); 926 if(rc <0) 927 { 928 fprintf(stderr, "Error updating inventory\n"); 929 } 930 } 931 932 // we are done with all that we wanted to do. This will do the job of 933 // calling any destructors too. 934 fru_area_vec.clear(); 935 936 return rc; 937 } 938