1 #include "config.h" 2 3 #include "ibm_vpd_utils.hpp" 4 5 #include "common_utility.hpp" 6 #include "const.hpp" 7 #include "defines.hpp" 8 #include "vpd_exceptions.hpp" 9 10 #include <boost/algorithm/string.hpp> 11 #include <gpiod.hpp> 12 #include <nlohmann/json.hpp> 13 #include <phosphor-logging/elog-errors.hpp> 14 #include <phosphor-logging/log.hpp> 15 #include <sdbusplus/server.hpp> 16 #include <xyz/openbmc_project/Common/error.hpp> 17 18 #include <filesystem> 19 #include <fstream> 20 #include <iomanip> 21 #include <regex> 22 #include <sstream> 23 #include <vector> 24 25 using json = nlohmann::json; 26 27 namespace openpower 28 { 29 namespace vpd 30 { 31 using namespace openpower::vpd::constants; 32 using namespace inventory; 33 using namespace phosphor::logging; 34 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 35 using namespace record; 36 using namespace openpower::vpd::exceptions; 37 using namespace common::utility; 38 using Severity = openpower::vpd::constants::PelSeverity; 39 namespace fs = std::filesystem; 40 41 // mapping of severity enum to severity interface 42 static std::unordered_map<Severity, std::string> sevMap = { 43 {Severity::INFORMATIONAL, 44 "xyz.openbmc_project.Logging.Entry.Level.Informational"}, 45 {Severity::DEBUG, "xyz.openbmc_project.Logging.Entry.Level.Debug"}, 46 {Severity::NOTICE, "xyz.openbmc_project.Logging.Entry.Level.Notice"}, 47 {Severity::WARNING, "xyz.openbmc_project.Logging.Entry.Level.Warning"}, 48 {Severity::CRITICAL, "xyz.openbmc_project.Logging.Entry.Level.Critical"}, 49 {Severity::EMERGENCY, "xyz.openbmc_project.Logging.Entry.Level.Emergency"}, 50 {Severity::ERROR, "xyz.openbmc_project.Logging.Entry.Level.Error"}, 51 {Severity::ALERT, "xyz.openbmc_project.Logging.Entry.Level.Alert"}}; 52 53 namespace inventory 54 { 55 56 MapperResponse 57 getObjectSubtreeForInterfaces(const std::string& root, const int32_t depth, 58 const std::vector<std::string>& interfaces) 59 { 60 auto bus = sdbusplus::bus::new_default(); 61 auto mapperCall = bus.new_method_call(mapperDestination, mapperObjectPath, 62 mapperInterface, "GetSubTree"); 63 mapperCall.append(root); 64 mapperCall.append(depth); 65 mapperCall.append(interfaces); 66 67 MapperResponse result = {}; 68 69 try 70 { 71 auto response = bus.call(mapperCall); 72 73 response.read(result); 74 } 75 catch (const sdbusplus::exception_t& e) 76 { 77 log<level::ERR>("Error in mapper GetSubTree", 78 entry("ERROR=%s", e.what())); 79 } 80 81 return result; 82 } 83 84 MapperGetObjectResponse getObject(const std::string& objectPath, 85 const std::vector<std::string>& interfaces) 86 { 87 auto bus = sdbusplus::bus::new_default(); 88 auto mapperCall = bus.new_method_call(mapperDestination, mapperObjectPath, 89 mapperInterface, "GetObject"); 90 mapperCall.append(objectPath); 91 mapperCall.append(interfaces); 92 93 MapperGetObjectResponse result = {}; 94 95 try 96 { 97 auto response = bus.call(mapperCall); 98 99 response.read(result); 100 } 101 catch (const sdbusplus::exception_t& e) 102 { 103 log<level::ERR>("Error in mapper GetObject", 104 entry("ERROR=%s", e.what())); 105 } 106 107 return result; 108 } 109 110 } // namespace inventory 111 112 LE2ByteData readUInt16LE(Binary::const_iterator iterator) 113 { 114 LE2ByteData lowByte = *iterator; 115 LE2ByteData highByte = *(iterator + 1); 116 lowByte |= (highByte << 8); 117 return lowByte; 118 } 119 120 /** @brief Encodes a keyword for D-Bus. 121 */ 122 std::string encodeKeyword(const std::string& kw, const std::string& encoding) 123 { 124 if (encoding == "MAC") 125 { 126 std::string res{}; 127 size_t first = kw[0]; 128 res += toHex(first >> 4); 129 res += toHex(first & 0x0f); 130 for (size_t i = 1; i < kw.size(); ++i) 131 { 132 res += ":"; 133 res += toHex(kw[i] >> 4); 134 res += toHex(kw[i] & 0x0f); 135 } 136 return res; 137 } 138 else if (encoding == "DATE") 139 { 140 // Date, represent as 141 // <year>-<month>-<day> <hour>:<min> 142 std::string res{}; 143 static constexpr uint8_t skipPrefix = 3; 144 145 auto strItr = kw.begin(); 146 advance(strItr, skipPrefix); 147 for_each(strItr, kw.end(), [&res](size_t c) { res += c; }); 148 149 res.insert(BD_YEAR_END, 1, '-'); 150 res.insert(BD_MONTH_END, 1, '-'); 151 res.insert(BD_DAY_END, 1, ' '); 152 res.insert(BD_HOUR_END, 1, ':'); 153 154 return res; 155 } 156 else // default to string encoding 157 { 158 return std::string(kw.begin(), kw.end()); 159 } 160 } 161 162 std::string readBusProperty(const std::string& obj, const std::string& inf, 163 const std::string& prop) 164 { 165 std::string propVal{}; 166 std::string object = INVENTORY_PATH + obj; 167 auto bus = sdbusplus::bus::new_default(); 168 auto properties = bus.new_method_call( 169 "xyz.openbmc_project.Inventory.Manager", object.c_str(), 170 "org.freedesktop.DBus.Properties", "Get"); 171 properties.append(inf); 172 properties.append(prop); 173 auto result = bus.call(properties); 174 if (!result.is_method_error()) 175 { 176 inventory::Value val; 177 result.read(val); 178 if (auto pVal = std::get_if<Binary>(&val)) 179 { 180 propVal.assign(reinterpret_cast<const char*>(pVal->data()), 181 pVal->size()); 182 } 183 else if (auto pVal = std::get_if<std::string>(&val)) 184 { 185 propVal.assign(pVal->data(), pVal->size()); 186 } 187 else if (auto pVal = get_if<bool>(&val)) 188 { 189 if (*pVal == false) 190 { 191 propVal = "false"; 192 } 193 else 194 { 195 propVal = "true"; 196 } 197 } 198 } 199 return propVal; 200 } 201 202 void createPEL(const std::map<std::string, std::string>& additionalData, 203 const Severity& sev, const std::string& errIntf, sd_bus* sdBus) 204 { 205 // This pointer will be NULL in case the call is made from ibm-read-vpd. In 206 // that case a sync call will do. 207 if (sdBus == nullptr) 208 { 209 createSyncPEL(additionalData, sev, errIntf); 210 } 211 else 212 { 213 std::string errDescription{}; 214 auto pos = additionalData.find("DESCRIPTION"); 215 if (pos != additionalData.end()) 216 { 217 errDescription = pos->second; 218 } 219 else 220 { 221 errDescription = "Description field missing in additional data"; 222 } 223 224 std::string pelSeverity = 225 "xyz.openbmc_project.Logging.Entry.Level.Error"; 226 auto itr = sevMap.find(sev); 227 if (itr != sevMap.end()) 228 { 229 pelSeverity = itr->second; 230 } 231 232 // Implies this is a call from Manager. Hence we need to make an async 233 // call to avoid deadlock with Phosphor-logging. 234 auto rc = sd_bus_call_method_async( 235 sdBus, NULL, loggerService, loggerObjectPath, loggerCreateInterface, 236 "Create", NULL, NULL, "ssa{ss}", errIntf.c_str(), 237 pelSeverity.c_str(), 1, "DESCRIPTION", errDescription.c_str()); 238 239 if (rc < 0) 240 { 241 log<level::ERR>("Error calling sd_bus_call_method_async", 242 entry("RC=%d", rc), entry("MSG=%s", strerror(-rc))); 243 } 244 } 245 } 246 247 void createSyncPEL(const std::map<std::string, std::string>& additionalData, 248 const Severity& sev, const std::string& errIntf) 249 { 250 try 251 { 252 std::string pelSeverity = 253 "xyz.openbmc_project.Logging.Entry.Level.Error"; 254 auto bus = sdbusplus::bus::new_default(); 255 auto service = getService(bus, loggerObjectPath, loggerCreateInterface); 256 auto method = bus.new_method_call(service.c_str(), loggerObjectPath, 257 loggerCreateInterface, "Create"); 258 259 auto itr = sevMap.find(sev); 260 if (itr != sevMap.end()) 261 { 262 pelSeverity = itr->second; 263 } 264 265 method.append(errIntf, pelSeverity, additionalData); 266 auto resp = bus.call(method); 267 } 268 catch (const sdbusplus::exception_t& e) 269 { 270 std::cerr << "Dbus call to phosphor-logging Create failed. Reason:" 271 << e.what(); 272 } 273 } 274 275 inventory::VPDfilepath getVpdFilePath(const std::string& jsonFile, 276 const std::string& ObjPath) 277 { 278 std::ifstream inventoryJson(jsonFile); 279 const auto& jsonObject = json::parse(inventoryJson); 280 inventory::VPDfilepath filePath{}; 281 282 if (jsonObject.find("frus") == jsonObject.end()) 283 { 284 throw(VpdJsonException( 285 "Invalid JSON structure - frus{} object not found in ", jsonFile)); 286 } 287 288 const nlohmann::json& groupFRUS = 289 jsonObject["frus"].get_ref<const nlohmann::json::object_t&>(); 290 for (const auto& itemFRUS : groupFRUS.items()) 291 { 292 const std::vector<nlohmann::json>& groupEEPROM = 293 itemFRUS.value().get_ref<const nlohmann::json::array_t&>(); 294 for (const auto& itemEEPROM : groupEEPROM) 295 { 296 if (itemEEPROM["inventoryPath"] 297 .get_ref<const nlohmann::json::string_t&>() == ObjPath) 298 { 299 filePath = itemFRUS.key(); 300 return filePath; 301 } 302 } 303 } 304 305 return filePath; 306 } 307 308 bool isPathInJson(const std::string& eepromPath) 309 { 310 bool present = false; 311 std::ifstream inventoryJson(INVENTORY_JSON_SYM_LINK); 312 313 try 314 { 315 auto js = json::parse(inventoryJson); 316 if (js.find("frus") == js.end()) 317 { 318 throw(VpdJsonException( 319 "Invalid JSON structure - frus{} object not found in ", 320 INVENTORY_JSON_SYM_LINK)); 321 } 322 json fruJson = js["frus"]; 323 324 if (fruJson.find(eepromPath) != fruJson.end()) 325 { 326 present = true; 327 } 328 } 329 catch (const json::parse_error& ex) 330 { 331 throw(VpdJsonException("Json Parsing failed", INVENTORY_JSON_SYM_LINK)); 332 } 333 return present; 334 } 335 336 bool isRecKwInDbusJson(const std::string& recordName, 337 const std::string& keyword) 338 { 339 std::ifstream propertyJson(DBUS_PROP_JSON); 340 json dbusProperty; 341 bool present = false; 342 343 if (propertyJson.is_open()) 344 { 345 try 346 { 347 auto dbusPropertyJson = json::parse(propertyJson); 348 if (dbusPropertyJson.find("dbusProperties") == 349 dbusPropertyJson.end()) 350 { 351 throw(VpdJsonException("dbusProperties{} object not found in " 352 "DbusProperties json : ", 353 DBUS_PROP_JSON)); 354 } 355 356 dbusProperty = dbusPropertyJson["dbusProperties"]; 357 if (dbusProperty.contains(recordName)) 358 { 359 const std::vector<std::string>& kwdsToPublish = 360 dbusProperty[recordName]; 361 if (find(kwdsToPublish.begin(), kwdsToPublish.end(), keyword) != 362 kwdsToPublish.end()) // present 363 { 364 present = true; 365 } 366 } 367 } 368 catch (const json::parse_error& ex) 369 { 370 throw(VpdJsonException("Json Parsing failed", DBUS_PROP_JSON)); 371 } 372 } 373 else 374 { 375 // If dbus properties json is not available, we assume the given 376 // record-keyword is part of dbus-properties json. So setting the bool 377 // variable to true. 378 present = true; 379 } 380 return present; 381 } 382 383 vpdType vpdTypeCheck(const Binary& vpdVector) 384 { 385 // Read first 3 Bytes to check the 11S bar code format 386 std::string is11SFormat = ""; 387 for (uint8_t i = 0; i < FORMAT_11S_LEN; i++) 388 { 389 is11SFormat += vpdVector[MEMORY_VPD_DATA_START + i]; 390 } 391 392 if (vpdVector[IPZ_DATA_START] == KW_VAL_PAIR_START_TAG) 393 { 394 // IPZ VPD FORMAT 395 return vpdType::IPZ_VPD; 396 } 397 else if (vpdVector[KW_VPD_DATA_START] == KW_VPD_START_TAG) 398 { 399 // KEYWORD VPD FORMAT 400 return vpdType::KEYWORD_VPD; 401 } 402 else if (((vpdVector[SPD_BYTE_3] & SPD_BYTE_BIT_0_3_MASK) == 403 SPD_MODULE_TYPE_DDIMM) && 404 (is11SFormat.compare(MEMORY_VPD_START_TAG) == 0)) 405 { 406 // DDIMM Memory VPD format 407 if ((vpdVector[SPD_BYTE_2] & SPD_BYTE_MASK) == SPD_DRAM_TYPE_DDR5) 408 { 409 return vpdType::DDR5_DDIMM_MEMORY_VPD; 410 } 411 else if ((vpdVector[SPD_BYTE_2] & SPD_BYTE_MASK) == SPD_DRAM_TYPE_DDR4) 412 { 413 return vpdType::DDR4_DDIMM_MEMORY_VPD; 414 } 415 } 416 else if ((vpdVector[SPD_BYTE_2] & SPD_BYTE_MASK) == SPD_DRAM_TYPE_DDR5) 417 { 418 // ISDIMM Memory VPD format 419 return vpdType::DDR5_ISDIMM_MEMORY_VPD; 420 } 421 else if ((vpdVector[SPD_BYTE_2] & SPD_BYTE_MASK) == SPD_DRAM_TYPE_DDR4) 422 { 423 // ISDIMM Memory VPD format 424 return vpdType::DDR4_ISDIMM_MEMORY_VPD; 425 } 426 427 // INVALID VPD FORMAT 428 return vpdType::INVALID_VPD_FORMAT; 429 } 430 431 const std::string getIM(const Parsed& vpdMap) 432 { 433 Binary imVal; 434 auto property = vpdMap.find("VSBP"); 435 if (property != vpdMap.end()) 436 { 437 auto kw = (property->second).find("IM"); 438 if (kw != (property->second).end()) 439 { 440 copy(kw->second.begin(), kw->second.end(), back_inserter(imVal)); 441 } 442 } 443 444 std::ostringstream oss; 445 for (auto& i : imVal) 446 { 447 oss << std::setw(2) << std::setfill('0') << std::hex 448 << static_cast<int>(i); 449 } 450 451 return oss.str(); 452 } 453 454 const std::string getHW(const Parsed& vpdMap) 455 { 456 Binary hwVal; 457 auto prop = vpdMap.find("VINI"); 458 if (prop != vpdMap.end()) 459 { 460 auto kw = (prop->second).find("HW"); 461 if (kw != (prop->second).end()) 462 { 463 copy(kw->second.begin(), kw->second.end(), back_inserter(hwVal)); 464 } 465 } 466 467 // The planar pass only comes from the LSB of the HW keyword, 468 // where as the MSB is used for other purposes such as signifying clock 469 // termination. 470 hwVal[0] = 0x00; 471 472 std::ostringstream hwString; 473 for (auto& i : hwVal) 474 { 475 hwString << std::setw(2) << std::setfill('0') << std::hex 476 << static_cast<int>(i); 477 } 478 479 return hwString.str(); 480 } 481 482 std::string getSystemsJson(const Parsed& vpdMap) 483 { 484 std::string jsonPath = "/usr/share/vpd/"; 485 std::string jsonName{}; 486 487 std::ifstream systemJson(SYSTEM_JSON); 488 if (!systemJson) 489 { 490 throw((VpdJsonException("Failed to access Json path", SYSTEM_JSON))); 491 } 492 493 try 494 { 495 auto js = json::parse(systemJson); 496 497 std::string hwKeyword = getHW(vpdMap); 498 const std::string imKeyword = getIM(vpdMap); 499 500 transform(hwKeyword.begin(), hwKeyword.end(), hwKeyword.begin(), 501 ::toupper); 502 503 if (js.find("system") == js.end()) 504 { 505 throw std::runtime_error("Invalid systems Json"); 506 } 507 508 if (js["system"].find(imKeyword) == js["system"].end()) 509 { 510 throw std::runtime_error( 511 "Invalid system. This system type is not present " 512 "in the systemsJson. IM: " + 513 imKeyword); 514 } 515 516 if ((js["system"][imKeyword].find("constraint") != 517 js["system"][imKeyword].end()) && 518 js["system"][imKeyword]["constraint"].find("HW") != 519 js["system"][imKeyword]["constraint"].end()) 520 { 521 // collect hw versions from json, and check hwKeyword is part of it 522 // if hwKeyword is found there then load respective json 523 // otherwise load default one. 524 for (const auto& hwVersion : 525 js["system"][imKeyword]["constraint"]["HW"]) 526 { 527 std::string hw = hwVersion; 528 transform(hw.begin(), hw.end(), hw.begin(), ::toupper); 529 530 if (hw == hwKeyword) 531 { 532 jsonName = js["system"][imKeyword]["constraint"]["json"]; 533 break; 534 } 535 } 536 537 if (jsonName.empty() && js["system"][imKeyword].find("default") != 538 js["system"][imKeyword].end()) 539 { 540 jsonName = js["system"][imKeyword]["default"]; 541 } 542 } 543 else if (js["system"][imKeyword].find("default") != 544 js["system"][imKeyword].end()) 545 { 546 jsonName = js["system"][imKeyword]["default"]; 547 } 548 else 549 { 550 throw std::runtime_error( 551 "Bad System json. Neither constraint nor default found"); 552 } 553 554 jsonPath += jsonName; 555 } 556 557 catch (const json::parse_error& ex) 558 { 559 throw(VpdJsonException("Json Parsing failed", SYSTEM_JSON)); 560 } 561 return jsonPath; 562 } 563 564 void udevToGenericPath(std::string& file, const std::string& driver) 565 { 566 // Sample udevEvent i2c path : 567 // "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a480.i2c-bus/i2c-8/8-0051/8-00510/nvmem" 568 // find if the path contains the word i2c in it. 569 if (file.find("i2c") != std::string::npos) 570 { 571 std::string i2cBusAddr{}; 572 573 // Every udev i2c path should have the common pattern 574 // "i2c-bus_number/bus_number-vpd_address". Search for 575 // "bus_number-vpd_address". 576 std::regex i2cPattern("((i2c)-[0-9]+\\/)([0-9]+-[0-9]{4})"); 577 std::smatch match; 578 if (std::regex_search(file, match, i2cPattern)) 579 { 580 i2cBusAddr = match.str(3); 581 } 582 else 583 { 584 std::cerr << "The given udev path < " << file 585 << " > doesn't match the required pattern. Skipping VPD " 586 "collection." 587 << std::endl; 588 exit(EXIT_SUCCESS); 589 } 590 // Forming the generic file path 591 file = i2cPathPrefix + driver + "/" + i2cBusAddr + "/eeprom"; 592 } 593 // Sample udevEvent spi path : 594 // "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/fsi-master/fsi0/slave@00:00/00:00:00:04/spi_master/spi2/spi2.0/spi2.00/nvmem" 595 // find if the path contains the word spi in it. 596 else if (file.find("spi") != std::string::npos) 597 { 598 // Every udev spi path will have common pattern "spi<Digit>/", which 599 // describes the spi bus number at which the fru is connected; Followed 600 // by a slash following the vpd address of the fru. Taking the above 601 // input as a common key, we try to search for the pattern "spi<Digit>/" 602 // using regular expression. 603 std::regex spiPattern("((spi)[0-9]+)(\\/)"); 604 std::string spiBus{}; 605 std::smatch match; 606 if (std::regex_search(file, match, spiPattern)) 607 { 608 spiBus = match.str(1); 609 } 610 else 611 { 612 std::cerr << "The given udev path < " << file 613 << " > doesn't match the required pattern. Skipping VPD " 614 "collection." 615 << std::endl; 616 exit(EXIT_SUCCESS); 617 } 618 // Forming the generic path 619 file = spiPathPrefix + driver + "/" + spiBus + ".0/eeprom"; 620 } 621 else 622 { 623 std::cerr << "\n The given EEPROM path < " << file 624 << " > is not valid. It's neither I2C nor " 625 "SPI path. Skipping VPD collection.." 626 << std::endl; 627 exit(EXIT_SUCCESS); 628 } 629 } 630 std::string getBadVpdName(const std::string& file) 631 { 632 std::string badVpd = BAD_VPD_DIR; 633 if (file.find("i2c") != std::string::npos) 634 { 635 badVpd += "i2c-"; 636 std::regex i2cPattern("(at24/)([0-9]+-[0-9]+)\\/"); 637 std::smatch match; 638 if (std::regex_search(file, match, i2cPattern)) 639 { 640 badVpd += match.str(2); 641 } 642 } 643 else if (file.find("spi") != std::string::npos) 644 { 645 std::regex spiPattern("((spi)[0-9]+)(.0)"); 646 std::smatch match; 647 if (std::regex_search(file, match, spiPattern)) 648 { 649 badVpd += match.str(1); 650 } 651 } 652 return badVpd; 653 } 654 655 void dumpBadVpd(const std::string& file, const Binary& vpdVector) 656 { 657 fs::path badVpdDir = BAD_VPD_DIR; 658 fs::create_directory(badVpdDir); 659 std::string badVpdPath = getBadVpdName(file); 660 if (fs::exists(badVpdPath)) 661 { 662 std::error_code ec; 663 fs::remove(badVpdPath, ec); 664 if (ec) // error code 665 { 666 std::string error = "Error removing the existing broken vpd in "; 667 error += badVpdPath; 668 error += ". Error code : "; 669 error += ec.value(); 670 error += ". Error message : "; 671 error += ec.message(); 672 throw std::runtime_error(error); 673 } 674 } 675 std::ofstream badVpdFileStream(badVpdPath, std::ofstream::binary); 676 if (!badVpdFileStream) 677 { 678 throw std::runtime_error( 679 "Failed to open bad vpd file path in /tmp/bad-vpd. " 680 "Unable to dump the broken/bad vpd file."); 681 } 682 badVpdFileStream.write(reinterpret_cast<const char*>(vpdVector.data()), 683 vpdVector.size()); 684 } 685 686 const std::string getKwVal(const Parsed& vpdMap, const std::string& rec, 687 const std::string& kwd) 688 { 689 std::string kwVal{}; 690 691 auto findRec = vpdMap.find(rec); 692 693 // check if record is found in map we got by parser 694 if (findRec != vpdMap.end()) 695 { 696 auto findKwd = findRec->second.find(kwd); 697 698 if (findKwd != findRec->second.end()) 699 { 700 kwVal = findKwd->second; 701 } 702 else 703 { 704 std::cout << "Keyword not found" << std::endl; 705 } 706 } 707 else 708 { 709 std::cerr << "Record not found" << std::endl; 710 } 711 712 return kwVal; 713 } 714 715 std::string hexString(const std::variant<Binary, std::string>& kw) 716 { 717 std::string hexString; 718 std::visit( 719 [&hexString](auto&& kw) { 720 std::stringstream ss; 721 std::string hexRep = "0x"; 722 ss << hexRep; 723 for (auto& kwVal : kw) 724 { 725 ss << std::setfill('0') << std::setw(2) << std::hex 726 << static_cast<int>(kwVal); 727 } 728 hexString = ss.str(); 729 }, 730 kw); 731 return hexString; 732 } 733 734 std::string getPrintableValue(const std::variant<Binary, std::string>& kwVal) 735 { 736 std::string kwString{}; 737 std::visit( 738 [&kwString](auto&& kwVal) { 739 const auto it = 740 std::find_if(kwVal.begin(), kwVal.end(), 741 [](const auto& kw) { return !isprint(kw); }); 742 if (it != kwVal.end()) 743 { 744 kwString = hexString(kwVal); 745 } 746 else 747 { 748 kwString = std::string(kwVal.begin(), kwVal.end()); 749 } 750 }, 751 kwVal); 752 return kwString; 753 } 754 755 void executePostFailAction(const nlohmann::json& json, const std::string& file) 756 { 757 if ((json["frus"][file].at(0)).find("postActionFail") == 758 json["frus"][file].at(0).end()) 759 { 760 return; 761 } 762 763 uint8_t pinValue = 0; 764 std::string pinName; 765 766 for (const auto& postAction : 767 (json["frus"][file].at(0))["postActionFail"].items()) 768 { 769 if (postAction.key() == "pin") 770 { 771 pinName = postAction.value(); 772 } 773 else if (postAction.key() == "value") 774 { 775 // Get the value to set 776 pinValue = postAction.value(); 777 } 778 } 779 780 std::cout << "Setting GPIO: " << pinName << " to " << (int)pinValue 781 << std::endl; 782 783 try 784 { 785 gpiod::line outputLine = gpiod::find_line(pinName); 786 787 if (!outputLine) 788 { 789 throw GpioException( 790 "Couldn't find output line for the GPIO. Skipping " 791 "this GPIO action."); 792 } 793 outputLine.request( 794 {"Disable line", ::gpiod::line_request::DIRECTION_OUTPUT, 0}, 795 pinValue); 796 } 797 catch (const std::exception& e) 798 { 799 std::string i2cBusAddr; 800 std::string errMsg = e.what(); 801 errMsg += "\nGPIO: " + pinName; 802 803 if ((json["frus"][file].at(0)["postActionFail"].find( 804 "gpioI2CAddress")) != 805 json["frus"][file].at(0)["postActionFail"].end()) 806 { 807 i2cBusAddr = 808 json["frus"][file].at(0)["postActionFail"]["gpioI2CAddress"]; 809 errMsg += " i2cBusAddress: " + i2cBusAddr; 810 } 811 812 throw GpioException(e.what()); 813 } 814 815 return; 816 } 817 818 std::optional<bool> isPresent(const nlohmann::json& json, 819 const std::string& file) 820 821 { 822 if ((json["frus"][file].at(0)).find("presence") != 823 json["frus"][file].at(0).end()) 824 { 825 if (((json["frus"][file].at(0)["presence"]).find("pin") != 826 json["frus"][file].at(0)["presence"].end()) && 827 ((json["frus"][file].at(0)["presence"]).find("value") != 828 json["frus"][file].at(0)["presence"].end())) 829 { 830 std::string presPinName = 831 json["frus"][file].at(0)["presence"]["pin"]; 832 Byte presPinValue = json["frus"][file].at(0)["presence"]["value"]; 833 834 try 835 { 836 gpiod::line presenceLine = gpiod::find_line(presPinName); 837 838 if (!presenceLine) 839 { 840 std::cerr << "Couldn't find the presence line for - " 841 << presPinName << std::endl; 842 843 throw GpioException( 844 "Couldn't find the presence line for the " 845 "GPIO. Skipping this GPIO action."); 846 } 847 848 presenceLine.request({"Read the presence line", 849 gpiod::line_request::DIRECTION_INPUT, 0}); 850 851 Byte gpioData = presenceLine.get_value(); 852 853 return (gpioData == presPinValue); 854 } 855 catch (const std::exception& e) 856 { 857 std::string i2cBusAddr; 858 std::string errMsg = e.what(); 859 errMsg += " GPIO : " + presPinName; 860 861 if ((json["frus"][file].at(0)["presence"]) 862 .find("gpioI2CAddress") != 863 json["frus"][file].at(0)["presence"].end()) 864 { 865 i2cBusAddr = 866 json["frus"][file].at(0)["presence"]["gpioI2CAddress"]; 867 errMsg += " i2cBusAddress: " + i2cBusAddr; 868 } 869 870 // Take failure postAction 871 executePostFailAction(json, file); 872 throw GpioException(errMsg); 873 } 874 } 875 else 876 { 877 // missing required informations 878 std::cerr 879 << "VPD inventory JSON missing basic informations of presence " 880 "for this FRU : [" 881 << file << "]. Executing executePostFailAction." << std::endl; 882 883 // Take failure postAction 884 executePostFailAction(json, file); 885 886 return false; 887 } 888 } 889 return std::optional<bool>{}; 890 } 891 892 bool executePreAction(const nlohmann::json& json, const std::string& file) 893 { 894 auto present = isPresent(json, file); 895 if (present && !present.value()) 896 { 897 executePostFailAction(json, file); 898 return false; 899 } 900 901 if ((json["frus"][file].at(0)).find("preAction") != 902 json["frus"][file].at(0).end()) 903 { 904 if (((json["frus"][file].at(0)["preAction"]).find("pin") != 905 json["frus"][file].at(0)["preAction"].end()) && 906 ((json["frus"][file].at(0)["preAction"]).find("value") != 907 json["frus"][file].at(0)["preAction"].end())) 908 { 909 std::string pinName = json["frus"][file].at(0)["preAction"]["pin"]; 910 // Get the value to set 911 Byte pinValue = json["frus"][file].at(0)["preAction"]["value"]; 912 913 std::cout << "Setting GPIO: " << pinName << " to " << (int)pinValue 914 << std::endl; 915 try 916 { 917 gpiod::line outputLine = gpiod::find_line(pinName); 918 919 if (!outputLine) 920 { 921 std::cerr << "Couldn't find the line for output pin - " 922 << pinName << std::endl; 923 throw GpioException( 924 "Couldn't find output line for the GPIO. " 925 "Skipping this GPIO action."); 926 } 927 outputLine.request({"FRU pre-action", 928 ::gpiod::line_request::DIRECTION_OUTPUT, 0}, 929 pinValue); 930 } 931 catch (const std::exception& e) 932 { 933 std::string i2cBusAddr; 934 std::string errMsg = e.what(); 935 errMsg += " GPIO : " + pinName; 936 937 if ((json["frus"][file].at(0)["preAction"]) 938 .find("gpioI2CAddress") != 939 json["frus"][file].at(0)["preAction"].end()) 940 { 941 i2cBusAddr = 942 json["frus"][file].at(0)["preAction"]["gpioI2CAddress"]; 943 errMsg += " i2cBusAddress: " + i2cBusAddr; 944 } 945 946 // Take failure postAction 947 executePostFailAction(json, file); 948 throw GpioException(errMsg); 949 } 950 } 951 else 952 { 953 // missing required informations 954 std::cerr 955 << "VPD inventory JSON missing basic informations of preAction " 956 "for this FRU : [" 957 << file << "]. Executing executePostFailAction." << std::endl; 958 959 // Take failure postAction 960 executePostFailAction(json, file); 961 return false; 962 } 963 } 964 return true; 965 } 966 967 void insertOrMerge(inventory::InterfaceMap& map, 968 const inventory::Interface& interface, 969 inventory::PropertyMap&& property) 970 { 971 if (map.find(interface) != map.end()) 972 { 973 auto& prop = map.at(interface); 974 prop.insert(property.begin(), property.end()); 975 } 976 else 977 { 978 map.emplace(interface, property); 979 } 980 } 981 982 BIOSAttrValueType readBIOSAttribute(const std::string& attrName) 983 { 984 std::tuple<std::string, BIOSAttrValueType, BIOSAttrValueType> attrVal; 985 auto bus = sdbusplus::bus::new_default(); 986 auto method = bus.new_method_call( 987 "xyz.openbmc_project.BIOSConfigManager", 988 "/xyz/openbmc_project/bios_config/manager", 989 "xyz.openbmc_project.BIOSConfig.Manager", "GetAttribute"); 990 method.append(attrName); 991 try 992 { 993 auto result = bus.call(method); 994 result.read(std::get<0>(attrVal), std::get<1>(attrVal), 995 std::get<2>(attrVal)); 996 } 997 catch (const sdbusplus::exception::SdBusError& e) 998 { 999 std::cerr << "Failed to read BIOS Attribute: " << attrName << std::endl; 1000 std::cerr << e.what() << std::endl; 1001 } 1002 return std::get<1>(attrVal); 1003 } 1004 1005 std::string getPowerState() 1006 { 1007 // TODO: How do we handle multiple chassis? 1008 std::string powerState{}; 1009 auto bus = sdbusplus::bus::new_default(); 1010 auto properties = bus.new_method_call("xyz.openbmc_project.State.Chassis", 1011 "/xyz/openbmc_project/state/chassis0", 1012 "org.freedesktop.DBus.Properties", 1013 "Get"); 1014 properties.append("xyz.openbmc_project.State.Chassis"); 1015 properties.append("CurrentPowerState"); 1016 auto result = bus.call(properties); 1017 if (!result.is_method_error()) 1018 { 1019 std::variant<std::string> val; 1020 result.read(val); 1021 if (auto pVal = std::get_if<std::string>(&val)) 1022 { 1023 powerState = *pVal; 1024 } 1025 } 1026 std::cout << "Power state is: " << powerState << std::endl; 1027 return powerState; 1028 } 1029 1030 Binary getVpdDataInVector(const nlohmann::json& js, const std::string& file) 1031 { 1032 uint32_t offset = 0; 1033 // check if offset present? 1034 for (const auto& item : js["frus"][file]) 1035 { 1036 if (item.find("offset") != item.end()) 1037 { 1038 offset = item["offset"]; 1039 } 1040 } 1041 1042 // TODO: Figure out a better way to get max possible VPD size. 1043 auto maxVPDSize = std::min(std::filesystem::file_size(file), 1044 static_cast<uintmax_t>(65504)); 1045 1046 Binary vpdVector; 1047 vpdVector.resize(maxVPDSize); 1048 std::ifstream vpdFile; 1049 vpdFile.exceptions(std::ifstream::badbit | std::ifstream::failbit); 1050 try 1051 { 1052 vpdFile.open(file, std::ios::binary | std::ios::in); 1053 vpdFile.seekg(offset, std::ios_base::cur); 1054 vpdFile.read(reinterpret_cast<char*>(&vpdVector[0]), maxVPDSize); 1055 vpdVector.resize(vpdFile.gcount()); 1056 } 1057 catch (const std::ifstream::failure& fail) 1058 { 1059 std::cerr << "Exception in file handling [" << file 1060 << "] error : " << fail.what(); 1061 std::cerr << "EEPROM file size =" << std::filesystem::file_size(file) 1062 << std::endl; 1063 std::cerr << "Stream file size = " << vpdFile.gcount() << std::endl; 1064 std::cerr << " Vector size" << vpdVector.size() << std::endl; 1065 throw; 1066 } 1067 1068 // Make sure we reset the EEPROM pointer to a "safe" location if it was 1069 // a DDIMM SPD that we just read. 1070 for (const auto& item : js["frus"][file]) 1071 { 1072 if (item.find("extraInterfaces") != item.end()) 1073 { 1074 if (item["extraInterfaces"].find( 1075 "xyz.openbmc_project.Inventory.Item.Dimm") != 1076 item["extraInterfaces"].end()) 1077 { 1078 // check added here for DDIMM only workarround 1079 vpdType dimmType = vpdTypeCheck(vpdVector); 1080 if (dimmType == constants::DDR4_DDIMM_MEMORY_VPD || 1081 dimmType == constants::DDR5_DDIMM_MEMORY_VPD) 1082 { 1083 try 1084 { 1085 // moves the EEPROM pointer to 2048 'th byte. 1086 vpdFile.seekg(2047, std::ios::beg); 1087 // Read that byte and discard - to affirm the move 1088 // operation. 1089 char ch; 1090 vpdFile.read(&ch, sizeof(ch)); 1091 } 1092 catch (const std::ifstream::failure& fail) 1093 { 1094 std::cerr << "Exception in file handling [" << file 1095 << "] error : " << fail.what(); 1096 std::cerr << "Stream file size = " << vpdFile.gcount() 1097 << std::endl; 1098 throw; 1099 } 1100 } 1101 break; 1102 } 1103 } 1104 } 1105 1106 return vpdVector; 1107 } 1108 1109 std::string getDbusNameForThisKw(const std::string& keyword) 1110 { 1111 if (keyword[0] == constants::POUND_KW) 1112 { 1113 return (std::string(constants::POUND_KW_PREFIX) + keyword[1]); 1114 } 1115 else if (isdigit(keyword[0])) 1116 { 1117 return (std::string(constants::NUMERIC_KW_PREFIX) + keyword); 1118 } 1119 return keyword; 1120 } 1121 1122 void clearVpdOnRemoval(const std::string& objPath, 1123 inventory::InterfaceMap& interfacesPropMap) 1124 { 1125 std::vector<std::string> vpdRelatedInterfaces{ 1126 constants::invOperationalStatusIntf, constants::invItemIntf, 1127 constants::invAssetIntf}; 1128 1129 std::vector<std::string> interfaces{}; 1130 auto mapperResponse = inventory::getObject(objPath, interfaces); 1131 1132 for (const auto& [service, interfaceList] : mapperResponse) 1133 { 1134 // Handle FRUs under PIM 1135 if (service.compare(pimService) != 0) 1136 { 1137 continue; 1138 } 1139 1140 for (const auto& interface : interfaceList) 1141 { 1142 // Only process for VPD related interfaces. 1143 if ((interface.find("com.ibm.ipzvpd") != std::string::npos) || 1144 ((std::find(vpdRelatedInterfaces.begin(), 1145 vpdRelatedInterfaces.end(), interface)) != 1146 vpdRelatedInterfaces.end())) 1147 { 1148 const auto propertyList = getAllDBusProperty<GetAllResultType>( 1149 service, objPath, interface); 1150 1151 inventory::PropertyMap propertyValueMap; 1152 for (auto aProperty : propertyList) 1153 { 1154 const auto& propertyName = std::get<0>(aProperty); 1155 const auto& propertyValue = std::get<1>(aProperty); 1156 1157 if (std::holds_alternative<Binary>(propertyValue)) 1158 { 1159 propertyValueMap.emplace(propertyName, Binary{}); 1160 } 1161 else if (std::holds_alternative<std::string>(propertyValue)) 1162 { 1163 propertyValueMap.emplace(propertyName, std::string{}); 1164 } 1165 else if (std::holds_alternative<bool>(propertyValue)) 1166 { 1167 if (propertyName.compare("Present") == 0) 1168 { 1169 propertyValueMap.emplace(propertyName, false); 1170 } 1171 else if (propertyName.compare("Functional") == 0) 1172 { 1173 propertyValueMap.emplace(propertyName, true); 1174 } 1175 } 1176 } 1177 interfacesPropMap.emplace(interface, 1178 std::move(propertyValueMap)); 1179 } 1180 } 1181 } 1182 } 1183 } // namespace vpd 1184 } // namespace openpower 1185