| #include "config.h" |
| |
| #include "ibm_vpd_utils.hpp" |
| |
| #include "common_utility.hpp" |
| #include "defines.hpp" |
| #include "vpd_exceptions.hpp" |
| |
| #include <boost/algorithm/string.hpp> |
| #include <filesystem> |
| #include <fstream> |
| #include <gpiod.hpp> |
| #include <iomanip> |
| #include <nlohmann/json.hpp> |
| #include <phosphor-logging/elog-errors.hpp> |
| #include <phosphor-logging/log.hpp> |
| #include <regex> |
| #include <sdbusplus/server.hpp> |
| #include <sstream> |
| #include <vector> |
| #include <xyz/openbmc_project/Common/error.hpp> |
| |
| using json = nlohmann::json; |
| |
| namespace openpower |
| { |
| namespace vpd |
| { |
| using namespace openpower::vpd::constants; |
| using namespace inventory; |
| using namespace phosphor::logging; |
| using namespace sdbusplus::xyz::openbmc_project::Common::Error; |
| using namespace record; |
| using namespace openpower::vpd::exceptions; |
| using namespace common::utility; |
| using Severity = openpower::vpd::constants::PelSeverity; |
| namespace fs = std::filesystem; |
| |
| // mapping of severity enum to severity interface |
| static std::unordered_map<Severity, std::string> sevMap = { |
| {Severity::INFORMATIONAL, |
| "xyz.openbmc_project.Logging.Entry.Level.Informational"}, |
| {Severity::DEBUG, "xyz.openbmc_project.Logging.Entry.Level.Debug"}, |
| {Severity::NOTICE, "xyz.openbmc_project.Logging.Entry.Level.Notice"}, |
| {Severity::WARNING, "xyz.openbmc_project.Logging.Entry.Level.Warning"}, |
| {Severity::CRITICAL, "xyz.openbmc_project.Logging.Entry.Level.Critical"}, |
| {Severity::EMERGENCY, "xyz.openbmc_project.Logging.Entry.Level.Emergency"}, |
| {Severity::ERROR, "xyz.openbmc_project.Logging.Entry.Level.Error"}, |
| {Severity::ALERT, "xyz.openbmc_project.Logging.Entry.Level.Alert"}}; |
| |
| namespace inventory |
| { |
| |
| MapperResponse |
| getObjectSubtreeForInterfaces(const std::string& root, const int32_t depth, |
| const std::vector<std::string>& interfaces) |
| { |
| auto bus = sdbusplus::bus::new_default(); |
| auto mapperCall = bus.new_method_call(mapperDestination, mapperObjectPath, |
| mapperInterface, "GetSubTree"); |
| mapperCall.append(root); |
| mapperCall.append(depth); |
| mapperCall.append(interfaces); |
| |
| MapperResponse result = {}; |
| |
| try |
| { |
| auto response = bus.call(mapperCall); |
| |
| response.read(result); |
| } |
| catch (const sdbusplus::exception_t& e) |
| { |
| log<level::ERR>("Error in mapper GetSubTree", |
| entry("ERROR=%s", e.what())); |
| } |
| |
| return result; |
| } |
| |
| } // namespace inventory |
| |
| LE2ByteData readUInt16LE(Binary::const_iterator iterator) |
| { |
| LE2ByteData lowByte = *iterator; |
| LE2ByteData highByte = *(iterator + 1); |
| lowByte |= (highByte << 8); |
| return lowByte; |
| } |
| |
| /** @brief Encodes a keyword for D-Bus. |
| */ |
| std::string encodeKeyword(const std::string& kw, const std::string& encoding) |
| { |
| if (encoding == "MAC") |
| { |
| std::string res{}; |
| size_t first = kw[0]; |
| res += toHex(first >> 4); |
| res += toHex(first & 0x0f); |
| for (size_t i = 1; i < kw.size(); ++i) |
| { |
| res += ":"; |
| res += toHex(kw[i] >> 4); |
| res += toHex(kw[i] & 0x0f); |
| } |
| return res; |
| } |
| else if (encoding == "DATE") |
| { |
| // Date, represent as |
| // <year>-<month>-<day> <hour>:<min> |
| std::string res{}; |
| static constexpr uint8_t skipPrefix = 3; |
| |
| auto strItr = kw.begin(); |
| advance(strItr, skipPrefix); |
| for_each(strItr, kw.end(), [&res](size_t c) { res += c; }); |
| |
| res.insert(BD_YEAR_END, 1, '-'); |
| res.insert(BD_MONTH_END, 1, '-'); |
| res.insert(BD_DAY_END, 1, ' '); |
| res.insert(BD_HOUR_END, 1, ':'); |
| |
| return res; |
| } |
| else // default to string encoding |
| { |
| return std::string(kw.begin(), kw.end()); |
| } |
| } |
| |
| std::string readBusProperty(const std::string& obj, const std::string& inf, |
| const std::string& prop) |
| { |
| std::string propVal{}; |
| std::string object = INVENTORY_PATH + obj; |
| auto bus = sdbusplus::bus::new_default(); |
| auto properties = bus.new_method_call( |
| "xyz.openbmc_project.Inventory.Manager", object.c_str(), |
| "org.freedesktop.DBus.Properties", "Get"); |
| properties.append(inf); |
| properties.append(prop); |
| auto result = bus.call(properties); |
| if (!result.is_method_error()) |
| { |
| inventory::Value val; |
| result.read(val); |
| if (auto pVal = std::get_if<Binary>(&val)) |
| { |
| propVal.assign(reinterpret_cast<const char*>(pVal->data()), |
| pVal->size()); |
| } |
| else if (auto pVal = std::get_if<std::string>(&val)) |
| { |
| propVal.assign(pVal->data(), pVal->size()); |
| } |
| else if (auto pVal = get_if<bool>(&val)) |
| { |
| if (*pVal == false) |
| { |
| propVal = "false"; |
| } |
| else |
| { |
| propVal = "true"; |
| } |
| } |
| } |
| return propVal; |
| } |
| |
| void createPEL(const std::map<std::string, std::string>& additionalData, |
| const Severity& sev, const std::string& errIntf, sd_bus* sdBus) |
| { |
| // This pointer will be NULL in case the call is made from ibm-read-vpd. In |
| // that case a sync call will do. |
| if (sdBus == nullptr) |
| { |
| createSyncPEL(additionalData, sev, errIntf); |
| } |
| else |
| { |
| std::string errDescription{}; |
| auto pos = additionalData.find("DESCRIPTION"); |
| if (pos != additionalData.end()) |
| { |
| errDescription = pos->second; |
| } |
| else |
| { |
| errDescription = "Description field missing in additional data"; |
| } |
| |
| std::string pelSeverity = |
| "xyz.openbmc_project.Logging.Entry.Level.Error"; |
| auto itr = sevMap.find(sev); |
| if (itr != sevMap.end()) |
| { |
| pelSeverity = itr->second; |
| } |
| |
| // Implies this is a call from Manager. Hence we need to make an async |
| // call to avoid deadlock with Phosphor-logging. |
| auto rc = sd_bus_call_method_async( |
| sdBus, NULL, loggerService, loggerObjectPath, loggerCreateInterface, |
| "Create", NULL, NULL, "ssa{ss}", errIntf.c_str(), |
| pelSeverity.c_str(), 1, "DESCRIPTION", errDescription.c_str()); |
| |
| if (rc < 0) |
| { |
| log<level::ERR>("Error calling sd_bus_call_method_async", |
| entry("RC=%d", rc), entry("MSG=%s", strerror(-rc))); |
| } |
| } |
| } |
| |
| void createSyncPEL(const std::map<std::string, std::string>& additionalData, |
| const Severity& sev, const std::string& errIntf) |
| { |
| try |
| { |
| std::string pelSeverity = |
| "xyz.openbmc_project.Logging.Entry.Level.Error"; |
| auto bus = sdbusplus::bus::new_default(); |
| auto service = getService(bus, loggerObjectPath, loggerCreateInterface); |
| auto method = bus.new_method_call(service.c_str(), loggerObjectPath, |
| loggerCreateInterface, "Create"); |
| |
| auto itr = sevMap.find(sev); |
| if (itr != sevMap.end()) |
| { |
| pelSeverity = itr->second; |
| } |
| |
| method.append(errIntf, pelSeverity, additionalData); |
| auto resp = bus.call(method); |
| } |
| catch (const sdbusplus::exception_t& e) |
| { |
| std::cerr << "Dbus call to phosphor-logging Create failed. Reason:" |
| << e.what(); |
| } |
| } |
| |
| inventory::VPDfilepath getVpdFilePath(const std::string& jsonFile, |
| const std::string& ObjPath) |
| { |
| std::ifstream inventoryJson(jsonFile); |
| const auto& jsonObject = json::parse(inventoryJson); |
| inventory::VPDfilepath filePath{}; |
| |
| if (jsonObject.find("frus") == jsonObject.end()) |
| { |
| throw(VpdJsonException( |
| "Invalid JSON structure - frus{} object not found in ", jsonFile)); |
| } |
| |
| const nlohmann::json& groupFRUS = |
| jsonObject["frus"].get_ref<const nlohmann::json::object_t&>(); |
| for (const auto& itemFRUS : groupFRUS.items()) |
| { |
| const std::vector<nlohmann::json>& groupEEPROM = |
| itemFRUS.value().get_ref<const nlohmann::json::array_t&>(); |
| for (const auto& itemEEPROM : groupEEPROM) |
| { |
| if (itemEEPROM["inventoryPath"] |
| .get_ref<const nlohmann::json::string_t&>() == ObjPath) |
| { |
| filePath = itemFRUS.key(); |
| return filePath; |
| } |
| } |
| } |
| |
| return filePath; |
| } |
| |
| bool isPathInJson(const std::string& eepromPath) |
| { |
| bool present = false; |
| std::ifstream inventoryJson(INVENTORY_JSON_SYM_LINK); |
| |
| try |
| { |
| auto js = json::parse(inventoryJson); |
| if (js.find("frus") == js.end()) |
| { |
| throw(VpdJsonException( |
| "Invalid JSON structure - frus{} object not found in ", |
| INVENTORY_JSON_SYM_LINK)); |
| } |
| json fruJson = js["frus"]; |
| |
| if (fruJson.find(eepromPath) != fruJson.end()) |
| { |
| present = true; |
| } |
| } |
| catch (const json::parse_error& ex) |
| { |
| throw(VpdJsonException("Json Parsing failed", INVENTORY_JSON_SYM_LINK)); |
| } |
| return present; |
| } |
| |
| bool isRecKwInDbusJson(const std::string& recordName, |
| const std::string& keyword) |
| { |
| std::ifstream propertyJson(DBUS_PROP_JSON); |
| json dbusProperty; |
| bool present = false; |
| |
| if (propertyJson.is_open()) |
| { |
| try |
| { |
| auto dbusPropertyJson = json::parse(propertyJson); |
| if (dbusPropertyJson.find("dbusProperties") == |
| dbusPropertyJson.end()) |
| { |
| throw(VpdJsonException("dbusProperties{} object not found in " |
| "DbusProperties json : ", |
| DBUS_PROP_JSON)); |
| } |
| |
| dbusProperty = dbusPropertyJson["dbusProperties"]; |
| if (dbusProperty.contains(recordName)) |
| { |
| const std::vector<std::string>& kwdsToPublish = |
| dbusProperty[recordName]; |
| if (find(kwdsToPublish.begin(), kwdsToPublish.end(), keyword) != |
| kwdsToPublish.end()) // present |
| { |
| present = true; |
| } |
| } |
| } |
| catch (const json::parse_error& ex) |
| { |
| throw(VpdJsonException("Json Parsing failed", DBUS_PROP_JSON)); |
| } |
| } |
| else |
| { |
| // If dbus properties json is not available, we assume the given |
| // record-keyword is part of dbus-properties json. So setting the bool |
| // variable to true. |
| present = true; |
| } |
| return present; |
| } |
| |
| vpdType vpdTypeCheck(const Binary& vpdVector) |
| { |
| // Read first 3 Bytes to check the 11S bar code format |
| std::string is11SFormat = ""; |
| for (uint8_t i = 0; i < FORMAT_11S_LEN; i++) |
| { |
| is11SFormat += vpdVector[MEMORY_VPD_DATA_START + i]; |
| } |
| |
| if (vpdVector[IPZ_DATA_START] == KW_VAL_PAIR_START_TAG) |
| { |
| // IPZ VPD FORMAT |
| return vpdType::IPZ_VPD; |
| } |
| else if (vpdVector[KW_VPD_DATA_START] == KW_VPD_START_TAG) |
| { |
| // KEYWORD VPD FORMAT |
| return vpdType::KEYWORD_VPD; |
| } |
| else if (((vpdVector[SPD_BYTE_3] & SPD_BYTE_BIT_0_3_MASK) == |
| SPD_MODULE_TYPE_DDIMM) && |
| (is11SFormat.compare(MEMORY_VPD_START_TAG))) |
| { |
| // DDIMM Memory VPD format |
| if ((vpdVector[SPD_BYTE_2] & SPD_BYTE_MASK) == SPD_DRAM_TYPE_DDR5) |
| { |
| return vpdType::DDR5_DDIMM_MEMORY_VPD; |
| } |
| else if ((vpdVector[SPD_BYTE_2] & SPD_BYTE_MASK) == SPD_DRAM_TYPE_DDR4) |
| { |
| return vpdType::DDR4_DDIMM_MEMORY_VPD; |
| } |
| } |
| else if ((vpdVector[SPD_BYTE_2] & SPD_BYTE_MASK) == SPD_DRAM_TYPE_DDR5) |
| { |
| // ISDIMM Memory VPD format |
| return vpdType::DDR5_ISDIMM_MEMORY_VPD; |
| } |
| else if ((vpdVector[SPD_BYTE_2] & SPD_BYTE_MASK) == SPD_DRAM_TYPE_DDR4) |
| { |
| // ISDIMM Memory VPD format |
| return vpdType::DDR4_ISDIMM_MEMORY_VPD; |
| } |
| |
| // INVALID VPD FORMAT |
| return vpdType::INVALID_VPD_FORMAT; |
| } |
| |
| const std::string getIM(const Parsed& vpdMap) |
| { |
| Binary imVal; |
| auto property = vpdMap.find("VSBP"); |
| if (property != vpdMap.end()) |
| { |
| auto kw = (property->second).find("IM"); |
| if (kw != (property->second).end()) |
| { |
| copy(kw->second.begin(), kw->second.end(), back_inserter(imVal)); |
| } |
| } |
| |
| std::ostringstream oss; |
| for (auto& i : imVal) |
| { |
| oss << std::setw(2) << std::setfill('0') << std::hex |
| << static_cast<int>(i); |
| } |
| |
| return oss.str(); |
| } |
| |
| const std::string getHW(const Parsed& vpdMap) |
| { |
| Binary hwVal; |
| auto prop = vpdMap.find("VINI"); |
| if (prop != vpdMap.end()) |
| { |
| auto kw = (prop->second).find("HW"); |
| if (kw != (prop->second).end()) |
| { |
| copy(kw->second.begin(), kw->second.end(), back_inserter(hwVal)); |
| } |
| } |
| |
| // The planar pass only comes from the LSB of the HW keyword, |
| // where as the MSB is used for other purposes such as signifying clock |
| // termination. |
| hwVal[0] = 0x00; |
| |
| std::ostringstream hwString; |
| for (auto& i : hwVal) |
| { |
| hwString << std::setw(2) << std::setfill('0') << std::hex |
| << static_cast<int>(i); |
| } |
| |
| return hwString.str(); |
| } |
| |
| std::string getSystemsJson(const Parsed& vpdMap) |
| { |
| std::string jsonPath = "/usr/share/vpd/"; |
| std::string jsonName{}; |
| |
| std::ifstream systemJson(SYSTEM_JSON); |
| if (!systemJson) |
| { |
| throw((VpdJsonException("Failed to access Json path", SYSTEM_JSON))); |
| } |
| |
| try |
| { |
| auto js = json::parse(systemJson); |
| |
| std::string hwKeyword = getHW(vpdMap); |
| const std::string imKeyword = getIM(vpdMap); |
| |
| transform(hwKeyword.begin(), hwKeyword.end(), hwKeyword.begin(), |
| ::toupper); |
| |
| if (js.find("system") == js.end()) |
| { |
| throw std::runtime_error("Invalid systems Json"); |
| } |
| |
| if (js["system"].find(imKeyword) == js["system"].end()) |
| { |
| throw std::runtime_error( |
| "Invalid system. This system type is not present " |
| "in the systemsJson. IM: " + |
| imKeyword); |
| } |
| |
| if ((js["system"][imKeyword].find("constraint") != |
| js["system"][imKeyword].end()) && |
| js["system"][imKeyword]["constraint"].find("HW") != |
| js["system"][imKeyword]["constraint"].end()) |
| { |
| // collect hw versions from json, and check hwKeyword is part of it |
| // if hwKeyword is found there then load respective json |
| // otherwise load default one. |
| for (const auto& hwVersion : |
| js["system"][imKeyword]["constraint"]["HW"]) |
| { |
| std::string hw = hwVersion; |
| transform(hw.begin(), hw.end(), hw.begin(), ::toupper); |
| |
| if (hw == hwKeyword) |
| { |
| jsonName = js["system"][imKeyword]["constraint"]["json"]; |
| break; |
| } |
| } |
| |
| if (jsonName.empty() && js["system"][imKeyword].find("default") != |
| js["system"][imKeyword].end()) |
| { |
| jsonName = js["system"][imKeyword]["default"]; |
| } |
| } |
| else if (js["system"][imKeyword].find("default") != |
| js["system"][imKeyword].end()) |
| { |
| jsonName = js["system"][imKeyword]["default"]; |
| } |
| else |
| { |
| throw std::runtime_error( |
| "Bad System json. Neither constraint nor default found"); |
| } |
| |
| jsonPath += jsonName; |
| } |
| |
| catch (const json::parse_error& ex) |
| { |
| throw(VpdJsonException("Json Parsing failed", SYSTEM_JSON)); |
| } |
| return jsonPath; |
| } |
| |
| void udevToGenericPath(std::string& file) |
| { |
| // Sample udevEvent i2c path : |
| // "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a480.i2c-bus/i2c-8/8-0051/8-00510/nvmem" |
| // find if the path contains the word i2c in it. |
| if (file.find("i2c") != std::string::npos) |
| { |
| std::string i2cBusAddr{}; |
| |
| // Every udev i2c path should have the common pattern |
| // "i2c-bus_number/bus_number-vpd_address". Search for |
| // "bus_number-vpd_address". |
| std::regex i2cPattern("((i2c)-[0-9]+\\/)([0-9]+-[0-9]{4})"); |
| std::smatch match; |
| if (std::regex_search(file, match, i2cPattern)) |
| { |
| i2cBusAddr = match.str(3); |
| } |
| else |
| { |
| std::cerr << "The given udev path < " << file |
| << " > doesn't match the required pattern. Skipping VPD " |
| "collection." |
| << std::endl; |
| exit(EXIT_SUCCESS); |
| } |
| // Forming the generic file path |
| file = i2cPathPrefix + i2cBusAddr + "/eeprom"; |
| } |
| // Sample udevEvent spi path : |
| // "/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" |
| // find if the path contains the word spi in it. |
| else if (file.find("spi") != std::string::npos) |
| { |
| // Every udev spi path will have common pattern "spi<Digit>/", which |
| // describes the spi bus number at which the fru is connected; Followed |
| // by a slash following the vpd address of the fru. Taking the above |
| // input as a common key, we try to search for the pattern "spi<Digit>/" |
| // using regular expression. |
| std::regex spiPattern("((spi)[0-9]+)(\\/)"); |
| std::string spiBus{}; |
| std::smatch match; |
| if (std::regex_search(file, match, spiPattern)) |
| { |
| spiBus = match.str(1); |
| } |
| else |
| { |
| std::cerr << "The given udev path < " << file |
| << " > doesn't match the required pattern. Skipping VPD " |
| "collection." |
| << std::endl; |
| exit(EXIT_SUCCESS); |
| } |
| // Forming the generic path |
| file = spiPathPrefix + spiBus + ".0/eeprom"; |
| } |
| else |
| { |
| std::cerr << "\n The given EEPROM path < " << file |
| << " > is not valid. It's neither I2C nor " |
| "SPI path. Skipping VPD collection.." |
| << std::endl; |
| exit(EXIT_SUCCESS); |
| } |
| } |
| std::string getBadVpdName(const std::string& file) |
| { |
| std::string badVpd = BAD_VPD_DIR; |
| if (file.find("i2c") != std::string::npos) |
| { |
| badVpd += "i2c-"; |
| std::regex i2cPattern("(at24/)([0-9]+-[0-9]+)\\/"); |
| std::smatch match; |
| if (std::regex_search(file, match, i2cPattern)) |
| { |
| badVpd += match.str(2); |
| } |
| } |
| else if (file.find("spi") != std::string::npos) |
| { |
| std::regex spiPattern("((spi)[0-9]+)(.0)"); |
| std::smatch match; |
| if (std::regex_search(file, match, spiPattern)) |
| { |
| badVpd += match.str(1); |
| } |
| } |
| return badVpd; |
| } |
| |
| void dumpBadVpd(const std::string& file, const Binary& vpdVector) |
| { |
| fs::path badVpdDir = BAD_VPD_DIR; |
| fs::create_directory(badVpdDir); |
| std::string badVpdPath = getBadVpdName(file); |
| if (fs::exists(badVpdPath)) |
| { |
| std::error_code ec; |
| fs::remove(badVpdPath, ec); |
| if (ec) // error code |
| { |
| std::string error = "Error removing the existing broken vpd in "; |
| error += badVpdPath; |
| error += ". Error code : "; |
| error += ec.value(); |
| error += ". Error message : "; |
| error += ec.message(); |
| throw std::runtime_error(error); |
| } |
| } |
| std::ofstream badVpdFileStream(badVpdPath, std::ofstream::binary); |
| if (!badVpdFileStream) |
| { |
| throw std::runtime_error( |
| "Failed to open bad vpd file path in /tmp/bad-vpd. " |
| "Unable to dump the broken/bad vpd file."); |
| } |
| badVpdFileStream.write(reinterpret_cast<const char*>(vpdVector.data()), |
| vpdVector.size()); |
| } |
| |
| const std::string getKwVal(const Parsed& vpdMap, const std::string& rec, |
| const std::string& kwd) |
| { |
| std::string kwVal{}; |
| |
| auto findRec = vpdMap.find(rec); |
| |
| // check if record is found in map we got by parser |
| if (findRec != vpdMap.end()) |
| { |
| auto findKwd = findRec->second.find(kwd); |
| |
| if (findKwd != findRec->second.end()) |
| { |
| kwVal = findKwd->second; |
| } |
| } |
| |
| return kwVal; |
| } |
| |
| std::string byteArrayToHexString(const Binary& vec) |
| { |
| std::stringstream ss; |
| std::string hexRep = "0x"; |
| ss << hexRep; |
| std::string str = ss.str(); |
| |
| // convert Decimal to Hex string |
| for (auto& v : vec) |
| { |
| ss << std::setfill('0') << std::setw(2) << std::hex << (int)v; |
| str = ss.str(); |
| } |
| return str; |
| } |
| |
| std::string getPrintableValue(const Binary& vec) |
| { |
| std::string str{}; |
| |
| // find for a non printable value in the vector |
| const auto it = std::find_if(vec.begin(), vec.end(), |
| [](const auto& ele) { return !isprint(ele); }); |
| |
| if (it != vec.end()) // if the given vector has any non printable value |
| { |
| for (auto itr = it; itr != vec.end(); itr++) |
| { |
| if (*itr != 0x00) |
| { |
| str = byteArrayToHexString(vec); |
| return str; |
| } |
| } |
| str = std::string(vec.begin(), it); |
| } |
| else |
| { |
| str = std::string(vec.begin(), vec.end()); |
| } |
| return str; |
| } |
| |
| void executePostFailAction(const nlohmann::json& json, const std::string& file) |
| { |
| if ((json["frus"][file].at(0)).find("postActionFail") == |
| json["frus"][file].at(0).end()) |
| { |
| return; |
| } |
| |
| uint8_t pinValue = 0; |
| std::string pinName; |
| |
| for (const auto& postAction : |
| (json["frus"][file].at(0))["postActionFail"].items()) |
| { |
| if (postAction.key() == "pin") |
| { |
| pinName = postAction.value(); |
| } |
| else if (postAction.key() == "value") |
| { |
| // Get the value to set |
| pinValue = postAction.value(); |
| } |
| } |
| |
| std::cout << "Setting GPIO: " << pinName << " to " << (int)pinValue |
| << std::endl; |
| |
| try |
| { |
| gpiod::line outputLine = gpiod::find_line(pinName); |
| |
| if (!outputLine) |
| { |
| throw GpioException( |
| "Couldn't find output line for the GPIO. Skipping " |
| "this GPIO action."); |
| } |
| outputLine.request( |
| {"Disable line", ::gpiod::line_request::DIRECTION_OUTPUT, 0}, |
| pinValue); |
| } |
| catch (const std::exception& e) |
| { |
| std::string i2cBusAddr; |
| std::string errMsg = e.what(); |
| errMsg += "\nGPIO: " + pinName; |
| |
| if ((json["frus"][file].at(0)["postActionFail"].find( |
| "gpioI2CAddress")) != |
| json["frus"][file].at(0)["postActionFail"].end()) |
| { |
| i2cBusAddr = |
| json["frus"][file].at(0)["postActionFail"]["gpioI2CAddress"]; |
| errMsg += " i2cBusAddress: " + i2cBusAddr; |
| } |
| |
| throw GpioException(e.what()); |
| } |
| |
| return; |
| } |
| |
| std::optional<bool> isPresent(const nlohmann::json& json, |
| const std::string& file) |
| |
| { |
| if ((json["frus"][file].at(0)).find("presence") != |
| json["frus"][file].at(0).end()) |
| { |
| if (((json["frus"][file].at(0)["presence"]).find("pin") != |
| json["frus"][file].at(0)["presence"].end()) && |
| ((json["frus"][file].at(0)["presence"]).find("value") != |
| json["frus"][file].at(0)["presence"].end())) |
| { |
| std::string presPinName = |
| json["frus"][file].at(0)["presence"]["pin"]; |
| Byte presPinValue = json["frus"][file].at(0)["presence"]["value"]; |
| |
| try |
| { |
| gpiod::line presenceLine = gpiod::find_line(presPinName); |
| |
| if (!presenceLine) |
| { |
| std::cerr << "Couldn't find the presence line for - " |
| << presPinName << std::endl; |
| |
| throw GpioException( |
| "Couldn't find the presence line for the " |
| "GPIO. Skipping this GPIO action."); |
| } |
| |
| presenceLine.request({"Read the presence line", |
| gpiod::line_request::DIRECTION_INPUT, 0}); |
| |
| Byte gpioData = presenceLine.get_value(); |
| |
| return (gpioData == presPinValue); |
| } |
| catch (const std::exception& e) |
| { |
| std::string i2cBusAddr; |
| std::string errMsg = e.what(); |
| errMsg += " GPIO : " + presPinName; |
| |
| if ((json["frus"][file].at(0)["presence"]) |
| .find("gpioI2CAddress") != |
| json["frus"][file].at(0)["presence"].end()) |
| { |
| i2cBusAddr = |
| json["frus"][file].at(0)["presence"]["gpioI2CAddress"]; |
| errMsg += " i2cBusAddress: " + i2cBusAddr; |
| } |
| |
| // Take failure postAction |
| executePostFailAction(json, file); |
| throw GpioException(errMsg); |
| } |
| } |
| else |
| { |
| // missing required informations |
| std::cerr |
| << "VPD inventory JSON missing basic informations of presence " |
| "for this FRU : [" |
| << file << "]. Executing executePostFailAction." << std::endl; |
| |
| // Take failure postAction |
| executePostFailAction(json, file); |
| |
| return false; |
| } |
| } |
| return std::optional<bool>{}; |
| } |
| |
| bool executePreAction(const nlohmann::json& json, const std::string& file) |
| { |
| auto present = isPresent(json, file); |
| if (present && !present.value()) |
| { |
| executePostFailAction(json, file); |
| return false; |
| } |
| |
| if ((json["frus"][file].at(0)).find("preAction") != |
| json["frus"][file].at(0).end()) |
| { |
| if (((json["frus"][file].at(0)["preAction"]).find("pin") != |
| json["frus"][file].at(0)["preAction"].end()) && |
| ((json["frus"][file].at(0)["preAction"]).find("value") != |
| json["frus"][file].at(0)["preAction"].end())) |
| { |
| std::string pinName = json["frus"][file].at(0)["preAction"]["pin"]; |
| // Get the value to set |
| Byte pinValue = json["frus"][file].at(0)["preAction"]["value"]; |
| |
| std::cout << "Setting GPIO: " << pinName << " to " << (int)pinValue |
| << std::endl; |
| try |
| { |
| gpiod::line outputLine = gpiod::find_line(pinName); |
| |
| if (!outputLine) |
| { |
| std::cerr << "Couldn't find the line for output pin - " |
| << pinName << std::endl; |
| throw GpioException( |
| "Couldn't find output line for the GPIO. " |
| "Skipping this GPIO action."); |
| } |
| outputLine.request({"FRU pre-action", |
| ::gpiod::line_request::DIRECTION_OUTPUT, 0}, |
| pinValue); |
| } |
| catch (const std::exception& e) |
| { |
| std::string i2cBusAddr; |
| std::string errMsg = e.what(); |
| errMsg += " GPIO : " + pinName; |
| |
| if ((json["frus"][file].at(0)["preAction"]) |
| .find("gpioI2CAddress") != |
| json["frus"][file].at(0)["preAction"].end()) |
| { |
| i2cBusAddr = |
| json["frus"][file].at(0)["preAction"]["gpioI2CAddress"]; |
| errMsg += " i2cBusAddress: " + i2cBusAddr; |
| } |
| |
| // Take failure postAction |
| executePostFailAction(json, file); |
| throw GpioException(errMsg); |
| } |
| } |
| else |
| { |
| // missing required informations |
| std::cerr |
| << "VPD inventory JSON missing basic informations of preAction " |
| "for this FRU : [" |
| << file << "]. Executing executePostFailAction." << std::endl; |
| |
| // Take failure postAction |
| executePostFailAction(json, file); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| void insertOrMerge(inventory::InterfaceMap& map, |
| const inventory::Interface& interface, |
| inventory::PropertyMap&& property) |
| { |
| if (map.find(interface) != map.end()) |
| { |
| auto& prop = map.at(interface); |
| prop.insert(property.begin(), property.end()); |
| } |
| else |
| { |
| map.emplace(interface, property); |
| } |
| } |
| |
| BIOSAttrValueType readBIOSAttribute(const std::string& attrName) |
| { |
| std::tuple<std::string, BIOSAttrValueType, BIOSAttrValueType> attrVal; |
| auto bus = sdbusplus::bus::new_default(); |
| auto method = bus.new_method_call( |
| "xyz.openbmc_project.BIOSConfigManager", |
| "/xyz/openbmc_project/bios_config/manager", |
| "xyz.openbmc_project.BIOSConfig.Manager", "GetAttribute"); |
| method.append(attrName); |
| try |
| { |
| auto result = bus.call(method); |
| result.read(std::get<0>(attrVal), std::get<1>(attrVal), |
| std::get<2>(attrVal)); |
| } |
| catch (const sdbusplus::exception::SdBusError& e) |
| { |
| std::cerr << "Failed to read BIOS Attribute: " << attrName << std::endl; |
| std::cerr << e.what() << std::endl; |
| } |
| return std::get<1>(attrVal); |
| } |
| |
| std::string getPowerState() |
| { |
| // TODO: How do we handle multiple chassis? |
| std::string powerState{}; |
| auto bus = sdbusplus::bus::new_default(); |
| auto properties = |
| bus.new_method_call("xyz.openbmc_project.State.Chassis", |
| "/xyz/openbmc_project/state/chassis0", |
| "org.freedesktop.DBus.Properties", "Get"); |
| properties.append("xyz.openbmc_project.State.Chassis"); |
| properties.append("CurrentPowerState"); |
| auto result = bus.call(properties); |
| if (!result.is_method_error()) |
| { |
| std::variant<std::string> val; |
| result.read(val); |
| if (auto pVal = std::get_if<std::string>(&val)) |
| { |
| powerState = *pVal; |
| } |
| } |
| std::cout << "Power state is: " << powerState << std::endl; |
| return powerState; |
| } |
| |
| Binary getVpdDataInVector(const nlohmann::json& js, const std::string& file) |
| { |
| uint32_t offset = 0; |
| // check if offset present? |
| for (const auto& item : js["frus"][file]) |
| { |
| if (item.find("offset") != item.end()) |
| { |
| offset = item["offset"]; |
| } |
| } |
| |
| // TODO: Figure out a better way to get max possible VPD size. |
| auto maxVPDSize = std::min(std::filesystem::file_size(file), |
| static_cast<uintmax_t>(65504)); |
| |
| Binary vpdVector; |
| vpdVector.resize(maxVPDSize); |
| std::ifstream vpdFile; |
| vpdFile.open(file, std::ios::binary); |
| |
| vpdFile.seekg(offset, std::ios_base::cur); |
| vpdFile.read(reinterpret_cast<char*>(&vpdVector[0]), maxVPDSize); |
| vpdVector.resize(vpdFile.gcount()); |
| |
| // Make sure we reset the EEPROM pointer to a "safe" location if it was DIMM |
| // SPD that we just read. |
| for (const auto& item : js["frus"][file]) |
| { |
| if (item.find("extraInterfaces") != item.end()) |
| { |
| if (item["extraInterfaces"].find( |
| "xyz.openbmc_project.Inventory.Item.Dimm") != |
| item["extraInterfaces"].end()) |
| { |
| // moves the EEPROM pointer to 2048 'th byte. |
| vpdFile.seekg(2047, std::ios::beg); |
| // Read that byte and discard - to affirm the move |
| // operation. |
| char ch; |
| vpdFile.read(&ch, sizeof(ch)); |
| break; |
| } |
| } |
| } |
| |
| return vpdVector; |
| } |
| } // namespace vpd |
| } // namespace openpower |