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