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