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) 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 + 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 + 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 byteArrayToHexString(const Binary& vec) 680 { 681 std::stringstream ss; 682 std::string hexRep = "0x"; 683 ss << hexRep; 684 std::string str = ss.str(); 685 686 // convert Decimal to Hex string 687 for (auto& v : vec) 688 { 689 ss << std::setfill('0') << std::setw(2) << std::hex << (int)v; 690 str = ss.str(); 691 } 692 return str; 693 } 694 695 std::string getPrintableValue(const Binary& vec) 696 { 697 std::string str{}; 698 699 // find for a non printable value in the vector 700 const auto it = std::find_if(vec.begin(), vec.end(), 701 [](const auto& ele) { return !isprint(ele); }); 702 703 if (it != vec.end()) // if the given vector has any non printable value 704 { 705 for (auto itr = it; itr != vec.end(); itr++) 706 { 707 if (*itr != 0x00) 708 { 709 str = byteArrayToHexString(vec); 710 return str; 711 } 712 } 713 str = std::string(vec.begin(), it); 714 } 715 else 716 { 717 str = std::string(vec.begin(), vec.end()); 718 } 719 return str; 720 } 721 722 void executePostFailAction(const nlohmann::json& json, const std::string& file) 723 { 724 if ((json["frus"][file].at(0)).find("postActionFail") == 725 json["frus"][file].at(0).end()) 726 { 727 return; 728 } 729 730 uint8_t pinValue = 0; 731 std::string pinName; 732 733 for (const auto& postAction : 734 (json["frus"][file].at(0))["postActionFail"].items()) 735 { 736 if (postAction.key() == "pin") 737 { 738 pinName = postAction.value(); 739 } 740 else if (postAction.key() == "value") 741 { 742 // Get the value to set 743 pinValue = postAction.value(); 744 } 745 } 746 747 std::cout << "Setting GPIO: " << pinName << " to " << (int)pinValue 748 << std::endl; 749 750 try 751 { 752 gpiod::line outputLine = gpiod::find_line(pinName); 753 754 if (!outputLine) 755 { 756 throw GpioException( 757 "Couldn't find output line for the GPIO. Skipping " 758 "this GPIO action."); 759 } 760 outputLine.request( 761 {"Disable line", ::gpiod::line_request::DIRECTION_OUTPUT, 0}, 762 pinValue); 763 } 764 catch (const std::exception& e) 765 { 766 std::string i2cBusAddr; 767 std::string errMsg = e.what(); 768 errMsg += "\nGPIO: " + pinName; 769 770 if ((json["frus"][file].at(0)["postActionFail"].find( 771 "gpioI2CAddress")) != 772 json["frus"][file].at(0)["postActionFail"].end()) 773 { 774 i2cBusAddr = 775 json["frus"][file].at(0)["postActionFail"]["gpioI2CAddress"]; 776 errMsg += " i2cBusAddress: " + i2cBusAddr; 777 } 778 779 throw GpioException(e.what()); 780 } 781 782 return; 783 } 784 785 std::optional<bool> isPresent(const nlohmann::json& json, 786 const std::string& file) 787 788 { 789 if ((json["frus"][file].at(0)).find("presence") != 790 json["frus"][file].at(0).end()) 791 { 792 if (((json["frus"][file].at(0)["presence"]).find("pin") != 793 json["frus"][file].at(0)["presence"].end()) && 794 ((json["frus"][file].at(0)["presence"]).find("value") != 795 json["frus"][file].at(0)["presence"].end())) 796 { 797 std::string presPinName = 798 json["frus"][file].at(0)["presence"]["pin"]; 799 Byte presPinValue = json["frus"][file].at(0)["presence"]["value"]; 800 801 try 802 { 803 gpiod::line presenceLine = gpiod::find_line(presPinName); 804 805 if (!presenceLine) 806 { 807 std::cerr << "Couldn't find the presence line for - " 808 << presPinName << std::endl; 809 810 throw GpioException( 811 "Couldn't find the presence line for the " 812 "GPIO. Skipping this GPIO action."); 813 } 814 815 presenceLine.request({"Read the presence line", 816 gpiod::line_request::DIRECTION_INPUT, 0}); 817 818 Byte gpioData = presenceLine.get_value(); 819 820 return (gpioData == presPinValue); 821 } 822 catch (const std::exception& e) 823 { 824 std::string i2cBusAddr; 825 std::string errMsg = e.what(); 826 errMsg += " GPIO : " + presPinName; 827 828 if ((json["frus"][file].at(0)["presence"]) 829 .find("gpioI2CAddress") != 830 json["frus"][file].at(0)["presence"].end()) 831 { 832 i2cBusAddr = 833 json["frus"][file].at(0)["presence"]["gpioI2CAddress"]; 834 errMsg += " i2cBusAddress: " + i2cBusAddr; 835 } 836 837 // Take failure postAction 838 executePostFailAction(json, file); 839 throw GpioException(errMsg); 840 } 841 } 842 else 843 { 844 // missing required informations 845 std::cerr 846 << "VPD inventory JSON missing basic informations of presence " 847 "for this FRU : [" 848 << file << "]. Executing executePostFailAction." << std::endl; 849 850 // Take failure postAction 851 executePostFailAction(json, file); 852 853 return false; 854 } 855 } 856 return std::optional<bool>{}; 857 } 858 859 bool executePreAction(const nlohmann::json& json, const std::string& file) 860 { 861 auto present = isPresent(json, file); 862 if (present && !present.value()) 863 { 864 executePostFailAction(json, file); 865 return false; 866 } 867 868 if ((json["frus"][file].at(0)).find("preAction") != 869 json["frus"][file].at(0).end()) 870 { 871 if (((json["frus"][file].at(0)["preAction"]).find("pin") != 872 json["frus"][file].at(0)["preAction"].end()) && 873 ((json["frus"][file].at(0)["preAction"]).find("value") != 874 json["frus"][file].at(0)["preAction"].end())) 875 { 876 std::string pinName = json["frus"][file].at(0)["preAction"]["pin"]; 877 // Get the value to set 878 Byte pinValue = json["frus"][file].at(0)["preAction"]["value"]; 879 880 std::cout << "Setting GPIO: " << pinName << " to " << (int)pinValue 881 << std::endl; 882 try 883 { 884 gpiod::line outputLine = gpiod::find_line(pinName); 885 886 if (!outputLine) 887 { 888 std::cerr << "Couldn't find the line for output pin - " 889 << pinName << std::endl; 890 throw GpioException( 891 "Couldn't find output line for the GPIO. " 892 "Skipping this GPIO action."); 893 } 894 outputLine.request({"FRU pre-action", 895 ::gpiod::line_request::DIRECTION_OUTPUT, 0}, 896 pinValue); 897 } 898 catch (const std::exception& e) 899 { 900 std::string i2cBusAddr; 901 std::string errMsg = e.what(); 902 errMsg += " GPIO : " + pinName; 903 904 if ((json["frus"][file].at(0)["preAction"]) 905 .find("gpioI2CAddress") != 906 json["frus"][file].at(0)["preAction"].end()) 907 { 908 i2cBusAddr = 909 json["frus"][file].at(0)["preAction"]["gpioI2CAddress"]; 910 errMsg += " i2cBusAddress: " + i2cBusAddr; 911 } 912 913 // Take failure postAction 914 executePostFailAction(json, file); 915 throw GpioException(errMsg); 916 } 917 } 918 else 919 { 920 // missing required informations 921 std::cerr 922 << "VPD inventory JSON missing basic informations of preAction " 923 "for this FRU : [" 924 << file << "]. Executing executePostFailAction." << std::endl; 925 926 // Take failure postAction 927 executePostFailAction(json, file); 928 return false; 929 } 930 } 931 return true; 932 } 933 934 void insertOrMerge(inventory::InterfaceMap& map, 935 const inventory::Interface& interface, 936 inventory::PropertyMap&& property) 937 { 938 if (map.find(interface) != map.end()) 939 { 940 auto& prop = map.at(interface); 941 prop.insert(property.begin(), property.end()); 942 } 943 else 944 { 945 map.emplace(interface, property); 946 } 947 } 948 949 BIOSAttrValueType readBIOSAttribute(const std::string& attrName) 950 { 951 std::tuple<std::string, BIOSAttrValueType, BIOSAttrValueType> attrVal; 952 auto bus = sdbusplus::bus::new_default(); 953 auto method = bus.new_method_call( 954 "xyz.openbmc_project.BIOSConfigManager", 955 "/xyz/openbmc_project/bios_config/manager", 956 "xyz.openbmc_project.BIOSConfig.Manager", "GetAttribute"); 957 method.append(attrName); 958 try 959 { 960 auto result = bus.call(method); 961 result.read(std::get<0>(attrVal), std::get<1>(attrVal), 962 std::get<2>(attrVal)); 963 } 964 catch (const sdbusplus::exception::SdBusError& e) 965 { 966 std::cerr << "Failed to read BIOS Attribute: " << attrName << std::endl; 967 std::cerr << e.what() << std::endl; 968 } 969 return std::get<1>(attrVal); 970 } 971 972 std::string getPowerState() 973 { 974 // TODO: How do we handle multiple chassis? 975 std::string powerState{}; 976 auto bus = sdbusplus::bus::new_default(); 977 auto properties = 978 bus.new_method_call("xyz.openbmc_project.State.Chassis", 979 "/xyz/openbmc_project/state/chassis0", 980 "org.freedesktop.DBus.Properties", "Get"); 981 properties.append("xyz.openbmc_project.State.Chassis"); 982 properties.append("CurrentPowerState"); 983 auto result = bus.call(properties); 984 if (!result.is_method_error()) 985 { 986 std::variant<std::string> val; 987 result.read(val); 988 if (auto pVal = std::get_if<std::string>(&val)) 989 { 990 powerState = *pVal; 991 } 992 } 993 std::cout << "Power state is: " << powerState << std::endl; 994 return powerState; 995 } 996 997 Binary getVpdDataInVector(const nlohmann::json& js, const std::string& file) 998 { 999 uint32_t offset = 0; 1000 // check if offset present? 1001 for (const auto& item : js["frus"][file]) 1002 { 1003 if (item.find("offset") != item.end()) 1004 { 1005 offset = item["offset"]; 1006 } 1007 } 1008 1009 // TODO: Figure out a better way to get max possible VPD size. 1010 auto maxVPDSize = std::min(std::filesystem::file_size(file), 1011 static_cast<uintmax_t>(65504)); 1012 1013 Binary vpdVector; 1014 vpdVector.resize(maxVPDSize); 1015 std::ifstream vpdFile; 1016 vpdFile.open(file, std::ios::binary); 1017 1018 vpdFile.seekg(offset, std::ios_base::cur); 1019 vpdFile.read(reinterpret_cast<char*>(&vpdVector[0]), maxVPDSize); 1020 vpdVector.resize(vpdFile.gcount()); 1021 1022 // Make sure we reset the EEPROM pointer to a "safe" location if it was DIMM 1023 // SPD that we just read. 1024 for (const auto& item : js["frus"][file]) 1025 { 1026 if (item.find("extraInterfaces") != item.end()) 1027 { 1028 if (item["extraInterfaces"].find( 1029 "xyz.openbmc_project.Inventory.Item.Dimm") != 1030 item["extraInterfaces"].end()) 1031 { 1032 // moves the EEPROM pointer to 2048 'th byte. 1033 vpdFile.seekg(2047, std::ios::beg); 1034 // Read that byte and discard - to affirm the move 1035 // operation. 1036 char ch; 1037 vpdFile.read(&ch, sizeof(ch)); 1038 break; 1039 } 1040 } 1041 } 1042 1043 return vpdVector; 1044 } 1045 } // namespace vpd 1046 } // namespace openpower 1047