Revamped code for VPD parser
The commit removes all the pre-existing code from the branch
and pushes the revamped code.
Major modification includes:
- Movement from multi exe to single daemon model.
- Multithreaded approach to parse FRU VPD.
- Better error handling.
- Refactored code for performance optimization.
Note: This code supports all the existing functionalities as it is.
Change-Id: I1ddce1f0725ac59020b72709689a1013643bda8b
Signed-off-by: Sunny Srivastava <sunnsr25@in.ibm.com>
diff --git a/vpd-manager/bios_handler.cpp b/vpd-manager/bios_handler.cpp
deleted file mode 100644
index 7cc5aac..0000000
--- a/vpd-manager/bios_handler.cpp
+++ /dev/null
@@ -1,691 +0,0 @@
-#include "config.h"
-
-#include "bios_handler.hpp"
-
-#include "const.hpp"
-#include "ibm_vpd_utils.hpp"
-#include "manager.hpp"
-#include "types.hpp"
-
-#include <sdbusplus/bus.hpp>
-
-#include <iostream>
-#include <memory>
-#include <string>
-#include <tuple>
-#include <variant>
-
-using namespace openpower::vpd;
-using namespace openpower::vpd::constants;
-
-namespace openpower
-{
-namespace vpd
-{
-namespace manager
-{
-void BiosHandler::checkAndListenPLDMService()
-{
- // Setup a match on NameOwnerChanged to determine when PLDM is up. In
- // the signal handler, call restoreBIOSAttribs
- static std::shared_ptr<sdbusplus::bus::match_t> nameOwnerMatch =
- std::make_shared<sdbusplus::bus::match_t>(
- *conn,
- sdbusplus::bus::match::rules::nameOwnerChanged(
- "xyz.openbmc_project.PLDM"),
- [this](sdbusplus::message_t& msg) {
- if (msg.is_method_error())
- {
- std::cerr
- << "Error in reading name owner signal " << std::endl;
- return;
- }
- std::string name;
- std::string newOwner;
- std::string oldOwner;
-
- msg.read(name, oldOwner, newOwner);
- if (newOwner != "" && name == "xyz.openbmc_project.PLDM")
- {
- this->restoreBIOSAttribs();
- // We don't need the match anymore
- nameOwnerMatch.reset();
- }
- });
- // Check if PLDM is already running, if it is, we can go ahead and attempt
- // to sync BIOS attributes (since PLDM would have initialized them by the
- // time it acquires a bus name).
- bool isPLDMRunning = false;
- try
- {
- auto bus = sdbusplus::bus::new_default();
- auto method =
- bus.new_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus",
- "org.freedesktop.DBus", "NameHasOwner");
- method.append("xyz.openbmc_project.PLDM");
-
- auto result = bus.call(method);
- result.read(isPLDMRunning);
- }
- catch (const sdbusplus::exception::SdBusError& e)
- {
- std::cerr << "Failed to check if PLDM is running, assume false"
- << std::endl;
- std::cerr << e.what() << std::endl;
- }
-
- std::cout << "Is PLDM running: " << isPLDMRunning << std::endl;
-
- if (isPLDMRunning)
- {
- nameOwnerMatch.reset();
- restoreBIOSAttribs();
- }
-}
-
-void BiosHandler::listenBiosAttribs()
-{
- static std::shared_ptr<sdbusplus::bus::match_t> biosMatcher =
- std::make_shared<sdbusplus::bus::match_t>(
- *conn,
- sdbusplus::bus::match::rules::propertiesChanged(
- "/xyz/openbmc_project/bios_config/manager",
- "xyz.openbmc_project.BIOSConfig.Manager"),
- [this](sdbusplus::message_t& msg) { biosAttribsCallback(msg); });
-}
-
-void BiosHandler::biosAttribsCallback(sdbusplus::message_t& msg)
-{
- if (msg.is_method_error())
- {
- std::cerr << "Error in reading BIOS attribute signal " << std::endl;
- return;
- }
- using BiosProperty = std::tuple<
- std::string, bool, std::string, std::string, std::string,
- std::variant<int64_t, std::string>, std::variant<int64_t, std::string>,
- std::vector<std::tuple<std::string, std::variant<int64_t, std::string>,
- std::string>>>;
-
- using BiosBaseTable =
- std::variant<std::monostate, std::map<std::string, BiosProperty>>;
- using BiosBaseTableType = std::map<std::string, BiosBaseTable>;
-
- std::string object;
- BiosBaseTableType propMap;
- msg.read(object, propMap);
- for (auto prop : propMap)
- {
- if (prop.first == "BaseBIOSTable")
- {
- if (auto list = std::get_if<std::map<std::string, BiosProperty>>(
- &(prop.second)))
- {
- for (const auto& item : *list)
- {
- std::string attributeName = std::get<0>(item);
- if (attributeName == "hb_memory_mirror_mode")
- {
- auto attrValue = std::get<5>(std::get<1>(item));
- auto val = std::get_if<std::string>(&attrValue);
- if (val)
- {
- saveAMMToVPD(*val);
- }
- }
- else if (attributeName == "hb_field_core_override")
- {
- auto attrValue = std::get<5>(std::get<1>(item));
- auto val = std::get_if<int64_t>(&attrValue);
- if (val)
- {
- saveFCOToVPD(*val);
- }
- }
- else if (attributeName == "pvm_keep_and_clear")
- {
- auto attrValue = std::get<5>(std::get<1>(item));
- auto val = std::get_if<std::string>(&attrValue);
- if (val)
- {
- saveKeepAndClearToVPD(*val);
- }
- }
- else if (attributeName == "pvm_create_default_lpar")
- {
- auto attrValue = std::get<5>(std::get<1>(item));
- auto val = std::get_if<std::string>(&attrValue);
- if (val)
- {
- saveCreateDefaultLparToVPD(*val);
- }
- }
- else if (attributeName == "pvm_clear_nvram")
- {
- auto attrValue = std::get<5>(std::get<1>(item));
- auto val = std::get_if<std::string>(&attrValue);
- if (val)
- {
- saveClearNVRAMToVPD(*val);
- }
- }
- }
- }
- else
- {
- inventory::PelAdditionalData additionalData;
- additionalData.emplace(
- "DESCRIPTION",
- "Invalid type received for BIOS base table property");
-
- createPEL(additionalData, PelSeverity::ERROR,
- errIntfForVPDDefault, nullptr);
- std::cerr
- << "Invalid type received for BIOS base table property"
- << std::endl;
- }
- }
- }
-}
-
-void BiosHandler::saveFCOToVPD(int64_t fcoVal)
-{
- if (fcoVal == -1)
- {
- std::cerr << "Invalid FCO value from BIOS: " << fcoVal << std::endl;
- return;
- }
-
- Binary vpdVal = {0, 0, 0, static_cast<uint8_t>(fcoVal)};
- auto valInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.VSYS", "RG");
-
- if (valInVPD.size() != 4)
- {
- std::cerr << "Read bad size for VSYS/RG: " << valInVPD.size()
- << std::endl;
- return;
- }
-
- if (std::memcmp(vpdVal.data(), valInVPD.data(), 4) != 0)
- {
- std::cout << "Writing FCO to VPD: " << fcoVal << std::endl;
- manager.writeKeyword(sdbusplus::message::object_path{SYSTEM_OBJECT},
- "VSYS", "RG", vpdVal);
- }
-}
-
-void BiosHandler::saveAMMToVPD(const std::string& mirrorMode)
-{
- Binary vpdVal;
- auto valInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D0");
-
- if (valInVPD.size() != 1)
- {
- std::cerr << "Read bad size for UTIL/D0: " << valInVPD.size()
- << std::endl;
- return;
- }
-
- if (mirrorMode != "Enabled" && mirrorMode != "Disabled")
- {
- std::cerr << "Bad value for Mirror mode BIOS attribute: " << mirrorMode
- << std::endl;
- return;
- }
-
- // Write to VPD only if the value is not already what we want to write.
- if (mirrorMode == "Enabled" && valInVPD.at(0) != 2)
- {
- vpdVal.emplace_back(2);
- }
- else if (mirrorMode == "Disabled" && valInVPD.at(0) != 1)
- {
- vpdVal.emplace_back(1);
- }
-
- if (!vpdVal.empty())
- {
- std::cout << "Writing AMM to VPD: " << static_cast<int>(vpdVal.at(0))
- << std::endl;
- manager.writeKeyword(sdbusplus::message::object_path{SYSTEM_OBJECT},
- "UTIL", "D0", vpdVal);
- }
-}
-
-void BiosHandler::saveKeepAndClearToVPD(const std::string& keepAndClear)
-{
- Binary vpdVal;
- auto valInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D1");
-
- if (valInVPD.size() != 1)
- {
- std::cerr << "Read bad size for UTIL/D1: " << valInVPD.size()
- << std::endl;
- return;
- }
-
- if (keepAndClear != "Enabled" && keepAndClear != "Disabled")
- {
- std::cerr << "Bad value for keep and clear BIOS attribute: "
- << keepAndClear << std::endl;
- return;
- }
-
- // Write to VPD only if the value is not already what we want to write.
- if (keepAndClear == "Enabled" && ((valInVPD.at(0) & 0x01) != 0x01))
- {
- vpdVal.emplace_back(valInVPD.at(0) | 0x01);
- }
- else if (keepAndClear == "Disabled" && ((valInVPD.at(0) & 0x01) != 0))
- {
- vpdVal.emplace_back(valInVPD.at(0) & ~(0x01));
- }
-
- if (!vpdVal.empty())
- {
- std::cout << "Writing Keep and Clear to VPD: "
- << static_cast<int>(vpdVal.at(0)) << std::endl;
- manager.writeKeyword(sdbusplus::message::object_path{SYSTEM_OBJECT},
- "UTIL", "D1", vpdVal);
- }
-}
-
-void BiosHandler::saveCreateDefaultLparToVPD(
- const std::string& createDefaultLpar)
-{
- Binary vpdVal;
- auto valInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D1");
-
- if (valInVPD.size() != 1)
- {
- std::cerr << "Read bad size for UTIL/D1: " << valInVPD.size()
- << std::endl;
- return;
- }
-
- if (createDefaultLpar != "Enabled" && createDefaultLpar != "Disabled")
- {
- std::cerr << "Bad value for create default lpar BIOS attribute: "
- << createDefaultLpar << std::endl;
- return;
- }
-
- // Write to VPD only if the value is not already what we want to write.
- if (createDefaultLpar == "Enabled" && ((valInVPD.at(0) & 0x02) != 0x02))
- {
- vpdVal.emplace_back(valInVPD.at(0) | 0x02);
- }
- else if (createDefaultLpar == "Disabled" && ((valInVPD.at(0) & 0x02) != 0))
- {
- vpdVal.emplace_back(valInVPD.at(0) & ~(0x02));
- }
-
- if (!vpdVal.empty())
- {
- std::cout << "Writing create default lpar to VPD: "
- << static_cast<int>(vpdVal.at(0)) << std::endl;
- manager.writeKeyword(sdbusplus::message::object_path{SYSTEM_OBJECT},
- "UTIL", "D1", vpdVal);
- }
-}
-
-void BiosHandler::saveClearNVRAMToVPD(const std::string& clearNVRAM)
-{
- Binary vpdVal;
- auto valInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D1");
-
- if (valInVPD.size() != 1)
- {
- std::cerr << "Read bad size for UTIL/D1: " << valInVPD.size()
- << std::endl;
- return;
- }
-
- if (clearNVRAM != "Enabled" && clearNVRAM != "Disabled")
- {
- std::cerr << "Bad value for clear NVRAM BIOS attribute: " << clearNVRAM
- << std::endl;
- return;
- }
-
- // Write to VPD only if the value is not already what we want to write.
- if (clearNVRAM == "Enabled" && ((valInVPD.at(0) & 0x04) != 0x04))
- {
- vpdVal.emplace_back(valInVPD.at(0) | 0x04);
- }
- else if (clearNVRAM == "Disabled" && ((valInVPD.at(0) & 0x04) != 0))
- {
- vpdVal.emplace_back(valInVPD.at(0) & ~(0x04));
- }
-
- if (!vpdVal.empty())
- {
- std::cout << "Writing clear NVRAM to VPD: "
- << static_cast<int>(vpdVal.at(0)) << std::endl;
- manager.writeKeyword(sdbusplus::message::object_path{SYSTEM_OBJECT},
- "UTIL", "D1", vpdVal);
- }
-}
-
-int64_t BiosHandler::readBIOSFCO()
-{
- int64_t fcoVal = -1;
- auto val = readBIOSAttribute("hb_field_core_override");
-
- if (auto pVal = std::get_if<int64_t>(&val))
- {
- fcoVal = *pVal;
- }
- else
- {
- std::cerr << "FCO is not an int" << std::endl;
- }
- return fcoVal;
-}
-
-std::string BiosHandler::readBIOSAMM()
-{
- std::string ammVal{};
- auto val = readBIOSAttribute("hb_memory_mirror_mode");
-
- if (auto pVal = std::get_if<std::string>(&val))
- {
- ammVal = *pVal;
- }
- else
- {
- std::cerr << "AMM is not a string" << std::endl;
- }
- return ammVal;
-}
-
-std::string BiosHandler::readBIOSKeepAndClear()
-{
- std::string keepAndClear{};
- auto val = readBIOSAttribute("pvm_keep_and_clear");
-
- if (auto pVal = std::get_if<std::string>(&val))
- {
- keepAndClear = *pVal;
- }
- else
- {
- std::cerr << "Keep and clear is not a string" << std::endl;
- }
- return keepAndClear;
-}
-
-std::string BiosHandler::readBIOSCreateDefaultLpar()
-{
- std::string createDefaultLpar{};
- auto val = readBIOSAttribute("pvm_create_default_lpar");
-
- if (auto pVal = std::get_if<std::string>(&val))
- {
- createDefaultLpar = *pVal;
- }
- else
- {
- std::cerr << "Create default LPAR is not a string" << std::endl;
- }
- return createDefaultLpar;
-}
-
-std::string BiosHandler::readBIOSClearNVRAM()
-{
- std::string clearNVRAM{};
- auto val = readBIOSAttribute("pvm_clear_nvram");
-
- if (auto pVal = std::get_if<std::string>(&val))
- {
- clearNVRAM = *pVal;
- }
- else
- {
- std::cerr << "Clear NVRAM is not a string" << std::endl;
- }
- return clearNVRAM;
-}
-
-void BiosHandler::saveFCOToBIOS(const std::string& fcoVal, int64_t fcoInBIOS)
-{
- if (fcoVal.size() != 4)
- {
- std::cerr << "Bad size for FCO in VPD: " << fcoVal.size() << std::endl;
- return;
- }
-
- // Need to write?
- if (fcoInBIOS == static_cast<int64_t>(fcoVal.at(3)))
- {
- std::cout << "Skip FCO BIOS write, value is already: " << fcoInBIOS
- << std::endl;
- return;
- }
-
- PendingBIOSAttrsType biosAttrs;
- biosAttrs.push_back(
- std::make_pair("hb_field_core_override",
- std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
- "AttributeType.Integer",
- fcoVal.at(3))));
-
- std::cout << "Set hb_field_core_override to: "
- << static_cast<int>(fcoVal.at(3)) << std::endl;
-
- setBusProperty<PendingBIOSAttrsType>(
- "xyz.openbmc_project.BIOSConfigManager",
- "/xyz/openbmc_project/bios_config/manager",
- "xyz.openbmc_project.BIOSConfig.Manager", "PendingAttributes",
- biosAttrs);
-}
-
-void BiosHandler::saveAMMToBIOS(const std::string& ammVal,
- const std::string& ammInBIOS)
-{
- if (ammVal.size() != 1)
- {
- std::cerr << "Bad size for AMM in VPD: " << ammVal.size() << std::endl;
- return;
- }
-
- // Make sure data in VPD is sane
- if (ammVal.at(0) != 1 && ammVal.at(0) != 2)
- {
- std::cerr << "Bad value for AMM read from VPD: "
- << static_cast<int>(ammVal.at(0)) << std::endl;
- return;
- }
-
- // Need to write?
- std::string toWrite = (ammVal.at(0) == 2) ? "Enabled" : "Disabled";
- if (ammInBIOS == toWrite)
- {
- std::cout << "Skip AMM BIOS write, value is already: " << toWrite
- << std::endl;
- return;
- }
-
- PendingBIOSAttrsType biosAttrs;
- biosAttrs.push_back(
- std::make_pair("hb_memory_mirror_mode",
- std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
- "AttributeType.Enumeration",
- toWrite)));
-
- std::cout << "Set hb_memory_mirror_mode to: " << toWrite << std::endl;
-
- setBusProperty<PendingBIOSAttrsType>(
- "xyz.openbmc_project.BIOSConfigManager",
- "/xyz/openbmc_project/bios_config/manager",
- "xyz.openbmc_project.BIOSConfig.Manager", "PendingAttributes",
- biosAttrs);
-}
-
-void BiosHandler::saveKeepAndClearToBIOS(const std::string& keepAndClear,
- const std::string& keepAndClearInBIOS)
-{
- if (keepAndClear.size() != 1)
- {
- std::cerr << "Bad size for Keep and Clear in VPD: "
- << keepAndClear.size() << std::endl;
- return;
- }
-
- // Need to write?
- std::string toWrite = (keepAndClear.at(0) & 0x01) ? "Enabled" : "Disabled";
- if (keepAndClearInBIOS == toWrite)
- {
- std::cout << "Skip Keep and Clear BIOS write, value is already: "
- << toWrite << std::endl;
- return;
- }
-
- PendingBIOSAttrsType biosAttrs;
- biosAttrs.push_back(
- std::make_pair("pvm_keep_and_clear",
- std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
- "AttributeType.Enumeration",
- toWrite)));
-
- std::cout << "Set pvm_keep_and_clear to: " << toWrite << std::endl;
-
- setBusProperty<PendingBIOSAttrsType>(
- "xyz.openbmc_project.BIOSConfigManager",
- "/xyz/openbmc_project/bios_config/manager",
- "xyz.openbmc_project.BIOSConfig.Manager", "PendingAttributes",
- biosAttrs);
-}
-
-void BiosHandler::saveCreateDefaultLparToBIOS(
- const std::string& createDefaultLpar,
- const std::string& createDefaultLparInBIOS)
-{
- if (createDefaultLpar.size() != 1)
- {
- std::cerr << "Bad size for Create default LPAR in VPD: "
- << createDefaultLpar.size() << std::endl;
- return;
- }
-
- // Need to write?
- std::string toWrite =
- (createDefaultLpar.at(0) & 0x02) ? "Enabled" : "Disabled";
- if (createDefaultLparInBIOS == toWrite)
- {
- std::cout << "Skip Create default LPAR BIOS write, value is already: "
- << toWrite << std::endl;
- return;
- }
-
- PendingBIOSAttrsType biosAttrs;
- biosAttrs.push_back(
- std::make_pair("pvm_create_default_lpar",
- std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
- "AttributeType.Enumeration",
- toWrite)));
-
- std::cout << "Set pvm_create_default_lpar to: " << toWrite << std::endl;
-
- setBusProperty<PendingBIOSAttrsType>(
- "xyz.openbmc_project.BIOSConfigManager",
- "/xyz/openbmc_project/bios_config/manager",
- "xyz.openbmc_project.BIOSConfig.Manager", "PendingAttributes",
- biosAttrs);
-}
-
-void BiosHandler::saveClearNVRAMToBIOS(const std::string& clearNVRAM,
- const std::string& clearNVRAMInBIOS)
-{
- if (clearNVRAM.size() != 1)
- {
- std::cerr << "Bad size for Clear NVRAM in VPD: " << clearNVRAM.size()
- << std::endl;
- return;
- }
-
- // Need to write?
- std::string toWrite = (clearNVRAM.at(0) & 0x04) ? "Enabled" : "Disabled";
- if (clearNVRAMInBIOS == toWrite)
- {
- std::cout << "Skip Clear NVRAM BIOS write, value is already: "
- << toWrite << std::endl;
- return;
- }
-
- PendingBIOSAttrsType biosAttrs;
- biosAttrs.push_back(
- std::make_pair("pvm_clear_nvram",
- std::make_tuple("xyz.openbmc_project.BIOSConfig.Manager."
- "AttributeType.Enumeration",
- toWrite)));
-
- std::cout << "Set pvm_clear_nvram to: " << toWrite << std::endl;
-
- setBusProperty<PendingBIOSAttrsType>(
- "xyz.openbmc_project.BIOSConfigManager",
- "/xyz/openbmc_project/bios_config/manager",
- "xyz.openbmc_project.BIOSConfig.Manager", "PendingAttributes",
- biosAttrs);
-}
-
-void BiosHandler::restoreBIOSAttribs()
-{
- // TODO: We could make this slightly more scalable by defining a table of
- // attributes and their corresponding VPD keywords. However, that needs much
- // more thought.
- std::cout << "Attempting BIOS attribute reset" << std::endl;
- // Check if the VPD contains valid data for FCO, AMM, Keep and Clear,
- // Create default LPAR and Clear NVRAM *and* that it differs from the data
- // already in the attributes. If so, set the BIOS attributes as per the
- // value in the VPD. If the VPD contains default data, then initialize the
- // VPD keywords with data taken from the BIOS.
- auto fcoInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.VSYS", "RG");
- auto ammInVPD = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D0");
- auto keepAndClearInVPD =
- readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D1");
- auto fcoInBIOS = readBIOSFCO();
- auto ammInBIOS = readBIOSAMM();
- auto keepAndClearInBIOS = readBIOSKeepAndClear();
- auto createDefaultLparInBIOS = readBIOSCreateDefaultLpar();
- auto clearNVRAMInBIOS = readBIOSClearNVRAM();
-
- if (fcoInVPD == " ")
- {
- saveFCOToVPD(fcoInBIOS);
- }
- else
- {
- saveFCOToBIOS(fcoInVPD, fcoInBIOS);
- }
-
- if (ammInVPD.at(0) == 0)
- {
- saveAMMToVPD(ammInBIOS);
- }
- else
- {
- saveAMMToBIOS(ammInVPD, ammInBIOS);
- }
-
- // No uninitialized handling needed for keep and clear, create default
- // lpar and clear nvram attributes. Their defaults in VPD are 0's which is
- // what we want.
- saveKeepAndClearToBIOS(keepAndClearInVPD, keepAndClearInBIOS);
- // Have to read D1 again because two attributes are stored in the same
- // keyword.
- auto createDefaultLparInVPD =
- readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D1");
- saveCreateDefaultLparToBIOS(createDefaultLparInVPD,
- createDefaultLparInBIOS);
-
- auto clearNVRAMInVPD =
- readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.UTIL", "D1");
- saveClearNVRAMToBIOS(clearNVRAMInVPD, clearNVRAMInBIOS);
-
- // Start listener now that we have done the restore
- listenBiosAttribs();
-}
-} // namespace manager
-} // namespace vpd
-} // namespace openpower
diff --git a/vpd-manager/bios_handler.hpp b/vpd-manager/bios_handler.hpp
deleted file mode 100644
index adee3c7..0000000
--- a/vpd-manager/bios_handler.hpp
+++ /dev/null
@@ -1,254 +0,0 @@
-#pragma once
-
-#include "types.hpp"
-
-#include <stdint.h>
-
-#include <sdbusplus/asio/connection.hpp>
-
-#include <string>
-
-namespace openpower
-{
-namespace vpd
-{
-namespace manager
-{
-
-class Manager;
-/**
- * @brief A class that handles changes to BIOS attributes backed by VPD.
- *
- * This class has APIs that handle updates to BIOS attributes that need to
- * be backed up to VPD. It mainly does the following:
- * 1) Checks if the VPD keywords that BIOS attributes are backed to are
- * uninitialized. If so, it initializes them.
- * 2) Listens for changes to BIOS attributes and synchronizes them to the
- * appropriate VPD keyword.
- *
- * Since on a factory reset like scenario, the BIOS attributes are initialized
- * by PLDM, this code waits until PLDM has grabbed a bus name before attempting
- * any syncs.
- */
-class BiosHandler
-{
- public:
- // Some default and deleted constructors and assignments.
- BiosHandler() = delete;
- BiosHandler(const BiosHandler&) = delete;
- BiosHandler& operator=(const BiosHandler&) = delete;
- BiosHandler(Manager&&) = delete;
- BiosHandler& operator=(BiosHandler&&) = delete;
- ~BiosHandler() = default;
-
- BiosHandler(std::shared_ptr<sdbusplus::asio::connection>& conn,
- Manager& manager) : conn(conn), manager(manager)
- {
- checkAndListenPLDMService();
- }
-
- private:
- /**
- * @brief Check if PLDM service is running and run BIOS sync
- *
- * This API checks if the PLDM service is running and if yes it will start
- * an immediate sync of BIOS attributes. If the service is not running, it
- * registers a listener to be notified when the service starts so that a
- * restore can be performed.
- */
- void checkAndListenPLDMService();
-
- /**
- * @brief Register listener for changes to BIOS Attributes.
- *
- * The VPD manager needs to listen to changes to certain BIOS attributes
- * that are backed by VPD. When the attributes we are interested in
- * change, the VPD manager will make sure that we write them back to the
- * VPD keywords that back them up.
- */
- void listenBiosAttribs();
-
- /**
- * @brief Callback for BIOS Attribute changes
- *
- * Checks if the BIOS attribute(s) changed are those backed up by VPD. If
- * yes, it will update the VPD with the new attribute value.
- * @param[in] msg - The callback message.
- */
- void biosAttribsCallback(sdbusplus::message_t& msg);
-
- /**
- * @brief Persistently saves the Memory mirror mode
- *
- * Memory mirror mode setting is saved to the UTIL/D0 keyword in the
- * motherboard VPD. If the mirror mode in BIOS is "Disabled", set D0 to 1,
- * if "Enabled" set D0 to 2
- *
- * @param[in] mirrorMode - The mirror mode BIOS attribute.
- */
- void saveAMMToVPD(const std::string& mirrorMode);
-
- /**
- * @brief Persistently saves the Field Core Override setting
- *
- * Saves the field core override value (FCO) into the VSYS/RG keyword in
- * the motherboard VPD.
- *
- * @param[in] fcoVal - The FCO value as an integer.
- */
- void saveFCOToVPD(int64_t fcoVal);
-
- /**
- * @brief Persistently saves the Keep and Clear setting
- *
- * Keep and clear setting is saved to the UTIL/D1 keyword's 0th bit in the
- * motherboard VPD. If the keep and clear in BIOS is "Disabled", set D1:0 to
- * 0, if "Enabled" set D1:0 to 1
- *
- * @param[in] keepAndClear - The keep and clear BIOS attribute.
- */
- void saveKeepAndClearToVPD(const std::string& keepAndClear);
-
- /**
- * @brief Persistently saves the Create default LPAR setting
- *
- * Create default LPAR setting is saved to the UTIL/D1 keyword's 1st bit in
- * the motherboard VPD. If the create default LPAR in BIOS is "Disabled",
- * set D1:1 to 0, if "Enabled" set D1:1 to 1
- *
- * @param[in] createDefaultLpar - The mirror mode BIOS attribute.
- */
- void saveCreateDefaultLparToVPD(const std::string& createDefaultLpar);
-
- /**
- * @brief Persistently saves the Clear NVRAM setting
- *
- * Create default LPAR setting is saved to the UTIL/D1 keyword's 2nd bit in
- * the motherboard VPD. If the clear NVRAM in BIOS is "Disabled",
- * set D1:2 to 0, if "Enabled" set D1:2 to 1
- *
- * @param[in] createDefaultLpar - The mirror mode BIOS attribute.
- */
- void saveClearNVRAMToVPD(const std::string& clearNVRAM);
-
- /**
- * @brief Writes Memory mirror mode to BIOS
- *
- * Writes to the hb_memory_mirror_mode BIOS attribute, if the value is
- * not already the same as we are trying to write.
- *
- * @param[in] ammVal - The mirror mode as read from VPD.
- * @param[in] ammInBIOS - The mirror mode in the BIOS table.
- */
- void saveAMMToBIOS(const std::string& ammVal, const std::string& ammInBIOS);
-
- /**
- * @brief Writes Field Core Override to BIOS
- *
- * Writes to the hb_field_core_override BIOS attribute, if the value is not
- * already the same as we are trying to write.
- *
- * @param[in] fcoVal - The FCO value as read from VPD.
- * @param[in] fcoInBIOS - The FCO value already in the BIOS table.
- */
- void saveFCOToBIOS(const std::string& fcoVal, int64_t fcoInBIOS);
-
- /**
- * @brief Writes Keep and clear setting to BIOS
- *
- * Writes to the pvm_keep_and_clear BIOS attribute, if the value is
- * not already the same as we are trying to write.
- *
- * @param[in] keepAndClear - The keep and clear as read from VPD.
- * @param[in] keepAndClearInBIOS - The keep and clear in the BIOS table.
- */
- void saveKeepAndClearToBIOS(const std::string& keepAndClear,
- const std::string& keepAndClearInBIOS);
-
- /**
- * @brief Writes Create default LPAR setting to BIOS
- *
- * Writes to the pvm_create_default_lpar BIOS attribute, if the value is
- * not already the same as we are trying to write.
- *
- * @param[in] createDefaultLpar - The create default LPAR as read from VPD.
- * @param[in] createDefaultLparInBIOS - The create default LPAR in the BIOS
- * table.
- */
- void
- saveCreateDefaultLparToBIOS(const std::string& createDefaultLpar,
- const std::string& createDefaultLparInBIOS);
-
- /**
- * @brief Writes Clear NVRAM setting to BIOS
- *
- * Writes to the pvm_clear_nvram BIOS attribute, if the value is
- * not already the same as we are trying to write.
- *
- * @param[in] clearNVRAM - The clear NVRAM as read from VPD.
- * @param[in] clearNVRAMInBIOS - The clear NVRAM in the BIOS table.
- */
- void saveClearNVRAMToBIOS(const std::string& clearNVRAM,
- const std::string& clearNVRAMInBIOS);
-
- /**
- * @brief Reads the hb_memory_mirror_mode attribute
- *
- * @return std::string - The AMM BIOS attribute. Empty string on failure.
- */
- std::string readBIOSAMM();
-
- /**
- * @brief Reads the hb_field_core_override attribute
- *
- * @return int64_t - The FCO BIOS attribute. -1 on failure.
- */
- int64_t readBIOSFCO();
-
- /**
- * @brief Reads the pvm_keep_and_clear attribute
- *
- * @return std::string - The Keep and clear BIOS attribute. Empty string on
- * failure.
- */
- std::string readBIOSKeepAndClear();
-
- /**
- * @brief Reads the pvm_create_default_lpar attribute
- *
- * @return std::string - The Create default LPAR BIOS attribute. Empty
- * string on failure.
- */
- std::string readBIOSCreateDefaultLpar();
-
- /**
- * @brief Reads the pvm_clear_nvram attribute
- *
- * @return std::string - The Clear NVRAM BIOS attribute. Empty
- * string on failure.
- */
- std::string readBIOSClearNVRAM();
-
- /**
- * @brief Restore BIOS attributes
- *
- * This function checks if we are coming out of a factory reset. If yes,
- * it checks the VPD cache for valid backed up copy of the applicable
- * BIOS attributes. If valid values are found in the VPD, it will apply
- * those to the BIOS attributes.
- */
- void restoreBIOSAttribs();
-
- /**
- * @brief Reference to the connection.
- */
- std::shared_ptr<sdbusplus::asio::connection>& conn;
-
- /**
- * @brief Reference to the manager.
- */
- Manager& manager;
-}; // class BiosHandler
-} // namespace manager
-} // namespace vpd
-} // namespace openpower
diff --git a/vpd-manager/editor_impl.cpp b/vpd-manager/editor_impl.cpp
deleted file mode 100644
index c24cc0f..0000000
--- a/vpd-manager/editor_impl.cpp
+++ /dev/null
@@ -1,732 +0,0 @@
-#include "config.h"
-
-#include "editor_impl.hpp"
-
-#include "vpdecc/vpdecc.h"
-
-#include "common_utility.hpp"
-#include "ibm_vpd_utils.hpp"
-#include "ipz_parser.hpp"
-#include "parser_factory.hpp"
-#include "vpd_exceptions.hpp"
-
-#include <phosphor-logging/elog-errors.hpp>
-#include <xyz/openbmc_project/Common/error.hpp>
-
-using namespace openpower::vpd::parser::interface;
-using namespace openpower::vpd::constants;
-using namespace openpower::vpd::parser::factory;
-using namespace openpower::vpd::ipz::parser;
-
-namespace openpower
-{
-namespace vpd
-{
-namespace manager
-{
-namespace editor
-{
-
-void EditorImpl::checkPTForRecord(Binary::const_iterator& iterator,
- Byte ptLength)
-{
- // auto iterator = ptRecord.cbegin();
- auto end = std::next(iterator, ptLength + 1);
-
- // Look at each entry in the PT keyword for the record name
- while (iterator < end)
- {
- auto stop = std::next(iterator, lengths::RECORD_NAME);
- std::string record(iterator, stop);
-
- if (record == thisRecord.recName)
- {
- // Skip record name and record type
- std::advance(iterator, lengths::RECORD_NAME + sizeof(RecordType));
-
- // Get record offset
- thisRecord.recOffset = readUInt16LE(iterator);
-
- // pass the record offset length to read record length
- std::advance(iterator, lengths::RECORD_OFFSET);
- thisRecord.recSize = readUInt16LE(iterator);
-
- std::advance(iterator, lengths::RECORD_LENGTH);
- thisRecord.recECCoffset = readUInt16LE(iterator);
-
- ECCLength len;
- std::advance(iterator, lengths::RECORD_ECC_OFFSET);
- len = readUInt16LE(iterator);
- thisRecord.recECCLength = len;
-
- // once we find the record we don't need to look further
- return;
- }
- else
- {
- // Jump the record
- std::advance(iterator,
- lengths::RECORD_NAME + sizeof(RecordType) +
- sizeof(RecordOffset) + sizeof(RecordLength) +
- sizeof(ECCOffset) + sizeof(ECCLength));
- }
- }
- // imples the record was not found
- throw std::runtime_error("Record not found");
-}
-
-void EditorImpl::updateData(const Binary& kwdData)
-{
- std::size_t lengthToUpdate = kwdData.size() <= thisRecord.kwdDataLength
- ? kwdData.size()
- : thisRecord.kwdDataLength;
-
- auto iteratorToNewdata = kwdData.cbegin();
- auto end = iteratorToNewdata;
- std::advance(end, lengthToUpdate);
-
- // update data in file buffer as it will be needed to update ECC
- // avoiding extra stream operation here
- auto iteratorToKWdData = vpdFile.begin();
- std::advance(iteratorToKWdData, thisRecord.kwDataOffset);
- std::copy(iteratorToNewdata, end, iteratorToKWdData);
-
-#ifdef ManagerTest
- auto startItr = vpdFile.begin();
- std::advance(iteratorToKWdData, thisRecord.kwDataOffset);
- auto endItr = startItr;
- std::advance(endItr, thisRecord.kwdDataLength);
-
- Binary updatedData(startItr, endItr);
- if (updatedData == kwdData)
- {
- throw std::runtime_error("Data updated successfully");
- }
-#else
-
- // update data in EEPROM as well. As we will not write complete file back
- vpdFileStream.seekp(startOffset + thisRecord.kwDataOffset, std::ios::beg);
-
- iteratorToNewdata = kwdData.cbegin();
- std::copy(iteratorToNewdata, end,
- std::ostreambuf_iterator<char>(vpdFileStream));
-
- // get a hold to new data in case encoding is needed
- thisRecord.kwdUpdatedData.resize(thisRecord.kwdDataLength);
- auto itrToKWdData = vpdFile.cbegin();
- std::advance(itrToKWdData, thisRecord.kwDataOffset);
- auto kwdDataEnd = itrToKWdData;
- std::advance(kwdDataEnd, thisRecord.kwdDataLength);
- std::copy(itrToKWdData, kwdDataEnd, thisRecord.kwdUpdatedData.begin());
-#endif
-}
-
-void EditorImpl::checkRecordForKwd()
-{
- RecordOffset recOffset = thisRecord.recOffset;
-
- // Amount to skip for record ID, size, and the RT keyword
- constexpr auto skipBeg = sizeof(RecordId) + sizeof(RecordSize) +
- lengths::KW_NAME + sizeof(KwSize);
-
- auto iterator = vpdFile.cbegin();
- std::advance(iterator, recOffset + skipBeg + lengths::RECORD_NAME);
-
- auto end = iterator;
- std::advance(end, thisRecord.recSize);
- std::size_t dataLength = 0;
-
- while (iterator < end)
- {
- // Note keyword name
- std::string kw(iterator, iterator + lengths::KW_NAME);
-
- // Check if the Keyword starts with '#'
- char kwNameStart = *iterator;
- std::advance(iterator, lengths::KW_NAME);
-
- // if keyword starts with #
- if (POUND_KW == kwNameStart)
- {
- // Note existing keyword data length
- dataLength = readUInt16LE(iterator);
-
- // Jump past 2Byte keyword length + data
- std::advance(iterator, sizeof(PoundKwSize));
- }
- else
- {
- // Note existing keyword data length
- dataLength = *iterator;
-
- // Jump past keyword length and data
- std::advance(iterator, sizeof(KwSize));
- }
-
- if (thisRecord.recKWd == kw)
- {
- thisRecord.kwDataOffset = std::distance(vpdFile.cbegin(), iterator);
- thisRecord.kwdDataLength = dataLength;
- return;
- }
-
- // jump the data of current kwd to point to next kwd name
- std::advance(iterator, dataLength);
- }
-
- throw std::runtime_error("Keyword not found");
-}
-
-void EditorImpl::updateRecordECC()
-{
- auto itrToRecordData = vpdFile.cbegin();
- std::advance(itrToRecordData, thisRecord.recOffset);
-
- auto itrToRecordECC = vpdFile.cbegin();
- std::advance(itrToRecordECC, thisRecord.recECCoffset);
-
- auto l_status = vpdecc_create_ecc(
- const_cast<uint8_t*>(&itrToRecordData[0]), thisRecord.recSize,
- const_cast<uint8_t*>(&itrToRecordECC[0]), &thisRecord.recECCLength);
- if (l_status != VPD_ECC_OK)
- {
- throw std::runtime_error("Ecc update failed");
- }
-
- auto end = itrToRecordECC;
- std::advance(end, thisRecord.recECCLength);
-
-#ifndef ManagerTest
- vpdFileStream.seekp(startOffset + thisRecord.recECCoffset, std::ios::beg);
- std::copy(itrToRecordECC, end,
- std::ostreambuf_iterator<char>(vpdFileStream));
-#endif
-}
-
-auto EditorImpl::getValue(offsets::Offsets offset)
-{
- auto itr = vpdFile.cbegin();
- std::advance(itr, offset);
- LE2ByteData lowByte = *itr;
- LE2ByteData highByte = *(itr + 1);
- lowByte |= (highByte << 8);
-
- return lowByte;
-}
-
-void EditorImpl::checkRecordData()
-{
- auto itrToRecordData = vpdFile.cbegin();
- std::advance(itrToRecordData, thisRecord.recOffset);
-
- auto itrToRecordECC = vpdFile.cbegin();
- std::advance(itrToRecordECC, thisRecord.recECCoffset);
-
- checkECC(itrToRecordData, itrToRecordECC, thisRecord.recSize,
- thisRecord.recECCLength);
-}
-
-void EditorImpl::checkECC(Binary::const_iterator& itrToRecData,
- Binary::const_iterator& itrToECCData,
- RecordLength recLength, ECCLength eccLength)
-{
- auto l_status =
- vpdecc_check_data(const_cast<uint8_t*>(&itrToRecData[0]), recLength,
- const_cast<uint8_t*>(&itrToECCData[0]), eccLength);
-
- if (l_status == VPD_ECC_CORRECTABLE_DATA)
- {
- try
- {
- if (vpdFileStream.is_open())
- {
- vpdFileStream.seekp(startOffset + thisRecord.recOffset,
- std::ios::beg);
- auto end = itrToRecData;
- std::advance(end, recLength);
- std::copy(itrToRecData, end,
- std::ostreambuf_iterator<char>(vpdFileStream));
- }
- else
- {
- throw std::runtime_error("Ecc correction failed");
- }
- }
- catch (const std::fstream::failure& e)
- {
- std::cout << "Error while operating on file with exception";
- throw std::runtime_error("Ecc correction failed");
- }
- }
- else if (l_status != VPD_ECC_OK)
- {
- throw std::runtime_error("Ecc check failed");
- }
-}
-
-void EditorImpl::readVTOC()
-{
- // read VTOC offset
- RecordOffset tocOffset = getValue(offsets::VTOC_PTR);
-
- // read VTOC record length
- RecordLength tocLength = getValue(offsets::VTOC_REC_LEN);
-
- // read TOC ecc offset
- ECCOffset tocECCOffset = getValue(offsets::VTOC_ECC_OFF);
-
- // read TOC ecc length
- ECCLength tocECCLength = getValue(offsets::VTOC_ECC_LEN);
-
- auto itrToRecord = vpdFile.cbegin();
- std::advance(itrToRecord, tocOffset);
-
- auto iteratorToECC = vpdFile.cbegin();
- std::advance(iteratorToECC, tocECCOffset);
-
- // validate ecc for the record
- checkECC(itrToRecord, iteratorToECC, tocLength, tocECCLength);
-
- // to get to the record name.
- std::advance(itrToRecord, sizeof(RecordId) + sizeof(RecordSize) +
- // Skip past the RT keyword, which contains
- // the record name.
- lengths::KW_NAME + sizeof(KwSize));
-
- std::string recordName(itrToRecord, itrToRecord + lengths::RECORD_NAME);
-
- if ("VTOC" != recordName)
- {
- throw std::runtime_error("VTOC record not found");
- }
-
- // jump to length of PT kwd
- std::advance(itrToRecord, lengths::RECORD_NAME + lengths::KW_NAME);
-
- // Note size of PT
- Byte ptLen = *itrToRecord;
- std::advance(itrToRecord, 1);
-
- checkPTForRecord(itrToRecord, ptLen);
-}
-
-template <typename T>
-void EditorImpl::makeDbusCall(
- const std::string& object, const std::string& interface,
- const std::string& property, const std::variant<T>& data)
-{
- auto bus = sdbusplus::bus::new_default();
- auto properties =
- bus.new_method_call(INVENTORY_MANAGER_SERVICE, object.c_str(),
- "org.freedesktop.DBus.Properties", "Set");
- properties.append(interface);
- properties.append(property);
- properties.append(data);
-
- auto result = bus.call(properties);
-
- if (result.is_method_error())
- {
- throw std::runtime_error("bus call failed");
- }
-}
-
-void EditorImpl::processAndUpdateCI(const std::string& objectPath)
-{
- inventory::ObjectMap objects;
- for (auto& commonInterface : jsonFile["commonInterfaces"].items())
- {
- for (auto& ciPropertyList : commonInterface.value().items())
- {
- if (ciPropertyList.value().type() ==
- nlohmann::json::value_t::object)
- {
- if ((ciPropertyList.value().value("recordName", "") ==
- thisRecord.recName) &&
- (ciPropertyList.value().value("keywordName", "") ==
- thisRecord.recKWd))
- {
- inventory::PropertyMap prop;
- inventory::InterfaceMap interfaces;
- std::string kwdData(thisRecord.kwdUpdatedData.begin(),
- thisRecord.kwdUpdatedData.end());
-
- prop.emplace(ciPropertyList.key(), std::move(kwdData));
- interfaces.emplace(commonInterface.key(), std::move(prop));
- objects.emplace(objectPath, std::move(interfaces));
- }
- }
- }
- }
- // Notify PIM
- common::utility::callPIM(std::move(objects));
-}
-
-void EditorImpl::processAndUpdateEI(const nlohmann::json& Inventory,
- const inventory::Path& objPath)
-{
- inventory::ObjectMap objects;
- for (const auto& extraInterface : Inventory["extraInterfaces"].items())
- {
- if (extraInterface.value() != NULL)
- {
- for (const auto& eiPropertyList : extraInterface.value().items())
- {
- if (eiPropertyList.value().type() ==
- nlohmann::json::value_t::object)
- {
- if ((eiPropertyList.value().value("recordName", "") ==
- thisRecord.recName) &&
- ((eiPropertyList.value().value("keywordName", "") ==
- thisRecord.recKWd)))
- {
- inventory::PropertyMap prop;
- inventory::InterfaceMap interfaces;
- std::string kwdData(thisRecord.kwdUpdatedData.begin(),
- thisRecord.kwdUpdatedData.end());
- encodeKeyword(kwdData, eiPropertyList.value().value(
- "encoding", ""));
-
- prop.emplace(eiPropertyList.key(), std::move(kwdData));
- interfaces.emplace(extraInterface.key(),
- std::move(prop));
- objects.emplace(objPath, std::move(interfaces));
- }
- }
- }
- }
- }
- // Notify PIM
- common::utility::callPIM(std::move(objects));
-}
-
-void EditorImpl::updateCache()
-{
- const std::vector<nlohmann::json>& groupEEPROM =
- jsonFile["frus"][vpdFilePath].get_ref<const nlohmann::json::array_t&>();
-
- inventory::ObjectMap objects;
- // iterate through all the inventories for this file path
- for (const auto& singleInventory : groupEEPROM)
- {
- inventory::PropertyMap prop;
- inventory::InterfaceMap interfaces;
- // by default inherit property is true
- bool isInherit = true;
-
- if (singleInventory.find("inherit") != singleInventory.end())
- {
- isInherit = singleInventory["inherit"].get<bool>();
- }
-
- if (isInherit)
- {
- prop.emplace(getDbusNameForThisKw(thisRecord.recKWd),
- thisRecord.kwdUpdatedData);
- interfaces.emplace(
- (IPZ_INTERFACE + (std::string) "." + thisRecord.recName),
- std::move(prop));
- objects.emplace(
- (singleInventory["inventoryPath"].get<std::string>()),
- std::move(interfaces));
-
- // process Common interface
- processAndUpdateCI(singleInventory["inventoryPath"]
- .get_ref<const nlohmann::json::string_t&>());
- }
-
- // process extra interfaces
- processAndUpdateEI(singleInventory,
- singleInventory["inventoryPath"]
- .get_ref<const nlohmann::json::string_t&>());
-
- // check if we need to copy some specific records in this case.
- if (singleInventory.find("copyRecords") != singleInventory.end())
- {
- if (find(singleInventory["copyRecords"].begin(),
- singleInventory["copyRecords"].end(),
- thisRecord.recName) !=
- singleInventory["copyRecords"].end())
- {
- prop.emplace(thisRecord.recKWd, thisRecord.kwdUpdatedData);
- interfaces.emplace(
- (IPZ_INTERFACE + std::string{"."} + thisRecord.recName),
- std::move(prop));
- objects.emplace(
- (singleInventory["inventoryPath"].get<std::string>()),
- std::move(interfaces));
- }
- }
- }
- // Notify PIM
- common::utility::callPIM(std::move(objects));
-}
-
-void EditorImpl::expandLocationCode(const std::string& locationCodeType)
-{
- std::string propertyFCorTM{};
- std::string propertySE{};
-
- if (locationCodeType == "fcs")
- {
- propertyFCorTM =
- readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.VCEN", "FC");
- propertySE =
- readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.VCEN", "SE");
- }
- else if (locationCodeType == "mts")
- {
- propertyFCorTM =
- readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.VSYS", "TM");
- propertySE =
- readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.VSYS", "SE");
- }
-
- const nlohmann::json& groupFRUS =
- jsonFile["frus"].get_ref<const nlohmann::json::object_t&>();
- inventory::ObjectMap objects;
-
- 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)
- {
- inventory::PropertyMap prop;
- inventory::InterfaceMap interfaces;
- const auto& objectPath = itemEEPROM["inventoryPath"];
- sdbusplus::message::object_path object(objectPath);
-
- // check if the given item implements location code interface
- if (itemEEPROM["extraInterfaces"].find(IBM_LOCATION_CODE_INF) !=
- itemEEPROM["extraInterfaces"].end())
- {
- const std::string& unexpandedLocationCode =
- itemEEPROM["extraInterfaces"][IBM_LOCATION_CODE_INF]
- ["LocationCode"]
- .get_ref<const nlohmann::json::string_t&>();
- std::size_t idx = unexpandedLocationCode.find(locationCodeType);
- if (idx != std::string::npos)
- {
- std::string expandedLocationCode(unexpandedLocationCode);
-
- if (locationCodeType == "fcs")
- {
- expandedLocationCode.replace(
- idx, 3,
- propertyFCorTM.substr(0, 4) + ".ND0." + propertySE);
- }
- else if (locationCodeType == "mts")
- {
- std::replace(propertyFCorTM.begin(),
- propertyFCorTM.end(), '-', '.');
- expandedLocationCode.replace(
- idx, 3, propertyFCorTM + "." + propertySE);
- }
-
- // update the DBUS interface COM as well as XYZ path
- prop.emplace("LocationCode", expandedLocationCode);
- // TODO deprecate this com.ibm interface later
- interfaces.emplace(IBM_LOCATION_CODE_INF, prop);
- interfaces.emplace(XYZ_LOCATION_CODE_INF, std::move(prop));
- }
- }
- objects.emplace(std::move(object), std::move(interfaces));
- }
- }
- // Notify PIM
- common::utility::callPIM(std::move(objects));
-}
-
-#ifndef ManagerTest
-static void enableRebootGuard()
-{
- try
- {
- auto bus = sdbusplus::bus::new_default();
- auto method = bus.new_method_call(
- "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager", "StartUnit");
- method.append("reboot-guard-enable.service", "replace");
- bus.call_noreply(method);
- }
- catch (const sdbusplus::exception_t& e)
- {
- std::string errMsg =
- "Bus call to enable BMC reboot failed for reason: ";
- errMsg += e.what();
-
- throw std::runtime_error(errMsg);
- }
-}
-
-static void disableRebootGuard()
-{
- try
- {
- auto bus = sdbusplus::bus::new_default();
- auto method = bus.new_method_call(
- "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager", "StartUnit");
- method.append("reboot-guard-disable.service", "replace");
- bus.call_noreply(method);
- }
- catch (const sdbusplus::exception_t& e)
- {
- using namespace phosphor::logging;
- using InternalFailure =
- sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
-
- std::string errMsg =
- "Bus call to disable BMC reboot failed for reason: ";
- errMsg += e.what();
-
- log<level::ERR>("Disable boot guard failed");
- elog<InternalFailure>();
-
- throw std::runtime_error(errMsg);
- }
-}
-#endif
-
-void EditorImpl::updateKeyword(const Binary& kwdData, uint32_t offset,
- const bool& updCache)
-{
- try
- {
- startOffset = offset;
-#ifndef ManagerTest
- // Restrict BMC from rebooting when VPD is being written. This will
- // prevent any data/ECC corruption in case BMC reboots while VPD update.
- enableRebootGuard();
-
- Binary completeVPDFile;
- vpdFileStream.exceptions(
- std::ifstream::badbit | std::ifstream::failbit);
- try
- {
- vpdFileStream.open(vpdFilePath,
- std::ios::in | std::ios::out | std::ios::binary);
-
- auto vpdFileSize =
- std::min(std::filesystem::file_size(vpdFilePath), MAX_VPD_SIZE);
- if (vpdFileSize == 0)
- {
- std::cerr << "File size is 0 for " << vpdFilePath << std::endl;
- throw std::runtime_error("File size is 0.");
- }
-
- completeVPDFile.resize(vpdFileSize);
- vpdFileStream.seekg(startOffset, std::ios_base::cur);
- vpdFileStream.read(reinterpret_cast<char*>(&completeVPDFile[0]),
- vpdFileSize);
- vpdFileStream.clear(std::ios_base::eofbit);
- }
- catch (const std::system_error& fail)
- {
- std::cerr << "Exception in file handling [" << vpdFilePath
- << "] error : " << fail.what();
- std::cerr << "Stream file size = " << vpdFileStream.gcount()
- << std::endl;
- throw;
- }
- vpdFile = completeVPDFile;
-
- if (objPath.empty() &&
- jsonFile["frus"].find(vpdFilePath) != jsonFile["frus"].end())
- {
- objPath = jsonFile["frus"][vpdFilePath][0]["inventoryPath"]
- .get_ref<const nlohmann::json::string_t&>();
- }
-
-#else
-
- Binary completeVPDFile = vpdFile;
-
-#endif
- if (vpdFile.empty())
- {
- throw std::runtime_error("Invalid File");
- }
- auto iterator = vpdFile.cbegin();
- std::advance(iterator, IPZ_DATA_START);
-
- Byte vpdType = *iterator;
- if (vpdType == KW_VAL_PAIR_START_TAG)
- {
- // objPath should be empty only in case of test run.
- ParserInterface* Iparser = ParserFactory::getParser(
- completeVPDFile, objPath, vpdFilePath, startOffset);
- IpzVpdParser* ipzParser = dynamic_cast<IpzVpdParser*>(Iparser);
-
- try
- {
- if (ipzParser == nullptr)
- {
- throw std::runtime_error("Invalid cast");
- }
-
- ipzParser->processHeader();
- delete ipzParser;
- ipzParser = nullptr;
- // ParserFactory::freeParser(Iparser);
-
- // process VTOC for PTT rkwd
- readVTOC();
-
- // check record for keywrod
- checkRecordForKwd();
-
- // Check Data before updating
- checkRecordData();
-
- // update the data to the file
- updateData(kwdData);
-
- // update the ECC data for the record once data has been updated
- updateRecordECC();
-
- if (updCache)
- {
-#ifndef ManagerTest
- // update the cache once data has been updated
- updateCache();
-#endif
- }
- }
- catch (const std::exception& e)
- {
- if (ipzParser != nullptr)
- {
- delete ipzParser;
- }
- throw std::runtime_error(e.what());
- }
-
-#ifndef ManagerTest
- // Once VPD data and Ecc update is done, disable BMC boot guard.
- disableRebootGuard();
-#endif
-
- return;
- }
- else
- {
- throw openpower::vpd::exceptions::VpdDataException(
- "Could not find start tag in VPD " + vpdFilePath);
- }
- }
- catch (const std::exception& e)
- {
-#ifndef ManagerTest
- // Disable reboot guard.
- disableRebootGuard();
-#endif
-
- throw std::runtime_error(e.what());
- }
-}
-} // namespace editor
-} // namespace manager
-} // namespace vpd
-} // namespace openpower
diff --git a/vpd-manager/editor_impl.hpp b/vpd-manager/editor_impl.hpp
deleted file mode 100644
index cf0ef2b..0000000
--- a/vpd-manager/editor_impl.hpp
+++ /dev/null
@@ -1,216 +0,0 @@
-#pragma once
-
-#include "const.hpp"
-#include "types.hpp"
-
-#include <nlohmann/json.hpp>
-
-#include <cstddef>
-#include <fstream>
-#include <tuple>
-
-namespace openpower
-{
-namespace vpd
-{
-namespace manager
-{
-namespace editor
-{
-
-/** @class EditorImpl */
-class EditorImpl
-{
- public:
- EditorImpl() = delete;
- EditorImpl(const EditorImpl&) = delete;
- EditorImpl& operator=(const EditorImpl&) = delete;
- EditorImpl(EditorImpl&&) = delete;
- EditorImpl& operator=(EditorImpl&&) = delete;
- ~EditorImpl() {}
-
- /** @brief Construct EditorImpl class
- *
- * @param[in] record - Record Name
- * @param[in] kwd - Keyword
- * @param[in] vpd - Vpd Vector
- */
- EditorImpl(const std::string& record, const std::string& kwd,
- Binary&& vpd) :
- startOffset(0), thisRecord(record, kwd), vpdFile(std::move(vpd))
- {}
-
- /** @brief Construct EditorImpl class
- *
- * @param[in] path - Path to the vpd file
- * @param[in] json - Parsed inventory json
- * @param[in] record - Record name
- * @param[in] kwd - Keyword
- * @param[in] inventoryPath - Inventory path of the vpd
- */
- EditorImpl(const inventory::Path& path, const nlohmann::json& json,
- const std::string& record, const std::string& kwd,
- const sdbusplus::message::object_path& inventoryPath) :
- vpdFilePath(path), objPath(inventoryPath), startOffset(0),
- jsonFile(json), thisRecord(record, kwd)
- {}
-
- /** @brief Construct EditorImpl class
- *
- * @param[in] path - EEPROM path
- * @param[in] json - Parsed inventory json object
- * @param[in] record - Record name
- * @param[in] kwd - Keyword name
- */
- EditorImpl(const inventory::Path& path, const nlohmann::json& json,
- const std::string& record, const std::string& kwd) :
- vpdFilePath(path), jsonFile(json), thisRecord(record, kwd)
- {}
-
- /**
- * @brief Update data for keyword
- * The method looks for the record name to update in VTOC and then
- * looks for the keyword name in that record. when found it updates the data
- * of keyword with the given data. It does not block keyword data update in
- * case the length of new data is greater than or less than the current data
- * length. If the new data length is more than the length allotted to that
- * keyword the new data will be truncated to update only the allotted
- * length. Similarly if the new data length is less then only that much data
- * will be updated for the keyword and remaining bits will be left
- * unchanged.
- *
- * Following is the algorithm used to update keyword:
- * 1) Look for the record name in the given VPD file
- * 2) Look for the keyword name for which data needs to be updated
- * which is the table of contents record.
- * 3) update the data for that keyword with the new data
- *
- * @param[in] kwdData - data to update
- * @param[in] offset - offset at which the VPD starts
- * @param[in] updCache - Flag which tells whether to update Cache or not.
- */
- void updateKeyword(const Binary& kwdData, uint32_t offset,
- const bool& updCache);
-
- /** @brief Expands location code on DBUS
- * @param[in] locationCodeType - "fcs" or "mts"
- */
- void expandLocationCode(const std::string& locationCodeType);
-
- private:
- /** @brief read VTOC record from the vpd file
- */
- void readVTOC();
-
- /** @brief validate ecc data for the VTOC record
- * @param[in] itrToRecData -iterator to the record data
- * @param[in] itrToECCData - iterator to the ECC data
- * @param[in] recLength - Length of the record
- * @param[in] eccLength - Length of the record's ECC
- */
- void checkECC(Binary::const_iterator& itrToRecData,
- Binary::const_iterator& itrToECCData,
- openpower::vpd::constants::RecordLength recLength,
- openpower::vpd::constants::ECCLength eccLength);
-
- /** @brief reads value at the given offset
- * @param[in] offset - offset value
- * @return value at that offset in bigendian
- */
- auto getValue(openpower::vpd::constants::offsets::Offsets offset);
-
- /** @brief Checks if required record name exist in the VPD file
- * @param[in] iterator - pointing to start of PT kwd
- * @param[in] ptLength - length of the PT kwd
- */
- void checkPTForRecord(Binary::const_iterator& iterator, Byte ptLength);
-
- /** @brief Checks for required keyword in the record */
- void checkRecordForKwd();
-
- /** @brief update data for given keyword
- * @param[in] kwdData- data to be updated
- */
- void updateData(const Binary& kwdData);
-
- /** @brief update record ECC */
- void updateRecordECC();
-
- /** @brief method to update cache once the data for keyword has been updated
- */
- void updateCache();
-
- /** @brief method to process and update CI in case required
- * @param[in] - objectPath - path of the object to introspect
- */
- void processAndUpdateCI(const std::string& objectPath);
-
- /** @brief method to process and update extra interface
- * @param[in] Inventory - single inventory json subpart
- * @param[in] objPath - path of the object to introspect
- */
- void processAndUpdateEI(const nlohmann::json& Inventory,
- const inventory::Path& objPath);
-
- /** @brief method to make busctl call
- *
- * @param[in] object - bus object path
- * @param[in] interface - bus interface
- * @param[in] property - property to update on BUS
- * @param[in] data - data to be updated on Bus
- *
- */
- template <typename T>
- void makeDbusCall(const std::string& object, const std::string& interface,
- const std::string& property, const std::variant<T>& data);
-
- /** @brief Method to check the record's Data using ECC */
- void checkRecordData();
-
- // path to the VPD file to edit
- inventory::Path vpdFilePath;
-
- // inventory path of the vpd fru to update keyword
- inventory::Path objPath{};
-
- // stream to perform operation on file
- std::fstream vpdFileStream;
-
- // stream to operate on VPD data
- std::fstream vpdDataFileStream;
-
- // offset to get vpd data from EEPROM
- uint32_t startOffset;
-
- // file to store parsed json
- const nlohmann::json jsonFile;
-
- // structure to hold info about record to edit
- struct RecInfo
- {
- Binary kwdUpdatedData; // need access to it in case encoding is needed
- const std::string recName;
- const std::string recKWd;
- openpower::vpd::constants::RecordOffset recOffset;
- openpower::vpd::constants::ECCOffset recECCoffset;
- std::size_t recECCLength;
- std::size_t kwdDataLength;
- openpower::vpd::constants::RecordSize recSize;
- openpower::vpd::constants::DataOffset kwDataOffset;
- // constructor
- RecInfo(const std::string& rec, const std::string& kwd) :
- recName(rec), recKWd(kwd), recOffset(0), recECCoffset(0),
- recECCLength(0), kwdDataLength(0), recSize(0), kwDataOffset(0)
- {}
- } thisRecord;
-
- Binary vpdFile;
-
- // If requested Interface is common Interface
- bool isCI;
-}; // class EditorImpl
-
-} // namespace editor
-} // namespace manager
-} // namespace vpd
-} // namespace openpower
diff --git a/vpd-manager/gpioMonitor.cpp b/vpd-manager/gpioMonitor.cpp
deleted file mode 100644
index 3196fee..0000000
--- a/vpd-manager/gpioMonitor.cpp
+++ /dev/null
@@ -1,201 +0,0 @@
-#include "gpioMonitor.hpp"
-
-#include "common_utility.hpp"
-#include "ibm_vpd_utils.hpp"
-
-#include <boost/asio.hpp>
-#include <boost/bind/bind.hpp>
-#include <gpiod.hpp>
-
-using namespace std;
-using namespace openpower::vpd::constants;
-
-namespace openpower
-{
-namespace vpd
-{
-namespace manager
-{
-
-bool GpioEventHandler::getPresencePinValue()
-{
- Byte gpioData = 1;
- gpiod::line presenceLine = gpiod::find_line(presencePin);
- if (!presenceLine)
- {
- cerr << "Error getPresencePinValue: couldn't find presence line:"
- << presencePin << " on GPIO \n";
- // return previous state as we couldn't read current state
- return prevPresPinValue;
- }
-
- presenceLine.request(
- {"Op-panel presence line", gpiod::line_request::DIRECTION_INPUT, 0});
-
- gpioData = presenceLine.get_value();
-
- return gpioData;
-}
-
-void GpioMonitor::initGpioInfos(
- std::shared_ptr<boost::asio::io_context>& ioContext)
-{
- Byte outputValue = 0;
- Byte presenceValue = 0;
- string presencePinName{}, outputPinName{};
- string devNameAddr{}, driverType{}, busType{}, objectPath{};
-
- for (const auto& eachFRU : jsonFile["frus"].items())
- {
- for (const auto& eachInventory : eachFRU.value())
- {
- objectPath = eachInventory["inventoryPath"];
-
- if ((eachInventory.find("presence") != eachInventory.end()) &&
- (eachInventory.find("preAction") != eachInventory.end()))
- {
- if (!eachInventory["presence"].value("pollingRequired", false))
- {
- // Polling not required for this FRU , skip.
- continue;
- }
-
- for (const auto& presStatus : eachInventory["presence"].items())
- {
- if (presStatus.key() == "pin")
- {
- presencePinName = presStatus.value();
- }
- else if (presStatus.key() == "value")
- {
- presenceValue = presStatus.value();
- }
- }
-
- // Based on presence pin value, preAction pin will be set/reset
- // This action will be taken before vpd collection.
- for (const auto& preAction : eachInventory["preAction"].items())
- {
- if (preAction.key() == "pin")
- {
- outputPinName = preAction.value();
- }
- else if (preAction.key() == "value")
- {
- outputValue = preAction.value();
- }
- }
-
- if ((eachInventory.find("devAddress") != eachInventory.end()) &&
- (eachInventory.find("driverType") != eachInventory.end()) &&
- (eachInventory.find("busType") != eachInventory.end()))
- {
- devNameAddr = eachInventory["devAddress"];
- driverType = eachInventory["driverType"];
- busType = eachInventory["busType"];
-
- // Init all Gpio info variables
- std::shared_ptr<GpioEventHandler> gpioObj =
- make_shared<GpioEventHandler>(
- presencePinName, presenceValue, outputPinName,
- outputValue, devNameAddr, driverType, busType,
- objectPath, ioContext);
-
- gpioObjects.push_back(gpioObj);
- }
- }
- }
- }
-}
-
-void GpioEventHandler::toggleGpio()
-{
- bool presPinVal = getPresencePinValue();
- bool isPresent = false;
-
- // preserve the new value
- prevPresPinValue = presPinVal;
-
- if (presPinVal == presenceValue)
- {
- isPresent = true;
- }
-
- // if FRU went away set the present property to false
- if (!isPresent)
- {
- inventory::ObjectMap objects;
- inventory::InterfaceMap interfaces;
- inventory::PropertyMap presProp;
-
- presProp.emplace("Present", false);
- interfaces.emplace("xyz.openbmc_project.Inventory.Item", presProp);
- objects.emplace(objectPath, move(interfaces));
-
- common::utility::callPIM(move(objects));
- }
-
- gpiod::line outputLine = gpiod::find_line(outputPin);
- if (!outputLine)
- {
- cerr << "Error: toggleGpio: couldn't find output line:" << outputPin
- << ". Skipping update\n";
-
- return;
- }
-
- outputLine.request({"FRU presence: update the output GPIO pin",
- gpiod::line_request::DIRECTION_OUTPUT, 0},
- isPresent ? outputValue : (!outputValue));
-
- string cmnd = createBindUnbindDriverCmnd(devNameAddr, busType, driverType,
- isPresent ? "bind" : "unbind");
-
- cout << cmnd << endl;
- executeCmd(cmnd);
-}
-
-void GpioEventHandler::handleTimerExpiry(
- const boost::system::error_code& ec,
- std::shared_ptr<boost::asio::steady_timer>& timer)
-{
- if (ec == boost::asio::error::operation_aborted)
- {
- return;
- }
-
- if (ec)
- {
- std::cerr << "Timer wait failed for gpio pin" << ec.message();
- return;
- }
-
- if (hasEventOccurred())
- {
- toggleGpio();
- }
- timer->expires_at(
- std::chrono::steady_clock::now() + std::chrono::seconds(5));
- timer->async_wait(boost::bind(&GpioEventHandler::handleTimerExpiry, this,
- boost::asio::placeholders::error, timer));
-}
-
-void GpioEventHandler::doEventAndTimerSetup(
- std::shared_ptr<boost::asio::io_context>& ioContext)
-{
- prevPresPinValue = getPresencePinValue();
-
- static vector<std::shared_ptr<boost::asio::steady_timer>> timers;
-
- auto timer = make_shared<boost::asio::steady_timer>(
- *ioContext, std::chrono::seconds(5));
-
- timer->async_wait(boost::bind(&GpioEventHandler::handleTimerExpiry, this,
- boost::asio::placeholders::error, timer));
-
- timers.push_back(timer);
-}
-
-} // namespace manager
-} // namespace vpd
-} // namespace openpower
diff --git a/vpd-manager/gpioMonitor.hpp b/vpd-manager/gpioMonitor.hpp
deleted file mode 100644
index 5f34336..0000000
--- a/vpd-manager/gpioMonitor.hpp
+++ /dev/null
@@ -1,143 +0,0 @@
-#pragma once
-#include "types.hpp"
-
-#include <boost/asio/steady_timer.hpp>
-#include <nlohmann/json.hpp>
-#include <sdbusplus/asio/connection.hpp>
-
-namespace openpower
-{
-namespace vpd
-{
-namespace manager
-{
-/** @class GpioEventHandler
- * @brief Responsible for catching the event and handle it.
- * This keeps checking for the FRU's presence.
- * If any attachment or de-attachment found, it enables/disables that
- * fru's output gpio and bind/unbind the driver, respectively.
- */
-class GpioEventHandler
-{
- public:
- GpioEventHandler() = default;
- ~GpioEventHandler() = default;
- GpioEventHandler(const GpioEventHandler&) = default;
- GpioEventHandler& operator=(const GpioEventHandler&) = delete;
- GpioEventHandler(GpioEventHandler&&) = delete;
- GpioEventHandler& operator=(GpioEventHandler&&) = delete;
-
- GpioEventHandler(std::string& presPin, Byte& presValue, std::string& outPin,
- Byte& outValue, std::string& devAddr, std::string& driver,
- std::string& bus, std::string& objPath,
- std::shared_ptr<boost::asio::io_context>& ioCon) :
- presencePin(presPin), presenceValue(presValue), outputPin(outPin),
- outputValue(outValue), devNameAddr(devAddr), driverType(driver),
- busType(bus), objectPath(objPath)
- {
- doEventAndTimerSetup(ioCon);
- }
-
- private:
- /** @brief GPIO information to get parsed from vpd json*/
-
- // gpio pin indicates presence/absence of fru
- const std::string presencePin;
- // value which means fru is present
- const Byte presenceValue;
- // gpio pin to enable If fru is present
- const std::string outputPin;
- // Value to set, to enable the output pin
- const Byte outputValue;
-
- // FRU address on bus
- const std::string devNameAddr;
- // Driver type
- const std::string driverType;
- // Bus type
- const std::string busType;
- // object path of FRU
- const std::string objectPath;
-
- /** Preserves the GPIO pin value to compare it next time. Default init by
- * false*/
- bool prevPresPinValue = false;
-
- /** @brief This is a helper function to read the
- * current value of Presence GPIO
- *
- * @returns The GPIO value
- */
- bool getPresencePinValue();
-
- /** @brief This function will toggle the output gpio as per the presence
- * state of fru.
- */
- void toggleGpio();
-
- /** @brief This function checks for fru's presence pin and detects change of
- * value on that pin, (in case of fru gets attached or de-attached).
- *
- * @returns true if presence pin value changed
- * false otherwise
- */
- inline bool hasEventOccurred()
- {
- return getPresencePinValue() != prevPresPinValue;
- }
-
- /** @brief This function runs a timer , which keeps checking for if an event
- * happened, if event occurred then takes action.
- *
- * @param[in] ioContext - Pointer to io context object.
- */
- void doEventAndTimerSetup(
- std::shared_ptr<boost::asio::io_context>& ioContext);
-
- /**
- * @brief Api to handle timer expiry.
- *
- * @param ec - Error code.
- * @param timer - Pointer to timer object.
- */
- void handleTimerExpiry(const boost::system::error_code& ec,
- std::shared_ptr<boost::asio::steady_timer>& timer);
-};
-
-/** @class GpioMonitor
- * @brief Responsible for initialising the private variables containing gpio
- * infos. These information will be fetched from vpd json.
- */
-class GpioMonitor
-{
- public:
- GpioMonitor() = delete;
- ~GpioMonitor() = default;
- GpioMonitor(const GpioMonitor&) = delete;
- GpioMonitor& operator=(const GpioMonitor&) = delete;
- GpioMonitor(GpioMonitor&&) = delete;
- GpioMonitor& operator=(GpioMonitor&&) = delete;
-
- GpioMonitor(nlohmann::json& js,
- std::shared_ptr<boost::asio::io_context>& ioCon) : jsonFile(js)
- {
- initGpioInfos(ioCon);
- }
-
- private:
- // Json file to get the data
- nlohmann::json& jsonFile;
- // Array of event handlers for all the attachable FRUs
- std::vector<std::shared_ptr<GpioEventHandler>> gpioObjects;
-
- /** @brief This function will extract the gpio information from vpd json
- * and store it in GpioEventHandler's private variables
- *
- * @param[in] ioContext - Pointer to io context object.
- */
- void initGpioInfos(std::shared_ptr<boost::asio::io_context>& ioContext);
-};
-
-} // namespace manager
-} // namespace vpd
-} // namespace openpower
diff --git a/vpd-manager/include/backup_restore.hpp b/vpd-manager/include/backup_restore.hpp
new file mode 100644
index 0000000..12ec384
--- /dev/null
+++ b/vpd-manager/include/backup_restore.hpp
@@ -0,0 +1,102 @@
+#pragma once
+
+#include "types.hpp"
+
+#include <nlohmann/json.hpp>
+
+#include <tuple>
+
+namespace vpd
+{
+
+// Backup and restore operation status.
+enum class BackupAndRestoreStatus : uint8_t
+{
+ NotStarted,
+ Invoked,
+ Completed
+};
+
+/**
+ * @brief class to implement backup and restore VPD.
+ *
+ */
+
+class BackupAndRestore
+{
+ public:
+ // delete functions
+ BackupAndRestore() = delete;
+ BackupAndRestore(const BackupAndRestore&) = delete;
+ BackupAndRestore& operator=(const BackupAndRestore&) = delete;
+ BackupAndRestore(BackupAndRestore&&) = delete;
+ BackupAndRestore& operator=(BackupAndRestore&&) = delete;
+
+ /**
+ * @brief Constructor.
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object.
+ *
+ * @throw std::runtime_error in case constructor failure.
+ */
+ BackupAndRestore(const nlohmann::json& i_sysCfgJsonObj);
+
+ /**
+ * @brief Default destructor.
+ */
+ ~BackupAndRestore() = default;
+
+ /**
+ * @brief An API to backup and restore VPD.
+ *
+ * Note: This API works on the keywords declared in the backup and restore
+ * config JSON. Restore or backup action could be triggered for each
+ * keyword, based on the keyword's value present in the source and
+ * destination keyword.
+ *
+ * Restore source keyword's value with destination keyword's value,
+ * when source keyword has default value but
+ * destination's keyword has non default value.
+ *
+ * Backup the source keyword value to the destination's keyword's value,
+ * when source keyword has non default value but
+ * destination's keyword has default value.
+ *
+ * @return Tuple of updated source and destination VPD map variant.
+ */
+ std::tuple<types::VPDMapVariant, types::VPDMapVariant> backupAndRestore();
+
+ /**
+ * @brief An API to set backup and restore status.
+ *
+ * @param[in] i_status - Status to set.
+ */
+ static void
+ setBackupAndRestoreStatus(const BackupAndRestoreStatus& i_status);
+
+ private:
+ /**
+ * @brief An API to handle backup and restore of IPZ type VPD.
+ *
+ * @param[in,out] io_srcVpdMap - Source VPD map.
+ * @param[in,out] io_dstVpdMap - Destination VPD map.
+ * @param[in] i_srcPath - Source EEPROM file path or inventory path.
+ * @param[in] i_dstPath - Destination EEPROM file path or inventory path.
+ *
+ * @throw std::runtime_error
+ */
+ void backupAndRestoreIpzVpd(
+ types::IPZVpdMap& io_srcVpdMap, types::IPZVpdMap& io_dstVpdMap,
+ const std::string& i_srcPath, const std::string& i_dstPath);
+
+ // System JSON config JSON object.
+ nlohmann::json m_sysCfgJsonObj{};
+
+ // Backup and restore config JSON object.
+ nlohmann::json m_backupAndRestoreCfgJsonObj{};
+
+ // Backup and restore status.
+ static BackupAndRestoreStatus m_backupAndRestoreStatus;
+};
+
+} // namespace vpd
diff --git a/vpd-manager/include/bios_handler.hpp b/vpd-manager/include/bios_handler.hpp
new file mode 100644
index 0000000..916811e
--- /dev/null
+++ b/vpd-manager/include/bios_handler.hpp
@@ -0,0 +1,273 @@
+#pragma once
+#include "manager.hpp"
+#include "types.hpp"
+
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/bus.hpp>
+
+namespace vpd
+{
+
+/**
+ * @brief Interface class for BIOS handling.
+ *
+ * The class layout has the virtual methods required to be implemented by any
+ * concrete class that intends to use the feature provided via BIOS handler
+ * class.
+ */
+class BiosHandlerInterface
+{
+ public:
+ /**
+ * @brief API to back up or restore BIOS attributes.
+ *
+ * Concrete class should implement the API and read the backed up data from
+ * its designated location and take a call if it should be backed up or
+ * restored.
+ */
+ virtual void backUpOrRestoreBiosAttributes() = 0;
+
+ /**
+ * @brief Callback API to be triggered on BIOS attribute change.
+ *
+ * Concrete class should implement the API to extract the attribute and its
+ * value from DBus message broadcasted on BIOS attribute change.
+ * The definition should be overridden in concrete class to deal with BIOS
+ * attributes interested in.
+ *
+ * @param[in] i_msg - The callback message.
+ */
+ virtual void biosAttributesCallback(sdbusplus::message_t& i_msg) = 0;
+};
+
+/**
+ * @brief IBM specifc BIOS handler class.
+ */
+class IbmBiosHandler : public BiosHandlerInterface
+{
+ public:
+ /**
+ * @brief Construct a new IBM BIOS Handler object
+ *
+ * This constructor constructs a new IBM BIOS Handler object
+ * @param[in] i_manager - Manager object.
+ */
+ explicit IbmBiosHandler(const std::shared_ptr<Manager>& i_manager) :
+ m_manager(i_manager)
+ {}
+
+ /**
+ * @brief API to back up or restore BIOS attributes.
+ *
+ * The API will read the backed up data from the VPD keyword and based on
+ * its value, either backs up or restores the data.
+ */
+ virtual void backUpOrRestoreBiosAttributes();
+
+ /**
+ * @brief Callback API to be triggered on BIOS attribute change.
+ *
+ * The API to extract the required attribute and its value from DBus message
+ * broadcasted on BIOS attribute change.
+ *
+ * @param[in] i_msg - The callback message.
+ */
+ virtual void biosAttributesCallback(sdbusplus::message_t& i_msg);
+
+ private:
+ /**
+ * @brief API to read given attribute from BIOS table.
+ *
+ * @param[in] attributeName - Attribute to be read.
+ * @return - Bios attribute current value.
+ */
+ types::BiosAttributeCurrentValue
+ readBiosAttribute(const std::string& attributeName);
+
+ /**
+ * @brief API to process "hb_field_core_override" attribute.
+ *
+ * The API checks value stored in VPD. If found default then the BIOS value
+ * is saved to VPD else VPD value is restored in BIOS pending attribute
+ * table.
+ */
+ void processFieldCoreOverride();
+
+ /**
+ * @brief API to save FCO data into VPD.
+ *
+ * @param[in] i_fcoInBios - FCO value.
+ */
+ void saveFcoToVpd(int64_t i_fcoInBios);
+
+ /**
+ * @brief API to save given value to "hb_field_core_override" attribute.
+ *
+ * @param[in] i_fcoVal - FCO value.
+ */
+ void saveFcoToBios(const types::BinaryVector& i_fcoVal);
+
+ /**
+ * @brief API to save AMM data into VPD.
+ *
+ * @param[in] i_memoryMirrorMode - Memory mirror mode value.
+ */
+ void saveAmmToVpd(const std::string& i_memoryMirrorMode);
+
+ /**
+ * @brief API to save given value to "hb_memory_mirror_mode" attribute.
+ *
+ * @param[in] i_ammVal - AMM value.
+ */
+ void saveAmmToBios(const std::string& i_ammVal);
+
+ /**
+ * @brief API to process "hb_memory_mirror_mode" attribute.
+ *
+ * The API checks value stored in VPD. If found default then the BIOS value
+ * is saved to VPD else VPD value is restored in BIOS pending attribute
+ * table.
+ */
+ void processActiveMemoryMirror();
+
+ /**
+ * @brief API to process "pvm_create_default_lpar" attribute.
+ *
+ * The API reads the value from VPD and restore it to the BIOS attribute
+ * in BIOS pending attribute table.
+ */
+ void processCreateDefaultLpar();
+
+ /**
+ * @brief API to save given value to "pvm_create_default_lpar" attribute.
+ *
+ * @param[in] i_createDefaultLparVal - Value to be saved;
+ */
+ void saveCreateDefaultLparToBios(const std::string& i_createDefaultLparVal);
+
+ /**
+ * @brief API to save given value to VPD.
+ *
+ * @param[in] i_createDefaultLparVal - Value to be saved.
+ *
+ */
+ void saveCreateDefaultLparToVpd(const std::string& i_createDefaultLparVal);
+
+ /**
+ * @brief API to process "pvm_clear_nvram" attribute.
+ *
+ * The API reads the value from VPD and restores it to the BIOS pending
+ * attribute table.
+ */
+ void processClearNvram();
+
+ /**
+ * @brief API to save given value to "pvm_clear_nvram" attribute.
+ *
+ * @param[in] i_clearNvramVal - Value to be saved.
+ */
+ void saveClearNvramToBios(const std::string& i_clearNvramVal);
+
+ /**
+ * @brief API to save given value to VPD.
+ *
+ * @param[in] i_clearNvramVal - Value to be saved.
+ */
+ void saveClearNvramToVpd(const std::string& i_clearNvramVal);
+
+ /**
+ * @brief API to process "pvm_keep_and_clear" attribute.
+ *
+ * The API reads the value from VPD and restore it to the BIOS pending
+ * attribute table.
+ */
+ void processKeepAndClear();
+
+ /**
+ * @brief API to save given value to "pvm_keep_and_clear" attribute.
+ *
+ * @param[in] i_KeepAndClearVal - Value to be saved.
+ */
+ void saveKeepAndClearToBios(const std::string& i_KeepAndClearVal);
+
+ /**
+ * @brief API to save given value to VPD.
+ *
+ * @param[in] i_KeepAndClearVal - Value to be saved.
+ */
+ void saveKeepAndClearToVpd(const std::string& i_KeepAndClearVal);
+
+ // const reference to shared pointer to Manager object.
+ const std::shared_ptr<Manager>& m_manager;
+};
+
+/**
+ * @brief A class to operate upon BIOS attributes.
+ *
+ * The class along with specific BIOS handler class(es), provides a feature
+ * where specific BIOS attributes identified by the concrete specific class can
+ * be listened for any change and can be backed up to a desired location or
+ * restored back to the BIOS table.
+ *
+ * To use the feature, "BiosHandlerInterface" should be implemented by a
+ * concrete class and the same should be used to instantiate BiosHandler.
+ *
+ * This class registers call back to listen to PLDM service as it is being used
+ * for reading/writing BIOS attributes.
+ *
+ * The feature can be used in a factory reset scenario where backed up values
+ * can be used to restore BIOS.
+ *
+ */
+template <typename T>
+class BiosHandler
+{
+ public:
+ // deleted APIs
+ BiosHandler() = delete;
+ BiosHandler(const BiosHandler&) = delete;
+ BiosHandler& operator=(const BiosHandler&) = delete;
+ BiosHandler& operator=(BiosHandler&&) = delete;
+ ~BiosHandler() = default;
+
+ /**
+ * @brief Constructor.
+ *
+ * @param[in] i_connection - Asio connection object.
+ * @param[in] i_manager - Manager object.
+ */
+ BiosHandler(
+ const std::shared_ptr<sdbusplus::asio::connection>& i_connection,
+ const std::shared_ptr<Manager>& i_manager) : m_asioConn(i_connection)
+ {
+ m_specificBiosHandler = std::make_shared<T>(i_manager);
+ checkAndListenPldmService();
+ }
+
+ private:
+ /**
+ * @brief API to check if PLDM service is running and run BIOS sync.
+ *
+ * This API checks if the PLDM service is running and if yes it will start
+ * an immediate sync of BIOS attributes. If the service is not running, it
+ * registers a listener to be notified when the service starts so that a
+ * restore can be performed.
+ */
+ void checkAndListenPldmService();
+
+ /**
+ * @brief Register listener for BIOS attribute property change.
+ *
+ * The VPD manager needs to listen for property change of certain BIOS
+ * attributes that are backed in VPD. When the attributes change, the new
+ * value is written back to the VPD keywords that backs them up.
+ */
+ void listenBiosAttributes();
+
+ // Reference to the connection.
+ const std::shared_ptr<sdbusplus::asio::connection>& m_asioConn;
+
+ // shared pointer to specific BIOS handler.
+ std::shared_ptr<T> m_specificBiosHandler;
+};
+} // namespace vpd
diff --git a/vpd-manager/include/constants.hpp b/vpd-manager/include/constants.hpp
new file mode 100644
index 0000000..5faf8da
--- /dev/null
+++ b/vpd-manager/include/constants.hpp
@@ -0,0 +1,196 @@
+#pragma once
+
+#include <cstdint>
+#include <iostream>
+namespace vpd
+{
+namespace constants
+{
+static constexpr auto KEYWORD_SIZE = 2;
+static constexpr auto RECORD_SIZE = 4;
+
+static constexpr uint8_t IPZ_DATA_START = 11;
+static constexpr uint8_t IPZ_DATA_START_TAG = 0x84;
+static constexpr uint8_t IPZ_RECORD_END_TAG = 0x78;
+
+static constexpr uint8_t KW_VPD_DATA_START = 0;
+static constexpr uint8_t KW_VPD_START_TAG = 0x82;
+static constexpr uint8_t KW_VPD_PAIR_START_TAG = 0x84;
+static constexpr uint8_t ALT_KW_VPD_PAIR_START_TAG = 0x90;
+static constexpr uint8_t KW_VPD_END_TAG = 0x78;
+static constexpr uint8_t KW_VAL_PAIR_END_TAG = 0x79;
+static constexpr uint8_t AMM_ENABLED_IN_VPD = 2;
+static constexpr uint8_t AMM_DISABLED_IN_VPD = 1;
+
+static constexpr auto DDIMM_11S_BARCODE_START = 416;
+static constexpr auto DDIMM_11S_BARCODE_START_TAG = "11S";
+static constexpr auto DDIMM_11S_FORMAT_LEN = 3;
+static constexpr auto DDIMM_11S_BARCODE_LEN = 26;
+static constexpr auto PART_NUM_LEN = 7;
+static constexpr auto SERIAL_NUM_LEN = 12;
+static constexpr auto CCIN_LEN = 4;
+static constexpr auto CONVERT_MB_TO_KB = 1024;
+static constexpr auto CONVERT_GB_TO_KB = 1024 * 1024;
+
+static constexpr auto SPD_BYTE_2 = 2;
+static constexpr auto SPD_BYTE_3 = 3;
+static constexpr auto SPD_BYTE_4 = 4;
+static constexpr auto SPD_BYTE_6 = 6;
+static constexpr auto SPD_BYTE_12 = 12;
+static constexpr auto SPD_BYTE_13 = 13;
+static constexpr auto SPD_BYTE_18 = 18;
+static constexpr auto SPD_BYTE_234 = 234;
+static constexpr auto SPD_BYTE_235 = 235;
+static constexpr auto SPD_BYTE_BIT_0_3_MASK = 0x0F;
+static constexpr auto SPD_BYTE_MASK = 0xFF;
+static constexpr auto SPD_MODULE_TYPE_DDIMM = 0x0A;
+static constexpr auto SPD_DRAM_TYPE_DDR5 = 0x12;
+static constexpr auto SPD_DRAM_TYPE_DDR4 = 0x0C;
+
+static constexpr auto JEDEC_SDRAM_CAP_MASK = 0x0F;
+static constexpr auto JEDEC_PRI_BUS_WIDTH_MASK = 0x07;
+static constexpr auto JEDEC_SDRAM_WIDTH_MASK = 0x07;
+static constexpr auto JEDEC_NUM_RANKS_MASK = 0x38;
+static constexpr auto JEDEC_DIE_COUNT_MASK = 0x70;
+static constexpr auto JEDEC_SINGLE_LOAD_STACK = 0x02;
+static constexpr auto JEDEC_SIGNAL_LOADING_MASK = 0x03;
+
+static constexpr auto JEDEC_SDRAMCAP_MULTIPLIER = 256;
+static constexpr auto JEDEC_PRI_BUS_WIDTH_MULTIPLIER = 8;
+static constexpr auto JEDEC_SDRAM_WIDTH_MULTIPLIER = 4;
+static constexpr auto JEDEC_SDRAMCAP_RESERVED = 7;
+static constexpr auto JEDEC_RESERVED_BITS = 3;
+static constexpr auto JEDEC_DIE_COUNT_RIGHT_SHIFT = 4;
+
+static constexpr auto LAST_KW = "PF";
+static constexpr auto POUND_KW = '#';
+static constexpr auto POUND_KW_PREFIX = "PD_";
+static constexpr auto MB_YEAR_END = 4;
+static constexpr auto MB_MONTH_END = 7;
+static constexpr auto MB_DAY_END = 10;
+static constexpr auto MB_HOUR_END = 13;
+static constexpr auto MB_MIN_END = 16;
+static constexpr auto MB_RESULT_LEN = 19;
+static constexpr auto MB_LEN_BYTES = 8;
+static constexpr auto UUID_LEN_BYTES = 16;
+static constexpr auto UUID_TIME_LOW_END = 8;
+static constexpr auto UUID_TIME_MID_END = 13;
+static constexpr auto UUID_TIME_HIGH_END = 18;
+static constexpr auto UUID_CLK_SEQ_END = 23;
+static constexpr auto MAC_ADDRESS_LEN_BYTES = 6;
+static constexpr auto ONE_BYTE = 1;
+static constexpr auto TWO_BYTES = 2;
+
+static constexpr auto VALUE_0 = 0;
+static constexpr auto VALUE_1 = 1;
+static constexpr auto VALUE_2 = 2;
+static constexpr auto VALUE_3 = 3;
+static constexpr auto VALUE_4 = 4;
+static constexpr auto VALUE_5 = 5;
+static constexpr auto VALUE_6 = 6;
+static constexpr auto VALUE_7 = 7;
+static constexpr auto VALUE_8 = 8;
+
+static constexpr auto MASK_BYTE_BITS_01 = 0x03;
+static constexpr auto MASK_BYTE_BITS_345 = 0x38;
+static constexpr auto MASK_BYTE_BITS_012 = 0x07;
+static constexpr auto MASK_BYTE_BITS_567 = 0xE0;
+static constexpr auto MASK_BYTE_BITS_01234 = 0x1F;
+
+static constexpr auto MASK_BYTE_BIT_6 = 0x40;
+static constexpr auto MASK_BYTE_BIT_7 = 0x80;
+
+static constexpr auto SHIFT_BITS_0 = 0;
+static constexpr auto SHIFT_BITS_3 = 3;
+static constexpr auto SHIFT_BITS_5 = 5;
+
+static constexpr auto ASCII_OF_SPACE = 32;
+
+// Size of 8 EQs' in CP00's PG keyword
+static constexpr auto SIZE_OF_8EQ_IN_PG = 24;
+
+// Zero based index position of first EQ in CP00's PG keyword
+static constexpr auto INDEX_OF_EQ0_IN_PG = 97;
+
+constexpr auto systemInvPath = "/xyz/openbmc_project/inventory/system";
+constexpr auto pimPath = "/xyz/openbmc_project/inventory";
+constexpr auto pimIntf = "xyz.openbmc_project.Inventory.Manager";
+constexpr auto ipzVpdInf = "com.ibm.ipzvpd.";
+constexpr auto kwdVpdInf = "com.ibm.ipzvpd.VINI";
+constexpr auto vsysInf = "com.ibm.ipzvpd.VSYS";
+constexpr auto utilInf = "com.ibm.ipzvpd.UTIL";
+constexpr auto vcenInf = "com.ibm.ipzvpd.VCEN";
+constexpr auto kwdCCIN = "CC";
+constexpr auto kwdRG = "RG";
+constexpr auto kwdAMM = "D0";
+constexpr auto kwdClearNVRAM_CreateLPAR = "D1";
+constexpr auto kwdKeepAndClear = "D1";
+constexpr auto kwdFC = "FC";
+constexpr auto kwdTM = "TM";
+constexpr auto kwdSE = "SE";
+constexpr auto recVSYS = "VSYS";
+constexpr auto recVCEN = "VCEN";
+constexpr auto locationCodeInf = "com.ibm.ipzvpd.Location";
+constexpr auto xyzLocationCodeInf =
+ "xyz.openbmc_project.Inventory.Decorator.LocationCode";
+constexpr auto operationalStatusInf =
+ "xyz.openbmc_project.State.Decorator.OperationalStatus";
+constexpr auto enableInf = "xyz.openbmc_project.Object.Enable";
+constexpr auto assetInf = "xyz.openbmc_project.Inventory.Decorator.Asset";
+constexpr auto inventoryItemInf = "xyz.openbmc_project.Inventory.Item";
+constexpr auto pldmServiceName = "xyz.openbmc_project.PLDM";
+constexpr auto pimServiceName = "xyz.openbmc_project.Inventory.Manager";
+constexpr auto biosConfigMgrObjPath =
+ "/xyz/openbmc_project/bios_config/manager";
+constexpr auto biosConfigMgrService = "xyz.openbmc_project.BIOSConfigManager";
+constexpr auto biosConfigMgrInterface =
+ "xyz.openbmc_project.BIOSConfig.Manager";
+constexpr auto objectMapperService = "xyz.openbmc_project.ObjectMapper";
+constexpr auto objectMapperPath = "/xyz/openbmc_project/object_mapper";
+constexpr auto objectMapperInf = "xyz.openbmc_project.ObjectMapper";
+constexpr auto systemVpdInvPath =
+ "/xyz/openbmc_project/inventory/system/chassis/motherboard";
+constexpr auto assetTagInf = "xyz.openbmc_project.Inventory.Decorator.AssetTag";
+constexpr auto hostObjectPath = "/xyz/openbmc_project/state/host0";
+constexpr auto hostInterface = "xyz.openbmc_project.State.Host";
+constexpr auto hostService = "xyz.openbmc_project.State.Host";
+constexpr auto hostRunningState =
+ "xyz.openbmc_project.State.Host.HostState.Running";
+static constexpr auto BD_YEAR_END = 4;
+static constexpr auto BD_MONTH_END = 7;
+static constexpr auto BD_DAY_END = 10;
+static constexpr auto BD_HOUR_END = 13;
+
+constexpr uint8_t UNEXP_LOCATION_CODE_MIN_LENGTH = 4;
+constexpr uint8_t EXP_LOCATION_CODE_MIN_LENGTH = 17;
+static constexpr auto SE_KWD_LENGTH = 7;
+static constexpr auto INVALID_NODE_NUMBER = -1;
+
+static constexpr auto CMD_BUFFER_LENGTH = 256;
+
+// To be explicitly used for string comparision.
+static constexpr auto STR_CMP_SUCCESS = 0;
+
+// Just a random value. Can be adjusted as required.
+static constexpr uint8_t MAX_THREADS = 10;
+
+static constexpr auto FAILURE = -1;
+static constexpr auto SUCCESS = 0;
+
+constexpr auto bmcStateService = "xyz.openbmc_project.State.BMC";
+constexpr auto bmcZeroStateObject = "/xyz/openbmc_project/state/bmc0";
+constexpr auto bmcStateInterface = "xyz.openbmc_project.State.BMC";
+constexpr auto currentBMCStateProperty = "CurrentBMCState";
+constexpr auto bmcReadyState = "xyz.openbmc_project.State.BMC.BMCState.Ready";
+
+static constexpr auto eventLoggingServiceName = "xyz.openbmc_project.Logging";
+static constexpr auto eventLoggingObjectPath = "/xyz/openbmc_project/logging";
+static constexpr auto eventLoggingInterface =
+ "xyz.openbmc_project.Logging.Create";
+
+static constexpr auto systemdService = "org.freedesktop.systemd1";
+static constexpr auto systemdObjectPath = "/org/freedesktop/systemd1";
+static constexpr auto systemdManagerInterface =
+ "org.freedesktop.systemd1.Manager";
+} // namespace constants
+} // namespace vpd
diff --git a/vpd-manager/include/ddimm_parser.hpp b/vpd-manager/include/ddimm_parser.hpp
new file mode 100644
index 0000000..a53ed17
--- /dev/null
+++ b/vpd-manager/include/ddimm_parser.hpp
@@ -0,0 +1,123 @@
+#pragma once
+
+#include "constants.hpp"
+#include "exceptions.hpp"
+#include "logger.hpp"
+#include "parser_interface.hpp"
+#include "types.hpp"
+
+namespace vpd
+{
+/**
+ * @brief Concrete class to implement DDIMM VPD parsing.
+ *
+ * The class inherits ParserInterface interface class and overrides the parser
+ * functionality to implement parsing logic for DDIMM VPD format.
+ */
+class DdimmVpdParser : public ParserInterface
+{
+ public:
+ // Deleted API's
+ DdimmVpdParser() = delete;
+ DdimmVpdParser(const DdimmVpdParser&) = delete;
+ DdimmVpdParser& operator=(const DdimmVpdParser&) = delete;
+ DdimmVpdParser(DdimmVpdParser&&) = delete;
+ DdimmVpdParser& operator=(DdimmVpdParser&&) = delete;
+
+ /**
+ * @brief Defaul destructor.
+ */
+ ~DdimmVpdParser() = default;
+
+ /**
+ * @brief Constructor
+ *
+ * @param[in] i_vpdVector - VPD data.
+ */
+ DdimmVpdParser(const types::BinaryVector& i_vpdVector) :
+ m_vpdVector(i_vpdVector)
+ {
+ if ((constants::DDIMM_11S_BARCODE_START +
+ constants::DDIMM_11S_BARCODE_LEN) > m_vpdVector.size())
+ {
+ throw(DataException("Malformed DDIMM VPD"));
+ }
+ }
+
+ /**
+ * @brief API to parse DDIMM VPD file.
+ *
+ * @return parsed VPD data
+ */
+ virtual types::VPDMapVariant parse() override;
+
+ private:
+ /**
+ * @brief API to read keyword data based on the type DDR4/DDR5.
+ *
+ * Updates the m_parsedVpdMap with read keyword data.
+ * @param[in] i_iterator - iterator to buffer containing VPD
+ */
+ void readKeywords(types::BinaryVector::const_iterator i_iterator);
+
+ /**
+ * @brief API to calculate DDIMM size from DDIMM VPD
+ *
+ * @param[in] i_iterator - iterator to buffer containing VPD
+ * @return calculated size or 0 in case of any error.
+ */
+ size_t getDdimmSize(types::BinaryVector::const_iterator i_iterator);
+
+ /**
+ * @brief This function calculates DDR5 based DDIMM's capacity
+ *
+ * @param[in] i_iterator - iterator to buffer containing VPD
+ * @return calculated size or 0 in case of any error.
+ */
+ size_t
+ getDdr5BasedDdimmSize(types::BinaryVector::const_iterator i_iterator);
+
+ /**
+ * @brief This function calculates DDR4 based DDIMM's capacity
+ *
+ * @param[in] i_iterator - iterator to buffer containing VPD
+ * @return calculated size or 0 in case of any error.
+ */
+ size_t
+ getDdr4BasedDdimmSize(types::BinaryVector::const_iterator i_iterator);
+
+ /**
+ * @brief This function calculates DDR5 based die per package
+ *
+ * @param[in] i_ByteValue - the bit value for calculation
+ * @return die per package value.
+ */
+ uint8_t getDdr5DiePerPackage(uint8_t i_ByteValue);
+
+ /**
+ * @brief This function calculates DDR5 based density per die
+ *
+ * @param[in] i_ByteValue - the bit value for calculation
+ * @return density per die.
+ */
+ uint8_t getDdr5DensityPerDie(uint8_t i_ByteValue);
+
+ /**
+ * @brief This function checks the validity of the bits
+ *
+ * @param[in] i_ByteValue - the byte value with relevant bits
+ * @param[in] i_shift - shifter value to selects needed bits
+ * @param[in] i_minValue - minimum value it can contain
+ * @param[in] i_maxValue - maximum value it can contain
+ * @return true if valid else false.
+ */
+ bool checkValidValue(uint8_t i_ByteValue, uint8_t i_shift,
+ uint8_t i_minValue, uint8_t i_maxValue);
+
+ // VPD file to be parsed
+ const types::BinaryVector& m_vpdVector;
+
+ // Stores parsed VPD data.
+ types::DdimmVpdMap m_parsedVpdMap{};
+};
+} // namespace vpd
diff --git a/vpd-manager/include/event_logger.hpp b/vpd-manager/include/event_logger.hpp
new file mode 100644
index 0000000..0080be6
--- /dev/null
+++ b/vpd-manager/include/event_logger.hpp
@@ -0,0 +1,170 @@
+#pragma once
+
+#include "constants.hpp"
+#include "types.hpp"
+
+#include <iostream>
+#include <optional>
+#include <string>
+#include <unordered_map>
+
+namespace vpd
+{
+/**
+ * @brief Class for logging events.
+ *
+ * Class handles logging PEL under 'logging' service.
+ * Provide separate async API's for calling out inventory_path, device_path and
+ * i2c bus.
+ */
+class EventLogger
+{
+ public:
+ /**
+ * @brief An API to create a PEL with inventory path callout.
+ *
+ * This API calls an async method to create PEL, and also handles inventory
+ * path callout.
+ *
+ * Note: If inventory path callout info is not provided, it will create a
+ * PEL without any callout. Currently only one callout is handled in this
+ * API.
+ *
+ * @todo: Symbolic FRU and procedure callout needs to be handled in this
+ * API.
+ *
+ * @param[in] i_errorType - Enum to map with event message name.
+ * @param[in] i_severity - Severity of the event.
+ * @param[in] i_callouts - Callout information, list of tuple having
+ * inventory path and priority as input [optional].
+ * @param[in] i_fileName - File name.
+ * @param[in] i_funcName - Function name.
+ * @param[in] i_internalRc - Internal return code.
+ * @param[in] i_description - Error description.
+ * @param[in] i_userData1 - Additional user data [optional].
+ * @param[in] i_userData2 - Additional user data [optional].
+ * @param[in] i_symFru - Symblolic FRU callout data [optional].
+ * @param[in] i_procedure - Procedure callout data [optional].
+ *
+ * @throw exception in case of error.
+ */
+ static void createAsyncPelWithInventoryCallout(
+ const types::ErrorType& i_errorType,
+ const types::SeverityType& i_severity,
+ const std::vector<types::InventoryCalloutData>& i_callouts,
+ const std::string& i_fileName, const std::string& i_funcName,
+ const uint8_t i_internalRc, const std::string& i_description,
+ const std::optional<std::string> i_userData1,
+ const std::optional<std::string> i_userData2,
+ const std::optional<std::string> i_symFru,
+ const std::optional<std::string> i_procedure);
+
+ /**
+ * @brief An API to create a PEL with device path callout.
+ *
+ * @param[in] i_errorType - Enum to map with event message name.
+ * @param[in] i_severity - Severity of the event.
+ * @param[in] i_callouts - Callout information, list of tuple having device
+ * path and error number as input.
+ * @param[in] i_fileName - File name.
+ * @param[in] i_funcName - Function name.
+ * @param[in] i_internalRc - Internal return code.
+ * @param[in] i_userData1 - Additional user data [optional].
+ * @param[in] i_userData2 - Additional user data [optional].
+ */
+ static void createAsyncPelWithI2cDeviceCallout(
+ const types::ErrorType i_errorType,
+ const types::SeverityType i_severity,
+ const std::vector<types::DeviceCalloutData>& i_callouts,
+ const std::string& i_fileName, const std::string& i_funcName,
+ const uint8_t i_internalRc,
+ const std::optional<std::pair<std::string, std::string>> i_userData1,
+ const std::optional<std::pair<std::string, std::string>> i_userData2);
+
+ /**
+ * @brief An API to create a PEL with I2c bus callout.
+ *
+ * @param[in] i_errorType - Enum to map with event message name.
+ * @param[in] i_severity - Severity of the event.
+ * @param[in] i_callouts - Callout information, list of tuple having i2c
+ * bus, i2c address and error number as input.
+ * @param[in] i_fileName - File name.
+ * @param[in] i_funcName - Function name.
+ * @param[in] i_internalRc - Internal return code.
+ * @param[in] i_userData1 - Additional user data [optional].
+ * @param[in] i_userData2 - Additional user data [optional].
+ */
+ static void createAsyncPelWithI2cBusCallout(
+ const types::ErrorType i_errorType,
+ const types::SeverityType i_severity,
+ const std::vector<types::I2cBusCalloutData>& i_callouts,
+ const std::string& i_fileName, const std::string& i_funcName,
+ const uint8_t i_internalRc,
+ const std::optional<std::pair<std::string, std::string>> i_userData1,
+ const std::optional<std::pair<std::string, std::string>> i_userData2);
+
+ /**
+ * @brief An API to create a PEL.
+ *
+ * @param[in] i_errorType - Enum to map with event message name.
+ * @param[in] i_severity - Severity of the event.
+ * @param[in] i_fileName - File name.
+ * @param[in] i_funcName - Function name.
+ * @param[in] i_internalRc - Internal return code.
+ * @param[in] i_description - Error description.
+ * @param[in] i_userData1 - Additional user data [optional].
+ * @param[in] i_userData2 - Additional user data [optional].
+ * @param[in] i_symFru - Symblolic FRU callout data [optional].
+ * @param[in] i_procedure - Procedure callout data [optional].
+ *
+ * @todo: Symbolic FRU and procedure callout needs to be handled in this
+ * API.
+ */
+ static void createAsyncPel(
+ const types::ErrorType& i_errorType,
+ const types::SeverityType& i_severity, const std::string& i_fileName,
+ const std::string& i_funcName, const uint8_t i_internalRc,
+ const std::string& i_description,
+ const std::optional<std::string> i_userData1,
+ const std::optional<std::string> i_userData2,
+ const std::optional<std::string> i_symFru,
+ const std::optional<std::string> i_procedure);
+
+ /**
+ * @brief An API to create PEL.
+ *
+ * This API makes synchronous call to phosphor-logging Create method.
+ *
+ * @param[in] i_errorType - Enum to map with event message name.
+ * @param[in] i_severity - Severity of the event.
+ * @param[in] i_fileName - File name.
+ * @param[in] i_funcName - Function name.
+ * @param[in] i_internalRc - Internal return code.
+ * @param[in] i_description - Error description.
+ * @param[in] i_userData1 - Additional user data [optional].
+ * @param[in] i_userData2 - Additional user data [optional].
+ * @param[in] i_symFru - Symblolic FRU callout data [optional].s
+ * @param[in] i_procedure - Procedure callout data [optional].
+ *
+ * @todo: Symbolic FRU and procedure callout needs to be handled in this
+ * API.
+ */
+ static void createSyncPel(
+ const types::ErrorType& i_errorType,
+ const types::SeverityType& i_severity, const std::string& i_fileName,
+ const std::string& i_funcName, const uint8_t i_internalRc,
+ const std::string& i_description,
+ const std::optional<std::string> i_userData1,
+ const std::optional<std::string> i_userData2,
+ const std::optional<std::string> i_symFru,
+ const std::optional<std::string> i_procedure);
+
+ private:
+ static const std::unordered_map<types::SeverityType, std::string>
+ m_severityMap;
+ static const std::unordered_map<types::ErrorType, std::string>
+ m_errorMsgMap;
+ static const std::unordered_map<types::CalloutPriority, std::string>
+ m_priorityMap;
+};
+} // namespace vpd
diff --git a/vpd-manager/include/exceptions.hpp b/vpd-manager/include/exceptions.hpp
new file mode 100644
index 0000000..a787d08
--- /dev/null
+++ b/vpd-manager/include/exceptions.hpp
@@ -0,0 +1,157 @@
+#pragma once
+
+#include <stdexcept>
+
+namespace vpd
+{
+/** @class Exception
+ * @brief This class inherits std::runtime_error and overrrides "what" method
+ * to return the description of exception.
+ * This class also works as base class for custom exception classes for
+ * VPD repository.
+ */
+class Exception : public std::runtime_error
+{
+ public:
+ // deleted methods
+ Exception() = delete;
+ Exception(const Exception&) = delete;
+ Exception(Exception&&) = delete;
+ Exception& operator=(const Exception&) = delete;
+
+ // default destructor
+ ~Exception() = default;
+
+ /** @brief constructor
+ *
+ * @param[in] msg - Information w.r.t exception.
+ */
+ explicit Exception(const std::string& msg) :
+ std::runtime_error(msg), m_errMsg(msg)
+ {}
+
+ /** @brief inline method to return exception string.
+ *
+ * This is overridden method of std::runtime class.
+ */
+ inline const char* what() const noexcept override
+ {
+ return m_errMsg.c_str();
+ }
+
+ private:
+ /** @brief string to hold the reason of exception */
+ std::string m_errMsg;
+
+}; // class Exception
+
+/** @class EccException
+ *
+ * @brief This class extends Exceptions class and define type for ECC related
+ * exception in VPD.
+ */
+class EccException : public Exception
+{
+ public:
+ // deleted methods
+ EccException() = delete;
+ EccException(const EccException&) = delete;
+ EccException(EccException&&) = delete;
+ EccException& operator=(const EccException&) = delete;
+
+ // default destructor
+ ~EccException() = default;
+
+ /** @brief constructor
+ *
+ * @param[in] msg - Information w.r.t exception.
+ */
+ explicit EccException(const std::string& msg) : Exception(msg) {}
+
+}; // class EccException
+
+/** @class DataException
+ *
+ * @brief This class extends Exceptions class and define type for data related
+ * exception in VPD
+ */
+class DataException : public Exception
+{
+ public:
+ // deleted methods
+ DataException() = delete;
+ DataException(const DataException&) = delete;
+ DataException(DataException&&) = delete;
+ DataException& operator=(const DataException&) = delete;
+
+ // default destructor
+ ~DataException() = default;
+
+ /** @brief constructor
+ *
+ * @param[in] msg - string to define exception
+ */
+ explicit DataException(const std::string& msg) : Exception(msg) {}
+
+}; // class DataException
+
+class JsonException : public Exception
+{
+ public:
+ // deleted methods
+ JsonException() = delete;
+ JsonException(const JsonException&) = delete;
+ JsonException(JsonException&&) = delete;
+ JsonException& operator=(const JsonException&) = delete;
+
+ // default destructor
+ ~JsonException() = default;
+
+ /** @brief constructor
+ * @param[in] msg - Information w.r.t. exception.
+ * @param[in] path - Json path
+ */
+ JsonException(const std::string& msg, const std::string& path) :
+ Exception(msg), m_jsonPath(path)
+ {}
+
+ /** @brief Json path getter method.
+ *
+ * @return - Json path
+ */
+ inline std::string getJsonPath() const
+ {
+ return m_jsonPath;
+ }
+
+ private:
+ /** To hold the path of Json that failed*/
+ std::string m_jsonPath;
+
+}; // class JSonException
+
+/** @class GpioException
+ * @brief Custom handler for GPIO exception.
+ *
+ * This class extends Exceptions class and define
+ * type for GPIO related exception in VPD.
+ */
+class GpioException : public Exception
+{
+ public:
+ // deleted methods
+ GpioException() = delete;
+ GpioException(const GpioException&) = delete;
+ GpioException(GpioException&&) = delete;
+ GpioException& operator=(const GpioException&) = delete;
+
+ // default destructor
+ ~GpioException() = default;
+
+ /** @brief constructor
+ * @param[in] msg - string to define exception
+ */
+ explicit GpioException(const std::string& msg) : Exception(msg) {}
+};
+
+} // namespace vpd
diff --git a/vpd-manager/include/gpio_monitor.hpp b/vpd-manager/include/gpio_monitor.hpp
new file mode 100644
index 0000000..476a31d
--- /dev/null
+++ b/vpd-manager/include/gpio_monitor.hpp
@@ -0,0 +1,142 @@
+#pragma once
+
+#include "worker.hpp"
+
+#include <boost/asio/steady_timer.hpp>
+#include <nlohmann/json.hpp>
+#include <sdbusplus/asio/connection.hpp>
+
+#include <vector>
+
+namespace vpd
+{
+/**
+ * @brief class for GPIO event handling.
+ *
+ * Responsible for detecting events and handling them. It continuously
+ * monitors the presence of the FRU. If it detects any change, performs
+ * deletion of FRU VPD if FRU is not present, otherwise performs VPD
+ * collection if FRU gets added.
+ */
+class GpioEventHandler
+{
+ public:
+ GpioEventHandler() = delete;
+ ~GpioEventHandler() = default;
+ GpioEventHandler(const GpioEventHandler&) = delete;
+ GpioEventHandler& operator=(const GpioEventHandler&) = delete;
+ GpioEventHandler(GpioEventHandler&&) = delete;
+ GpioEventHandler& operator=(GpioEventHandler&&) = delete;
+
+ /**
+ * @brief Constructor
+ *
+ * @param[in] i_fruPath - EEPROM path of the FRU.
+ * @param[in] i_worker - pointer to the worker object.
+ * @param[in] i_ioContext - pointer to the io context object
+ */
+ GpioEventHandler(
+ const std::string i_fruPath, const std::shared_ptr<Worker>& i_worker,
+ const std::shared_ptr<boost::asio::io_context>& i_ioContext) :
+ m_fruPath(i_fruPath), m_worker(i_worker)
+ {
+ setEventHandlerForGpioPresence(i_ioContext);
+ }
+
+ private:
+ /**
+ * @brief API to take action based on GPIO presence pin value.
+ *
+ * This API takes action based on the change in the presence pin value.
+ * It performs deletion of FRU VPD if FRU is not present, otherwise performs
+ * VPD collection if FRU gets added.
+ *
+ * @param[in] i_isFruPresent - Holds the present status of the FRU.
+ */
+ void handleChangeInGpioPin(const bool& i_isFruPresent);
+
+ /**
+ * @brief An API to set event handler for FRUs GPIO presence.
+ *
+ * An API to set timer to call event handler to detect GPIO presence
+ * of the FRU.
+ *
+ * @param[in] i_ioContext - pointer to io context object
+ */
+ void setEventHandlerForGpioPresence(
+ const std::shared_ptr<boost::asio::io_context>& i_ioContext);
+
+ /**
+ * @brief API to handle timer expiry.
+ *
+ * This API handles timer expiry and checks on the GPIO presence state,
+ * takes action if there is any change in the GPIO presence value.
+ *
+ * @param[in] i_errorCode - Error Code
+ * @param[in] i_timerObj - Pointer to timer Object.
+ */
+ void handleTimerExpiry(
+ const boost::system::error_code& i_errorCode,
+ const std::shared_ptr<boost::asio::steady_timer>& i_timerObj);
+
+ const std::string m_fruPath;
+
+ const std::shared_ptr<Worker>& m_worker;
+
+ // Preserves the GPIO pin value to compare. Default value is false.
+ bool m_prevPresencePinValue = false;
+};
+
+class GpioMonitor
+{
+ public:
+ GpioMonitor() = delete;
+ ~GpioMonitor() = default;
+ GpioMonitor(const GpioMonitor&) = delete;
+ GpioMonitor& operator=(const GpioMonitor&) = delete;
+ GpioMonitor(GpioMonitor&&) = delete;
+ GpioMonitor& operator=(GpioMonitor&&) = delete;
+
+ /**
+ * @brief constructor
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON Object.
+ * @param[in] i_worker - pointer to the worker object.
+ * @param[in] i_ioContext - pointer to IO context object.
+ */
+ GpioMonitor(const nlohmann::json i_sysCfgJsonObj,
+ const std::shared_ptr<Worker>& i_worker,
+ const std::shared_ptr<boost::asio::io_context>& i_ioContext) :
+ m_sysCfgJsonObj(i_sysCfgJsonObj)
+ {
+ if (!m_sysCfgJsonObj.empty())
+ {
+ initHandlerForGpio(i_ioContext, i_worker);
+ }
+ else
+ {
+ throw std::runtime_error(
+ "Gpio Monitoring can't be instantiated with empty config JSON");
+ }
+ }
+
+ private:
+ /**
+ * @brief API to instantiate GpioEventHandler for GPIO pins.
+ *
+ * This API will extract the GPIO information from system config JSON
+ * and instantiate event handler for GPIO pins.
+ *
+ * @param[in] i_ioContext - Pointer to IO context object.
+ * @param[in] i_worker - Pointer to worker class.
+ */
+ void initHandlerForGpio(
+ const std::shared_ptr<boost::asio::io_context>& i_ioContext,
+ const std::shared_ptr<Worker>& i_worker);
+
+ // Array of event handlers for all the attachable FRUs.
+ std::vector<std::shared_ptr<GpioEventHandler>> m_gpioEventHandlerObjects;
+
+ const nlohmann::json& m_sysCfgJsonObj;
+};
+} // namespace vpd
diff --git a/vpd-manager/include/ipz_parser.hpp b/vpd-manager/include/ipz_parser.hpp
new file mode 100644
index 0000000..2d50ddd
--- /dev/null
+++ b/vpd-manager/include/ipz_parser.hpp
@@ -0,0 +1,273 @@
+#pragma once
+
+#include "logger.hpp"
+#include "parser_interface.hpp"
+#include "types.hpp"
+
+#include <fstream>
+#include <string_view>
+
+namespace vpd
+{
+/**
+ * @brief Concrete class to implement IPZ VPD parsing.
+ *
+ * The class inherits ParserInterface interface class and overrides the parser
+ * functionality to implement parsing logic for IPZ VPD format.
+ */
+class IpzVpdParser : public ParserInterface
+{
+ public:
+ // Deleted APIs
+ IpzVpdParser() = delete;
+ IpzVpdParser(const IpzVpdParser&) = delete;
+ IpzVpdParser& operator=(const IpzVpdParser&) = delete;
+ IpzVpdParser(IpzVpdParser&&) = delete;
+ IpzVpdParser& operator=(IpzVpdParser&&) = delete;
+
+ /**
+ * @brief Constructor.
+ *
+ * @param[in] vpdVector - VPD data.
+ * @param[in] vpdFilePath - Path to VPD EEPROM.
+ * @param[in] vpdStartOffset - Offset from where VPD starts in the file.
+ * Defaulted to 0.
+ */
+ IpzVpdParser(const types::BinaryVector& vpdVector,
+ const std::string& vpdFilePath, size_t vpdStartOffset = 0) :
+ m_vpdVector(vpdVector), m_vpdFilePath(vpdFilePath),
+ m_vpdStartOffset(vpdStartOffset)
+ {
+ try
+ {
+ m_vpdFileStream.exceptions(
+ std::ifstream::badbit | std::ifstream::failbit);
+ m_vpdFileStream.open(vpdFilePath, std::ios::in | std::ios::out |
+ std::ios::binary);
+ }
+ catch (const std::fstream::failure& e)
+ {
+ logging::logMessage(e.what());
+ }
+ }
+
+ /**
+ * @brief Defaul destructor.
+ */
+ ~IpzVpdParser() = default;
+
+ /**
+ * @brief API to parse IPZ VPD file.
+ *
+ * Note: Caller needs to check validity of the map returned. Throws
+ * exception in certain situation, needs to be handled accordingly.
+ *
+ * @return parsed VPD data.
+ */
+ virtual types::VPDMapVariant parse() override;
+
+ /**
+ * @brief API to check validity of VPD header.
+ *
+ * Note: The API throws exception in case of any failure or malformed VDP.
+ *
+ * @param[in] itrToVPD - Iterator to the beginning of VPD file.
+ * Don't change the parameter to reference as movement of passsed iterator
+ * to an offset is not required after header check.
+ */
+ void checkHeader(types::BinaryVector::const_iterator itrToVPD);
+
+ /**
+ * @brief API to read keyword's value from hardware
+ *
+ * @param[in] i_paramsToReadData - Data required to perform read
+ *
+ * @throw
+ * sdbusplus::xyz::openbmc_project::Common::Device::Error::ReadFailure.
+ *
+ * @return On success return the value read. On failure throw exception.
+ */
+ types::DbusVariantType
+ readKeywordFromHardware(const types::ReadVpdParams i_paramsToReadData);
+
+ /**
+ * @brief API to write keyword's value on hardware.
+ *
+ * @param[in] i_paramsToWriteData - Data required to perform write.
+ *
+ * @throw sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument.
+ * @throw sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed.
+ * @throw DataException
+ * @throw EccException
+ *
+ *
+ * @return On success returns number of bytes written on hardware, On
+ * failure throws exception.
+ */
+ int writeKeywordOnHardware(const types::WriteVpdParams i_paramsToWriteData);
+
+ private:
+ /**
+ * @brief Check ECC of VPD header.
+ *
+ * @return true/false based on check result.
+ */
+ bool vhdrEccCheck();
+
+ /**
+ * @brief Check ECC of VTOC.
+ *
+ * @return true/false based on check result.
+ */
+ bool vtocEccCheck();
+
+ /**
+ * @brief Check ECC of a record.
+ *
+ * Note: Throws exception in case of failure. Caller need to handle as
+ * required.
+ *
+ * @param[in] iterator - Iterator to the record.
+ * @return success/failre
+ */
+ bool recordEccCheck(types::BinaryVector::const_iterator iterator);
+
+ /**
+ * @brief API to read VTOC record.
+ *
+ * The API reads VTOC record and returns the length of PT keyword.
+ *
+ * Note: Throws exception in case of any error. Caller need to handle as
+ * required.
+ *
+ * @param[in] itrToVPD - Iterator to begining of VPD data.
+ * @return Length of PT keyword.
+ */
+ auto readTOC(types::BinaryVector::const_iterator& itrToVPD);
+
+ /**
+ * @brief API to read PT record.
+ *
+ * Note: Throws exception in case ECC check fails.
+ *
+ * @param[in] itrToPT - Iterator to PT record in VPD vector.
+ * @param[in] ptLength - length of the PT record.
+ * @return List of record's offset.
+ */
+ types::RecordOffsetList readPT(types::BinaryVector::const_iterator& itrToPT,
+ auto ptLength);
+
+ /**
+ * @brief API to read keyword data based on its encoding type.
+ *
+ * @param[in] kwdName - Name of the keyword.
+ * @param[in] kwdDataLength - Length of keyword data.
+ * @param[in] itrToKwdData - Iterator to start of keyword data.
+ * @return keyword data, empty otherwise.
+ */
+ std::string readKwData(std::string_view kwdName, std::size_t kwdDataLength,
+ types::BinaryVector::const_iterator itrToKwdData);
+
+ /**
+ * @brief API to read keyword and its value under a record.
+ *
+ * @param[in] iterator - pointer to the start of keywords under the record.
+ * @return keyword-value map of keywords under that record.
+ */
+ types::IPZVpdMap::mapped_type
+ readKeywords(types::BinaryVector::const_iterator& itrToKwds);
+
+ /**
+ * @brief API to process a record.
+ *
+ * @param[in] recordOffset - Offset of the record in VPD.
+ */
+ void processRecord(auto recordOffset);
+
+ /**
+ * @brief Get keyword's value from record
+ *
+ * @param[in] i_record - Record's name
+ * @param[in] i_keyword - Keyword's name
+ * @param[in] i_recordDataOffset - Record's offset value
+ *
+ * @throw std::runtime_error
+ *
+ * @return On success return bytes read, on failure throws error.
+ */
+ types::BinaryVector getKeywordValueFromRecord(
+ const types::Record& i_recordName, const types::Keyword& i_keywordName,
+ const types::RecordOffset& i_recordDataOffset);
+
+ /**
+ * @brief Get record's details from VTOC's PT keyword value
+ *
+ * This API parses through VTOC's PT keyword value and returns the given
+ * record's offset, record's length, ECC offset and ECC length.
+ *
+ * @param[in] i_record - Record's name.
+ * @param[in] i_vtocOffset - Offset to VTOC record
+ *
+ * @return On success return record's details, on failure return empty
+ * buffer.
+ */
+ types::RecordData
+ getRecordDetailsFromVTOC(const types::Record& l_recordName,
+ const types::RecordOffset& i_vtocOffset);
+
+ /**
+ * @brief API to update record's ECC
+ *
+ * This API is required to update the record's ECC based on the record's
+ * current data.
+ *
+ * @param[in] i_recordDataOffset - Record's data offset
+ * @param[in] i_recordDataLength - Record's data length
+ * @param[in] i_recordECCOffset - Record's ECC offset
+ * @param[in] i_recordECCLength - Record's ECC length
+ * @param[in,out] io_vpdVector - FRU VPD in vector to update record's ECC.
+ *
+ * @throw EccException
+ */
+ void updateRecordECC(const auto& i_recordDataOffset,
+ const auto& i_recordDataLength,
+ const auto& i_recordECCOffset,
+ size_t i_recordECCLength,
+ types::BinaryVector& io_vpdVector);
+
+ /**
+ * @brief API to set record's keyword's value on hardware.
+ *
+ * @param[in] i_recordName - Record name.
+ * @param[in] i_keywordName - Keyword name.
+ * @param[in] i_keywordData - Keyword data.
+ * @param[in] i_recordDataOffset - Offset to record's data.
+ * @param[in,out] io_vpdVector - FRU VPD in vector to read and write
+ * keyword's value.
+ *
+ * @throw DataException
+ *
+ * @return On success returns number of bytes set. On failure returns -1.
+ */
+ int setKeywordValueInRecord(const types::Record& i_recordName,
+ const types::Keyword& i_keywordName,
+ const types::BinaryVector& i_keywordData,
+ const types::RecordOffset& i_recordDataOffset,
+ types::BinaryVector& io_vpdVector);
+
+ // Holds VPD data.
+ const types::BinaryVector& m_vpdVector;
+
+ // stores parsed VPD data.
+ types::IPZVpdMap m_parsedVPDMap{};
+
+ // Holds the VPD file path
+ const std::string& m_vpdFilePath;
+
+ // Stream to the VPD file. Required to correct ECC
+ std::fstream m_vpdFileStream;
+
+ // VPD start offset. Required for ECC correction.
+ size_t m_vpdStartOffset = 0;
+};
+} // namespace vpd
diff --git a/vpd-manager/include/isdimm_parser.hpp b/vpd-manager/include/isdimm_parser.hpp
new file mode 100644
index 0000000..091285c
--- /dev/null
+++ b/vpd-manager/include/isdimm_parser.hpp
@@ -0,0 +1,150 @@
+#pragma once
+
+#include "parser_interface.hpp"
+#include "types.hpp"
+
+#include <string_view>
+
+namespace vpd
+{
+/**
+ * @brief Concrete class to implement JEDEC SPD parsing.
+ *
+ * The class inherits ParserInterface interface class and overrides the parser
+ * functionality to implement parsing logic for JEDEC SPD format.
+ */
+class JedecSpdParser : public ParserInterface
+{
+ public:
+ // Deleted API's
+ JedecSpdParser() = delete;
+ JedecSpdParser(const JedecSpdParser&) = delete;
+ JedecSpdParser& operator=(const JedecSpdParser&) = delete;
+ JedecSpdParser(JedecSpdParser&&) = delete;
+ JedecSpdParser& operator=(JedecSpdParser&&) = delete;
+ ~JedecSpdParser() = default;
+
+ /**
+ * @brief Constructor
+ *
+ * @param[in] i_spdVector - JEDEC SPD data.
+ */
+ explicit JedecSpdParser(const types::BinaryVector& i_spdVector) :
+ m_memSpd(i_spdVector)
+ {}
+
+ /**
+ * @brief Parse the memory SPD binary data.
+ *
+ * Collects and emplace the keyword-value pairs in map.
+ *
+ * @return map of keyword:value
+ */
+ types::VPDMapVariant parse();
+
+ private:
+ /**
+ * @brief An API to read keywords.
+ *
+ * @param[in] i_iterator - iterator to buffer containing SPD
+ * @return- map of kwd:value
+ */
+ types::JedecSpdMap
+ readKeywords(types::BinaryVector::const_iterator& i_iterator);
+
+ /**
+ * @brief This function calculates DIMM size from DDR4 SPD
+ *
+ * @param[in] i_iterator - iterator to buffer containing SPD
+ * @return calculated size or 0 in case of any error.
+ */
+ auto getDDR4DimmCapacity(types::BinaryVector::const_iterator& i_iterator);
+
+ /**
+ * @brief This function calculates part number from DDR4 SPD
+ *
+ * @param[in] i_iterator - iterator to buffer containing SPD
+ * @return calculated part number or a default value.
+ */
+ std::string_view
+ getDDR4PartNumber(types::BinaryVector::const_iterator& i_iterator);
+
+ /**
+ * @brief This function calculates serial number from DDR4 SPD
+ *
+ * @param[in] i_iterator - iterator to buffer containing SPD
+ * @return calculated serial number or a default value.
+ */
+ std::string
+ getDDR4SerialNumber(types::BinaryVector::const_iterator& i_iterator);
+
+ /**
+ * @brief This function allocates FRU number based on part number
+ *
+ * Mappings for FRU number from calculated part number is used
+ * for DDR4 ISDIMM.
+ *
+ * @param[in] i_partNumber - part number of the DIMM
+ * @param[in] i_iterator - iterator to buffer containing SPD
+ * @return allocated FRU number or a default value
+ */
+ std::string_view
+ getDDR4FruNumber(const std::string& i_partNumber,
+ types::BinaryVector::const_iterator& i_iterator);
+
+ /**
+ * @brief This function allocates CCIN based on part number for DDR4 SPD
+ *
+ * @param[in] i_partNumber - part number of the DIMM
+ * @return allocated CCIN or a default value.
+ */
+ std::string_view getDDR4CCIN(const std::string& i_partNumber);
+
+ /**
+ * @brief This function calculates DIMM size from DDR5 SPD
+ *
+ * @param[in] i_iterator - iterator to buffer containing SPD
+ * @return calculated size or 0 in case of any error.
+ */
+ auto getDDR5DimmCapacity(types::BinaryVector::const_iterator& i_iterator);
+
+ /**
+ * @brief This function calculates part number from DDR5 SPD
+ *
+ * @param[in] i_iterator - iterator to buffer containing SPD
+ * @return calculated part number or a default value.
+ */
+ auto getDDR5PartNumber(types::BinaryVector::const_iterator& i_iterator);
+
+ /**
+ * @brief This function calculates serial number from DDR5 SPD
+ *
+ * @param[in] i_iterator - iterator to buffer containing SPD
+ * @return calculated serial number.
+ */
+ auto getDDR5SerialNumber(types::BinaryVector::const_iterator& i_iterator);
+
+ /**
+ * @brief This function allocates FRU number based on part number
+ *
+ * Mappings for FRU number from calculated part number is used
+ * for DDR5 ISDIMM.
+ *
+ * @param[in] i_partNumber - part number of the DIMM
+ * @return allocated FRU number.
+ */
+ auto getDDR5FruNumber(const std::string& i_partNumber);
+
+ /**
+ * @brief This function allocates CCIN based on part number for DDR5 SPD
+ *
+ * @param[in] i_partNumber - part number of the DIMM
+ * @return allocated CCIN.
+ */
+ auto getDDR5CCIN(const std::string& i_partNumber);
+
+ // SPD file to be parsed
+ const types::BinaryVector& m_memSpd;
+};
+
+} // namespace vpd
diff --git a/vpd-manager/include/keyword_vpd_parser.hpp b/vpd-manager/include/keyword_vpd_parser.hpp
new file mode 100644
index 0000000..ae30a34
--- /dev/null
+++ b/vpd-manager/include/keyword_vpd_parser.hpp
@@ -0,0 +1,97 @@
+#pragma once
+
+#include "parser_interface.hpp"
+#include "types.hpp"
+
+namespace vpd
+{
+
+/**
+ * @brief Concrete class to implement Keyword VPD parsing
+ *
+ * The class inherits ParserInterface class and overrides the parser
+ * functionality to implement parsing logic for Keyword VPD format.
+ */
+class KeywordVpdParser : public ParserInterface
+{
+ public:
+ KeywordVpdParser() = delete;
+ KeywordVpdParser(const KeywordVpdParser&) = delete;
+ KeywordVpdParser(KeywordVpdParser&&) = delete;
+ ~KeywordVpdParser() = default;
+
+ /**
+ * @brief Constructor
+ *
+ * @param kwVpdVector - move it to object's m_keywordVpdVector
+ */
+ KeywordVpdParser(const types::BinaryVector& kwVpdVector) :
+ m_keywordVpdVector(kwVpdVector),
+ m_vpdIterator(m_keywordVpdVector.begin())
+ {}
+
+ /**
+ * @brief A wrapper function to parse the keyword VPD binary data.
+ *
+ * It validates certain tags and checksum data, calls helper function
+ * to parse and emplace the data as keyword-value pairs in KeywordVpdMap.
+ *
+ * @throw DataException - VPD is not valid
+ * @return map of keyword:value
+ */
+ types::VPDMapVariant parse();
+
+ private:
+ /**
+ * @brief Parse the VPD data and emplace them as pair into the Map.
+ *
+ * @throw DataException - VPD data size is 0, check VPD
+ * @return map of keyword:value
+ */
+ types::KeywordVpdMap populateVpdMap();
+
+ /**
+ * @brief Validate checksum.
+ *
+ * Finding the 2's complement of sum of all the
+ * keywords,values and large resource identifier string.
+ *
+ * @param[in] i_checkSumStart - VPD iterator pointing at checksum start
+ * value
+ * @param[in] i_checkSumEnd - VPD iterator pointing at checksum end value
+ * @throw DataException - checksum invalid, check VPD
+ */
+ void validateChecksum(types::BinaryVector::const_iterator i_checkSumStart,
+ types::BinaryVector::const_iterator i_checkSumEnd);
+
+ /**
+ * @brief It reads 2 bytes from current VPD pointer
+ *
+ * @return 2 bytes of VPD data
+ */
+ inline size_t getKwDataSize()
+ {
+ return (*(m_vpdIterator + 1) << 8 | *m_vpdIterator);
+ }
+
+ /**
+ * @brief Check for given number of bytes validity
+ *
+ * Check if number of elements from (begining of the vector) to (iterator +
+ * numberOfBytes) is lesser than or equal to the total no.of elements in
+ * the vector. This check is performed before advancement of the iterator.
+ *
+ * @param[in] numberOfBytes - no.of positions the iterator is going to be
+ * iterated
+ *
+ * @throw DataException - Truncated VPD data, check VPD.
+ */
+ void checkNextBytesValidity(uint8_t numberOfBytes);
+
+ /*Vector of keyword VPD data*/
+ const types::BinaryVector& m_keywordVpdVector;
+
+ /*Iterator to VPD data*/
+ types::BinaryVector::const_iterator m_vpdIterator;
+};
+} // namespace vpd
diff --git a/vpd-manager/include/logger.hpp b/vpd-manager/include/logger.hpp
new file mode 100644
index 0000000..a4160c1
--- /dev/null
+++ b/vpd-manager/include/logger.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <iostream>
+#include <source_location>
+#include <string_view>
+
+namespace vpd
+{
+/**
+ * @brief The namespace defines logging related methods for VPD.
+ */
+namespace logging
+{
+
+/**
+ * @brief An api to log message.
+ * This API should be called to log message. It will auto append information
+ * like file name, line and function name to the message being logged.
+ *
+ * @param[in] message - Information that we want to log.
+ * @param[in] location - Object of source_location class.
+ */
+void logMessage(std::string_view message, const std::source_location& location =
+ std::source_location::current());
+} // namespace logging
+} // namespace vpd
diff --git a/vpd-manager/include/manager.hpp b/vpd-manager/include/manager.hpp
new file mode 100644
index 0000000..cb64123
--- /dev/null
+++ b/vpd-manager/include/manager.hpp
@@ -0,0 +1,300 @@
+#pragma once
+
+#include "constants.hpp"
+#include "gpio_monitor.hpp"
+#include "types.hpp"
+#include "worker.hpp"
+
+#include <sdbusplus/asio/object_server.hpp>
+
+namespace vpd
+{
+/**
+ * @brief Class to manage VPD processing.
+ *
+ * The class is responsible to implement methods to manage VPD on the system.
+ * It also implements methods to be exposed over D-Bus required to access/edit
+ * VPD data.
+ */
+class Manager
+{
+ public:
+ /**
+ * List of deleted methods.
+ */
+ Manager(const Manager&) = delete;
+ Manager& operator=(const Manager&) = delete;
+ Manager(Manager&&) = delete;
+
+ /**
+ * @brief Constructor.
+ *
+ * @param[in] ioCon - IO context.
+ * @param[in] iFace - interface to implement.
+ * @param[in] connection - Dbus Connection.
+ */
+ Manager(const std::shared_ptr<boost::asio::io_context>& ioCon,
+ const std::shared_ptr<sdbusplus::asio::dbus_interface>& iFace,
+ const std::shared_ptr<sdbusplus::asio::connection>& asioConnection);
+
+ /**
+ * @brief Destructor.
+ */
+ ~Manager() = default;
+
+ /**
+ * @brief Update keyword value.
+ *
+ * This API is used to update keyword value on the given input path and its
+ * redundant path(s) if any taken from system config JSON.
+ *
+ * To update IPZ type VPD, input parameter for writing should be in the form
+ * of (Record, Keyword, Value). Eg: ("VINI", "SN", {0x01, 0x02, 0x03}).
+ *
+ * To update Keyword type VPD, input parameter for writing should be in the
+ * form of (Keyword, Value). Eg: ("PE", {0x01, 0x02, 0x03}).
+ *
+ * @param[in] i_vpdPath - Path (inventory object path/FRU EEPROM path).
+ * @param[in] i_paramsToWriteData - Input details.
+ *
+ * @return On success returns number of bytes written, on failure returns
+ * -1.
+ */
+ int updateKeyword(const types::Path i_vpdPath,
+ const types::WriteVpdParams i_paramsToWriteData);
+
+ /**
+ * @brief Update keyword value on hardware.
+ *
+ * This API is used to update keyword value on hardware. Updates only on the
+ * given input hardware path, does not look for corresponding redundant or
+ * primary path against the given path. To update corresponding paths, make
+ * separate call with respective path.
+ *
+ * To update IPZ type VPD, input parameter for writing should be in the form
+ * of (Record, Keyword, Value). Eg: ("VINI", "SN", {0x01, 0x02, 0x03}).
+ *
+ * To update Keyword type VPD, input parameter for writing should be in the
+ * form of (Keyword, Value). Eg: ("PE", {0x01, 0x02, 0x03}).
+ *
+ * @param[in] i_fruPath - EEPROM path of the FRU.
+ * @param[in] i_paramsToWriteData - Input details.
+ *
+ * @return On success returns number of bytes written, on failure returns
+ * -1.
+ */
+ int updateKeywordOnHardware(
+ const types::Path i_fruPath,
+ const types::WriteVpdParams i_paramsToWriteData) noexcept;
+
+ /**
+ * @brief Read keyword value.
+ *
+ * API can be used to read VPD keyword from the given input path.
+ *
+ * To read keyword of type IPZ, input parameter for reading should be in the
+ * form of (Record, Keyword). Eg: ("VINI", "SN").
+ *
+ * To read keyword from keyword type VPD, just keyword name has to be
+ * supplied in the input parameter. Eg: ("SN").
+ *
+ * @param[in] i_fruPath - EEPROM path.
+ * @param[in] i_paramsToReadData - Input details.
+ *
+ * @throw
+ * sdbusplus::xyz::openbmc_project::Common::Device::Error::ReadFailure.
+ *
+ * @return On success returns the read value in variant of array of bytes.
+ * On failure throws exception.
+ */
+ types::DbusVariantType
+ readKeyword(const types::Path i_fruPath,
+ const types::ReadVpdParams i_paramsToReadData);
+
+ /**
+ * @brief Collect single FRU VPD
+ * API can be used to perform VPD collection for the given FRU, only if the
+ * current state of the system matches with the state at which the FRU is
+ * allowed for VPD recollection.
+ *
+ * @param[in] i_dbusObjPath - D-bus object path
+ */
+ void collectSingleFruVpd(
+ const sdbusplus::message::object_path& i_dbusObjPath);
+
+ /**
+ * @brief Delete single FRU VPD
+ * API can be used to perform VPD deletion for the given FRU.
+ *
+ * @param[in] i_dbusObjPath - D-bus object path
+ */
+ void deleteSingleFruVpd(
+ const sdbusplus::message::object_path& i_dbusObjPath);
+
+ /**
+ * @brief Get expanded location code.
+ *
+ * API to get expanded location code from the unexpanded location code.
+ *
+ * @param[in] i_unexpandedLocationCode - Unexpanded location code.
+ * @param[in] i_nodeNumber - Denotes the node in case of a multi-node
+ * configuration, defaulted to zero incase of single node system.
+ *
+ * @throw xyz.openbmc_project.Common.Error.InvalidArgument for
+ * invalid argument.
+ *
+ * @return Location code of the FRU.
+ */
+ std::string getExpandedLocationCode(
+ const std::string& i_unexpandedLocationCode,
+ [[maybe_unused]] const uint16_t i_nodeNumber = 0);
+
+ /**
+ * @brief Get D-Bus object path of FRUs from expanded location code.
+ *
+ * An API to get list of FRU D-Bus object paths for a given expanded
+ * location code.
+ *
+ * @param[in] i_expandedLocationCode - Expanded location code.
+ *
+ * @throw xyz.openbmc_project.Common.Error.InvalidArgument for
+ * invalid argument.
+ *
+ * @return List of FRUs D-Bus object paths for the given location code.
+ */
+ types::ListOfPaths getFrusByExpandedLocationCode(
+ const std::string& i_expandedLocationCode);
+
+ /**
+ * @brief Get D-Bus object path of FRUs from unexpanded location code.
+ *
+ * An API to get list of FRU D-Bus object paths for a given unexpanded
+ * location code.
+ *
+ * @param[in] i_unexpandedLocationCode - Unexpanded location code.
+ * @param[in] i_nodeNumber - Denotes the node in case of a multi-node
+ * configuration, defaulted to zero incase of single node system.
+ *
+ * @throw xyz.openbmc_project.Common.Error.InvalidArgument for
+ * invalid argument.
+ *
+ * @return List of FRUs D-Bus object paths for the given location code.
+ */
+ types::ListOfPaths getFrusByUnexpandedLocationCode(
+ const std::string& i_unexpandedLocationCode,
+ [[maybe_unused]] const uint16_t i_nodeNumber = 0);
+
+ /**
+ * @brief Get Hardware path
+ * API can be used to get EEPROM path for the given inventory path.
+ *
+ * @param[in] i_dbusObjPath - D-bus object path
+ *
+ * @return Corresponding EEPROM path.
+ */
+ std::string getHwPath(const sdbusplus::message::object_path& i_dbusObjPath);
+
+ /**
+ * @brief Perform VPD recollection
+ * This api will trigger parser to perform VPD recollection for FRUs that
+ * can be replaced at standby.
+ */
+ void performVpdRecollection();
+
+ /**
+ * @brief Get unexpanded location code.
+ *
+ * An API to get unexpanded location code and node number from expanded
+ * location code.
+ *
+ * @param[in] i_expandedLocationCode - Expanded location code.
+ *
+ * @throw xyz.openbmc_project.Common.Error.InvalidArgument for
+ * invalid argument.
+ *
+ * @return Location code in unexpanded format and its node number.
+ */
+ std::tuple<std::string, uint16_t>
+ getUnexpandedLocationCode(const std::string& i_expandedLocationCode);
+
+ private:
+#ifdef IBM_SYSTEM
+ /**
+ * @brief API to set timer to detect system VPD over D-Bus.
+ *
+ * System VPD is required before bus name for VPD-Manager is claimed. Once
+ * system VPD is published, VPD for other FRUs should be collected. This API
+ * detects id system VPD is already published on D-Bus and based on that
+ * triggers VPD collection for rest of the FRUs.
+ *
+ * Note: Throws exception in case of any failure. Needs to be handled by the
+ * caller.
+ */
+ void SetTimerToDetectSVPDOnDbus();
+
+ /**
+ * @brief Set timer to detect and set VPD collection status for the system.
+ *
+ * Collection of FRU VPD is triggered in a separate thread. Resulting in
+ * multiple threads at a given time. The API creates a timer which on
+ * regular interval will check if all the threads were collected back and
+ * sets the status of the VPD collection for the system accordingly.
+ *
+ * @throw std::runtime_error
+ */
+ void SetTimerToDetectVpdCollectionStatus();
+
+ /**
+ * @brief API to register callback for "AssetTag" property change.
+ */
+ void registerAssetTagChangeCallback();
+
+ /**
+ * @brief Callback API to be triggered on "AssetTag" property change.
+ *
+ * @param[in] i_msg - The callback message.
+ */
+ void processAssetTagChangeCallback(sdbusplus::message_t& i_msg);
+#endif
+
+ /**
+ * @brief An api to check validity of unexpanded location code.
+ *
+ * @param[in] i_locationCode - Unexpanded location code.
+ *
+ * @return True/False based on validity check.
+ */
+ bool isValidUnexpandedLocationCode(const std::string& i_locationCode);
+
+ /**
+ * @brief API to register callback for Host state change.
+ */
+ void registerHostStateChangeCallback();
+
+ /**
+ * @brief API to process host state change callback.
+ *
+ * @param[in] i_msg - Callback message.
+ */
+ void hostStateChangeCallBack(sdbusplus::message_t& i_msg);
+
+ // Shared pointer to asio context object.
+ const std::shared_ptr<boost::asio::io_context>& m_ioContext;
+
+ // Shared pointer to Dbus interface class.
+ const std::shared_ptr<sdbusplus::asio::dbus_interface>& m_interface;
+
+ // Shared pointer to bus connection.
+ const std::shared_ptr<sdbusplus::asio::connection>& m_asioConnection;
+
+ // Shared pointer to worker class
+ std::shared_ptr<Worker> m_worker;
+
+ // Shared pointer to GpioMonitor class
+ std::shared_ptr<GpioMonitor> m_gpioMonitor;
+
+ // Variable to hold current collection status
+ std::string m_vpdCollectionStatus = "NotStarted";
+};
+
+} // namespace vpd
diff --git a/vpd-manager/include/parser.hpp b/vpd-manager/include/parser.hpp
new file mode 100644
index 0000000..9af3088
--- /dev/null
+++ b/vpd-manager/include/parser.hpp
@@ -0,0 +1,126 @@
+#pragma once
+
+#include "parser_factory.hpp"
+#include "parser_interface.hpp"
+#include "types.hpp"
+
+#include <string.h>
+
+#include <nlohmann/json.hpp>
+
+#include <iostream>
+
+namespace vpd
+{
+/**
+ * @brief Class to implement a wrapper around concrete parser class.
+ * The class based on VPD file passed, selects the required parser and exposes
+ * API to parse the VPD and return the parsed data in required format to the
+ * caller.
+ */
+class Parser
+{
+ public:
+ /**
+ * @brief Constructor
+ *
+ * @param[in] vpdFilePath - Path to the VPD file.
+ * @param[in] parsedJson - Parsed JSON.
+ */
+ Parser(const std::string& vpdFilePath, nlohmann::json parsedJson);
+
+ /**
+ * @brief API to implement a generic parsing logic.
+ *
+ * This API is called to select parser based on the vpd data extracted from
+ * the VPD file path passed to the constructor of the class.
+ * It further parses the data based on the parser selected and returned
+ * parsed map to the caller.
+ */
+ types::VPDMapVariant parse();
+
+ /**
+ * @brief API to get parser instance based on VPD type.
+ *
+ * This API detects the VPD type based on the file path passed to the
+ * constructor of the class and returns the respective parser instance.
+ *
+ * @return Parser instance.
+ */
+ std::shared_ptr<vpd::ParserInterface> getVpdParserInstance();
+
+ /**
+ * @brief Update keyword value.
+ *
+ * This API is used to update keyword value on the EEPROM path and its
+ * redundant path(s) if any taken from system config JSON. And also updates
+ * keyword value on DBus.
+ *
+ * To update IPZ type VPD, input parameter for writing should be in the form
+ * of (Record, Keyword, Value). Eg: ("VINI", "SN", {0x01, 0x02, 0x03}).
+ *
+ * To update Keyword type VPD, input parameter for writing should be in the
+ * form of (Keyword, Value). Eg: ("PE", {0x01, 0x02, 0x03}).
+ *
+ * @param[in] i_paramsToWriteData - Input details.
+ *
+ * @return On success returns number of bytes written, on failure returns
+ * -1.
+ */
+ int updateVpdKeyword(const types::WriteVpdParams& i_paramsToWriteData);
+
+ /**
+ * @brief Update keyword value on hardware.
+ *
+ * This API is used to update keyword value on the hardware path.
+ *
+ * To update IPZ type VPD, input parameter for writing should be in the form
+ * of (Record, Keyword, Value). Eg: ("VINI", "SN", {0x01, 0x02, 0x03}).
+ *
+ * To update Keyword type VPD, input parameter for writing should be in the
+ * form of (Keyword, Value). Eg: ("PE", {0x01, 0x02, 0x03}).
+ *
+ * @param[in] i_paramsToWriteData - Input details.
+ *
+ * @return On success returns number of bytes written, on failure returns
+ * -1.
+ */
+ int updateVpdKeywordOnHardware(
+ const types::WriteVpdParams& i_paramsToWriteData);
+
+ private:
+ /**
+ * @brief Update keyword value on redundant path.
+ *
+ * This API is used to update keyword value on the given redundant path(s).
+ *
+ * To update IPZ type VPD, input parameter for writing should be in the form
+ * of (Record, Keyword, Value). Eg: ("VINI", "SN", {0x01, 0x02, 0x03}).
+ *
+ * To update Keyword type VPD, input parameter for writing should be in the
+ * form of (Keyword, Value). Eg: ("PE", {0x01, 0x02, 0x03}).
+ *
+ * @param[in] i_fruPath - Redundant EEPROM path.
+ * @param[in] i_paramsToWriteData - Input details.
+ *
+ * @return On success returns number of bytes written, on failure returns
+ * -1.
+ */
+ int updateVpdKeywordOnRedundantPath(
+ const std::string& i_fruPath,
+ const types::WriteVpdParams& i_paramsToWriteData);
+
+ // holds offfset to VPD if applicable.
+ size_t m_vpdStartOffset = 0;
+
+ // Path to the VPD file
+ const std::string& m_vpdFilePath;
+
+ // Path to configuration file, can be empty.
+ nlohmann::json m_parsedJson;
+
+ // Vector to hold VPD.
+ types::BinaryVector m_vpdVector;
+
+}; // parser
+} // namespace vpd
diff --git a/vpd-manager/include/parser_factory.hpp b/vpd-manager/include/parser_factory.hpp
new file mode 100644
index 0000000..819ec59
--- /dev/null
+++ b/vpd-manager/include/parser_factory.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include "logger.hpp"
+#include "parser_interface.hpp"
+#include "types.hpp"
+
+#include <memory>
+
+namespace vpd
+{
+/**
+ * @brief Factory calss to instantiate concrete parser class.
+ *
+ * This class should be used to instantiate an instance of parser class based
+ * on the type of vpd file.
+ */
+class ParserFactory
+{
+ public:
+ // Deleted APIs
+ ParserFactory() = delete;
+ ~ParserFactory() = delete;
+ ParserFactory(const ParserFactory&) = delete;
+ ParserFactory& operator=(const ParserFactory&) = delete;
+ ParserFactory(ParserFactory&&) = delete;
+ ParserFactory& operator=(ParserFactory&&) = delete;
+
+ /**
+ * @brief An API to get object of concrete parser class.
+ *
+ * The method is used to get object of respective parser class based on the
+ * type of VPD extracted from VPD vector passed to the API.
+ * To detect respective parser from VPD vector, add logic into the API
+ * vpdTypeCheck.
+ *
+ * Note: API throws DataException in case vpd type check fails for any
+ * unknown type. Caller responsibility to handle the exception.
+ *
+ * @param[in] i_vpdVector - vpd file content to check for the type.
+ * @param[in] i_vpdFilePath - FRU EEPROM path.
+ * @param[in] i_vpdStartOffset - Offset from where VPD starts in the VPD
+ * file.
+ *
+ * @return - Pointer to concrete parser class object.
+ */
+ static std::shared_ptr<ParserInterface>
+ getParser(const types::BinaryVector& i_vpdVector,
+ const std::string& i_vpdFilePath, size_t i_vpdStartOffset);
+};
+} // namespace vpd
diff --git a/vpd-manager/include/parser_interface.hpp b/vpd-manager/include/parser_interface.hpp
new file mode 100644
index 0000000..0ff2862
--- /dev/null
+++ b/vpd-manager/include/parser_interface.hpp
@@ -0,0 +1,69 @@
+#pragma once
+
+#include "types.hpp"
+
+#include <variant>
+
+namespace vpd
+{
+/**
+ * @brief Interface class for parsers.
+ *
+ * Any concrete parser class, implementing parsing logic need to derive from
+ * this interface class and override the parse method declared in this class.
+ */
+class ParserInterface
+{
+ public:
+ /**
+ * @brief Pure virtual function for parsing VPD file.
+ *
+ * The API needs to be overridden by all the classes deriving from parser
+ * inerface class to implement respective VPD parsing logic.
+ *
+ * @return parsed format for VPD data, depending upon the
+ * parsing logic.
+ */
+ virtual types::VPDMapVariant parse() = 0;
+
+ /**
+ * @brief Read keyword's value from hardware
+ *
+ * @param[in] types::ReadVpdParams - Parameters required to perform read.
+ *
+ * @return keyword's value on successful read. Exception on failure.
+ */
+ virtual types::DbusVariantType
+ readKeywordFromHardware(const types::ReadVpdParams)
+ {
+ return types::DbusVariantType();
+ }
+
+ /**
+ * @brief API to write keyword's value on hardware.
+ *
+ * This virtual method is created to achieve runtime polymorphism for
+ * hardware writes on VPD of different types. This virtual method can be
+ * redefined at derived classess to implement write according to the type of
+ * VPD.
+ *
+ * @param[in] i_paramsToWriteData - Data required to perform write.
+ *
+ * @throw May throw exception depending on the implementation of derived
+ * methods.
+ * @return On success returns number of bytes written on hardware, On
+ * failure returns -1.
+ */
+ virtual int
+ writeKeywordOnHardware(const types::WriteVpdParams i_paramsToWriteData)
+ {
+ (void)i_paramsToWriteData;
+ return -1;
+ }
+
+ /**
+ * @brief Virtual destructor.
+ */
+ virtual ~ParserInterface() {}
+};
+} // namespace vpd
diff --git a/vpd-manager/include/types.hpp b/vpd-manager/include/types.hpp
new file mode 100644
index 0000000..314aebd
--- /dev/null
+++ b/vpd-manager/include/types.hpp
@@ -0,0 +1,192 @@
+#pragma once
+
+#include <phosphor-logging/elog-errors.hpp>
+#include <sdbusplus/asio/property.hpp>
+#include <sdbusplus/server.hpp>
+#include <xyz/openbmc_project/Common/Device/error.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+#include <tuple>
+#include <unordered_map>
+#include <variant>
+
+namespace vpd
+{
+namespace types
+{
+
+using BiosProperty = std::tuple<
+ std::string, bool, std::string, std::string, std::string,
+ std::variant<int64_t, std::string>, std::variant<int64_t, std::string>,
+ std::vector<std::tuple<std::string, std::variant<int64_t, std::string>,
+ std::string>>>;
+using BiosBaseTable =
+ std::variant<std::monostate, std::map<std::string, BiosProperty>>;
+using BiosBaseTableType = std::map<std::string, BiosBaseTable>;
+using BiosAttributeCurrentValue =
+ std::variant<std::monostate, int64_t, std::string>;
+using BiosAttributePendingValue = std::variant<int64_t, std::string>;
+using BiosGetAttrRetType = std::tuple<std::string, BiosAttributeCurrentValue,
+ BiosAttributePendingValue>;
+using PendingBIOSAttrItem =
+ std::pair<std::string, std::tuple<std::string, BiosAttributePendingValue>>;
+using PendingBIOSAttrs = std::vector<PendingBIOSAttrItem>;
+
+using BinaryVector = std::vector<uint8_t>;
+
+// This covers mostly all the data type supported over Dbus for a property.
+// clang-format off
+using DbusVariantType = std::variant<
+ std::vector<std::tuple<std::string, std::string, std::string>>,
+ std::vector<std::string>,
+ std::vector<double>,
+ std::string,
+ int64_t,
+ uint64_t,
+ double,
+ int32_t,
+ uint32_t,
+ int16_t,
+ uint16_t,
+ uint8_t,
+ bool,
+ BinaryVector,
+ std::vector<uint32_t>,
+ std::vector<uint16_t>,
+ sdbusplus::message::object_path,
+ std::tuple<uint64_t, std::vector<std::tuple<std::string, std::string, double, uint64_t>>>,
+ std::vector<std::tuple<std::string, std::string>>,
+ std::vector<std::tuple<uint32_t, std::vector<uint32_t>>>,
+ std::vector<std::tuple<uint32_t, size_t>>,
+ std::vector<std::tuple<sdbusplus::message::object_path, std::string,
+ std::string, std::string>>,
+ PendingBIOSAttrs
+ >;
+
+using MapperGetObject =
+ std::vector<std::pair<std::string, std::vector<std::string>>>;
+using MapperGetSubTree = std::map<std::string, std::map<std::string, std::vector<std::string>>>;
+
+/* A type for holding the innermost map of property::value.*/
+using IPZKwdValueMap = std::unordered_map<std::string, std::string>;
+/*IPZ VPD Map of format <Record name, <keyword, value>>*/
+using IPZVpdMap = std::unordered_map<std::string, IPZKwdValueMap>;
+
+/*Value types supported by Keyword VPD*/
+using KWdVPDValueType = std::variant<BinaryVector,std::string, size_t>;
+/* This hold map of parsed data of keyword VPD type*/
+using KeywordVpdMap = std::unordered_map<std::string, KWdVPDValueType>;
+
+/**
+ * Both Keyword VPD parser and DDIMM parser stores the
+ * parsed VPD in the same format.
+ * To have better readability, two types are defined for underneath data structure.
+*/
+using DdimmVpdMap = KeywordVpdMap;
+
+/**
+ * Both Keyword VPD parser and ISDIMM parser stores the
+ * parsed SPD in the same format.
+*/
+using JedecSpdMap = KeywordVpdMap;
+
+/**
+ * Type to hold keyword::value map of a VPD.
+ * Variant can be extended to support additional type.
+*/
+using VPDKWdValueMap = std::variant<IPZKwdValueMap, KeywordVpdMap>;
+
+/* Map<Property, Value>*/
+using PropertyMap = std::map<std::string, DbusVariantType>;
+/* Map<Interface<Property, Value>>*/
+using InterfaceMap = std::map<std::string, PropertyMap>;
+using ObjectMap = std::map<sdbusplus::message::object_path, InterfaceMap>;
+
+using KwSize = uint8_t;
+using RecordId = uint8_t;
+using RecordSize = uint16_t;
+using RecordType = uint16_t;
+using RecordOffset = uint16_t;
+using RecordLength = uint16_t;
+using ECCOffset = uint16_t;
+using ECCLength = uint16_t;
+using PoundKwSize = uint16_t;
+
+using RecordOffsetList = std::vector<uint32_t>;
+
+using VPDMapVariant = std::variant<std::monostate, IPZVpdMap, KeywordVpdMap>;
+
+using HWVerList = std::vector<std::pair<std::string, std::string>>;
+/**
+ * Map of <systemIM, pair<Default version, vector<HW version, JSON suffix>>>
+*/
+using SystemTypeMap =
+ std::unordered_map<std::string, std::pair<std::string, HWVerList>>;
+
+using Path = std::string;
+using Record = std::string;
+using Keyword = std::string;
+
+using IpzData = std::tuple<Record, Keyword, BinaryVector>;
+using KwData = std::tuple<Keyword, BinaryVector>;
+using VpdData = std::variant<IpzData, KwData>;
+
+using IpzType = std::tuple<Record, Keyword>;
+using ReadVpdParams = std::variant<IpzType, Keyword>;
+using WriteVpdParams = std::variant<IpzData, KwData>;
+
+using ListOfPaths = std::vector<sdbusplus::message::object_path>;
+using RecordData = std::tuple<RecordOffset, RecordLength, ECCOffset, ECCLength>;
+
+using DbusInvalidArgument =
+ sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
+using DbusNotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
+
+using InvalidArgument = phosphor::logging::xyz::openbmc_project::Common::InvalidArgument;
+
+namespace DeviceError = sdbusplus::xyz::openbmc_project::Common::Device::Error;
+
+/* PEL Severity enum as defined in [xyz.openbmc_project.Logging.Entry.Level]log.hpp from 'phosphor-logging' repo. */
+enum SeverityType
+{
+ Notice,
+ Informational,
+ Debug,
+ Warning,
+ Critical,
+ Emergency,
+ Alert,
+ Error
+};
+
+/* PEL callout priority from 'phosphor-logging' pel_types.hpp. If any change in 'phosphor-logging', it needs update here as well. */
+enum CalloutPriority
+{
+ High,
+ Medium,
+ MediumGroupA,
+ MediumGroupB,
+ MediumGroupC,
+ Low
+};
+
+/* The Message property of the event entry for creating PEL, to introduce new message needs to be added in 'phosphor-logging' message_registry.json as well. */
+enum ErrorType
+{
+ DefaultValue,
+ InvalidVpdMessage,
+ VpdMismatch,
+ InvalidEeprom,
+ EccCheckFailed,
+ JsonFailure,
+ DbusFailure,
+ InvalidSystem,
+ EssentialFru,
+ GpioError
+};
+
+using InventoryCalloutData = std::tuple<std::string, CalloutPriority>;
+using DeviceCalloutData = std::tuple<std::string, std::string>;
+using I2cBusCalloutData = std::tuple<std::string, std::string, std::string>;
+} // namespace types
+} // namespace vpd
diff --git a/vpd-manager/include/utility/common_utility.hpp b/vpd-manager/include/utility/common_utility.hpp
new file mode 100644
index 0000000..15b33a2
--- /dev/null
+++ b/vpd-manager/include/utility/common_utility.hpp
@@ -0,0 +1,117 @@
+#pragma once
+
+#include "constants.hpp"
+#include "logger.hpp"
+
+#include <algorithm>
+#include <cstdio>
+#include <cstdlib>
+#include <vector>
+
+/**
+ * @brief Namespace to host common utility methods.
+ *
+ * A method qualifies as a common utility function if,
+ * A)It is being used by the utility namespace at the same level as well as
+ * other files directly.
+ * B) The utility should be a leaf node and should not be dependent on any other
+ * utility.
+ * *******************
+ * | Commmon Utility | - - - - - - -
+ * ******************* |
+ * /\ |
+ * / \ |
+ * **************** **************** |
+ * | json utility | | dbus utility | |
+ * **************** **************** |
+ * \ / |
+ * \ / |
+ * ************************ |
+ * | Vpd specific Utility | - - - - - - -
+ * ************************
+ */
+
+namespace vpd
+{
+
+namespace commonUtility
+{
+/** @brief Return the hex representation of the incoming byte.
+ *
+ * @param [in] i_aByte - The input byte.
+ * @returns Hex representation of the byte as a character.
+ */
+constexpr auto toHex(size_t i_aByte)
+{
+ constexpr auto l_map = "0123456789abcdef";
+ return l_map[i_aByte];
+}
+
+/**
+ * @brief API to return null at the end of variadic template args.
+ *
+ * @return empty string.
+ */
+inline std::string getCommand()
+{
+ return "";
+}
+
+/**
+ * @brief API to arrange create command.
+ *
+ * @param[in] arguments to create the command
+ * @return Command string
+ */
+template <typename T, typename... Types>
+inline std::string getCommand(T i_arg1, Types... i_args)
+{
+ std::string l_cmd = " " + i_arg1 + getCommand(i_args...);
+
+ return l_cmd;
+}
+
+/**
+ * @brief API to create shell command and execute.
+ *
+ * @throw std::runtime_error.
+ *
+ * @param[in] arguments for command
+ * @returns output of that command
+ */
+template <typename T, typename... Types>
+inline std::vector<std::string> executeCmd(T&& i_path, Types... i_args)
+{
+ std::vector<std::string> l_cmdOutput;
+ std::array<char, constants::CMD_BUFFER_LENGTH> l_buffer;
+
+ std::string l_cmd = i_path + getCommand(i_args...);
+
+ std::unique_ptr<FILE, decltype(&pclose)> l_cmdPipe(
+ popen(l_cmd.c_str(), "r"), pclose);
+
+ if (!l_cmdPipe)
+ {
+ logging::logMessage(
+ "popen failed with error " + std::string(strerror(errno)));
+ throw std::runtime_error("popen failed!");
+ }
+ while (fgets(l_buffer.data(), l_buffer.size(), l_cmdPipe.get()) != nullptr)
+ {
+ l_cmdOutput.emplace_back(l_buffer.data());
+ }
+
+ return l_cmdOutput;
+}
+
+/** @brief Converts string to lower case.
+ *
+ * @param [in] i_string - Input string.
+ */
+inline void toLower(std::string& i_string)
+{
+ std::transform(i_string.begin(), i_string.end(), i_string.begin(),
+ [](unsigned char l_char) { return std::tolower(l_char); });
+}
+} // namespace commonUtility
+} // namespace vpd
diff --git a/vpd-manager/include/utility/dbus_utility.hpp b/vpd-manager/include/utility/dbus_utility.hpp
new file mode 100644
index 0000000..4c81815
--- /dev/null
+++ b/vpd-manager/include/utility/dbus_utility.hpp
@@ -0,0 +1,567 @@
+#pragma once
+
+#include "constants.hpp"
+#include "logger.hpp"
+#include "types.hpp"
+
+#include <chrono>
+
+namespace vpd
+{
+/**
+ * @brief The namespace defines utlity methods for generic D-Bus operations.
+ */
+namespace dbusUtility
+{
+
+/**
+ * @brief An API to get Map of service and interfaces for an object path.
+ *
+ * The API returns a Map of service name and interfaces for a given pair of
+ * object path and interface list. It can be used to determine service name
+ * which implemets a particular object path and interface.
+ *
+ * Note: It will be caller's responsibility to check for empty map returned and
+ * generate appropriate error.
+ *
+ * @param [in] objectPath - Object path under the service.
+ * @param [in] interfaces - Array of interface(s).
+ * @return - A Map of service name to object to interface(s), if success.
+ * If failed, empty map.
+ */
+inline types::MapperGetObject getObjectMap(const std::string& objectPath,
+ std::span<const char*> interfaces)
+{
+ types::MapperGetObject getObjectMap;
+
+ // interface list is optional argument, hence no check required.
+ if (objectPath.empty())
+ {
+ logging::logMessage("Path value is empty, invalid call to GetObject");
+ return getObjectMap;
+ }
+
+ try
+ {
+ auto bus = sdbusplus::bus::new_default();
+ auto method = bus.new_method_call(
+ "xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetObject");
+
+ method.append(objectPath, interfaces);
+ auto result = bus.call(method);
+ result.read(getObjectMap);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ // logging::logMessage(e.what());
+ return getObjectMap;
+ }
+
+ return getObjectMap;
+}
+
+/**
+ * @brief An API to get property map for an interface.
+ *
+ * This API returns a map of property and its value with respect to a particular
+ * interface.
+ *
+ * Note: It will be caller's responsibility to check for empty map returned and
+ * generate appropriate error.
+ *
+ * @param[in] i_service - Service name.
+ * @param[in] i_objectPath - object path.
+ * @param[in] i_interface - Interface, for the properties to be listed.
+ *
+ * @return - A map of property and value of an interface, if success.
+ * if failed, empty map.
+ */
+inline types::PropertyMap getPropertyMap(const std::string& i_service,
+ const std::string& i_objectPath,
+ const std::string& i_interface)
+{
+ types::PropertyMap l_propertyValueMap;
+ if (i_service.empty() || i_objectPath.empty() || i_interface.empty())
+ {
+ logging::logMessage("Invalid parameters to get property map");
+ return l_propertyValueMap;
+ }
+
+ try
+ {
+ auto l_bus = sdbusplus::bus::new_default();
+ auto l_method =
+ l_bus.new_method_call(i_service.c_str(), i_objectPath.c_str(),
+ "org.freedesktop.DBus.Properties", "GetAll");
+ l_method.append(i_interface);
+ auto l_result = l_bus.call(l_method);
+ l_result.read(l_propertyValueMap);
+ }
+ catch (const sdbusplus::exception::SdBusError& l_ex)
+ {
+ logging::logMessage(l_ex.what());
+ }
+
+ return l_propertyValueMap;
+}
+
+/**
+ * @brief API to get object subtree from D-bus.
+ *
+ * The API returns the map of object, services and interfaces in the
+ * subtree that implement a certain interface. If no interfaces are provided
+ * then all the objects, services and interfaces under the subtree will
+ * be returned.
+ *
+ * Note: Depth can be 0 and interfaces can be null.
+ * It will be caller's responsibility to check for empty vector returned
+ * and generate appropriate error.
+ *
+ * @param[in] i_objectPath - Path to search for an interface.
+ * @param[in] i_depth - Maximum depth of the tree to search.
+ * @param[in] i_interfaces - List of interfaces to search.
+ *
+ * @return - A map of object and its related services and interfaces, if
+ * success. If failed, empty map.
+ */
+
+inline types::MapperGetSubTree
+ getObjectSubTree(const std::string& i_objectPath, const int& i_depth,
+ const std::vector<std::string>& i_interfaces)
+{
+ types::MapperGetSubTree l_subTreeMap;
+
+ if (i_objectPath.empty())
+ {
+ logging::logMessage("Object path is empty.");
+ return l_subTreeMap;
+ }
+
+ try
+ {
+ auto l_bus = sdbusplus::bus::new_default();
+ auto l_method = l_bus.new_method_call(
+ constants::objectMapperService, constants::objectMapperPath,
+ constants::objectMapperInf, "GetSubTree");
+ l_method.append(i_objectPath, i_depth, i_interfaces);
+ auto l_result = l_bus.call(l_method);
+ l_result.read(l_subTreeMap);
+ }
+ catch (const sdbusplus::exception::SdBusError& l_ex)
+ {
+ logging::logMessage(l_ex.what());
+ }
+
+ return l_subTreeMap;
+}
+
+/**
+ * @brief An API to read property from Dbus.
+ *
+ * The caller of the API needs to validate the validatity and correctness of the
+ * type and value of data returned. The API will just fetch and retun the data
+ * without any data validation.
+ *
+ * Note: It will be caller's responsibility to check for empty value returned
+ * and generate appropriate error if required.
+ *
+ * @param [in] serviceName - Name of the Dbus service.
+ * @param [in] objectPath - Object path under the service.
+ * @param [in] interface - Interface under which property exist.
+ * @param [in] property - Property whose value is to be read.
+ * @return - Value read from Dbus, if success.
+ * If failed, empty variant.
+ */
+inline types::DbusVariantType readDbusProperty(
+ const std::string& serviceName, const std::string& objectPath,
+ const std::string& interface, const std::string& property)
+{
+ types::DbusVariantType propertyValue;
+
+ // Mandatory fields to make a read dbus call.
+ if (serviceName.empty() || objectPath.empty() || interface.empty() ||
+ property.empty())
+ {
+ logging::logMessage(
+ "One of the parameter to make Dbus read call is empty.");
+ return propertyValue;
+ }
+
+ try
+ {
+ auto bus = sdbusplus::bus::new_default();
+ auto method =
+ bus.new_method_call(serviceName.c_str(), objectPath.c_str(),
+ "org.freedesktop.DBus.Properties", "Get");
+ method.append(interface, property);
+
+ auto result = bus.call(method);
+ result.read(propertyValue);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ return propertyValue;
+ }
+ return propertyValue;
+}
+
+/**
+ * @brief An API to write property on Dbus.
+ *
+ * The caller of this API needs to handle exception thrown by this method to
+ * identify any write failure. The API in no other way indicate write failure
+ * to the caller.
+ *
+ * Note: It will be caller's responsibility ho handle the exception thrown in
+ * case of write failure and generate appropriate error.
+ *
+ * @param [in] serviceName - Name of the Dbus service.
+ * @param [in] objectPath - Object path under the service.
+ * @param [in] interface - Interface under which property exist.
+ * @param [in] property - Property whose value is to be written.
+ * @param [in] propertyValue - The value to be written.
+ */
+inline void writeDbusProperty(
+ const std::string& serviceName, const std::string& objectPath,
+ const std::string& interface, const std::string& property,
+ const types::DbusVariantType& propertyValue)
+{
+ // Mandatory fields to make a write dbus call.
+ if (serviceName.empty() || objectPath.empty() || interface.empty() ||
+ property.empty())
+ {
+ logging::logMessage(
+ "One of the parameter to make Dbus read call is empty.");
+
+ // caller need to handle the throw to ensure Dbus write success.
+ throw std::runtime_error("Dbus write failed, Parameter empty");
+ }
+
+ try
+ {
+ auto bus = sdbusplus::bus::new_default();
+ auto method =
+ bus.new_method_call(serviceName.c_str(), objectPath.c_str(),
+ "org.freedesktop.DBus.Properties", "Set");
+ method.append(interface, property, propertyValue);
+ bus.call(method);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ logging::logMessage(e.what());
+
+ // caller needs to handle this throw to handle error in writing Dbus.
+ throw std::runtime_error("Dbus write failed");
+ }
+}
+
+/**
+ * @brief API to publish data on PIM
+ *
+ * The API calls notify on PIM object to publlish VPD.
+ *
+ * @param[in] objectMap - Object, its interface and data.
+ * @return bool - Status of call to PIM notify.
+ */
+inline bool callPIM(types::ObjectMap&& objectMap)
+{
+ try
+ {
+ for (const auto& l_objectKeyValue : objectMap)
+ {
+ auto l_nodeHandle = objectMap.extract(l_objectKeyValue.first);
+
+ if (l_nodeHandle.key().str.find(constants::pimPath, 0) !=
+ std::string::npos)
+ {
+ l_nodeHandle.key() = l_nodeHandle.key().str.replace(
+ 0, std::strlen(constants::pimPath), "");
+ objectMap.insert(std::move(l_nodeHandle));
+ }
+ }
+
+ auto bus = sdbusplus::bus::new_default();
+ auto pimMsg =
+ bus.new_method_call(constants::pimServiceName, constants::pimPath,
+ constants::pimIntf, "Notify");
+ pimMsg.append(std::move(objectMap));
+ bus.call(pimMsg);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ return false;
+ }
+ return true;
+}
+
+/**
+ * @brief API to check if a D-Bus service is running or not.
+ *
+ * Any failure in calling the method "NameHasOwner" implies that the service is
+ * not in a running state. Hence the API returns false in case of any exception
+ * as well.
+ *
+ * @param[in] i_serviceName - D-Bus service name whose status is to be checked.
+ * @return bool - True if the service is running, false otherwise.
+ */
+inline bool isServiceRunning(const std::string& i_serviceName)
+{
+ bool l_retVal = false;
+
+ try
+ {
+ auto l_bus = sdbusplus::bus::new_default();
+ auto l_method = l_bus.new_method_call(
+ "org.freedesktop.DBus", "/org/freedesktop/DBus",
+ "org.freedesktop.DBus", "NameHasOwner");
+ l_method.append(i_serviceName);
+
+ l_bus.call(l_method).read(l_retVal);
+ }
+ catch (const sdbusplus::exception::SdBusError& l_ex)
+ {
+ logging::logMessage(
+ "Call to check service status failed with exception: " +
+ std::string(l_ex.what()));
+ }
+
+ return l_retVal;
+}
+
+/**
+ * @brief API to call "GetAttribute" method uner BIOS manager.
+ *
+ * The API reads the given attribuute from BIOS and returns a tuple of both
+ * current as well as pending value for that attribute.
+ * The API return only the current attribute value if found.
+ * API returns an empty variant of type BiosAttributeCurrentValue in case of any
+ * error.
+ *
+ * @param[in] i_attributeName - Attribute to be read.
+ * @return Tuple of PLDM attribute Type, current attribute value and pending
+ * attribute value.
+ */
+inline types::BiosAttributeCurrentValue
+ biosGetAttributeMethodCall(const std::string& i_attributeName)
+{
+ auto l_bus = sdbusplus::bus::new_default();
+ auto l_method = l_bus.new_method_call(
+ constants::biosConfigMgrService, constants::biosConfigMgrObjPath,
+ constants::biosConfigMgrInterface, "GetAttribute");
+ l_method.append(i_attributeName);
+
+ types::BiosGetAttrRetType l_attributeVal;
+ try
+ {
+ auto l_result = l_bus.call(l_method);
+ l_result.read(std::get<0>(l_attributeVal), std::get<1>(l_attributeVal),
+ std::get<2>(l_attributeVal));
+ }
+ catch (const sdbusplus::exception::SdBusError& l_ex)
+ {
+ logging::logMessage(
+ "Failed to read BIOS Attribute: " + i_attributeName +
+ " due to error " + std::string(l_ex.what()));
+
+ // TODO: Log an informational PEL here.
+ }
+
+ return std::get<1>(l_attributeVal);
+}
+
+/**
+ * @brief API to check if Chassis is powered on.
+ *
+ * This API queries Phosphor Chassis State Manager to know whether
+ * Chassis is powered on.
+ *
+ * @return true if chassis is powered on, false otherwise
+ */
+inline bool isChassisPowerOn()
+{
+ auto powerState = dbusUtility::readDbusProperty(
+ "xyz.openbmc_project.State.Chassis",
+ "/xyz/openbmc_project/state/chassis0",
+ "xyz.openbmc_project.State.Chassis", "CurrentPowerState");
+
+ if (auto curPowerState = std::get_if<std::string>(&powerState))
+ {
+ if ("xyz.openbmc_project.State.Chassis.PowerState.On" == *curPowerState)
+ {
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ TODO: Add PEL.
+ Callout: Firmware callout
+ Type: Informational
+ Description: Chassis state can't be determined, defaulting to chassis
+ off. : e.what()
+ */
+ return false;
+}
+
+/**
+ * @brief API to check if host is in running state.
+ *
+ * This API reads the current host state from D-bus and returns true if the host
+ * is running.
+ *
+ * @return true if host is in running state. false otherwise.
+ */
+inline bool isHostRunning()
+{
+ const auto l_hostState = dbusUtility::readDbusProperty(
+ constants::hostService, constants::hostObjectPath,
+ constants::hostInterface, "CurrentHostState");
+
+ if (const auto l_currHostState = std::get_if<std::string>(&l_hostState))
+ {
+ if (*l_currHostState == constants::hostRunningState)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * @brief API to check if BMC is in ready state.
+ *
+ * This API reads the current state of BMC from D-bus and returns true if BMC is
+ * in ready state.
+ *
+ * @return true if BMC is ready, false otherwise.
+ */
+inline bool isBMCReady()
+{
+ const auto l_bmcState = dbusUtility::readDbusProperty(
+ constants::bmcStateService, constants::bmcZeroStateObject,
+ constants::bmcStateInterface, constants::currentBMCStateProperty);
+
+ if (const auto l_currBMCState = std::get_if<std::string>(&l_bmcState))
+ {
+ if (*l_currBMCState == constants::bmcReadyState)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * @brief An API to enable BMC reboot guard
+ *
+ * This API does a D-Bus method call to enable BMC reboot guard.
+ *
+ * @return On success, returns 0, otherwise returns -1.
+ */
+inline int EnableRebootGuard() noexcept
+{
+ int l_rc{constants::FAILURE};
+ try
+ {
+ auto l_bus = sdbusplus::bus::new_default();
+ auto l_method = l_bus.new_method_call(
+ constants::systemdService, constants::systemdObjectPath,
+ constants::systemdManagerInterface, "StartUnit");
+ l_method.append("reboot-guard-enable.service", "replace");
+ l_bus.call_noreply(l_method);
+ l_rc = constants::SUCCESS;
+ }
+ catch (const sdbusplus::exception::SdBusError& l_ex)
+ {
+ std::string l_errMsg =
+ "D-Bus call to enable BMC reboot guard failed for reason: ";
+ l_errMsg += l_ex.what();
+
+ logging::logMessage(l_errMsg);
+ }
+ return l_rc;
+}
+
+/**
+ * @brief An API to disable BMC reboot guard
+ *
+ * This API disables BMC reboot guard. This API has an inbuilt re-try mechanism.
+ * If Disable Reboot Guard fails, this API attempts to Disable Reboot Guard for
+ * 3 more times at an interval of 333ms.
+ *
+ * @return On success, returns 0, otherwise returns -1.
+ */
+inline int DisableRebootGuard() noexcept
+{
+ int l_rc{constants::FAILURE};
+
+ // A lambda which executes the DBus call to disable BMC reboot guard.
+ auto l_executeDisableRebootGuard = []() -> int {
+ int l_dBusCallRc{constants::FAILURE};
+ try
+ {
+ auto l_bus = sdbusplus::bus::new_default();
+ auto l_method = l_bus.new_method_call(
+ constants::systemdService, constants::systemdObjectPath,
+ constants::systemdManagerInterface, "StartUnit");
+ l_method.append("reboot-guard-disable.service", "replace");
+ l_bus.call_noreply(l_method);
+ l_dBusCallRc = constants::SUCCESS;
+ }
+ catch (const sdbusplus::exception::SdBusError& l_ex)
+ {}
+ return l_dBusCallRc;
+ };
+
+ if (constants::FAILURE == l_executeDisableRebootGuard())
+ {
+ std::function<void()> l_retryDisableRebootGuard;
+
+ // A lambda which tries to disable BMC reboot guard for 3 times at an
+ // interval of 333 ms.
+ l_retryDisableRebootGuard = [&]() {
+ constexpr int MAX_RETRIES{3};
+ static int l_numRetries{0};
+
+ if (l_numRetries < MAX_RETRIES)
+ {
+ l_numRetries++;
+ if (constants::FAILURE == l_executeDisableRebootGuard())
+ {
+ // sleep for 333ms before next retry. This is just a random
+ // value so that 3 re-tries * 333ms takes ~1 second in the
+ // worst case.
+ const std::chrono::milliseconds l_sleepTime{333};
+ std::this_thread::sleep_for(l_sleepTime);
+ l_retryDisableRebootGuard();
+ }
+ else
+ {
+ l_numRetries = 0;
+ l_rc = constants::SUCCESS;
+ }
+ }
+ else
+ {
+ // Failed to Disable Reboot Guard even after 3 retries.
+ logging::logMessage("Failed to Disable Reboot Guard after " +
+ std::to_string(MAX_RETRIES) + " re-tries");
+ l_numRetries = 0;
+ }
+ };
+
+ l_retryDisableRebootGuard();
+ }
+ else
+ {
+ l_rc = constants::SUCCESS;
+ }
+ return l_rc;
+}
+
+} // namespace dbusUtility
+} // namespace vpd
diff --git a/vpd-manager/include/utility/json_utility.hpp b/vpd-manager/include/utility/json_utility.hpp
new file mode 100644
index 0000000..3dab105
--- /dev/null
+++ b/vpd-manager/include/utility/json_utility.hpp
@@ -0,0 +1,1043 @@
+#pragma once
+
+#include "event_logger.hpp"
+#include "exceptions.hpp"
+#include "logger.hpp"
+#include "types.hpp"
+
+#include <gpiod.hpp>
+#include <nlohmann/json.hpp>
+#include <utility/common_utility.hpp>
+
+#include <fstream>
+#include <type_traits>
+#include <unordered_map>
+
+namespace vpd
+{
+namespace jsonUtility
+{
+
+// forward declaration of API for function map.
+bool processSystemCmdTag(const nlohmann::json& i_parsedConfigJson,
+ const std::string& i_vpdFilePath,
+ const std::string& i_baseAction,
+ const std::string& i_flagToProcess);
+
+// forward declaration of API for function map.
+bool processGpioPresenceTag(
+ const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
+ const std::string& i_baseAction, const std::string& i_flagToProcess);
+
+// forward declaration of API for function map.
+bool procesSetGpioTag(const nlohmann::json& i_parsedConfigJson,
+ const std::string& i_vpdFilePath,
+ const std::string& i_baseAction,
+ const std::string& i_flagToProcess);
+
+// Function pointers to process tags from config JSON.
+typedef bool (*functionPtr)(
+ const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
+ const std::string& i_baseAction, const std::string& i_flagToProcess);
+
+inline std::unordered_map<std::string, functionPtr> funcionMap{
+ {"gpioPresence", processGpioPresenceTag},
+ {"setGpio", procesSetGpioTag},
+ {"systemCmd", processSystemCmdTag}};
+
+/**
+ * @brief API to read VPD offset from JSON file.
+ *
+ * @param[in] i_sysCfgJsonObj - Parsed system config JSON object.
+ * @param[in] i_vpdFilePath - VPD file path.
+ * @return VPD offset if found in JSON, 0 otherwise.
+ */
+inline size_t getVPDOffset(const nlohmann::json& i_sysCfgJsonObj,
+ const std::string& i_vpdFilePath)
+{
+ if (i_vpdFilePath.empty() || (i_sysCfgJsonObj.empty()) ||
+ (!i_sysCfgJsonObj.contains("frus")))
+ {
+ return 0;
+ }
+
+ if (i_sysCfgJsonObj["frus"].contains(i_vpdFilePath))
+ {
+ return i_sysCfgJsonObj["frus"][i_vpdFilePath].at(0).value("offset", 0);
+ }
+
+ const nlohmann::json& l_fruList =
+ i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
+
+ for (const auto& l_fru : l_fruList.items())
+ {
+ const auto l_fruPath = l_fru.key();
+
+ // check if given path is redundant FRU path
+ if (i_vpdFilePath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
+ "redundantEeprom", ""))
+ {
+ // Return the offset of redundant EEPROM taken from JSON.
+ return i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("offset", 0);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * @brief API to parse respective JSON.
+ *
+ * Exception is thrown in case of JSON parse error.
+ *
+ * @param[in] pathToJson - Path to JSON.
+ * @return Parsed JSON.
+ */
+inline nlohmann::json getParsedJson(const std::string& pathToJson)
+{
+ if (pathToJson.empty())
+ {
+ throw std::runtime_error("Path to JSON is missing");
+ }
+
+ if (!std::filesystem::exists(pathToJson) ||
+ std::filesystem::is_empty(pathToJson))
+ {
+ throw std::runtime_error("Incorrect File Path or empty file");
+ }
+
+ std::ifstream jsonFile(pathToJson);
+ if (!jsonFile)
+ {
+ throw std::runtime_error("Failed to access Json path = " + pathToJson);
+ }
+
+ try
+ {
+ return nlohmann::json::parse(jsonFile);
+ }
+ catch (const nlohmann::json::parse_error& e)
+ {
+ throw std::runtime_error("Failed to parse JSON file");
+ }
+}
+
+/**
+ * @brief Get inventory object path from system config JSON.
+ *
+ * Given either D-bus inventory path/FRU EEPROM path/redundant EEPROM path,
+ * this API returns D-bus inventory path if present in JSON.
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object
+ * @param[in] i_vpdPath - Path to where VPD is stored.
+ *
+ * @throw std::runtime_error.
+ *
+ * @return On success a valid path is returned, on failure an empty string is
+ * returned or an exception is thrown.
+ */
+inline std::string getInventoryObjPathFromJson(
+ const nlohmann::json& i_sysCfgJsonObj, const std::string& i_vpdPath)
+{
+ if (i_vpdPath.empty())
+ {
+ throw std::runtime_error("Path parameter is empty.");
+ }
+
+ if (!i_sysCfgJsonObj.contains("frus"))
+ {
+ throw std::runtime_error("Missing frus tag in system config JSON.");
+ }
+
+ // check if given path is FRU path
+ if (i_sysCfgJsonObj["frus"].contains(i_vpdPath))
+ {
+ return i_sysCfgJsonObj["frus"][i_vpdPath].at(0).value(
+ "inventoryPath", "");
+ }
+
+ const nlohmann::json& l_fruList =
+ i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
+
+ for (const auto& l_fru : l_fruList.items())
+ {
+ const auto l_fruPath = l_fru.key();
+ const auto l_invObjPath =
+ i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("inventoryPath", "");
+
+ // check if given path is redundant FRU path or inventory path
+ if (i_vpdPath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
+ "redundantEeprom", "") ||
+ (i_vpdPath == l_invObjPath))
+ {
+ return l_invObjPath;
+ }
+ }
+ return std::string();
+}
+
+/**
+ * @brief Process "PostFailAction" defined in config JSON.
+ *
+ * In case there is some error in the processing of "preAction" execution and a
+ * set of procedure needs to be done as a part of post fail action. This base
+ * action can be defined in the config JSON for that FRU and it will be handled
+ * under this API.
+ *
+ * @param[in] i_parsedConfigJson - config JSON
+ * @param[in] i_vpdFilePath - EEPROM file path
+ * @param[in] i_flagToProcess - To identify which flag(s) needs to be processed
+ * under PostFailAction tag of config JSON.
+ * @return - success or failure
+ */
+inline bool executePostFailAction(const nlohmann::json& i_parsedConfigJson,
+ const std::string& i_vpdFilePath,
+ const std::string& i_flagToProcess)
+{
+ if (i_parsedConfigJson.empty() || i_vpdFilePath.empty() ||
+ i_flagToProcess.empty())
+ {
+ logging::logMessage(
+ "Invalid parameters. Abort processing for post fail action");
+
+ return false;
+ }
+
+ if (!(i_parsedConfigJson["frus"][i_vpdFilePath].at(0))["PostFailAction"]
+ .contains(i_flagToProcess))
+ {
+ logging::logMessage(
+ "Config JSON missing flag " + i_flagToProcess +
+ " to execute post fail action for path = " + i_vpdFilePath);
+
+ return false;
+ }
+
+ for (const auto& l_tags : (i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0))["PostFailAction"][i_flagToProcess]
+ .items())
+ {
+ auto itrToFunction = funcionMap.find(l_tags.key());
+ if (itrToFunction != funcionMap.end())
+ {
+ if (!itrToFunction->second(i_parsedConfigJson, i_vpdFilePath,
+ "PostFailAction", i_flagToProcess))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+/**
+ * @brief Process "systemCmd" tag for a given FRU.
+ *
+ * The API will process "systemCmd" tag if it is defined in the config
+ * JSON for the given FRU.
+ *
+ * @param[in] i_parsedConfigJson - config JSON
+ * @param[in] i_vpdFilePath - EEPROM file path
+ * @param[in] i_baseAction - Base action for which this tag has been called.
+ * @param[in] i_flagToProcess - Flag nested under the base action for which this
+ * tag has been called.
+ * @return Execution status.
+ */
+inline bool processSystemCmdTag(
+ const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
+ const std::string& i_baseAction, const std::string& i_flagToProcess)
+{
+ if (i_vpdFilePath.empty() || i_parsedConfigJson.empty() ||
+ i_baseAction.empty() || i_flagToProcess.empty())
+ {
+ logging::logMessage(
+ "Invalid parameter. Abort processing of processSystemCmd.");
+ return false;
+ }
+
+ if (!((i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0)[i_baseAction][i_flagToProcess]["systemCmd"])
+ .contains("cmd")))
+ {
+ logging::logMessage(
+ "Config JSON missing required information to execute system command for EEPROM " +
+ i_vpdFilePath);
+
+ return false;
+ }
+
+ try
+ {
+ const std::string& l_systemCommand =
+ i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0)[i_baseAction][i_flagToProcess]["systemCmd"]["cmd"];
+
+ commonUtility::executeCmd(l_systemCommand);
+ return true;
+ }
+ catch (const std::exception& e)
+ {
+ std::string l_errMsg = "Process system tag failed for exception: ";
+ l_errMsg += e.what();
+
+ logging::logMessage(l_errMsg);
+ return false;
+ }
+}
+
+/**
+ * @brief Checks for presence of a given FRU using GPIO line.
+ *
+ * This API returns the presence information of the FRU corresponding to the
+ * given VPD file path by setting the presence pin.
+ *
+ * @param[in] i_parsedConfigJson - config JSON
+ * @param[in] i_vpdFilePath - EEPROM file path
+ * @param[in] i_baseAction - Base action for which this tag has been called.
+ * @param[in] i_flagToProcess - Flag nested under the base action for which this
+ * tag has been called.
+ * @return Execution status.
+ */
+inline bool processGpioPresenceTag(
+ const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
+ const std::string& i_baseAction, const std::string& i_flagToProcess)
+{
+ if (i_vpdFilePath.empty() || i_parsedConfigJson.empty() ||
+ i_baseAction.empty() || i_flagToProcess.empty())
+ {
+ logging::logMessage(
+ "Invalid parameter. Abort processing of processGpioPresence tag");
+ return false;
+ }
+
+ if (!(((i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0)[i_baseAction][i_flagToProcess]["gpioPresence"])
+ .contains("pin")) &&
+ ((i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0)[i_baseAction][i_flagToProcess]["gpioPresence"])
+ .contains("value"))))
+ {
+ logging::logMessage(
+ "Config JSON missing required information to detect presence for EEPROM " +
+ i_vpdFilePath);
+
+ return false;
+ }
+
+ // get the pin name
+ const std::string& l_presencePinName =
+ i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0)[i_baseAction][i_flagToProcess]["gpioPresence"]["pin"];
+
+ // get the pin value
+ uint8_t l_presencePinValue = i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0)[i_baseAction][i_flagToProcess]["gpioPresence"]["value"];
+
+ try
+ {
+ gpiod::line l_presenceLine = gpiod::find_line(l_presencePinName);
+
+ if (!l_presenceLine)
+ {
+ throw GpioException("Couldn't find the GPIO line.");
+ }
+
+ l_presenceLine.request({"Read the presence line",
+ gpiod::line_request::DIRECTION_INPUT, 0});
+
+ return (l_presencePinValue == l_presenceLine.get_value());
+ }
+ catch (const std::exception& ex)
+ {
+ std::string l_errMsg = "Exception on GPIO line: ";
+ l_errMsg += l_presencePinName;
+ l_errMsg += " Reason: ";
+ l_errMsg += ex.what();
+ l_errMsg += " File: " + i_vpdFilePath + " Pel Logged";
+
+ // ToDo -- Update Internal Rc code.
+ EventLogger::createAsyncPelWithInventoryCallout(
+ types::ErrorType::GpioError, types::SeverityType::Informational,
+ {{getInventoryObjPathFromJson(i_parsedConfigJson, i_vpdFilePath),
+ types::CalloutPriority::High}},
+ std::source_location::current().file_name(),
+ std::source_location::current().function_name(), 0, l_errMsg,
+ std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+
+ logging::logMessage(l_errMsg);
+
+ // Except when GPIO pin value is false, we go and try collecting the
+ // FRU VPD as we couldn't able to read GPIO pin value due to some
+ // error/exception. So returning true in error scenario.
+ return true;
+ }
+}
+
+/**
+ * @brief Process "setGpio" tag for a given FRU.
+ *
+ * This API enables the GPIO line.
+ *
+ * @param[in] i_parsedConfigJson - config JSON
+ * @param[in] i_vpdFilePath - EEPROM file path
+ * @param[in] i_baseAction - Base action for which this tag has been called.
+ * @param[in] i_flagToProcess - Flag nested under the base action for which this
+ * tag has been called.
+ * @return Execution status.
+ */
+inline bool procesSetGpioTag(
+ const nlohmann::json& i_parsedConfigJson, const std::string& i_vpdFilePath,
+ const std::string& i_baseAction, const std::string& i_flagToProcess)
+{
+ if (i_vpdFilePath.empty() || i_parsedConfigJson.empty() ||
+ i_baseAction.empty() || i_flagToProcess.empty())
+ {
+ logging::logMessage(
+ "Invalid parameter. Abort processing of procesSetGpio.");
+ return false;
+ }
+
+ if (!(((i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0)[i_baseAction][i_flagToProcess]["setGpio"])
+ .contains("pin")) &&
+ ((i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0)[i_baseAction][i_flagToProcess]["setGpio"])
+ .contains("value"))))
+ {
+ logging::logMessage(
+ "Config JSON missing required information to set gpio line for EEPROM " +
+ i_vpdFilePath);
+
+ return false;
+ }
+
+ const std::string& l_pinName = i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0)[i_baseAction][i_flagToProcess]["setGpio"]["pin"];
+
+ // Get the value to set
+ uint8_t l_pinValue = i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0)[i_baseAction][i_flagToProcess]["setGpio"]["value"];
+
+ logging::logMessage(
+ "Setting GPIO: " + l_pinName + " to " + std::to_string(l_pinValue));
+ try
+ {
+ gpiod::line l_outputLine = gpiod::find_line(l_pinName);
+
+ if (!l_outputLine)
+ {
+ throw GpioException("Couldn't find GPIO line.");
+ }
+
+ l_outputLine.request(
+ {"FRU Action", ::gpiod::line_request::DIRECTION_OUTPUT, 0},
+ l_pinValue);
+ return true;
+ }
+ catch (const std::exception& ex)
+ {
+ std::string l_errMsg = "Exception on GPIO line: ";
+ l_errMsg += l_pinName;
+ l_errMsg += " Reason: ";
+ l_errMsg += ex.what();
+ l_errMsg += " File: " + i_vpdFilePath + " Pel Logged";
+
+ // ToDo -- Update Internal RC code
+ EventLogger::createAsyncPelWithInventoryCallout(
+ types::ErrorType::GpioError, types::SeverityType::Informational,
+ {{getInventoryObjPathFromJson(i_parsedConfigJson, i_vpdFilePath),
+ types::CalloutPriority::High}},
+ std::source_location::current().file_name(),
+ std::source_location::current().function_name(), 0, l_errMsg,
+ std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+
+ logging::logMessage(l_errMsg);
+
+ return false;
+ }
+}
+
+/**
+ * @brief Process any action, if defined in config JSON.
+ *
+ * If any FRU(s) requires any special handling, then this base action can be
+ * defined for that FRU in the config JSON, processing of which will be handled
+ * in this API.
+ * Examples of action - preAction, PostAction etc.
+ *
+ * @param[in] i_parsedConfigJson - config JSON
+ * @param[in] i_action - Base action to be performed.
+ * @param[in] i_vpdFilePath - EEPROM file path
+ * @param[in] i_flagToProcess - To identify which flag(s) needs to be processed
+ * under PreAction tag of config JSON.
+ * @return - success or failure
+ */
+inline bool executeBaseAction(
+ const nlohmann::json& i_parsedConfigJson, const std::string& i_action,
+ const std::string& i_vpdFilePath, const std::string& i_flagToProcess)
+{
+ if (i_flagToProcess.empty() || i_action.empty() || i_vpdFilePath.empty() ||
+ !i_parsedConfigJson.contains("frus"))
+ {
+ logging::logMessage("Invalid parameter");
+ return false;
+ }
+
+ if (!i_parsedConfigJson["frus"].contains(i_vpdFilePath))
+ {
+ logging::logMessage(
+ "File path: " + i_vpdFilePath + " not found in JSON");
+ return false;
+ }
+
+ if (!i_parsedConfigJson["frus"][i_vpdFilePath].at(0).contains(i_action))
+ {
+ logging::logMessage("Action [" + i_action +
+ "] not defined for file path:" + i_vpdFilePath);
+ return false;
+ }
+
+ if (!(i_parsedConfigJson["frus"][i_vpdFilePath].at(0))[i_action].contains(
+ i_flagToProcess))
+ {
+ logging::logMessage("Config JSON missing flag [" + i_flagToProcess +
+ "] to execute action for path = " + i_vpdFilePath);
+
+ return false;
+ }
+
+ const nlohmann::json& l_tagsJson =
+ (i_parsedConfigJson["frus"][i_vpdFilePath].at(
+ 0))[i_action][i_flagToProcess];
+
+ for (const auto& l_tag : l_tagsJson.items())
+ {
+ auto itrToFunction = funcionMap.find(l_tag.key());
+ if (itrToFunction != funcionMap.end())
+ {
+ if (!itrToFunction->second(i_parsedConfigJson, i_vpdFilePath,
+ i_action, i_flagToProcess))
+ {
+ // In case any of the tag fails to execute. Mark action
+ // as failed for that flag.
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+/**
+ * @brief Get redundant FRU path from system config JSON
+ *
+ * Given either D-bus inventory path/FRU path/redundant FRU path, this
+ * API returns the redundant FRU path taken from "redundantEeprom" tag from
+ * system config JSON.
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object.
+ * @param[in] i_vpdPath - Path to where VPD is stored.
+ *
+ * @throw std::runtime_error.
+ * @return On success return valid path, on failure return empty string.
+ */
+inline std::string getRedundantEepromPathFromJson(
+ const nlohmann::json& i_sysCfgJsonObj, const std::string& i_vpdPath)
+{
+ if (i_vpdPath.empty())
+ {
+ throw std::runtime_error("Path parameter is empty.");
+ }
+
+ if (!i_sysCfgJsonObj.contains("frus"))
+ {
+ throw std::runtime_error("Missing frus tag in system config JSON.");
+ }
+
+ // check if given path is FRU path
+ if (i_sysCfgJsonObj["frus"].contains(i_vpdPath))
+ {
+ return i_sysCfgJsonObj["frus"][i_vpdPath].at(0).value(
+ "redundantEeprom", "");
+ }
+
+ const nlohmann::json& l_fruList =
+ i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
+
+ for (const auto& l_fru : l_fruList.items())
+ {
+ const std::string& l_fruPath = l_fru.key();
+ const std::string& l_redundantFruPath =
+ i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("redundantEeprom",
+ "");
+
+ // check if given path is inventory path or redundant FRU path
+ if ((i_sysCfgJsonObj["frus"][l_fruPath].at(0).value("inventoryPath",
+ "") == i_vpdPath) ||
+ (l_redundantFruPath == i_vpdPath))
+ {
+ return l_redundantFruPath;
+ }
+ }
+ return std::string();
+}
+
+/**
+ * @brief Get FRU EEPROM path from system config JSON
+ *
+ * Given either D-bus inventory path/FRU EEPROM path/redundant EEPROM path,
+ * this API returns FRU EEPROM path if present in JSON.
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object
+ * @param[in] i_vpdPath - Path to where VPD is stored.
+ *
+ * @throw std::runtime_error.
+ *
+ * @return On success return valid path, on failure return empty string.
+ */
+inline std::string getFruPathFromJson(const nlohmann::json& i_sysCfgJsonObj,
+ const std::string& i_vpdPath)
+{
+ if (i_vpdPath.empty())
+ {
+ throw std::runtime_error("Path parameter is empty.");
+ }
+
+ if (!i_sysCfgJsonObj.contains("frus"))
+ {
+ throw std::runtime_error("Missing frus tag in system config JSON.");
+ }
+
+ // check if given path is FRU path
+ if (i_sysCfgJsonObj["frus"].contains(i_vpdPath))
+ {
+ return i_vpdPath;
+ }
+
+ const nlohmann::json& l_fruList =
+ i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
+
+ for (const auto& l_fru : l_fruList.items())
+ {
+ const auto l_fruPath = l_fru.key();
+
+ // check if given path is redundant FRU path or inventory path
+ if (i_vpdPath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
+ "redundantEeprom", "") ||
+ (i_vpdPath == i_sysCfgJsonObj["frus"][l_fruPath].at(0).value(
+ "inventoryPath", "")))
+ {
+ return l_fruPath;
+ }
+ }
+ return std::string();
+}
+
+/**
+ * @brief An API to check backup and restore VPD is required.
+ *
+ * The API checks if there is provision for backup and restore mentioned in the
+ * system config JSON, by looking "backupRestoreConfigPath" tag.
+ * Checks if the path mentioned is a hardware path, by checking if the file path
+ * exists and size of contents in the path.
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object.
+ *
+ * @return true if backup and restore is required, false otherwise.
+ */
+inline bool isBackupAndRestoreRequired(const nlohmann::json& i_sysCfgJsonObj)
+{
+ try
+ {
+ const std::string& l_backupAndRestoreCfgFilePath =
+ i_sysCfgJsonObj.value("backupRestoreConfigPath", "");
+ if (!l_backupAndRestoreCfgFilePath.empty() &&
+ std::filesystem::exists(l_backupAndRestoreCfgFilePath) &&
+ !std::filesystem::is_empty(l_backupAndRestoreCfgFilePath))
+ {
+ return true;
+ }
+ }
+ catch (std::exception& ex)
+ {
+ logging::logMessage(ex.what());
+ }
+ return false;
+}
+
+/** @brief API to check if an action is required for given EEPROM path.
+ *
+ * System config JSON can contain pre-action, post-action etc. like actions
+ * defined for an EEPROM path. The API will check if any such action is defined
+ * for the EEPROM.
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object.
+ * @param[in] i_vpdFruPath - EEPROM path.
+ * @param[in] i_action - Action to be checked.
+ * @param[in] i_flowFlag - Denotes the flow w.r.t which the action should be
+ * triggered.
+ * @return - True if action is defined for the flow, false otherwise.
+ */
+inline bool isActionRequired(
+ const nlohmann::json& i_sysCfgJsonObj, const std::string& i_vpdFruPath,
+ const std::string& i_action, const std::string& i_flowFlag)
+{
+ if (i_vpdFruPath.empty() || i_action.empty() || i_flowFlag.empty())
+ {
+ logging::logMessage("Invalid parameters recieved.");
+ return false;
+ }
+
+ if (!i_sysCfgJsonObj.contains("frus"))
+ {
+ logging::logMessage("Invalid JSON object recieved.");
+ return false;
+ }
+
+ if (!i_sysCfgJsonObj["frus"].contains(i_vpdFruPath))
+ {
+ logging::logMessage(
+ "JSON object does not contain EEPROM path " + i_vpdFruPath);
+ return false;
+ }
+
+ if ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0)).contains(i_action))
+ {
+ if ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))[i_action].contains(
+ i_flowFlag))
+ {
+ return true;
+ }
+
+ logging::logMessage("Flow flag: [" + i_flowFlag +
+ "], not found in JSON for path: " + i_vpdFruPath);
+ return false;
+ }
+ return false;
+}
+
+/**
+ * @brief An API to return list of FRUs that needs GPIO polling.
+ *
+ * An API that checks for the FRUs that requires GPIO polling and returns
+ * a list of FRUs that needs polling. Returns an empty list if there are
+ * no FRUs that requires polling.
+ *
+ * @throw std::runtime_error
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object.
+ *
+ * @return list of FRUs parameters that needs polling.
+ */
+inline std::vector<std::string>
+ getListOfGpioPollingFrus(const nlohmann::json& i_sysCfgJsonObj)
+{
+ if (i_sysCfgJsonObj.empty())
+ {
+ throw std::runtime_error("Invalid Parameters");
+ }
+
+ if (!i_sysCfgJsonObj.contains("frus"))
+ {
+ throw std::runtime_error("Missing frus section in system config JSON");
+ }
+
+ std::vector<std::string> l_gpioPollingRequiredFrusList;
+
+ for (const auto& l_fru : i_sysCfgJsonObj["frus"].items())
+ {
+ const auto l_fruPath = l_fru.key();
+
+ try
+ {
+ if (isActionRequired(i_sysCfgJsonObj, l_fruPath, "pollingRequired",
+ "hotPlugging"))
+ {
+ if (i_sysCfgJsonObj["frus"][l_fruPath]
+ .at(0)["pollingRequired"]["hotPlugging"]
+ .contains("gpioPresence"))
+ {
+ l_gpioPollingRequiredFrusList.push_back(l_fruPath);
+ }
+ }
+ }
+ catch (const std::exception& l_ex)
+ {
+ logging::logMessage(l_ex.what());
+ }
+ }
+
+ return l_gpioPollingRequiredFrusList;
+}
+
+/**
+ * @brief Get all related path(s) to update keyword value.
+ *
+ * Given FRU EEPROM path/Inventory path needs keyword's value update, this API
+ * returns tuple of FRU EEPROM path, inventory path and redundant EEPROM path if
+ * exists in the system config JSON.
+ *
+ * Note: If the inventory object path or redundant EEPROM path(s) are not found
+ * in the system config JSON, corresponding fields will have empty value in the
+ * returning tuple.
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object.
+ * @param[in,out] io_vpdPath - Inventory object path or FRU EEPROM path.
+ *
+ * @return On success returns tuple of EEPROM path, inventory path & redundant
+ * path, on failure returns tuple with given input path alone.
+ */
+inline std::tuple<std::string, std::string, std::string>
+ getAllPathsToUpdateKeyword(const nlohmann::json& i_sysCfgJsonObj,
+ std::string io_vpdPath)
+{
+ types::Path l_inventoryObjPath;
+ types::Path l_redundantFruPath;
+ try
+ {
+ if (!i_sysCfgJsonObj.empty())
+ {
+ // Get hardware path from system config JSON.
+ const types::Path l_fruPath =
+ jsonUtility::getFruPathFromJson(i_sysCfgJsonObj, io_vpdPath);
+
+ if (!l_fruPath.empty())
+ {
+ io_vpdPath = l_fruPath;
+
+ // Get inventory object path from system config JSON
+ l_inventoryObjPath = jsonUtility::getInventoryObjPathFromJson(
+ i_sysCfgJsonObj, l_fruPath);
+
+ // Get redundant hardware path if present in system config JSON
+ l_redundantFruPath =
+ jsonUtility::getRedundantEepromPathFromJson(i_sysCfgJsonObj,
+ l_fruPath);
+ }
+ }
+ }
+ catch (const std::exception& l_exception)
+ {
+ logging::logMessage(
+ "Failed to get all paths to update keyword value, error " +
+ std::string(l_exception.what()));
+ }
+ return std::make_tuple(io_vpdPath, l_inventoryObjPath, l_redundantFruPath);
+}
+
+/**
+ * @brief An API to get DBus service name.
+ *
+ * Given DBus inventory path, this API returns DBus service name if present in
+ * the JSON.
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object.
+ * @param[in] l_inventoryPath - DBus inventory path.
+ *
+ * @return On success returns the service name present in the system config
+ * JSON, otherwise empty string.
+ *
+ * Note: Caller has to handle in case of empty string received.
+ */
+inline std::string getServiceName(const nlohmann::json& i_sysCfgJsonObj,
+ const std::string& l_inventoryPath)
+{
+ try
+ {
+ if (l_inventoryPath.empty())
+ {
+ throw std::runtime_error("Path parameter is empty.");
+ }
+
+ if (!i_sysCfgJsonObj.contains("frus"))
+ {
+ throw std::runtime_error("Missing frus tag in system config JSON.");
+ }
+
+ const nlohmann::json& l_listOfFrus =
+ i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
+
+ for (const auto& l_frus : l_listOfFrus.items())
+ {
+ for (const auto& l_inventoryItem : l_frus.value())
+ {
+ if (l_inventoryPath.compare(l_inventoryItem["inventoryPath"]) ==
+ constants::STR_CMP_SUCCESS)
+ {
+ return l_inventoryItem["serviceName"];
+ }
+ }
+ }
+ throw std::runtime_error(
+ "Inventory path not found in the system config JSON");
+ }
+ catch (const std::exception& l_exception)
+ {
+ logging::logMessage(
+ "Error while getting DBus service name for given path " +
+ l_inventoryPath + ", error: " + std::string(l_exception.what()));
+ // TODO:log PEL
+ }
+ return std::string{};
+}
+
+/**
+ * @brief An API to check if a FRU is tagged as "powerOffOnly"
+ *
+ * Given the system config JSON and VPD FRU path, this API checks if the FRU
+ * VPD can be collected at Chassis Power Off state only.
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object.
+ * @param[in] i_vpdFruPath - EEPROM path.
+ * @return - True if FRU VPD can be collected at Chassis Power Off state only.
+ * False otherwise
+ */
+inline bool isFruPowerOffOnly(const nlohmann::json& i_sysCfgJsonObj,
+ const std::string& i_vpdFruPath)
+{
+ if (i_vpdFruPath.empty())
+ {
+ logging::logMessage("FRU path is empty.");
+ return false;
+ }
+
+ if (!i_sysCfgJsonObj.contains("frus"))
+ {
+ logging::logMessage("Missing frus tag in system config JSON.");
+ return false;
+ }
+
+ if (!i_sysCfgJsonObj["frus"].contains(i_vpdFruPath))
+ {
+ logging::logMessage("JSON object does not contain EEPROM path \'" +
+ i_vpdFruPath + "\'");
+ return false;
+ }
+
+ return ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))
+ .contains("powerOffOnly") &&
+ (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0)["powerOffOnly"]));
+}
+
+/**
+ * @brief API which tells if the FRU is replaceable at runtime
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object.
+ * @param[in] i_vpdFruPath - EEPROM path.
+ *
+ * @return true if FRU is replaceable at runtime. false otherwise.
+ */
+inline bool isFruReplaceableAtRuntime(const nlohmann::json& i_sysCfgJsonObj,
+ const std::string& i_vpdFruPath)
+{
+ try
+ {
+ if (i_vpdFruPath.empty())
+ {
+ throw std::runtime_error("Given FRU path is empty.");
+ }
+
+ if (i_sysCfgJsonObj.empty() || (!i_sysCfgJsonObj.contains("frus")))
+ {
+ throw std::runtime_error("Invalid system config JSON object.");
+ }
+
+ return ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))
+ .contains("replaceableAtRuntime") &&
+ (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(
+ 0)["replaceableAtRuntime"]));
+ }
+ catch (const std::exception& l_error)
+ {
+ // TODO: Log PEL
+ logging::logMessage(l_error.what());
+ }
+
+ return false;
+}
+
+/**
+ * @brief API which tells if the FRU is replaceable at standby
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object.
+ * @param[in] i_vpdFruPath - EEPROM path.
+ *
+ * @return true if FRU is replaceable at standby. false otherwise.
+ */
+inline bool isFruReplaceableAtStandby(const nlohmann::json& i_sysCfgJsonObj,
+ const std::string& i_vpdFruPath)
+{
+ try
+ {
+ if (i_vpdFruPath.empty())
+ {
+ throw std::runtime_error("Given FRU path is empty.");
+ }
+
+ if (i_sysCfgJsonObj.empty() || (!i_sysCfgJsonObj.contains("frus")))
+ {
+ throw std::runtime_error("Invalid system config JSON object.");
+ }
+
+ return ((i_sysCfgJsonObj["frus"][i_vpdFruPath].at(0))
+ .contains("replaceableAtStandby") &&
+ (i_sysCfgJsonObj["frus"][i_vpdFruPath].at(
+ 0)["replaceableAtStandby"]));
+ }
+ catch (const std::exception& l_error)
+ {
+ // TODO: Log PEL
+ logging::logMessage(l_error.what());
+ }
+
+ return false;
+}
+
+/**
+ * @brief API to get list of FRUs replaceable at standby from JSON.
+ *
+ * The API will return a vector of FRUs inventory path which are replaceable at
+ * standby.
+ * The API can throw exception in case of error scenarios. Caller's
+ * responsibility to handle those exceptions.
+ *
+ * @param[in] i_sysCfgJsonObj - System config JSON object.
+ *
+ * @return - List of FRUs replaceable at standby.
+ */
+inline std::vector<std::string>
+ getListOfFrusReplaceableAtStandby(const nlohmann::json& i_sysCfgJsonObj)
+{
+ std::vector<std::string> l_frusReplaceableAtStandby;
+
+ if (!i_sysCfgJsonObj.contains("frus"))
+ {
+ logging::logMessage("Missing frus tag in system config JSON.");
+ return l_frusReplaceableAtStandby;
+ }
+
+ const nlohmann::json& l_fruList =
+ i_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
+
+ for (const auto& l_fru : l_fruList.items())
+ {
+ if (i_sysCfgJsonObj["frus"][l_fru.key()].at(0).value(
+ "replaceableAtStandby", false))
+ {
+ const std::string& l_inventoryObjectPath =
+ i_sysCfgJsonObj["frus"][l_fru.key()].at(0).value(
+ "inventoryPath", "");
+
+ if (!l_inventoryObjectPath.empty())
+ {
+ l_frusReplaceableAtStandby.emplace_back(l_inventoryObjectPath);
+ }
+ }
+ }
+
+ return l_frusReplaceableAtStandby;
+}
+
+} // namespace jsonUtility
+} // namespace vpd
diff --git a/vpd-manager/include/utility/vpd_specific_utility.hpp b/vpd-manager/include/utility/vpd_specific_utility.hpp
new file mode 100644
index 0000000..d6b92fd
--- /dev/null
+++ b/vpd-manager/include/utility/vpd_specific_utility.hpp
@@ -0,0 +1,556 @@
+#pragma once
+
+#include "config.h"
+
+#include "constants.hpp"
+#include "exceptions.hpp"
+#include "logger.hpp"
+#include "types.hpp"
+
+#include <nlohmann/json.hpp>
+#include <utility/common_utility.hpp>
+#include <utility/dbus_utility.hpp>
+
+#include <filesystem>
+#include <fstream>
+#include <regex>
+
+namespace vpd
+{
+namespace vpdSpecificUtility
+{
+/**
+ * @brief API to generate file name for bad VPD.
+ *
+ * For i2c eeproms - the pattern of the vpd-name will be
+ * i2c-<bus-number>-<eeprom-address>.
+ * For spi eeproms - the pattern of the vpd-name will be spi-<spi-number>.
+ *
+ * @param[in] vpdFilePath - file path of the vpd.
+ * @return Generated file name.
+ */
+inline std::string generateBadVPDFileName(const std::string& vpdFilePath)
+{
+ std::string badVpdFileName = BAD_VPD_DIR;
+ if (vpdFilePath.find("i2c") != std::string::npos)
+ {
+ badVpdFileName += "i2c-";
+ std::regex i2cPattern("(at24/)([0-9]+-[0-9]+)\\/");
+ std::smatch match;
+ if (std::regex_search(vpdFilePath, match, i2cPattern))
+ {
+ badVpdFileName += match.str(2);
+ }
+ }
+ else if (vpdFilePath.find("spi") != std::string::npos)
+ {
+ std::regex spiPattern("((spi)[0-9]+)(.0)");
+ std::smatch match;
+ if (std::regex_search(vpdFilePath, match, spiPattern))
+ {
+ badVpdFileName += match.str(1);
+ }
+ }
+ return badVpdFileName;
+}
+
+/**
+ * @brief API which dumps the broken/bad vpd in a directory.
+ * When the vpd is bad, this API places the bad vpd file inside
+ * "/tmp/bad-vpd" in BMC, in order to collect bad VPD data as a part of user
+ * initiated BMC dump.
+ *
+ * Note: Throws exception in case of any failure.
+ *
+ * @param[in] vpdFilePath - vpd file path
+ * @param[in] vpdVector - vpd vector
+ */
+inline void dumpBadVpd(const std::string& vpdFilePath,
+ const types::BinaryVector& vpdVector)
+{
+ std::filesystem::create_directory(BAD_VPD_DIR);
+ auto badVpdPath = generateBadVPDFileName(vpdFilePath);
+
+ if (std::filesystem::exists(badVpdPath))
+ {
+ std::error_code ec;
+ std::filesystem::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.is_open())
+ {
+ 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());
+}
+
+/**
+ * @brief An API to read value of a keyword.
+ *
+ * Note: Throws exception. Caller needs to handle.
+ *
+ * @param[in] kwdValueMap - A map having Kwd value pair.
+ * @param[in] kwd - keyword name.
+ * @param[out] kwdValue - Value of the keyword read from map.
+ */
+inline void getKwVal(const types::IPZKwdValueMap& kwdValueMap,
+ const std::string& kwd, std::string& kwdValue)
+{
+ if (kwd.empty())
+ {
+ logging::logMessage("Invalid parameters");
+ throw std::runtime_error("Invalid parameters");
+ }
+
+ auto itrToKwd = kwdValueMap.find(kwd);
+ if (itrToKwd != kwdValueMap.end())
+ {
+ kwdValue = itrToKwd->second;
+ return;
+ }
+
+ throw std::runtime_error("Keyword not found");
+}
+
+/**
+ * @brief An API to process encoding of a keyword.
+ *
+ * @param[in] keyword - Keyword to be processed.
+ * @param[in] encoding - Type of encoding.
+ * @return Value after being processed for encoded type.
+ */
+inline std::string encodeKeyword(const std::string& keyword,
+ const std::string& encoding)
+{
+ // Default value is keyword value
+ std::string result(keyword.begin(), keyword.end());
+
+ if (encoding == "MAC")
+ {
+ result.clear();
+ size_t firstByte = keyword[0];
+ result += commonUtility::toHex(firstByte >> 4);
+ result += commonUtility::toHex(firstByte & 0x0f);
+ for (size_t i = 1; i < keyword.size(); ++i)
+ {
+ result += ":";
+ result += commonUtility::toHex(keyword[i] >> 4);
+ result += commonUtility::toHex(keyword[i] & 0x0f);
+ }
+ }
+ else if (encoding == "DATE")
+ {
+ // Date, represent as
+ // <year>-<month>-<day> <hour>:<min>
+ result.clear();
+ static constexpr uint8_t skipPrefix = 3;
+
+ auto strItr = keyword.begin();
+ advance(strItr, skipPrefix);
+ for_each(strItr, keyword.end(), [&result](size_t c) { result += c; });
+
+ result.insert(constants::BD_YEAR_END, 1, '-');
+ result.insert(constants::BD_MONTH_END, 1, '-');
+ result.insert(constants::BD_DAY_END, 1, ' ');
+ result.insert(constants::BD_HOUR_END, 1, ':');
+ }
+
+ return result;
+}
+
+/**
+ * @brief Helper function to insert or merge in map.
+ *
+ * This method checks in an interface if the given interface exists. If the
+ * interface key already exists, property map is inserted corresponding to it.
+ * If the key does'nt exist then given interface and property map pair is newly
+ * created. If the property present in propertymap already exist in the
+ * InterfaceMap, then the new property value is ignored.
+ *
+ * @param[in,out] map - Interface map.
+ * @param[in] interface - Interface to be processed.
+ * @param[in] propertyMap - new property map that needs to be emplaced.
+ */
+inline void insertOrMerge(types::InterfaceMap& map,
+ const std::string& interface,
+ types::PropertyMap&& propertyMap)
+{
+ if (map.find(interface) != map.end())
+ {
+ try
+ {
+ auto& prop = map.at(interface);
+ std::for_each(propertyMap.begin(), propertyMap.end(),
+ [&prop](auto l_keyValue) {
+ prop[l_keyValue.first] = l_keyValue.second;
+ });
+ }
+ catch (const std::exception& l_ex)
+ {
+ // ToDo:: Log PEL
+ logging::logMessage(
+ "Inserting properties into interface[" + interface +
+ "] map is failed, reason: " + std::string(l_ex.what()));
+ }
+ }
+ else
+ {
+ map.emplace(interface, propertyMap);
+ }
+}
+
+/**
+ * @brief API to expand unpanded location code.
+ *
+ * Note: The API handles all the exception internally, in case of any error
+ * unexpanded location code will be returned as it is.
+ *
+ * @param[in] unexpandedLocationCode - Unexpanded location code.
+ * @param[in] parsedVpdMap - Parsed VPD map.
+ * @return Expanded location code. In case of any error, unexpanded is returned
+ * as it is.
+ */
+inline std::string
+ getExpandedLocationCode(const std::string& unexpandedLocationCode,
+ const types::VPDMapVariant& parsedVpdMap)
+{
+ auto expanded{unexpandedLocationCode};
+
+ try
+ {
+ // Expanded location code is formed by combining two keywords
+ // depending on type in unexpanded one. Second one is always "SE".
+ std::string kwd1, kwd2{constants::kwdSE};
+
+ // interface to search for required keywords;
+ std::string kwdInterface;
+
+ // record which holds the required keywords.
+ std::string recordName;
+
+ auto pos = unexpandedLocationCode.find("fcs");
+ if (pos != std::string::npos)
+ {
+ kwd1 = constants::kwdFC;
+ kwdInterface = constants::vcenInf;
+ recordName = constants::recVCEN;
+ }
+ else
+ {
+ pos = unexpandedLocationCode.find("mts");
+ if (pos != std::string::npos)
+ {
+ kwd1 = constants::kwdTM;
+ kwdInterface = constants::vsysInf;
+ recordName = constants::recVSYS;
+ }
+ else
+ {
+ throw std::runtime_error(
+ "Error detecting type of unexpanded location code.");
+ }
+ }
+
+ std::string firstKwdValue, secondKwdValue;
+
+ if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap);
+ ipzVpdMap && (*ipzVpdMap).find(recordName) != (*ipzVpdMap).end())
+ {
+ auto itrToVCEN = (*ipzVpdMap).find(recordName);
+ // The exceptions will be cautght at end.
+ getKwVal(itrToVCEN->second, kwd1, firstKwdValue);
+ getKwVal(itrToVCEN->second, kwd2, secondKwdValue);
+ }
+ else
+ {
+ std::array<const char*, 1> interfaceList = {kwdInterface.c_str()};
+
+ types::MapperGetObject mapperRetValue = dbusUtility::getObjectMap(
+ std::string(constants::systemVpdInvPath), interfaceList);
+
+ if (mapperRetValue.empty())
+ {
+ throw std::runtime_error("Mapper failed to get service");
+ }
+
+ const std::string& serviceName = std::get<0>(mapperRetValue.at(0));
+
+ auto retVal = dbusUtility::readDbusProperty(
+ serviceName, std::string(constants::systemVpdInvPath),
+ kwdInterface, kwd1);
+
+ if (auto kwdVal = std::get_if<types::BinaryVector>(&retVal))
+ {
+ firstKwdValue.assign(
+ reinterpret_cast<const char*>(kwdVal->data()),
+ kwdVal->size());
+ }
+ else
+ {
+ throw std::runtime_error(
+ "Failed to read value of " + kwd1 + " from Bus");
+ }
+
+ retVal = dbusUtility::readDbusProperty(
+ serviceName, std::string(constants::systemVpdInvPath),
+ kwdInterface, kwd2);
+
+ if (auto kwdVal = std::get_if<types::BinaryVector>(&retVal))
+ {
+ secondKwdValue.assign(
+ reinterpret_cast<const char*>(kwdVal->data()),
+ kwdVal->size());
+ }
+ else
+ {
+ throw std::runtime_error(
+ "Failed to read value of " + kwd2 + " from Bus");
+ }
+ }
+
+ if (unexpandedLocationCode.find("fcs") != std::string::npos)
+ {
+ // TODO: See if ND0 can be placed in the JSON
+ expanded.replace(
+ pos, 3, firstKwdValue.substr(0, 4) + ".ND0." + secondKwdValue);
+ }
+ else
+ {
+ replace(firstKwdValue.begin(), firstKwdValue.end(), '-', '.');
+ expanded.replace(pos, 3, firstKwdValue + "." + secondKwdValue);
+ }
+ }
+ catch (const std::exception& ex)
+ {
+ logging::logMessage("Failed to expand location code with exception: " +
+ std::string(ex.what()));
+ }
+
+ return expanded;
+}
+
+/**
+ * @brief An API to get VPD in a vector.
+ *
+ * The vector is required by the respective parser to fill the VPD map.
+ * Note: API throws exception in case of failure. Caller needs to handle.
+ *
+ * @param[in] vpdFilePath - EEPROM path of the FRU.
+ * @param[out] vpdVector - VPD in vector form.
+ * @param[in] vpdStartOffset - Offset of VPD data in EEPROM.
+ */
+inline void getVpdDataInVector(const std::string& vpdFilePath,
+ types::BinaryVector& vpdVector,
+ size_t& vpdStartOffset)
+{
+ try
+ {
+ std::fstream vpdFileStream;
+ vpdFileStream.exceptions(
+ std::ifstream::badbit | std::ifstream::failbit);
+ vpdFileStream.open(vpdFilePath, std::ios::in | std::ios::binary);
+ auto vpdSizeToRead = std::min(std::filesystem::file_size(vpdFilePath),
+ static_cast<uintmax_t>(65504));
+ vpdVector.resize(vpdSizeToRead);
+
+ vpdFileStream.seekg(vpdStartOffset, std::ios_base::beg);
+ vpdFileStream.read(reinterpret_cast<char*>(&vpdVector[0]),
+ vpdSizeToRead);
+
+ vpdVector.resize(vpdFileStream.gcount());
+ vpdFileStream.clear(std::ios_base::eofbit);
+ }
+ catch (const std::ifstream::failure& fail)
+ {
+ std::cerr << "Exception in file handling [" << vpdFilePath
+ << "] error : " << fail.what();
+ throw;
+ }
+}
+
+/**
+ * @brief An API to get D-bus representation of given VPD keyword.
+ *
+ * @param[in] i_keywordName - VPD keyword name.
+ *
+ * @return D-bus representation of given keyword.
+ */
+inline std::string getDbusPropNameForGivenKw(const std::string& i_keywordName)
+{
+ // Check for "#" prefixed VPD keyword.
+ if ((i_keywordName.size() == vpd::constants::TWO_BYTES) &&
+ (i_keywordName.at(0) == constants::POUND_KW))
+ {
+ // D-bus doesn't support "#". Replace "#" with "PD_" for those "#"
+ // prefixed keywords.
+ return (std::string(constants::POUND_KW_PREFIX) +
+ i_keywordName.substr(1));
+ }
+
+ // Return the keyword name back, if D-bus representation is same as the VPD
+ // keyword name.
+ return i_keywordName;
+}
+
+/**
+ * @brief API to find CCIN in parsed VPD map.
+ *
+ * Few FRUs need some special handling. To identify those FRUs CCIN are used.
+ * The API will check from parsed VPD map if the FRU is the one with desired
+ * CCIN.
+ *
+ * @throw std::runtime_error
+ * @throw DataException
+ *
+ * @param[in] i_JsonObject - Any JSON which contains CCIN tag to match.
+ * @param[in] i_parsedVpdMap - Parsed VPD map.
+ * @return True if found, false otherwise.
+ */
+inline bool findCcinInVpd(const nlohmann::json& i_JsonObject,
+ const types::VPDMapVariant& i_parsedVpdMap)
+{
+ if (i_JsonObject.empty())
+ {
+ throw std::runtime_error("Json object is empty. Can't find CCIN");
+ }
+
+ if (auto l_ipzVPDMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
+ {
+ auto l_itrToRec = (*l_ipzVPDMap).find("VINI");
+ if (l_itrToRec == (*l_ipzVPDMap).end())
+ {
+ throw DataException(
+ "VINI record not found in parsed VPD. Can't find CCIN");
+ }
+
+ std::string l_ccinFromVpd;
+ vpdSpecificUtility::getKwVal(l_itrToRec->second, "CC", l_ccinFromVpd);
+ if (l_ccinFromVpd.empty())
+ {
+ throw DataException("Empty CCIN value in VPD map. Can't find CCIN");
+ }
+
+ transform(l_ccinFromVpd.begin(), l_ccinFromVpd.end(),
+ l_ccinFromVpd.begin(), ::toupper);
+
+ for (std::string l_ccinValue : i_JsonObject["ccin"])
+ {
+ transform(l_ccinValue.begin(), l_ccinValue.end(),
+ l_ccinValue.begin(), ::toupper);
+
+ if (l_ccinValue.compare(l_ccinFromVpd) ==
+ constants::STR_CMP_SUCCESS)
+ {
+ // CCIN found
+ return true;
+ }
+ }
+
+ logging::logMessage("No match found for CCIN");
+ return false;
+ }
+
+ logging::logMessage("VPD type not supported. Can't find CCIN");
+ return false;
+}
+
+/**
+ * @brief API to reset data of a FRU populated under PIM.
+ *
+ * This API resets the data for particular interfaces of a FRU under PIM.
+ *
+ * @param[in] i_objectPath - DBus object path of the FRU.
+ * @param[in] io_interfaceMap - Interface and its properties map.
+ */
+inline void resetDataUnderPIM(const std::string& i_objectPath,
+ types::InterfaceMap& io_interfaceMap)
+{
+ try
+ {
+ std::array<const char*, 0> l_interfaces;
+ const types::MapperGetObject& l_getObjectMap =
+ dbusUtility::getObjectMap(i_objectPath, l_interfaces);
+
+ const std::vector<std::string>& l_vpdRelatedInterfaces{
+ constants::operationalStatusInf, constants::inventoryItemInf,
+ constants::assetInf};
+
+ for (const auto& [l_service, l_interfaceList] : l_getObjectMap)
+ {
+ if (l_service.compare(constants::pimServiceName) !=
+ constants::STR_CMP_SUCCESS)
+ {
+ continue;
+ }
+
+ for (const auto& l_interface : l_interfaceList)
+ {
+ if ((l_interface.find(constants::ipzVpdInf) !=
+ std::string::npos) ||
+ ((std::find(l_vpdRelatedInterfaces.begin(),
+ l_vpdRelatedInterfaces.end(), l_interface)) !=
+ l_vpdRelatedInterfaces.end()))
+ {
+ const types::PropertyMap& l_propertyValueMap =
+ dbusUtility::getPropertyMap(l_service, i_objectPath,
+ l_interface);
+
+ types::PropertyMap l_propertyMap;
+
+ for (const auto& l_aProperty : l_propertyValueMap)
+ {
+ const std::string& l_propertyName = l_aProperty.first;
+ const auto& l_propertyValue = l_aProperty.second;
+
+ if (std::holds_alternative<types::BinaryVector>(
+ l_propertyValue))
+ {
+ l_propertyMap.emplace(l_propertyName,
+ types::BinaryVector{});
+ }
+ else if (std::holds_alternative<std::string>(
+ l_propertyValue))
+ {
+ l_propertyMap.emplace(l_propertyName,
+ std::string{});
+ }
+ else if (std::holds_alternative<bool>(l_propertyValue))
+ {
+ // ToDo -- Update the functional status property
+ // to true.
+ if (l_propertyName.compare("Present") ==
+ constants::STR_CMP_SUCCESS)
+ {
+ l_propertyMap.emplace(l_propertyName, false);
+ }
+ }
+ }
+ io_interfaceMap.emplace(l_interface,
+ std::move(l_propertyMap));
+ }
+ }
+ }
+ }
+ catch (const std::exception& l_ex)
+ {
+ logging::logMessage("Failed to remove VPD for FRU: " + i_objectPath +
+ " with error: " + std::string(l_ex.what()));
+ }
+}
+} // namespace vpdSpecificUtility
+} // namespace vpd
diff --git a/vpd-manager/include/worker.hpp b/vpd-manager/include/worker.hpp
new file mode 100644
index 0000000..85a1818
--- /dev/null
+++ b/vpd-manager/include/worker.hpp
@@ -0,0 +1,529 @@
+#pragma once
+
+#include "constants.hpp"
+#include "types.hpp"
+
+#include <nlohmann/json.hpp>
+
+#include <mutex>
+#include <optional>
+#include <semaphore>
+#include <tuple>
+
+namespace vpd
+{
+/**
+ * @brief A class to process and publish VPD data.
+ *
+ * The class works on VPD and is mainly responsible for following tasks:
+ * 1) Select appropriate device tree and JSON. Reboot if required.
+ * 2) Get desired parser using parser factory.
+ * 3) Calling respective parser class to get parsed VPD.
+ * 4) Arranging VPD data under required interfaces.
+ * 5) Calling PIM to publish VPD.
+ *
+ * The class may also implement helper functions required for VPD handling.
+ */
+class Worker
+{
+ public:
+ /**
+ * List of deleted functions.
+ */
+ Worker(const Worker&);
+ Worker& operator=(const Worker&);
+ Worker(Worker&&) = delete;
+
+ /**
+ * @brief Constructor.
+ *
+ * In case the processing is not JSON based, no argument needs to be passed.
+ * Constructor will also, based on symlink pick the correct JSON and
+ * initialize the parsed JSON variable.
+ *
+ * @param[in] pathToConfigJSON - Path to the config JSON, if applicable.
+ *
+ * Note: Throws std::exception in case of construction failure. Caller needs
+ * to handle to detect successful object creation.
+ */
+ Worker(std::string pathToConfigJson = std::string());
+
+ /**
+ * @brief Destructor
+ */
+ ~Worker() = default;
+
+#ifdef IBM_SYSTEM
+ /**
+ * @brief API to perform initial setup before manager claims Bus name.
+ *
+ * Before BUS name for VPD-Manager is claimed, fitconfig whould be set for
+ * corret device tree, inventory JSON w.r.t system should be linked and
+ * system VPD should be on DBus.
+ */
+ void performInitialSetup();
+#endif
+
+ /**
+ * @brief An API to check if system VPD is already published.
+ *
+ * @return Status, true if system is already collected else false.
+ */
+ bool isSystemVPDOnDBus() const;
+
+ /**
+ * @brief API to process all FRUs presnt in config JSON file.
+ *
+ * This API based on config JSON passed/selected for the system, will
+ * trigger parser for all the FRUs and publish it on DBus.
+ *
+ * Note: Config JSON file path should be passed to worker class constructor
+ * to make use of this API.
+ *
+ */
+ void collectFrusFromJson();
+
+ /**
+ * @brief API to parse VPD data
+ *
+ * @param[in] i_vpdFilePath - Path to the VPD file.
+ */
+ types::VPDMapVariant parseVpdFile(const std::string& i_vpdFilePath);
+
+ /**
+ * @brief An API to populate DBus interfaces for a FRU.
+ *
+ * Note: Call this API to populate D-Bus. Also caller should handle empty
+ * objectInterfaceMap.
+ *
+ * @param[in] parsedVpdMap - Parsed VPD as a map.
+ * @param[out] objectInterfaceMap - Object and its interfaces map.
+ * @param[in] vpdFilePath - EEPROM path of FRU.
+ */
+ void populateDbus(const types::VPDMapVariant& parsedVpdMap,
+ types::ObjectMap& objectInterfaceMap,
+ const std::string& vpdFilePath);
+
+ /**
+ * @brief An API to delete FRU VPD over DBus.
+ *
+ * @param[in] i_dbusObjPath - Dbus object path of the FRU.
+ *
+ * @throw std::runtime_error if given input path is empty.
+ */
+ void deleteFruVpd(const std::string& i_dbusObjPath);
+
+ /**
+ * @brief API to get status of VPD collection process.
+ *
+ * @return - True when done, false otherwise.
+ */
+ inline bool isAllFruCollectionDone() const
+ {
+ return m_isAllFruCollected;
+ }
+
+ /**
+ * @brief API to get system config JSON object
+ *
+ * @return System config JSON object.
+ */
+ inline nlohmann::json getSysCfgJsonObj() const
+ {
+ return m_parsedJson;
+ }
+
+ /**
+ * @brief API to get active thread count.
+ *
+ * Each FRU is collected in a separate thread. This API gives the active
+ * thread collecting FRU's VPD at any given time.
+ *
+ * @return Count of active threads.
+ */
+ size_t getActiveThreadCount() const
+ {
+ return m_activeCollectionThreadCount;
+ }
+
+ private:
+ /**
+ * @brief An API to parse and publish a FRU VPD over D-Bus.
+ *
+ * Note: This API will handle all the exceptions internally and will only
+ * return status of parsing and publishing of VPD over D-Bus.
+ *
+ * @param[in] i_vpdFilePath - Path of file containing VPD.
+ * @return Tuple of status and file path. Status, true if successfull else
+ * false.
+ */
+ std::tuple<bool, std::string>
+ parseAndPublishVPD(const std::string& i_vpdFilePath);
+
+ /**
+ * @brief An API to set appropriate device tree and JSON.
+ *
+ * This API based on system chooses corresponding device tree and JSON.
+ * If device tree change is required, it updates the "fitconfig" and reboots
+ * the system. Else it is NOOP.
+ *
+ * @throw std::runtime_error
+ */
+ void setDeviceTreeAndJson();
+
+ /**
+ * @brief API to select system specific JSON.
+ *
+ * The API based on the IM value of VPD, will select appropriate JSON for
+ * the system. In case no system is found corresponding to the extracted IM
+ * value, error will be logged.
+ *
+ * @param[out] systemJson - System JSON name.
+ * @param[in] parsedVpdMap - Parsed VPD map.
+ */
+ void getSystemJson(std::string& systemJson,
+ const types::VPDMapVariant& parsedVpdMap);
+
+ /**
+ * @brief An API to read IM value from VPD.
+ *
+ * Note: Throws exception in case of error. Caller need to handle.
+ *
+ * @param[in] parsedVpd - Parsed VPD.
+ */
+ std::string getIMValue(const types::IPZVpdMap& parsedVpd) const;
+
+ /**
+ * @brief An API to read HW version from VPD.
+ *
+ * Note: Throws exception in case of error. Caller need to handle.
+ *
+ * @param[in] parsedVpd - Parsed VPD.
+ */
+ std::string getHWVersion(const types::IPZVpdMap& parsedVpd) const;
+
+ /**
+ * @brief An API to parse given VPD file path.
+ *
+ * @param[in] vpdFilePath - EEPROM file path.
+ * @param[out] parsedVpd - Parsed VPD as a map.
+ */
+ void fillVPDMap(const std::string& vpdFilePath,
+ types::VPDMapVariant& parsedVpd);
+
+ /**
+ * @brief An API to parse and publish system VPD on D-Bus.
+ *
+ * Note: Throws exception in case of invalid VPD format.
+ *
+ * @param[in] parsedVpdMap - Parsed VPD as a map.
+ */
+ void publishSystemVPD(const types::VPDMapVariant& parsedVpdMap);
+
+ /**
+ * @brief An API to process extrainterfaces w.r.t a FRU.
+ *
+ * @param[in] singleFru - JSON block for a single FRU.
+ * @param[out] interfaces - Map to hold interface along with its properties.
+ * @param[in] parsedVpdMap - Parsed VPD as a map.
+ */
+ void processExtraInterfaces(const nlohmann::json& singleFru,
+ types::InterfaceMap& interfaces,
+ const types::VPDMapVariant& parsedVpdMap);
+
+ /**
+ * @brief An API to process embedded and synthesized FRUs.
+ *
+ * @param[in] singleFru - FRU to be processed.
+ * @param[out] interfaces - Map to hold interface along with its properties.
+ */
+ void processEmbeddedAndSynthesizedFrus(const nlohmann::json& singleFru,
+ types::InterfaceMap& interfaces);
+
+ /**
+ * @brief An API to read process FRU based in CCIN.
+ *
+ * For some FRUs VPD can be processed only if the FRU has some specific
+ * value for CCIN. In case the value is not from that set, VPD for those
+ * FRUs can't be processed.
+ *
+ * @param[in] singleFru - Fru whose CCIN value needs to be matched.
+ * @param[in] parsedVpdMap - Parsed VPD map.
+ */
+ bool processFruWithCCIN(const nlohmann::json& singleFru,
+ const types::VPDMapVariant& parsedVpdMap);
+
+ /**
+ * @brief API to process json's inherit flag.
+ *
+ * Inherit flag denotes that some property in the child FRU needs to be
+ * inherited from parent FRU.
+ *
+ * @param[in] parsedVpdMap - Parsed VPD as a map.
+ * @param[out] interfaces - Map to hold interface along with its properties.
+ */
+ void processInheritFlag(const types::VPDMapVariant& parsedVpdMap,
+ types::InterfaceMap& interfaces);
+
+ /**
+ * @brief API to process json's "copyRecord" flag.
+ *
+ * copyRecord flag denotes if some record data needs to be copies in the
+ * given FRU.
+ *
+ * @param[in] singleFru - FRU being processed.
+ * @param[in] parsedVpdMap - Parsed VPD as a map.
+ * @param[out] interfaces - Map to hold interface along with its properties.
+ */
+ void processCopyRecordFlag(const nlohmann::json& singleFru,
+ const types::VPDMapVariant& parsedVpdMap,
+ types::InterfaceMap& interfaces);
+
+ /**
+ * @brief An API to populate IPZ VPD property map.
+ *
+ * @param[out] interfacePropMap - Map of interface and properties under it.
+ * @param[in] keyordValueMap - Keyword value map of IPZ VPD.
+ * @param[in] interfaceName - Name of the interface.
+ */
+ void populateIPZVPDpropertyMap(types::InterfaceMap& interfacePropMap,
+ const types::IPZKwdValueMap& keyordValueMap,
+ const std::string& interfaceName);
+
+ /**
+ * @brief An API to populate Kwd VPD property map.
+ *
+ * @param[in] keyordValueMap - Keyword value map of Kwd VPD.
+ * @param[out] interfaceMap - interface and property,value under it.
+ */
+ void populateKwdVPDpropertyMap(const types::KeywordVpdMap& keyordVPDMap,
+ types::InterfaceMap& interfaceMap);
+
+ /**
+ * @brief API to populate all required interface for a FRU.
+ *
+ * @param[in] interfaceJson - JSON containing interfaces to be populated.
+ * @param[out] interfaceMap - Map to hold populated interfaces.
+ * @param[in] parsedVpdMap - Parsed VPD as a map.
+ */
+ void populateInterfaces(const nlohmann::json& interfaceJson,
+ types::InterfaceMap& interfaceMap,
+ const types::VPDMapVariant& parsedVpdMap);
+
+ /**
+ * @brief Helper function to insert or merge in map.
+ *
+ * This method checks in the given inventory::InterfaceMap if the given
+ * interface key is existing or not. If the interface key already exists,
+ * given property map is inserted into it. If the key does'nt exist then
+ * given interface and property map pair is newly created. If the property
+ * present in propertymap already exist in the InterfaceMap, then the new
+ * property value is ignored.
+ *
+ * @param[in,out] interfaceMap - map object of type inventory::InterfaceMap
+ * only.
+ * @param[in] interface - Interface name.
+ * @param[in] property - new property map that needs to be emplaced.
+ */
+ void insertOrMerge(types::InterfaceMap& interfaceMap,
+ const std::string& interface,
+ types::PropertyMap&& property);
+
+ /**
+ * @brief Check if the given CPU is an IO only chip.
+ *
+ * The CPU is termed as IO, whose all of the cores are bad and can never be
+ * used. Those CPU chips can be used for IO purpose like connecting PCIe
+ * devices etc., The CPU whose every cores are bad, can be identified from
+ * the CP00 record's PG keyword, only if all of the 8 EQs' value equals
+ * 0xE7F9FF. (1EQ has 4 cores grouped together by sharing its cache memory.)
+ *
+ * @param [in] pgKeyword - PG Keyword of CPU.
+ * @return true if the given cpu is an IO, false otherwise.
+ */
+ bool isCPUIOGoodOnly(const std::string& pgKeyword);
+
+ /**
+ * @brief API to prime inventory Objects.
+ *
+ * @param[in] i_vpdFilePath - EEPROM file path.
+ * @return true if the prime inventory is success, false otherwise.
+ */
+ bool primeInventory(const std::string& i_vpdFilePath);
+
+ /**
+ * @brief API to process preAction(base_action) defined in config JSON.
+ *
+ * @note sequence of tags under any given flag of preAction is EXTREMELY
+ * important to ensure proper processing. The API will process all the
+ * nested items under the base action sequentially. Also if any of the tag
+ * processing fails, the code will not process remaining tags under the
+ * flag.
+ * ******** sample format **************
+ * fru EEPROM path: {
+ * base_action: {
+ * flag1: {
+ * tag1: {
+ * },
+ * tag2: {
+ * }
+ * }
+ * flag2: {
+ * tags: {
+ * }
+ * }
+ * }
+ * }
+ * *************************************
+ *
+ * @param[in] i_vpdFilePath - Path to the EEPROM file.
+ * @param[in] i_flagToProcess - To identify which flag(s) needs to be
+ * processed under PreAction tag of config JSON.
+ * @return Execution status.
+ */
+ bool processPreAction(const std::string& i_vpdFilePath,
+ const std::string& i_flagToProcess);
+
+ /**
+ * @brief API to process postAction(base_action) defined in config JSON.
+ *
+ * @note Sequence of tags under any given flag of postAction is EXTREMELY
+ * important to ensure proper processing. The API will process all the
+ * nested items under the base action sequentially. Also if any of the tag
+ * processing fails, the code will not process remaining tags under the
+ * flag.
+ * ******** sample format **************
+ * fru EEPROM path: {
+ * base_action: {
+ * flag1: {
+ * tag1: {
+ * },
+ * tag2: {
+ * }
+ * }
+ * flag2: {
+ * tags: {
+ * }
+ * }
+ * }
+ * }
+ * *************************************
+ * Also, if post action is required to be processed only for FRUs with
+ * certain CCIN then CCIN list can be provided under flag.
+ *
+ * @param[in] i_vpdFruPath - Path to the EEPROM file.
+ * @param[in] i_flagToProcess - To identify which flag(s) needs to be
+ * processed under postAction tag of config JSON.
+ * @param[in] i_parsedVpd - Optional Parsed VPD map. If CCIN match is
+ * required.
+ * @return Execution status.
+ */
+ bool processPostAction(
+ const std::string& i_vpdFruPath, const std::string& i_flagToProcess,
+ const std::optional<types::VPDMapVariant> i_parsedVpd = std::nullopt);
+
+ /**
+ * @brief Function to enable and bring MUX out of idle state.
+ *
+ * This finds all the MUX defined in the system json and enables them by
+ * setting the holdidle parameter to 0.
+ *
+ * @throw std::runtime_error
+ */
+ void enableMuxChips();
+
+ /**
+ * @brief An API to perform backup or restore of VPD.
+ *
+ * @param[in,out] io_srcVpdMap - Source VPD map.
+ */
+ void performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap);
+
+ /**
+ * @brief API to update "Functional" property.
+ *
+ * The API sets the default value for "Functional" property once if the
+ * property is not yet populated over DBus. As the property value is not
+ * controlled by the VPD-Collection process, if it is found already
+ * populated, the functions skips re-populating the property so that already
+ * existing value can be retained.
+ *
+ * @param[in] i_inventoryObjPath - Inventory path as read from config JSON.
+ * @param[in] io_interfaces - Map to hold all the interfaces for the FRU.
+ */
+ void processFunctionalProperty(const std::string& i_inventoryObjPath,
+ types::InterfaceMap& io_interfaces);
+
+ /**
+ * @brief API to update "enabled" property.
+ *
+ * The API sets the default value for "enabled" property once if the
+ * property is not yet populated over DBus. As the property value is not
+ * controlled by the VPD-Collection process, if it is found already
+ * populated, the functions skips re-populating the property so that already
+ * existing value can be retained.
+ *
+ * @param[in] i_inventoryObjPath - Inventory path as read from config JSON.
+ * @param[in] io_interfaces - Map to hold all the interfaces for the FRU.
+ */
+ void processEnabledProperty(const std::string& i_inventoryObjPath,
+ types::InterfaceMap& io_interfaces);
+
+ /**
+ * @brief API to form asset tag string for the system.
+ *
+ * @param[in] i_parsedVpdMap - Parsed VPD map.
+ *
+ * @throw std::runtime_error
+ *
+ * @return - Formed asset tag string.
+ */
+ std::string
+ createAssetTagString(const types::VPDMapVariant& i_parsedVpdMap);
+
+ /**
+ * @brief API to prime system blueprint.
+ *
+ * The API will traverse the system config JSON and will prime all the FRU
+ * paths which qualifies for priming.
+ */
+ void primeSystemBlueprint();
+
+ /**
+ * @brief API to set symbolic link for system config JSON.
+ *
+ * Once correct device tree is set, symbolic link to the correct sytsem
+ * config JSON is set to be used in subsequent BMC boot.
+ *
+ * @param[in] i_systemJson - system config JSON.
+ */
+ void setJsonSymbolicLink(const std::string& i_systemJson);
+
+ // Parsed JSON file.
+ nlohmann::json m_parsedJson{};
+
+ // Hold if symlink is present or not.
+ bool m_isSymlinkPresent = false;
+
+ // Path to config JSON if applicable.
+ std::string& m_configJsonPath;
+
+ // Keeps track of active thread(s) doing VPD collection.
+ size_t m_activeCollectionThreadCount = 0;
+
+ // Holds status, if VPD collection has been done or not.
+ // Note: This variable does not give information about successfull or failed
+ // collection. It just states, if the VPD collection process is over or not.
+ bool m_isAllFruCollected = false;
+
+ // To distinguish the factory reset path.
+ bool m_isFactoryResetDone = false;
+
+ // Mutex to guard critical resource m_activeCollectionThreadCount.
+ std::mutex m_mutex;
+
+ // Counting semaphore to limit the number of threads.
+ std::counting_semaphore<constants::MAX_THREADS> m_semaphore{
+ constants::MAX_THREADS};
+};
+} // namespace vpd
diff --git a/vpd-manager/manager.cpp b/vpd-manager/manager.cpp
deleted file mode 100644
index 8913474..0000000
--- a/vpd-manager/manager.cpp
+++ /dev/null
@@ -1,918 +0,0 @@
-#include "config.h"
-
-#include "manager.hpp"
-
-#include "common_utility.hpp"
-#include "editor_impl.hpp"
-#include "ibm_vpd_utils.hpp"
-#include "ipz_parser.hpp"
-#include "parser_factory.hpp"
-#include "reader_impl.hpp"
-#include "vpd_exceptions.hpp"
-
-#include <unistd.h>
-
-#include <phosphor-logging/elog-errors.hpp>
-#include <xyz/openbmc_project/Common/error.hpp>
-
-#include <filesystem>
-
-using namespace openpower::vpd::constants;
-using namespace openpower::vpd::inventory;
-using namespace openpower::vpd::manager::editor;
-using namespace openpower::vpd::manager::reader;
-using namespace std;
-using namespace openpower::vpd::parser;
-using namespace openpower::vpd::parser::factory;
-using namespace openpower::vpd::ipz::parser;
-using namespace openpower::vpd::exceptions;
-using namespace phosphor::logging;
-
-namespace openpower
-{
-namespace vpd
-{
-namespace manager
-{
-
-Manager::Manager(std::shared_ptr<boost::asio::io_context>& ioCon,
- std::shared_ptr<sdbusplus::asio::dbus_interface>& iFace,
- std::shared_ptr<sdbusplus::asio::connection>& conn) :
- ioContext(ioCon), interface(iFace), conn(conn)
-{
- interface->register_method(
- "WriteKeyword",
- [this](const sdbusplus::message::object_path& path,
- const std::string& recordName, const std::string& keyword,
- const Binary& value) {
- this->writeKeyword(path, recordName, keyword, value);
- });
-
- interface->register_method(
- "GetFRUsByUnexpandedLocationCode",
- [this](const std::string& locationCode,
- const uint16_t nodeNumber) -> inventory::ListOfPaths {
- return this->getFRUsByUnexpandedLocationCode(locationCode,
- nodeNumber);
- });
-
- interface->register_method(
- "GetFRUsByExpandedLocationCode",
- [this](const std::string& locationCode) -> inventory::ListOfPaths {
- return this->getFRUsByExpandedLocationCode(locationCode);
- });
-
- interface->register_method(
- "GetExpandedLocationCode",
- [this](const std::string& locationCode,
- const uint16_t nodeNumber) -> std::string {
- return this->getExpandedLocationCode(locationCode, nodeNumber);
- });
-
- interface->register_method("PerformVPDRecollection", [this]() {
- this->performVPDRecollection();
- });
-
- interface->register_method(
- "deleteFRUVPD", [this](const sdbusplus::message::object_path& path) {
- this->deleteFRUVPD(path);
- });
-
- interface->register_method(
- "CollectFRUVPD", [this](const sdbusplus::message::object_path& path) {
- this->collectFRUVPD(path);
- });
-
- sd_bus_default(&sdBus);
- initManager();
-}
-
-void Manager::initManager()
-{
- try
- {
- processJSON();
- restoreSystemVpd();
- listenHostState();
- listenAssetTag();
-
- // Create an instance of the BIOS handler
- biosHandler = std::make_shared<BiosHandler>(conn, *this);
-
- // instantiate gpioMonitor class
- gpioMon = std::make_shared<GpioMonitor>(jsonFile, ioContext);
- }
- catch (const std::exception& e)
- {
- std::cerr << e.what() << "\n";
- }
-}
-
-/**
- * @brief An api to get list of blank system VPD properties.
- * @param[in] vpdMap - IPZ vpd map.
- * @param[in] objectPath - Object path for the FRU.
- * @param[out] blankPropertyList - Properties which are blank in System VPD and
- * needs to be updated as standby.
- */
-static void
- getListOfBlankSystemVpd(Parsed& vpdMap, const string& objectPath,
- std::vector<RestoredEeproms>& blankPropertyList)
-{
- for (const auto& systemRecKwdPair : svpdKwdMap)
- {
- auto it = vpdMap.find(systemRecKwdPair.first);
-
- // check if record is found in map we got by parser
- if (it != vpdMap.end())
- {
- const auto& kwdListForRecord = systemRecKwdPair.second;
- for (const auto& keywordInfo : kwdListForRecord)
- {
- const auto& keyword = get<0>(keywordInfo);
-
- DbusPropertyMap& kwdValMap = it->second;
- auto iterator = kwdValMap.find(keyword);
-
- if (iterator != kwdValMap.end())
- {
- string& kwdValue = iterator->second;
-
- // check bus data
- const string& recordName = systemRecKwdPair.first;
- const string& busValue = readBusProperty(
- objectPath, ipzVpdInf + recordName, keyword);
-
- const auto& defaultValue = get<1>(keywordInfo);
-
- if (Binary(busValue.begin(), busValue.end()) !=
- defaultValue)
- {
- if (Binary(kwdValue.begin(), kwdValue.end()) ==
- defaultValue)
- {
- // implies data is blank on EEPROM but not on cache.
- // So EEPROM vpd update is required.
- Binary busData(busValue.begin(), busValue.end());
-
- blankPropertyList.push_back(std::make_tuple(
- objectPath, recordName, keyword, busData));
- }
- }
- }
- }
- }
- }
-}
-
-void Manager::restoreSystemVpd()
-{
- std::cout << "Attempting system VPD restore" << std::endl;
- ParserInterface* parser = nullptr;
- try
- {
- auto vpdVector = getVpdDataInVector(jsonFile, systemVpdFilePath);
- uint32_t vpdStartOffset = 0;
- const auto& inventoryPath =
- jsonFile["frus"][systemVpdFilePath][0]["inventoryPath"]
- .get_ref<const nlohmann::json::string_t&>();
-
- parser = ParserFactory::getParser(vpdVector, (pimPath + inventoryPath),
- systemVpdFilePath, vpdStartOffset);
- auto parseResult = parser->parse();
-
- if (auto pVal = std::get_if<Store>(&parseResult))
- {
- // map to hold all the keywords whose value is blank and
- // needs to be updated at standby.
- std::vector<RestoredEeproms> blankSystemVpdProperties{};
- getListOfBlankSystemVpd(pVal->getVpdMap(), SYSTEM_OBJECT,
- blankSystemVpdProperties);
-
- // if system VPD restore is required, update the
- // EEPROM
- for (const auto& item : blankSystemVpdProperties)
- {
- std::cout << "Restoring keyword: " << std::get<2>(item)
- << std::endl;
- writeKeyword(std::get<0>(item), std::get<1>(item),
- std::get<2>(item), std::get<3>(item));
- }
- }
- else
- {
- std::cerr << "Not a valid format to restore system VPD"
- << std::endl;
- }
- }
- catch (const std::exception& e)
- {
- std::cerr << "Failed to restore system VPD due to exception: "
- << e.what() << std::endl;
- }
- // release the parser object
- ParserFactory::freeParser(parser);
-}
-
-void Manager::listenHostState()
-{
- static std::shared_ptr<sdbusplus::bus::match_t> hostState =
- std::make_shared<sdbusplus::bus::match_t>(
- *conn,
- sdbusplus::bus::match::rules::propertiesChanged(
- "/xyz/openbmc_project/state/host0",
- "xyz.openbmc_project.State.Host"),
- [this](sdbusplus::message_t& msg) { hostStateCallBack(msg); });
-}
-
-void Manager::checkEssentialFrus()
-{
- for (const auto& invPath : essentialFrus)
- {
- const auto res = readBusProperty(invPath, invItemIntf, "Present");
-
- // implies the essential FRU is missing. Log PEL.
- if (res == "false")
- {
- auto rc = sd_bus_call_method_async(
- sdBus, NULL, loggerService, loggerObjectPath,
- loggerCreateInterface, "Create", NULL, NULL, "ssa{ss}",
- errIntfForEssentialFru,
- "xyz.openbmc_project.Logging.Entry.Level.Warning", 2,
- "DESCRIPTION", "Essential fru missing from the system.",
- "CALLOUT_INVENTORY_PATH", (pimPath + invPath).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 Manager::hostStateCallBack(sdbusplus::message_t& msg)
-{
- if (msg.is_method_error())
- {
- std::cerr << "Error in reading signal " << std::endl;
- }
-
- Path object;
- PropertyMap propMap;
- msg.read(object, propMap);
- const auto itr = propMap.find("CurrentHostState");
- if (itr != propMap.end())
- {
- if (auto hostState = std::get_if<std::string>(&(itr->second)))
- {
- // implies system is moving from standby to power on state
- if (*hostState == "xyz.openbmc_project.State.Host.HostState."
- "TransitioningToRunning")
- {
- // detect if essential frus are present in the system.
- checkEssentialFrus();
-
- // check and perform recollection for FRUs replaceable at
- // standby.
- performVPDRecollection();
- return;
- }
- }
- }
-}
-
-void Manager::listenAssetTag()
-{
- static std::shared_ptr<sdbusplus::bus::match_t> assetMatcher =
- std::make_shared<sdbusplus::bus::match_t>(
- *conn,
- sdbusplus::bus::match::rules::propertiesChanged(
- "/xyz/openbmc_project/inventory/system",
- "xyz.openbmc_project.Inventory.Decorator.AssetTag"),
- [this](sdbusplus::message_t& msg) { assetTagCallback(msg); });
-}
-
-void Manager::assetTagCallback(sdbusplus::message_t& msg)
-{
- if (msg.is_method_error())
- {
- std::cerr << "Error in reading signal " << std::endl;
- }
-
- Path object;
- PropertyMap propMap;
- msg.read(object, propMap);
- const auto itr = propMap.find("AssetTag");
- if (itr != propMap.end())
- {
- if (auto assetTag = std::get_if<std::string>(&(itr->second)))
- {
- // Call Notify to persist the AssetTag
- inventory::ObjectMap objectMap = {
- {std::string{"/system"},
- {{"xyz.openbmc_project.Inventory.Decorator.AssetTag",
- {{"AssetTag", *assetTag}}}}}};
-
- common::utility::callPIM(std::move(objectMap));
- }
- else
- {
- std::cerr << "Failed to read asset tag" << std::endl;
- }
- }
-}
-
-void Manager::processJSON()
-{
- std::ifstream json(INVENTORY_JSON_SYM_LINK, std::ios::binary);
-
- if (!json)
- {
- throw std::runtime_error("json file not found");
- }
-
- jsonFile = nlohmann::json::parse(json);
- if (jsonFile.find("frus") == jsonFile.end())
- {
- throw std::runtime_error("frus group not found in json");
- }
-
- const nlohmann::json& groupFRUS =
- jsonFile["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)
- {
- bool isMotherboard = false;
- std::string redundantPath;
-
- if (itemEEPROM["extraInterfaces"].find(
- "xyz.openbmc_project.Inventory.Item.Board.Motherboard") !=
- itemEEPROM["extraInterfaces"].end())
- {
- isMotherboard = true;
- }
- if (itemEEPROM.find("redundantEeprom") != itemEEPROM.end())
- {
- redundantPath = itemEEPROM["redundantEeprom"]
- .get_ref<const nlohmann::json::string_t&>();
- }
- frus.emplace(
- itemEEPROM["inventoryPath"]
- .get_ref<const nlohmann::json::string_t&>(),
- std::make_tuple(itemFRUS.key(), redundantPath, isMotherboard));
-
- if (itemEEPROM["extraInterfaces"].find(IBM_LOCATION_CODE_INF) !=
- itemEEPROM["extraInterfaces"].end())
- {
- fruLocationCode.emplace(
- itemEEPROM["extraInterfaces"][IBM_LOCATION_CODE_INF]
- ["LocationCode"]
- .get_ref<const nlohmann::json::string_t&>(),
- itemEEPROM["inventoryPath"]
- .get_ref<const nlohmann::json::string_t&>());
- }
-
- if (itemEEPROM.value("replaceableAtStandby", false))
- {
- replaceableFrus.emplace_back(itemFRUS.key());
- }
-
- if (itemEEPROM.value("essentialFru", false))
- {
- essentialFrus.emplace_back(itemEEPROM["inventoryPath"]);
- }
- }
- }
-}
-
-void Manager::updateSystemVPDBackUpFRU(const std::string& recordName,
- const std::string& keyword,
- const Binary& value)
-{
- const std::string& systemVpdBackupPath =
- jsonFile["frus"][systemVpdFilePath].at(0).value("systemVpdBackupPath",
- "");
-
- if (!systemVpdBackupPath.empty() &&
- jsonFile["frus"][systemVpdBackupPath].at(0).contains("inventoryPath"))
- {
- std::string systemVpdBackupInvPath =
- jsonFile["frus"][systemVpdBackupPath][0]["inventoryPath"]
- .get_ref<const nlohmann::json::string_t&>();
-
- const auto& itr = svpdKwdMap.find(recordName);
- if (itr != svpdKwdMap.end())
- {
- auto systemKwdInfoList = itr->second;
- const auto& itrToKwd =
- find_if(systemKwdInfoList.begin(), systemKwdInfoList.end(),
- [&keyword](const auto& kwdInfo) {
- return (keyword == std::get<0>(kwdInfo));
- });
-
- if (itrToKwd != systemKwdInfoList.end())
- {
- EditorImpl edit(systemVpdBackupPath, jsonFile,
- std::get<4>(*itrToKwd), std::get<5>(*itrToKwd),
- systemVpdBackupInvPath);
-
- // Setup offset, if any
- uint32_t offset = 0;
- if (jsonFile["frus"][systemVpdBackupPath].at(0).contains(
- "offset"))
- {
- offset =
- jsonFile["frus"][systemVpdBackupPath].at(0).contains(
- "offset");
- }
-
- edit.updateKeyword(value, offset, true);
- }
- }
- }
- else
- {
- if (systemVpdBackupPath.empty())
- {
- throw std::runtime_error(
- "Invalid entry for systemVpdBackupPath in JSON");
- }
- else
- {
- throw std::runtime_error(
- "Inventory path missing for systemVpdBackupPath");
- }
- }
-}
-
-void Manager::writeKeyword(const sdbusplus::message::object_path& path,
- const std::string& recordName,
- const std::string& keyword, const Binary& value)
-{
- try
- {
- std::string objPath{path};
- // Strip any inventory prefix in path
- if (objPath.find(INVENTORY_PATH) == 0)
- {
- objPath = objPath.substr(sizeof(INVENTORY_PATH) - 1);
- }
-
- if (frus.find(objPath) == frus.end())
- {
- throw std::runtime_error("Inventory path not found");
- }
-
- inventory::Path vpdFilePath = std::get<0>(frus.find(objPath)->second);
-
- // instantiate editor class to update the data
- EditorImpl edit(vpdFilePath, jsonFile, recordName, keyword, objPath);
-
- uint32_t offset = 0;
- // Setup offset, if any
- for (const auto& item : jsonFile["frus"][vpdFilePath])
- {
- if (item.find("offset") != item.end())
- {
- offset = item["offset"];
- break;
- }
- }
-
- edit.updateKeyword(value, offset, true);
-
- // If system VPD is being updated and system VPD is marked for back up
- // on another FRU, update data on back up as well.
- if (objPath == sdbusplus::message::object_path{SYSTEM_OBJECT} &&
- jsonFile["frus"][systemVpdFilePath].at(0).contains(
- "systemVpdBackupPath"))
- {
- updateSystemVPDBackUpFRU(recordName, keyword, value);
- }
-
- // If we have a redundant EEPROM to update, then update just the EEPROM,
- // not the cache since that is already done when we updated the primary
- if (!std::get<1>(frus.find(objPath)->second).empty())
- {
- EditorImpl edit(std::get<1>(frus.find(objPath)->second), jsonFile,
- recordName, keyword, objPath);
- edit.updateKeyword(value, offset, false);
- }
-
- // if it is a motehrboard FRU need to check for location expansion
- if (std::get<2>(frus.find(objPath)->second))
- {
- if (recordName == "VCEN" && (keyword == "FC" || keyword == "SE"))
- {
- edit.expandLocationCode("fcs");
- }
- else if (recordName == "VSYS" &&
- (keyword == "TM" || keyword == "SE"))
- {
- edit.expandLocationCode("mts");
- }
- }
-
- return;
- }
- catch (const std::exception& e)
- {
- std::cerr << e.what() << std::endl;
- }
-}
-
-ListOfPaths Manager::getFRUsByUnexpandedLocationCode(
- const LocationCode& locationCode, const NodeNumber nodeNumber)
-{
- ReaderImpl read;
- return read.getFrusAtLocation(locationCode, nodeNumber, fruLocationCode);
-}
-
-ListOfPaths
- Manager::getFRUsByExpandedLocationCode(const LocationCode& locationCode)
-{
- ReaderImpl read;
- return read.getFRUsByExpandedLocationCode(locationCode, fruLocationCode);
-}
-
-LocationCode Manager::getExpandedLocationCode(const LocationCode& locationCode,
- const NodeNumber nodeNumber)
-{
- ReaderImpl read;
- return read.getExpandedLocationCode(locationCode, nodeNumber,
- fruLocationCode);
-}
-
-void Manager::performVPDRecollection()
-{
- // get list of FRUs replaceable at standby
- for (const auto& item : replaceableFrus)
- {
- const vector<nlohmann::json>& groupEEPROM = jsonFile["frus"][item];
- const nlohmann::json& singleFru = groupEEPROM[0];
-
- const string& inventoryPath =
- singleFru["inventoryPath"]
- .get_ref<const nlohmann::json::string_t&>();
-
- bool prePostActionRequired = false;
-
- if ((jsonFile["frus"][item].at(0)).find("preAction") !=
- jsonFile["frus"][item].at(0).end())
- {
- try
- {
- if (!executePreAction(jsonFile, item))
- {
- // if the FRU has preAction defined then its execution
- // should pass to ensure bind/unbind of data.
- // preAction execution failed. should not call
- // bind/unbind.
- log<level::ERR>(
- "Pre-Action execution failed for the FRU",
- entry("ERROR=%s",
- ("Inventory path: " + inventoryPath).c_str()));
-
- // As recollection failed delete FRU data.
- deleteFRUVPD(inventoryPath);
- continue;
- }
- }
- catch (const GpioException& e)
- {
- log<level::ERR>(e.what());
- PelAdditionalData additionalData{};
- additionalData.emplace("DESCRIPTION", e.what());
- createPEL(additionalData, PelSeverity::WARNING,
- errIntfForGpioError, sdBus);
-
- // As recollection failed delete FRU data.
- deleteFRUVPD(inventoryPath);
-
- continue;
- }
- prePostActionRequired = true;
- }
-
- // unbind, bind the driver to trigger parser.
- triggerVpdCollection(singleFru, inventoryPath);
-
- // this check is added to avoid file system expensive call in case not
- // required.
- if (prePostActionRequired)
- {
- // The sleep of 1sec is sliced up in 10 retries of 10 milliseconds
- // each.
- for (auto retryCounter = VALUE_0; retryCounter <= VALUE_10;
- retryCounter++)
- {
- // sleep for 10 millisecond
- if (usleep(VALUE_100000) != VALUE_0)
- {
- std::cout << "Sleep failed before accessing the file"
- << std::endl;
- }
-
- // Check if file showed up
- if (!filesystem::exists(item))
- {
- // Do we need to retry?
- if (retryCounter < VALUE_10)
- {
- continue;
- }
-
- try
- {
- // If not, then take failure postAction
- executePostFailAction(jsonFile, item);
-
- // As recollection failed delete FRU data.
- deleteFRUVPD(inventoryPath);
- }
- catch (const GpioException& e)
- {
- PelAdditionalData additionalData{};
- additionalData.emplace("DESCRIPTION", e.what());
- createPEL(additionalData, PelSeverity::WARNING,
- errIntfForGpioError, sdBus);
-
- // As recollection failed delete FRU data.
- deleteFRUVPD(inventoryPath);
- }
- }
- else
- {
- // bind the LED driver
- string chipAddr = singleFru.value("pcaChipAddress", "");
- cout
- << "performVPDRecollection: Executing driver binding for "
- "chip "
- "address - "
- << chipAddr << endl;
-
- executeCmd(createBindUnbindDriverCmnd(
- chipAddr, "i2c", "leds-pca955x", "/bind"));
-
- // File has been found, kill the retry loop.
- break;
- }
- }
- }
- }
-}
-
-void Manager::collectFRUVPD(const sdbusplus::message::object_path& path)
-{
- std::cout << "Manager called to collect vpd for fru: " << std::string{path}
- << std::endl;
-
- using InvalidArgument =
- sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
- using Argument = xyz::openbmc_project::Common::InvalidArgument;
-
- std::string objPath{path};
-
- // Strip any inventory prefix in path
- if (objPath.find(INVENTORY_PATH) == 0)
- {
- objPath = objPath.substr(sizeof(INVENTORY_PATH) - 1);
- }
-
- // if path not found in Json.
- if (frus.find(objPath) == frus.end())
- {
- elog<InvalidArgument>(Argument::ARGUMENT_NAME("Object Path"),
- Argument::ARGUMENT_VALUE(objPath.c_str()));
- }
-
- inventory::Path vpdFilePath = std::get<0>(frus.find(objPath)->second);
-
- const std::vector<nlohmann::json>& groupEEPROM =
- jsonFile["frus"][vpdFilePath].get_ref<const nlohmann::json::array_t&>();
-
- nlohmann::json singleFru{};
- for (const auto& item : groupEEPROM)
- {
- if (item["inventoryPath"] == objPath)
- {
- // this is the inventory we are looking for
- singleFru = item;
- break;
- }
- }
-
- // check if the device qualifies for CM.
- if (singleFru.value("concurrentlyMaintainable", false))
- {
- bool prePostActionRequired = false;
-
- if ((jsonFile["frus"][vpdFilePath].at(0)).find("preAction") !=
- jsonFile["frus"][vpdFilePath].at(0).end())
- {
- if (!executePreAction(jsonFile, vpdFilePath))
- {
- // if the FRU has preAction defined then its execution should
- // pass to ensure bind/unbind of data.
- // preAction execution failed. should not call bind/unbind.
- log<level::ERR>("Pre-Action execution failed for the FRU");
- return;
- }
-
- prePostActionRequired = true;
- }
-
- // unbind, bind the driver to trigger parser.
- triggerVpdCollection(singleFru, objPath);
-
- // this check is added to avoid file system expensive call in case not
- // required.
- if (prePostActionRequired)
- {
- // Check if device showed up (test for file)
- if (!filesystem::exists(vpdFilePath))
- {
- try
- {
- // If not, then take failure postAction
- executePostFailAction(jsonFile, vpdFilePath);
- }
- catch (const GpioException& e)
- {
- PelAdditionalData additionalData{};
- additionalData.emplace("DESCRIPTION", e.what());
- createPEL(additionalData, PelSeverity::WARNING,
- errIntfForGpioError, sdBus);
- }
- }
- else
- {
- // bind the LED driver
- string chipAddr = jsonFile["frus"][vpdFilePath].at(0).value(
- "pcaChipAddress", "");
- cout << "Executing driver binding for chip address - "
- << chipAddr << endl;
-
- executeCmd(createBindUnbindDriverCmnd(chipAddr, "i2c",
- "leds-pca955x", "/bind"));
- }
- }
- return;
- }
- else
- {
- elog<InvalidArgument>(Argument::ARGUMENT_NAME("Object Path"),
- Argument::ARGUMENT_VALUE(objPath.c_str()));
- }
-}
-
-void Manager::triggerVpdCollection(const nlohmann::json& singleFru,
- const std::string& path)
-{
- if ((singleFru.find("devAddress") == singleFru.end()) ||
- (singleFru.find("driverType") == singleFru.end()) ||
- (singleFru.find("busType") == singleFru.end()))
- {
- // The FRUs is marked for collection but missing mandatory
- // fields for collection. Log error and return.
- log<level::ERR>(
- "Collection Failed as mandatory field missing in Json",
- entry("ERROR=%s", ("Recollection failed for " + (path)).c_str()));
-
- return;
- }
-
- string deviceAddress = singleFru["devAddress"];
- const string& driverType = singleFru["driverType"];
- const string& busType = singleFru["busType"];
-
- // devTreeStatus flag is present in json as false to mention
- // that the EEPROM is not mentioned in device tree. If this flag
- // is absent consider the value to be true, i.e EEPROM is
- // mentioned in device tree
- if (!singleFru.value("devTreeStatus", true))
- {
- auto pos = deviceAddress.find('-');
- if (pos != string::npos)
- {
- string busNum = deviceAddress.substr(0, pos);
- deviceAddress = "0x" + deviceAddress.substr(pos + 1, string::npos);
-
- string deleteDevice =
- "echo" + deviceAddress + " > /sys/bus/" + busType +
- "/devices/" + busType + "-" + busNum + "/delete_device";
- executeCmd(deleteDevice);
-
- string addDevice =
- "echo" + driverType + " " + deviceAddress + " > /sys/bus/" +
- busType + "/devices/" + busType + "-" + busNum + "/new_device";
- executeCmd(addDevice);
- }
- else
- {
- const string& inventoryPath =
- singleFru["inventoryPath"]
- .get_ref<const nlohmann::json::string_t&>();
-
- log<level::ERR>(
- "Wrong format of device address in Json",
- entry("ERROR=%s",
- ("Recollection failed for " + inventoryPath).c_str()));
- }
- }
- else
- {
- executeCmd(createBindUnbindDriverCmnd(deviceAddress, busType,
- driverType, "/unbind"));
- executeCmd(createBindUnbindDriverCmnd(deviceAddress, busType,
- driverType, "/bind"));
- }
-}
-
-void Manager::deleteFRUVPD(const sdbusplus::message::object_path& path)
-{
- std::cout << "Manager called to delete vpd for fru: " << std::string{path}
- << std::endl;
-
- using InvalidArgument =
- sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
- using Argument = xyz::openbmc_project::Common::InvalidArgument;
-
- std::string objPath{path};
-
- // Strip any inventory prefix in path
- if (objPath.find(INVENTORY_PATH) == 0)
- {
- objPath = objPath.substr(sizeof(INVENTORY_PATH) - 1);
- }
-
- // if path not found in Json.
- if (frus.find(objPath) == frus.end())
- {
- elog<InvalidArgument>(Argument::ARGUMENT_NAME("Object Path"),
- Argument::ARGUMENT_VALUE(objPath.c_str()));
- }
-
- inventory::Path& vpdFilePath = std::get<0>(frus.find(objPath)->second);
-
- string chipAddress =
- jsonFile["frus"][vpdFilePath].at(0).value("pcaChipAddress", "");
-
- // if the FRU is present, then unbind the LED driver if any
- if (readBusProperty(objPath, "xyz.openbmc_project.Inventory.Item",
- "Present") == "true")
- {
- // check if we have cxp-port populated for the given object path.
- std::vector<std::string> interfaceList{
- "xyz.openbmc_project.State.Decorator.OperationalStatus"};
- MapperResponse subTree = getObjectSubtreeForInterfaces(
- INVENTORY_PATH + objPath, 0, interfaceList);
-
- if (subTree.size() != 0)
- {
- for (auto [objectPath, serviceInterfaceMap] : subTree)
- {
- std::string subTreeObjPath{objectPath};
-
- // Strip any inventory prefix in path
- if (subTreeObjPath.find(INVENTORY_PATH) == 0)
- {
- subTreeObjPath =
- subTreeObjPath.substr(sizeof(INVENTORY_PATH) - 1);
- }
-
- inventory::ObjectMap objectMap{
- {subTreeObjPath,
- {{"xyz.openbmc_project.State.Decorator.OperationalStatus",
- {{"Functional", true}}},
- {"xyz.openbmc_project.Inventory.Item",
- {{"Present", false}}}}}};
-
- // objectMap.emplace(objectPath, move(interfaceMap));
- common::utility::callPIM(move(objectMap));
- }
- }
-
- // Unbind the LED driver for this FRU
- cout << "Unbinding device- " << chipAddress << endl;
- executeCmd(createBindUnbindDriverCmnd(chipAddress, "i2c",
- "leds-pca955x", "/unbind"));
-
- inventory::InterfaceMap interfacesPropMap;
- clearVpdOnRemoval(INVENTORY_PATH + objPath, interfacesPropMap);
-
- inventory::ObjectMap objectMap;
- objectMap.emplace(objPath, move(interfacesPropMap));
-
- common::utility::callPIM(move(objectMap));
- }
-}
-
-} // namespace manager
-} // namespace vpd
-} // namespace openpower
diff --git a/vpd-manager/manager.hpp b/vpd-manager/manager.hpp
deleted file mode 100644
index ae278ac..0000000
--- a/vpd-manager/manager.hpp
+++ /dev/null
@@ -1,232 +0,0 @@
-#pragma once
-
-#include "bios_handler.hpp"
-#include "editor_impl.hpp"
-#include "gpioMonitor.hpp"
-
-#include <sdbusplus/asio/object_server.hpp>
-
-#include <map>
-
-namespace openpower
-{
-namespace vpd
-{
-namespace manager
-{
-
-/** @class Manager
- * @brief OpenBMC VPD Manager implementation.
- *
- * Implements methods under interface com.ibm.vpd.Manager.
- */
-class Manager
-{
- public:
- /* Define all of the basic class operations:
- * Not allowed:
- * - Default constructor to avoid nullptrs.
- * - Copy operations due to internal unique_ptr.
- * - Move operations due to 'this' being registered as the
- * 'context' with sdbus.
- * Allowed:
- * - Destructor.
- */
- Manager() = delete;
- Manager(const Manager&) = delete;
- Manager& operator=(const Manager&) = delete;
- Manager(Manager&&) = delete;
- ~Manager()
- {
- sd_bus_unref(sdBus);
- }
-
- /** @brief Constructor.
- * @param[in] ioCon - IO context.
- * @param[in] iFace - interface to implement.
- * @param[in] connection - Dbus Connection.
- */
- Manager(std::shared_ptr<boost::asio::io_context>& ioCon,
- std::shared_ptr<sdbusplus::asio::dbus_interface>& iFace,
- std::shared_ptr<sdbusplus::asio::connection>& conn);
-
- /** @brief Implementation for WriteKeyword
- * Api to update the keyword value for a given inventory.
- *
- * @param[in] path - Path to the D-Bus object that represents the FRU.
- * @param[in] recordName - name of the record for which the keyword value
- * has to be modified
- * @param[in] keyword - keyword whose value needs to be updated
- * @param[in] value - value that needs to be updated
- */
- void writeKeyword(const sdbusplus::message::object_path& path,
- const std::string& recordName, const std::string& keyword,
- const Binary& value);
-
- /** @brief Implementation for GetFRUsByUnexpandedLocationCode
- * A method to get list of FRU D-BUS object paths for a given unexpanded
- * location code. Returns empty vector if no FRU found for that location
- * code.
- *
- * @param[in] locationCode - An un-expanded Location code.
- * @param[in] nodeNumber - Denotes the node in case of a multi-node
- * configuration, ignored on a single node system.
- *
- * @return inventoryList[std::vector<sdbusplus::message::object_path>] -
- * List of all the FRUs D-Bus object paths for the given location code.
- */
- inventory::ListOfPaths getFRUsByUnexpandedLocationCode(
- const std::string& locationCode, const uint16_t nodeNumber);
-
- /** @brief Implementation for GetFRUsByExpandedLocationCode
- * A method to get list of FRU D-BUS object paths for a given expanded
- * location code. Returns empty vector if no FRU found for that location
- * code.
- *
- * @param[in] locationCode - Location code in expanded format.
- *
- * @return inventoryList[std::vector<sdbusplus::message::object_path>] -
- * List of all the FRUs D-Bus object path for the given location code.
- */
- inventory::ListOfPaths
- getFRUsByExpandedLocationCode(const std::string& locationCode);
-
- /** @brief Implementation for GetExpandedLocationCode
- * An API to get expanded location code corresponding to a given
- * un-expanded location code.
- *
- * @param[in] locationCode - Location code in un-expaned format.
- * @param[in] nodeNumber - Denotes the node in case of multi-node
- * configuration. Ignored in case of single node configuration.
- *
- * @return locationCode[std::string] - Location code in expanded format.
- */
- std::string getExpandedLocationCode(const std::string& locationCode,
- const uint16_t nodeNumber);
-
- /** @brief Api to perform VPD recollection.
- * This api will trigger parser to perform VPD recollection for FRUs that
- * can be replaced at standby.
- */
- void performVPDRecollection();
-
- /** @brief Api to delete FRU VPD.
- * This api will set the present property of given FRU to false. If already
- * set to false, It will log an error.
- * @param[in] path - Object path of FRU.
- */
- void deleteFRUVPD(const sdbusplus::message::object_path& path);
-
- /** @brief Api to perform VPD collection for a single fru.
- * @param[in] path - Dbus object path of that fru.
- */
- void collectFRUVPD(const sdbusplus::message::object_path& path);
-
- private:
- /**
- * @brief An api to process some initial requirements.
- */
- void initManager();
-
- /** @brief process the given JSON file
- */
- void processJSON();
-
- /** @brief Api to register host state callback.
- * This api will register callback to listen for host state property change.
- */
- void listenHostState();
-
- /** @brief Callback to listen for Host state change
- * @param[in] msg - callback message.
- */
- void hostStateCallBack(sdbusplus::message_t& msg);
-
- /** @brief Api to register AssetTag property change.
- * This api will register callback to listen for asset tag property change.
- */
- void listenAssetTag();
-
- /** @brief Callback to listen for Asset tag change
- * @param[in] msg - callback message.
- */
- void assetTagCallback(sdbusplus::message_t& msg);
-
- /**
- * @brief Restores and defaulted VPD on the system VPD EEPROM.
- *
- * This function will read the system VPD EEPROM and check if any of the
- * keywords that need to be preserved across FRU replacements are defaulted
- * in the EEPROM. If they are, this function will restore them from the
- * value that is in the D-Bus cache.
- */
- void restoreSystemVpd();
-
- /**
- * @brief An api to trigger vpd collection for a fru by bind/unbind of
- * driver.
- * @param[in] singleFru - Json of a single fru inder a given EEPROM path.
- * @param[in] path - Inventory path.
- */
- void triggerVpdCollection(const nlohmann::json& singleFru,
- const std::string& path);
-
- /** @brief Update FRU that back up system VPD.
- *
- * The API checks if the FRU being updated is system FRU and the record
- * keyword pair being updated is the one that needs to be backed up and
- * updates the back up FRU accordingly.
- *
- * @param[in] recordName - name of the record.
- * @param[in] keyword - keyword whose value needs to be updated.
- * @param[in] value - value that needs to be updated.
- */
- void updateSystemVPDBackUpFRU(const std::string& recordName,
- const std::string& keyword,
- const Binary& value);
-
- /**
- * @brief Check for essential fru in the system.
- * The api check for the presence of FRUs marked as essential and logs PEL
- * in case they are missing.
- */
- void checkEssentialFrus();
-
- // Shared pointer to asio context object.
- std::shared_ptr<boost::asio::io_context>& ioContext;
-
- // Shared pointer to Dbus interface class.
- std::shared_ptr<sdbusplus::asio::dbus_interface>& interface;
-
- // Shared pointer to bus connection.
- std::shared_ptr<sdbusplus::asio::connection>& conn;
-
- // file to store parsed json
- nlohmann::json jsonFile;
-
- // map to hold mapping to inventory path to vpd file path
- // we need as map here as it is in reverse order to that of json
- inventory::FrusMap frus;
-
- // map to hold the mapping of location code and inventory path
- inventory::LocationCodeMap fruLocationCode;
-
- // map to hold FRUs which can be replaced at standby
- inventory::ReplaceableFrus replaceableFrus;
-
- // Shared pointer to gpio monitor object.
- std::shared_ptr<GpioMonitor> gpioMon;
-
- // Shared pointer to instance of the BIOS handler.
- std::shared_ptr<BiosHandler> biosHandler;
-
- // List of FRUs marked as essential in the system.
- inventory::EssentialFrus essentialFrus;
-
- // sd-bus
- sd_bus* sdBus = nullptr;
-};
-
-} // namespace manager
-} // namespace vpd
-} // namespace openpower
diff --git a/vpd-manager/manager_main.cpp b/vpd-manager/manager_main.cpp
deleted file mode 100644
index bf8c0d9..0000000
--- a/vpd-manager/manager_main.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-#include "config.h"
-
-#include "manager.hpp"
-
-#include <sdbusplus/asio/connection.hpp>
-
-int main(int /*argc*/, char** /*argv*/)
-{
- try
- {
- auto io_con = std::make_shared<boost::asio::io_context>();
- auto connection =
- std::make_shared<sdbusplus::asio::connection>(*io_con);
- connection->request_name(BUSNAME);
-
- auto server = sdbusplus::asio::object_server(connection);
-
- std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
- server.add_interface(OBJPATH, IFACE);
-
- auto vpdManager = std::make_shared<openpower::vpd::manager::Manager>(
- io_con, interface, connection);
- interface->initialize();
-
- // Start event loop.
- io_con->run();
-
- exit(EXIT_SUCCESS);
- }
- catch (const std::exception& e)
- {
- std::cerr << e.what() << "\n";
- }
- exit(EXIT_FAILURE);
-}
diff --git a/vpd-manager/meson.build b/vpd-manager/meson.build
index 22ac81a..4e9c10b 100644
--- a/vpd-manager/meson.build
+++ b/vpd-manager/meson.build
@@ -1,42 +1,47 @@
-systemd = dependency('libsystemd', version: '>= 221')
-sdeventplus = dependency('sdeventplus')
+common_SOURCES = ['src/logger.cpp',
+ 'src/parser_factory.cpp',
+ 'src/ipz_parser.cpp',
+ 'src/keyword_vpd_parser.cpp',
+ 'src/ddimm_parser.cpp',
+ 'src/isdimm_parser.cpp',
+ 'src/parser.cpp',
+ 'src/worker.cpp',
+ 'src/backup_restore.cpp',
+ 'src/gpio_monitor.cpp',
+ 'src/event_logger.cpp']
-configuration_inc = include_directories('.', '../', '../vpd-parser/')
+vpd_manager_SOURCES = ['src/manager_main.cpp',
+ 'src/manager.cpp',
+ 'src/bios_handler.cpp',
+ ] + common_SOURCES
-vpd_manager_SOURCES = [
- 'manager_main.cpp',
- 'manager.cpp',
- 'editor_impl.cpp',
- 'reader_impl.cpp',
- 'gpioMonitor.cpp',
- 'bios_handler.cpp',
- '../impl.cpp',
- '../vpd-parser/ipz_parser.cpp',
- '../ibm_vpd_utils.cpp',
- '../common_utility.cpp',
- '../vpd-parser//keyword_vpd_parser.cpp',
- '../vpd-parser/memory_vpd_parser.cpp',
- '../vpd-parser/isdimm_vpd_parser.cpp',
- '../vpd-parser/parser_factory.cpp'
-]
+parser_dependencies = [sdbusplus, libgpiodcxx, phosphor_logging, phosphor_dbus_interfaces]
-vpd_manager_dependencies = [
- CLI11_dep,
- libgpiodcxx,
- phosphor_logging,
- sdeventplus,
- systemd,
- nlohmann_json_dep,
-]
+parser_build_arguments = []
+if get_option('ibm_system').enabled()
+ parser_build_arguments += ['-DIBM_SYSTEM']
+endif
vpd_manager_exe = executable(
- 'vpd-manager',
- vpd_manager_SOURCES,
- include_directories : configuration_inc,
- dependencies : [
- vpd_manager_dependencies,
+ 'vpd-manager',
+ vpd_manager_SOURCES,
+ include_directories : ['../', 'include/', '../configuration/'],
+ link_with : libvpdecc,
+ dependencies : [
+ parser_dependencies,
],
- link_with : libvpdecc,
- install : true,
- cpp_args : '-DIPZ_PARSER'
+ install : true,
+ cpp_args : parser_build_arguments,
)
+
+vpd_parser_SOURCES = ['src/vpd_parser_main.cpp',
+ ]+ common_SOURCES
+
+vpd_parser_exe = executable(
+ 'vpd-parser',
+ vpd_parser_SOURCES,
+ include_directories : ['../', 'include/', '../configuration/'],
+ link_with : libvpdecc,
+ dependencies : parser_dependencies,
+ install : true,
+ )
\ No newline at end of file
diff --git a/vpd-manager/meson.options b/vpd-manager/meson.options
deleted file mode 100644
index 92afa55..0000000
--- a/vpd-manager/meson.options
+++ /dev/null
@@ -1,5 +0,0 @@
-option('BUSNAME', type : 'string', value : 'com.ibm.VPD.Manager', description : 'BUS NAME FOR THE SERVICE')
-option('OBJPATH', type : 'string', value : '/com/ibm/VPD/Manager', description : 'OBJECT PATH FOR THE SERVICE')
-option('IFACE', type : 'string', value : 'com.ibm.VPD.Manager', description : 'INTERFACE NAME')
-option('oe-sdk', type : 'feature', value : 'disabled', description : 'ENABLE OE SDK FOR VPD KEYWORD EDITOR')
-option('INVENTORY_JSON', type : 'string', value : '/var/lib/vpd/vpd_inventory.json', description : 'PATH TO INVENTORY JSON FILE')
diff --git a/vpd-manager/reader_impl.cpp b/vpd-manager/reader_impl.cpp
deleted file mode 100644
index e4f2d8b..0000000
--- a/vpd-manager/reader_impl.cpp
+++ /dev/null
@@ -1,229 +0,0 @@
-#include "config.h"
-
-#include "reader_impl.hpp"
-
-#include "ibm_vpd_utils.hpp"
-
-#include <com/ibm/VPD/error.hpp>
-#include <phosphor-logging/elog-errors.hpp>
-#include <xyz/openbmc_project/Common/error.hpp>
-
-#include <algorithm>
-#include <map>
-#include <vector>
-
-#ifdef ManagerTest
-#include "reader_test.hpp"
-#endif
-
-namespace openpower
-{
-namespace vpd
-{
-namespace manager
-{
-namespace reader
-{
-
-using namespace phosphor::logging;
-using namespace openpower::vpd::inventory;
-using namespace openpower::vpd::constants;
-using namespace openpower::vpd::utils::interface;
-
-using InvalidArgument =
- sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
-using Argument = xyz::openbmc_project::Common::InvalidArgument;
-using LocationNotFound = sdbusplus::com::ibm::VPD::Error::LocationNotFound;
-
-bool ReaderImpl::isValidLocationCode(const LocationCode& locationCode) const
-{
- if ((locationCode.length() < UNEXP_LOCATION_CODE_MIN_LENGTH) ||
- (locationCode[0] != 'U') ||
- ((locationCode.find("fcs", 1, 3) == std::string::npos) &&
- (locationCode.find("mts", 1, 3) == std::string::npos)))
- {
- return false;
- }
-
- return true;
-}
-
-LocationCode ReaderImpl::getExpandedLocationCode(
- const LocationCode& locationCode, const NodeNumber& nodeNumber,
- const LocationCodeMap& frusLocationCode) const
-{
- // unused at this moment. Hence to avoid warnings
- (void)nodeNumber;
- if (!isValidLocationCode(locationCode))
- {
- // argument is not valid
- elog<InvalidArgument>(Argument::ARGUMENT_NAME("LOCATIONCODE"),
- Argument::ARGUMENT_VALUE(locationCode.c_str()));
- }
- auto iterator = frusLocationCode.find(locationCode);
- if (iterator == frusLocationCode.end())
- {
- // TODO: Implementation of error logic till then throwing invalid
- // argument
- // the location code was not found in the system
- // elog<LocationNotFound>();
- elog<InvalidArgument>(Argument::ARGUMENT_NAME("LOCATIONCODE"),
- Argument::ARGUMENT_VALUE(locationCode.c_str()));
- }
-
- std::string expandedLocationCode{};
-#ifndef ManagerTest
- utility utilObj;
-#endif
- expandedLocationCode = utilObj.readBusProperty(
- iterator->second, IBM_LOCATION_CODE_INF, "LocationCode");
- return expandedLocationCode;
-}
-
-ListOfPaths ReaderImpl::getFrusAtLocation(
- const LocationCode& locationCode, const NodeNumber& nodeNumber,
- const LocationCodeMap& frusLocationCode) const
-{
- // unused at this moment, to avoid compilation warning
- (void)nodeNumber;
-
- // TODO:Implementation related to node number
- if (!isValidLocationCode(locationCode))
- {
- // argument is not valid
- elog<InvalidArgument>(Argument::ARGUMENT_NAME("LOCATIONCODE"),
- Argument::ARGUMENT_VALUE(locationCode.c_str()));
- }
-
- auto range = frusLocationCode.equal_range(locationCode);
-
- if (range.first == frusLocationCode.end())
- {
- // TODO: Implementation of error logic till then throwing invalid
- // argument
- // the location code was not found in the system
- // elog<LocationNotFound>();
- elog<InvalidArgument>(Argument::ARGUMENT_NAME("LOCATIONCODE"),
- Argument::ARGUMENT_VALUE(locationCode.c_str()));
- }
-
- ListOfPaths inventoryPaths;
-
- for_each(range.first, range.second,
- [&inventoryPaths](
- const inventory::LocationCodeMap::value_type& mappedItem) {
- inventoryPaths.push_back(INVENTORY_PATH + mappedItem.second);
- });
- return inventoryPaths;
-}
-
-std::tuple<LocationCode, NodeNumber>
- ReaderImpl::getCollapsedLocationCode(const LocationCode& locationCode) const
-{
- // Location code should always start with U and fulfil minimum length
- // criteria.
- if (locationCode[0] != 'U' ||
- locationCode.length() < EXP_LOCATIN_CODE_MIN_LENGTH)
- {
- elog<InvalidArgument>(Argument::ARGUMENT_NAME("LOCATIONCODE"),
- Argument::ARGUMENT_VALUE(locationCode.c_str()));
- }
-
- std::string fc{};
-#ifndef ManagerTest
- utility utilObj;
-#endif
-
- fc = utilObj.readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.VCEN", "FC");
-
- // get the first part of expanded location code to check for FC or TM
- std::string firstKeyword = locationCode.substr(1, 4);
-
- LocationCode unexpandedLocationCode{};
- NodeNumber nodeNummber = INVALID_NODE_NUMBER;
-
- // check if this value matches the value of FC kwd
- if (fc.substr(0, 4) ==
- firstKeyword) // implies this is Ufcs format location code
- {
- // period(.) should be there in expanded location code to seggregate FC,
- // Node number and SE values.
- size_t nodeStartPos = locationCode.find('.');
- if (nodeStartPos == std::string::npos)
- {
- elog<InvalidArgument>(
- Argument::ARGUMENT_NAME("LOCATIONCODE"),
- Argument::ARGUMENT_VALUE(locationCode.c_str()));
- }
-
- // second period(.) should be there to end the node details in non
- // system location code
- size_t nodeEndPos = locationCode.find('.', nodeStartPos + 1);
- if (nodeEndPos == std::string::npos)
- {
- elog<InvalidArgument>(
- Argument::ARGUMENT_NAME("LOCATIONCODE"),
- Argument::ARGUMENT_VALUE(locationCode.c_str()));
- }
-
- // skip 3 for '.ND'
- nodeNummber = std::stoi(locationCode.substr(
- nodeStartPos + 3, (nodeEndPos - nodeStartPos - 3)));
-
- // confirm if there are other details apart FC, Node number and SE in
- // location code
- if (locationCode.length() > EXP_LOCATIN_CODE_MIN_LENGTH)
- {
- unexpandedLocationCode =
- locationCode[0] + (std::string) "fcs" +
- locationCode.substr(nodeEndPos + 1 + SE_KWD_LENGTH,
- std::string::npos);
- }
- else
- {
- unexpandedLocationCode = "Ufcs";
- }
- }
- else
- {
- std::string tm{};
- // read TM kwd value
- tm =
- utilObj.readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.VSYS", "TM");
- ;
-
- // check if the substr matches to TM kwd
- if (tm.substr(0, 4) ==
- firstKeyword) // implies this is Umts format of location code
- {
- // system location code will not have any other details and node
- // number
- unexpandedLocationCode = "Umts";
- }
- // it does not belong to either "fcs" or "mts"
- else
- {
- elog<InvalidArgument>(
- Argument::ARGUMENT_NAME("LOCATIONCODE"),
- Argument::ARGUMENT_VALUE(locationCode.c_str()));
- }
- }
-
- return std::make_tuple(unexpandedLocationCode, nodeNummber);
-}
-
-ListOfPaths ReaderImpl::getFRUsByExpandedLocationCode(
- const inventory::LocationCode& locationCode,
- const inventory::LocationCodeMap& frusLocationCode) const
-{
- std::tuple<LocationCode, NodeNumber> locationAndNodePair =
- getCollapsedLocationCode(locationCode);
-
- return getFrusAtLocation(std::get<0>(locationAndNodePair),
- std::get<1>(locationAndNodePair),
- frusLocationCode);
-}
-} // namespace reader
-} // namespace manager
-} // namespace vpd
-} // namespace openpower
diff --git a/vpd-manager/reader_impl.hpp b/vpd-manager/reader_impl.hpp
deleted file mode 100644
index 7525a4f..0000000
--- a/vpd-manager/reader_impl.hpp
+++ /dev/null
@@ -1,90 +0,0 @@
-#pragma once
-
-#include "types.hpp"
-#include "utilInterface.hpp"
-
-namespace openpower
-{
-namespace vpd
-{
-namespace manager
-{
-namespace reader
-{
-
-using IUtil = openpower::vpd::utils::interface::UtilityInterface;
-/** @class ReaderImpl
- * @brief Implements functionalities related to reading of VPD related data
- * from the system.
- */
-class ReaderImpl
-{
- public:
- ReaderImpl() = default;
- ReaderImpl(const ReaderImpl&) = default;
- ReaderImpl& operator=(const ReaderImpl&) = delete;
- ReaderImpl(ReaderImpl&&) = default;
- ReaderImpl& operator=(ReaderImpl&&) = delete;
- ~ReaderImpl() = default;
-
-#ifdef ManagerTest
- explicit ReaderImpl(IUtil& obj) : utilObj(obj) {}
-#endif
-
- /** @brief An API to expand a given unexpanded location code.
- * @param[in] locationCode - unexpanded location code.
- * @param[in] nodeNumber - node on which we are looking for location code.
- * @param[in] frusLocationCode - mapping of inventory path and location
- * code.
- * @return Expanded location code.
- */
- inventory::LocationCode getExpandedLocationCode(
- const inventory::LocationCode& locationCode,
- const inventory::NodeNumber& nodeNumber,
- const inventory::LocationCodeMap& frusLocationCode) const;
-
- /** @brief An API to get list of all the FRUs at the given location code
- * @param[in] - location code in unexpanded format
- * @param[in] - node number
- * @param[in] - mapping of location code and Inventory path
- * @return list of Inventory paths at the given location
- */
- inventory::ListOfPaths getFrusAtLocation(
- const inventory::LocationCode& locationCode,
- const inventory::NodeNumber& nodeNumber,
- const inventory::LocationCodeMap& frusLocationCode) const;
-
- /** @brief An API to get list of all the FRUs at the given location code
- * @param[in] - location code in unexpanded format
- * @param[in] - mapping of location code and Inventory path
- * @return list of Inventory paths at the given location
- */
- inventory::ListOfPaths getFRUsByExpandedLocationCode(
- const inventory::LocationCode& locationCode,
- const inventory::LocationCodeMap& frusLocationCode) const;
-
- private:
- /** @brief An api to check validity of location code
- * @param[in] - location code
- * @return true/false based on validity check
- */
- bool isValidLocationCode(const inventory::LocationCode& locationCode) const;
-
- /** @brief An API to split expanded location code to its un-expanded
- * format as represented in VPD JSON and the node number.
- * @param[in] Location code in expanded format.
- * @return Location code in un-expanded format and its node number.
- */
- std::tuple<inventory::LocationCode, inventory::NodeNumber>
- getCollapsedLocationCode(
- const inventory::LocationCode& locationCode) const;
-#ifdef ManagerTest
- IUtil& utilObj;
-#endif
-
-}; // class ReaderImpl
-
-} // namespace reader
-} // namespace manager
-} // namespace vpd
-} // namespace openpower
diff --git a/vpd-manager/src/backup_restore.cpp b/vpd-manager/src/backup_restore.cpp
new file mode 100644
index 0000000..294efc4
--- /dev/null
+++ b/vpd-manager/src/backup_restore.cpp
@@ -0,0 +1,384 @@
+#include "backup_restore.hpp"
+
+#include "constants.hpp"
+#include "event_logger.hpp"
+#include "exceptions.hpp"
+#include "logger.hpp"
+#include "parser.hpp"
+#include "types.hpp"
+
+#include <utility/json_utility.hpp>
+#include <utility/vpd_specific_utility.hpp>
+
+namespace vpd
+{
+BackupAndRestoreStatus BackupAndRestore::m_backupAndRestoreStatus =
+ BackupAndRestoreStatus::NotStarted;
+
+BackupAndRestore::BackupAndRestore(const nlohmann::json& i_sysCfgJsonObj) :
+ m_sysCfgJsonObj(i_sysCfgJsonObj)
+{
+ std::string l_backupAndRestoreCfgFilePath =
+ i_sysCfgJsonObj.value("backupRestoreConfigPath", "");
+ try
+ {
+ m_backupAndRestoreCfgJsonObj =
+ jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath);
+ }
+ catch (const std::exception& ex)
+ {
+ logging::logMessage(
+ "Failed to intialize backup and restore object for file = " +
+ l_backupAndRestoreCfgFilePath);
+ throw(ex);
+ }
+}
+
+std::tuple<types::VPDMapVariant, types::VPDMapVariant>
+ BackupAndRestore::backupAndRestore()
+{
+ auto l_emptyVariantPair =
+ std::make_tuple(std::monostate{}, std::monostate{});
+
+ if (m_backupAndRestoreStatus >= BackupAndRestoreStatus::Invoked)
+ {
+ logging::logMessage("Backup and restore invoked already.");
+ return l_emptyVariantPair;
+ }
+
+ m_backupAndRestoreStatus = BackupAndRestoreStatus::Invoked;
+ try
+ {
+ if (m_backupAndRestoreCfgJsonObj.empty() ||
+ !m_backupAndRestoreCfgJsonObj.contains("source") ||
+ !m_backupAndRestoreCfgJsonObj.contains("destination") ||
+ !m_backupAndRestoreCfgJsonObj.contains("type") ||
+ !m_backupAndRestoreCfgJsonObj.contains("backupMap"))
+ {
+ logging::logMessage(
+ "Backup restore config JSON is missing necessary tag(s), can't initiate backup and restore.");
+ return l_emptyVariantPair;
+ }
+
+ std::string l_srcVpdPath;
+ types::VPDMapVariant l_srcVpdVariant;
+ if (l_srcVpdPath = m_backupAndRestoreCfgJsonObj["source"].value(
+ "hardwarePath", "");
+ !l_srcVpdPath.empty() && std::filesystem::exists(l_srcVpdPath))
+ {
+ std::shared_ptr<Parser> l_vpdParser =
+ std::make_shared<Parser>(l_srcVpdPath, m_sysCfgJsonObj);
+ l_srcVpdVariant = l_vpdParser->parse();
+ }
+ else if (l_srcVpdPath = m_backupAndRestoreCfgJsonObj["source"].value(
+ "inventoryPath", "");
+ l_srcVpdPath.empty())
+ {
+ logging::logMessage(
+ "Couldn't extract source path, can't initiate backup and restore.");
+ return l_emptyVariantPair;
+ }
+
+ std::string l_dstVpdPath;
+ types::VPDMapVariant l_dstVpdVariant;
+ if (l_dstVpdPath = m_backupAndRestoreCfgJsonObj["destination"].value(
+ "hardwarePath", "");
+ !l_dstVpdPath.empty() && std::filesystem::exists(l_dstVpdPath))
+ {
+ std::shared_ptr<Parser> l_vpdParser =
+ std::make_shared<Parser>(l_dstVpdPath, m_sysCfgJsonObj);
+ l_dstVpdVariant = l_vpdParser->parse();
+ }
+ else if (l_dstVpdPath = m_backupAndRestoreCfgJsonObj["destination"]
+ .value("inventoryPath", "");
+ l_dstVpdPath.empty())
+ {
+ logging::logMessage(
+ "Couldn't extract destination path, can't initiate backup and restore.");
+ return l_emptyVariantPair;
+ }
+
+ // Implement backup and restore for IPZ type VPD
+ auto l_backupAndRestoreType =
+ m_backupAndRestoreCfgJsonObj.value("type", "");
+ if (l_backupAndRestoreType.compare("IPZ") == constants::STR_CMP_SUCCESS)
+ {
+ types::IPZVpdMap l_srcVpdMap;
+ if (auto l_srcVpdPtr =
+ std::get_if<types::IPZVpdMap>(&l_srcVpdVariant))
+ {
+ l_srcVpdMap = *l_srcVpdPtr;
+ }
+ else if (!std::holds_alternative<std::monostate>(l_srcVpdVariant))
+ {
+ logging::logMessage("Source VPD is not of IPZ type.");
+ return l_emptyVariantPair;
+ }
+
+ types::IPZVpdMap l_dstVpdMap;
+ if (auto l_dstVpdPtr =
+ std::get_if<types::IPZVpdMap>(&l_dstVpdVariant))
+ {
+ l_dstVpdMap = *l_dstVpdPtr;
+ }
+ else if (!std::holds_alternative<std::monostate>(l_dstVpdVariant))
+ {
+ logging::logMessage("Destination VPD is not of IPZ type.");
+ return l_emptyVariantPair;
+ }
+
+ backupAndRestoreIpzVpd(l_srcVpdMap, l_dstVpdMap, l_srcVpdPath,
+ l_dstVpdPath);
+ m_backupAndRestoreStatus = BackupAndRestoreStatus::Completed;
+
+ return std::make_tuple(l_srcVpdMap, l_dstVpdMap);
+ }
+ // Note: add implementation here to support any other VPD type.
+ }
+ catch (const std::exception& ex)
+ {
+ logging::logMessage("Back up and restore failed with exception: " +
+ std::string(ex.what()));
+ }
+ return l_emptyVariantPair;
+}
+
+void BackupAndRestore::backupAndRestoreIpzVpd(
+ types::IPZVpdMap& io_srcVpdMap, types::IPZVpdMap& io_dstVpdMap,
+ const std::string& i_srcPath, const std::string& i_dstPath)
+{
+ if (!m_backupAndRestoreCfgJsonObj["backupMap"].is_array())
+ {
+ logging::logMessage(
+ "Invalid value found for tag backupMap, in backup and restore config JSON.");
+ return;
+ }
+
+ const std::string l_srcFruPath =
+ jsonUtility::getFruPathFromJson(m_sysCfgJsonObj, i_srcPath);
+ const std::string l_dstFruPath =
+ jsonUtility::getFruPathFromJson(m_sysCfgJsonObj, i_dstPath);
+ if (l_srcFruPath.empty() || l_dstFruPath.empty())
+ {
+ logging::logMessage(
+ "Couldn't find either source or destination FRU path.");
+ return;
+ }
+
+ const std::string l_srcInvPath =
+ jsonUtility::getInventoryObjPathFromJson(m_sysCfgJsonObj, i_srcPath);
+ const std::string l_dstInvPath =
+ jsonUtility::getInventoryObjPathFromJson(m_sysCfgJsonObj, i_dstPath);
+ if (l_srcInvPath.empty() || l_dstInvPath.empty())
+ {
+ logging::logMessage(
+ "Couldn't find either source or destination inventory path.");
+ return;
+ }
+
+ const std::string l_srcServiceName =
+ jsonUtility::getServiceName(m_sysCfgJsonObj, l_srcInvPath);
+ const std::string l_dstServiceName =
+ jsonUtility::getServiceName(m_sysCfgJsonObj, l_dstInvPath);
+ if (l_srcServiceName.empty() || l_dstServiceName.empty())
+ {
+ logging::logMessage(
+ "Couldn't find either source or destination DBus service name.");
+ return;
+ }
+
+ for (const auto& l_aRecordKwInfo :
+ m_backupAndRestoreCfgJsonObj["backupMap"])
+ {
+ const std::string& l_srcRecordName =
+ l_aRecordKwInfo.value("sourceRecord", "");
+ const std::string& l_srcKeywordName =
+ l_aRecordKwInfo.value("sourceKeyword", "");
+ const std::string& l_dstRecordName =
+ l_aRecordKwInfo.value("destinationRecord", "");
+ const std::string& l_dstKeywordName =
+ l_aRecordKwInfo.value("destinationKeyword", "");
+
+ if (l_srcRecordName.empty() || l_dstRecordName.empty() ||
+ l_srcKeywordName.empty() || l_dstKeywordName.empty())
+ {
+ logging::logMessage(
+ "Record or keyword not found in the backup and restore config JSON.");
+ continue;
+ }
+
+ if (!io_srcVpdMap.empty() &&
+ io_srcVpdMap.find(l_srcRecordName) == io_srcVpdMap.end())
+ {
+ logging::logMessage(
+ "Record: " + l_srcRecordName +
+ ", is not found in the source path: " + i_srcPath);
+ continue;
+ }
+
+ if (!io_dstVpdMap.empty() &&
+ io_dstVpdMap.find(l_dstRecordName) == io_dstVpdMap.end())
+ {
+ logging::logMessage(
+ "Record: " + l_dstRecordName +
+ ", is not found in the destination path: " + i_dstPath);
+ continue;
+ }
+
+ types::BinaryVector l_defaultBinaryValue;
+ if (l_aRecordKwInfo.contains("defaultValue") &&
+ l_aRecordKwInfo["defaultValue"].is_array())
+ {
+ l_defaultBinaryValue =
+ l_aRecordKwInfo["defaultValue"].get<types::BinaryVector>();
+ }
+ else
+ {
+ logging::logMessage(
+ "Couldn't read default value for record name: " +
+ l_srcRecordName + ", keyword name: " + l_srcKeywordName +
+ " from backup and restore config JSON file.");
+ continue;
+ }
+
+ bool l_isPelRequired = l_aRecordKwInfo.value("isPelRequired", false);
+
+ types::BinaryVector l_srcBinaryValue;
+ std::string l_srcStrValue;
+ if (!io_srcVpdMap.empty())
+ {
+ vpdSpecificUtility::getKwVal(io_srcVpdMap.at(l_srcRecordName),
+ l_srcKeywordName, l_srcStrValue);
+ l_srcBinaryValue =
+ types::BinaryVector(l_srcStrValue.begin(), l_srcStrValue.end());
+ }
+ else
+ {
+ // Read keyword value from DBus
+ const auto l_value = dbusUtility::readDbusProperty(
+ l_srcServiceName, l_srcInvPath,
+ constants::ipzVpdInf + l_srcRecordName, l_srcKeywordName);
+ if (const auto l_binaryValue =
+ std::get_if<types::BinaryVector>(&l_value))
+ {
+ l_srcBinaryValue = *l_binaryValue;
+ l_srcStrValue = std::string(l_srcBinaryValue.begin(),
+ l_srcBinaryValue.end());
+ }
+ }
+
+ types::BinaryVector l_dstBinaryValue;
+ std::string l_dstStrValue;
+ if (!io_dstVpdMap.empty())
+ {
+ vpdSpecificUtility::getKwVal(io_dstVpdMap.at(l_dstRecordName),
+ l_dstKeywordName, l_dstStrValue);
+ l_dstBinaryValue =
+ types::BinaryVector(l_dstStrValue.begin(), l_dstStrValue.end());
+ }
+ else
+ {
+ // Read keyword value from DBus
+ const auto l_value = dbusUtility::readDbusProperty(
+ l_dstServiceName, l_dstInvPath,
+ constants::ipzVpdInf + l_dstRecordName, l_dstKeywordName);
+ if (const auto l_binaryValue =
+ std::get_if<types::BinaryVector>(&l_value))
+ {
+ l_dstBinaryValue = *l_binaryValue;
+ l_dstStrValue = std::string(l_dstBinaryValue.begin(),
+ l_dstBinaryValue.end());
+ }
+ }
+
+ if (l_srcBinaryValue != l_dstBinaryValue)
+ {
+ // ToDo: Handle if there is no valid default value in the backup and
+ // restore config JSON.
+ if (l_dstBinaryValue == l_defaultBinaryValue)
+ {
+ // Update keyword's value on hardware
+ auto l_vpdParser =
+ std::make_shared<Parser>(l_dstFruPath, m_sysCfgJsonObj);
+
+ auto l_bytesUpdatedOnHardware = l_vpdParser->updateVpdKeyword(
+ types::IpzData(l_dstRecordName, l_dstKeywordName,
+ l_srcBinaryValue));
+
+ /* To keep the data in sync between hardware and parsed map
+ updating the io_dstVpdMap. This should only be done if write
+ on hardware returns success.*/
+ if (!io_dstVpdMap.empty() && l_bytesUpdatedOnHardware > 0)
+ {
+ io_dstVpdMap[l_dstRecordName][l_dstKeywordName] =
+ l_srcStrValue;
+ }
+ continue;
+ }
+
+ if (l_srcBinaryValue == l_defaultBinaryValue)
+ {
+ // Update keyword's value on hardware
+ auto l_vpdParser =
+ std::make_shared<Parser>(l_srcFruPath, m_sysCfgJsonObj);
+
+ auto l_bytesUpdatedOnHardware = l_vpdParser->updateVpdKeyword(
+ types::IpzData(l_srcRecordName, l_srcKeywordName,
+ l_dstBinaryValue));
+
+ /* To keep the data in sync between hardware and parsed map
+ updating the io_srcVpdMap. This should only be done if write
+ on hardware returns success.*/
+ if (!io_srcVpdMap.empty() && l_bytesUpdatedOnHardware > 0)
+ {
+ io_srcVpdMap[l_srcRecordName][l_srcKeywordName] =
+ l_dstStrValue;
+ }
+ }
+ else
+ {
+ /**
+ * Update io_srcVpdMap to publish the same data on DBus, which
+ * is already present on the DBus. Because after calling
+ * backupAndRestore API the map value will get published to DBus
+ * in the worker flow.
+ */
+ if (!io_srcVpdMap.empty() && io_dstVpdMap.empty())
+ {
+ io_srcVpdMap[l_srcRecordName][l_srcKeywordName] =
+ l_dstStrValue;
+ }
+
+ std::string l_errorMsg(
+ "Mismatch found between source and destination VPD for record : " +
+ l_srcRecordName + " and keyword : " + l_srcKeywordName +
+ " . Value read from source : " + l_srcStrValue +
+ " . Value read from destination : " + l_dstStrValue);
+
+ EventLogger::createSyncPel(
+ types::ErrorType::VpdMismatch, types::SeverityType::Warning,
+ __FILE__, __FUNCTION__, 0, l_errorMsg, std::nullopt,
+ std::nullopt, std::nullopt, std::nullopt);
+ }
+ }
+ else if (l_srcBinaryValue == l_defaultBinaryValue &&
+ l_dstBinaryValue == l_defaultBinaryValue && l_isPelRequired)
+ {
+ std::string l_errorMsg(
+ "Default value found on both source and destination VPD, for record: " +
+ l_srcRecordName + " and keyword: " + l_srcKeywordName);
+
+ EventLogger::createSyncPel(
+ types::ErrorType::DefaultValue, types::SeverityType::Error,
+ __FILE__, __FUNCTION__, 0, l_errorMsg, std::nullopt,
+ std::nullopt, std::nullopt, std::nullopt);
+ }
+ }
+}
+
+void BackupAndRestore::setBackupAndRestoreStatus(
+ const BackupAndRestoreStatus& i_status)
+{
+ m_backupAndRestoreStatus = i_status;
+}
+} // namespace vpd
diff --git a/vpd-manager/src/bios_handler.cpp b/vpd-manager/src/bios_handler.cpp
new file mode 100644
index 0000000..44afff3
--- /dev/null
+++ b/vpd-manager/src/bios_handler.cpp
@@ -0,0 +1,764 @@
+#include "config.h"
+
+#include "bios_handler.hpp"
+
+#include "constants.hpp"
+#include "logger.hpp"
+
+#include <sdbusplus/bus/match.hpp>
+#include <utility/common_utility.hpp>
+#include <utility/dbus_utility.hpp>
+
+#include <string>
+
+namespace vpd
+{
+// Template declaration to define APIs.
+template class BiosHandler<IbmBiosHandler>;
+
+template <typename T>
+void BiosHandler<T>::checkAndListenPldmService()
+{
+ // Setup a call back match on NameOwnerChanged to determine when PLDM is up.
+ static std::shared_ptr<sdbusplus::bus::match_t> l_nameOwnerMatch =
+ std::make_shared<sdbusplus::bus::match_t>(
+ *m_asioConn,
+ sdbusplus::bus::match::rules::nameOwnerChanged(
+ constants::pldmServiceName),
+ [this](sdbusplus::message_t& l_msg) {
+ if (l_msg.is_method_error())
+ {
+ logging::logMessage(
+ "Error in reading PLDM name owner changed signal.");
+ return;
+ }
+
+ std::string l_name;
+ std::string l_newOwner;
+ std::string l_oldOwner;
+
+ l_msg.read(l_name, l_oldOwner, l_newOwner);
+
+ if (!l_newOwner.empty() &&
+ (l_name.compare(constants::pldmServiceName) ==
+ constants::STR_CMP_SUCCESS))
+ {
+ m_specificBiosHandler->backUpOrRestoreBiosAttributes();
+
+ // Start listener now that we have done the restore.
+ listenBiosAttributes();
+
+ // We don't need the match anymore
+ l_nameOwnerMatch.reset();
+ }
+ });
+
+ // Based on PLDM service status reset owner match registered above and
+ // trigger BIOS attribute sync.
+ if (dbusUtility::isServiceRunning(constants::pldmServiceName))
+ {
+ l_nameOwnerMatch.reset();
+ m_specificBiosHandler->backUpOrRestoreBiosAttributes();
+
+ // Start listener now that we have done the restore.
+ listenBiosAttributes();
+ }
+}
+
+template <typename T>
+void BiosHandler<T>::listenBiosAttributes()
+{
+ static std::shared_ptr<sdbusplus::bus::match_t> l_biosMatch =
+ std::make_shared<sdbusplus::bus::match_t>(
+ *m_asioConn,
+ sdbusplus::bus::match::rules::propertiesChanged(
+ constants::biosConfigMgrObjPath,
+ constants::biosConfigMgrInterface),
+ [this](sdbusplus::message_t& l_msg) {
+ m_specificBiosHandler->biosAttributesCallback(l_msg);
+ });
+}
+
+void IbmBiosHandler::biosAttributesCallback(sdbusplus::message_t& i_msg)
+{
+ if (i_msg.is_method_error())
+ {
+ logging::logMessage("Error in reading BIOS attribute signal. ");
+ return;
+ }
+
+ std::string l_objPath;
+ types::BiosBaseTableType l_propMap;
+ i_msg.read(l_objPath, l_propMap);
+
+ for (auto l_property : l_propMap)
+ {
+ if (l_property.first != "BaseBIOSTable")
+ {
+ // Looking for change in Base BIOS table only.
+ continue;
+ }
+
+ if (auto l_attributeList =
+ std::get_if<std::map<std::string, types::BiosProperty>>(
+ &(l_property.second)))
+ {
+ for (const auto& l_attribute : *l_attributeList)
+ {
+ if (auto l_val = std::get_if<std::string>(
+ &(std::get<5>(std::get<1>(l_attribute)))))
+ {
+ std::string l_attributeName = std::get<0>(l_attribute);
+ if (l_attributeName == "hb_memory_mirror_mode")
+ {
+ saveAmmToVpd(*l_val);
+ }
+
+ if (l_attributeName == "pvm_keep_and_clear")
+ {
+ saveKeepAndClearToVpd(*l_val);
+ }
+
+ if (l_attributeName == "pvm_create_default_lpar")
+ {
+ saveCreateDefaultLparToVpd(*l_val);
+ }
+
+ if (l_attributeName == "pvm_clear_nvram")
+ {
+ saveClearNvramToVpd(*l_val);
+ }
+
+ continue;
+ }
+
+ if (auto l_val = std::get_if<int64_t>(
+ &(std::get<5>(std::get<1>(l_attribute)))))
+ {
+ std::string l_attributeName = std::get<0>(l_attribute);
+ if (l_attributeName == "hb_field_core_override")
+ {
+ saveFcoToVpd(*l_val);
+ }
+ }
+ }
+ }
+ else
+ {
+ // TODO: log a predicitive PEL.
+ logging::logMessage("Invalid typre received from BIOS table.");
+ break;
+ }
+ }
+}
+
+void IbmBiosHandler::backUpOrRestoreBiosAttributes()
+{
+ // process FCO
+ processFieldCoreOverride();
+
+ // process AMM
+ processActiveMemoryMirror();
+
+ // process LPAR
+ processCreateDefaultLpar();
+
+ // process clear NVRAM
+ processClearNvram();
+
+ // process keep and clear
+ processKeepAndClear();
+}
+
+types::BiosAttributeCurrentValue
+ IbmBiosHandler::readBiosAttribute(const std::string& i_attributeName)
+{
+ types::BiosAttributeCurrentValue l_attrValueVariant =
+ dbusUtility::biosGetAttributeMethodCall(i_attributeName);
+
+ return l_attrValueVariant;
+}
+
+void IbmBiosHandler::processFieldCoreOverride()
+{
+ // TODO: Should we avoid doing this at runtime?
+
+ // Read required keyword from Dbus.
+ auto l_kwdValueVariant = dbusUtility::readDbusProperty(
+ constants::pimServiceName, constants::systemVpdInvPath,
+ constants::vsysInf, constants::kwdRG);
+
+ if (auto l_fcoInVpd = std::get_if<types::BinaryVector>(&l_kwdValueVariant))
+ {
+ // default length of the keyword is 4 bytes.
+ if (l_fcoInVpd->size() != constants::VALUE_4)
+ {
+ logging::logMessage(
+ "Invalid value read for FCO from D-Bus. Skipping.");
+ }
+
+ // If FCO in VPD contains anything other that ASCII Space, restore to
+ // BIOS
+ if (std::any_of(l_fcoInVpd->cbegin(), l_fcoInVpd->cend(),
+ [](uint8_t l_val) {
+ return l_val != constants::ASCII_OF_SPACE;
+ }))
+ {
+ // Restore the data to BIOS.
+ saveFcoToBios(*l_fcoInVpd);
+ }
+ else
+ {
+ types::BiosAttributeCurrentValue l_attrValueVariant =
+ readBiosAttribute("hb_field_core_override");
+
+ if (auto l_fcoInBios = std::get_if<int64_t>(&l_attrValueVariant))
+ {
+ // save the BIOS data to VPD
+ saveFcoToVpd(*l_fcoInBios);
+
+ return;
+ }
+ logging::logMessage("Invalid type recieved for FCO from BIOS.");
+ }
+ return;
+ }
+ logging::logMessage("Invalid type recieved for FCO from VPD.");
+}
+
+void IbmBiosHandler::saveFcoToVpd(int64_t i_fcoInBios)
+{
+ if (i_fcoInBios < 0)
+ {
+ logging::logMessage("Invalid FCO value in BIOS. Skip updating to VPD");
+ return;
+ }
+
+ // Read required keyword from Dbus.
+ auto l_kwdValueVariant = dbusUtility::readDbusProperty(
+ constants::pimServiceName, constants::systemVpdInvPath,
+ constants::vsysInf, constants::kwdRG);
+
+ if (auto l_fcoInVpd = std::get_if<types::BinaryVector>(&l_kwdValueVariant))
+ {
+ // default length of the keyword is 4 bytes.
+ if (l_fcoInVpd->size() != constants::VALUE_4)
+ {
+ logging::logMessage(
+ "Invalid value read for FCO from D-Bus. Skipping.");
+ return;
+ }
+
+ // convert to VPD value type
+ types::BinaryVector l_biosValInVpdFormat = {
+ 0, 0, 0, static_cast<uint8_t>(i_fcoInBios)};
+
+ // Update only when the data are different.
+ if (std::memcmp(l_biosValInVpdFormat.data(), l_fcoInVpd->data(),
+ constants::VALUE_4) != constants::SUCCESS)
+ {
+ if (constants::FAILURE ==
+ m_manager->updateKeyword(
+ SYSTEM_VPD_FILE_PATH,
+ types::IpzData("VSYS", constants::kwdRG,
+ l_biosValInVpdFormat)))
+ {
+ logging::logMessage(
+ "Failed to update " + std::string(constants::kwdRG) +
+ " keyword to VPD.");
+ }
+ }
+ }
+ else
+ {
+ logging::logMessage("Invalid type read for FCO from DBus.");
+ }
+}
+
+void IbmBiosHandler::saveFcoToBios(const types::BinaryVector& i_fcoVal)
+{
+ if (i_fcoVal.size() != constants::VALUE_4)
+ {
+ logging::logMessage("Bad size for FCO received. Skip writing to BIOS");
+ return;
+ }
+
+ types::PendingBIOSAttrs l_pendingBiosAttribute;
+ l_pendingBiosAttribute.push_back(std::make_pair(
+ "hb_field_core_override",
+ std::make_tuple(
+ "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.Integer",
+ i_fcoVal.at(constants::VALUE_3))));
+
+ try
+ {
+ dbusUtility::writeDbusProperty(
+ constants::biosConfigMgrService, constants::biosConfigMgrObjPath,
+ constants::biosConfigMgrInterface, "PendingAttributes",
+ l_pendingBiosAttribute);
+ }
+ catch (const std::exception& l_ex)
+ {
+ // TODO: Should we log informational PEL here as well?
+ logging::logMessage(
+ "DBus call to update FCO value in pending attribute failed. " +
+ std::string(l_ex.what()));
+ }
+}
+
+void IbmBiosHandler::saveAmmToVpd(const std::string& i_memoryMirrorMode)
+{
+ if (i_memoryMirrorMode.empty())
+ {
+ logging::logMessage(
+ "Empty memory mirror mode value from BIOS. Skip writing to VPD");
+ return;
+ }
+
+ // Read existing value.
+ auto l_kwdValueVariant = dbusUtility::readDbusProperty(
+ constants::pimServiceName, constants::systemVpdInvPath,
+ constants::utilInf, constants::kwdAMM);
+
+ if (auto l_pVal = std::get_if<types::BinaryVector>(&l_kwdValueVariant))
+ {
+ auto l_ammValInVpd = *l_pVal;
+
+ types::BinaryVector l_valToUpdateInVpd{
+ (i_memoryMirrorMode == "Enabled" ? constants::AMM_ENABLED_IN_VPD
+ : constants::AMM_DISABLED_IN_VPD)};
+
+ // Check if value is already updated on VPD.
+ if (l_ammValInVpd.at(0) == l_valToUpdateInVpd.at(0))
+ {
+ return;
+ }
+
+ if (constants::FAILURE ==
+ m_manager->updateKeyword(
+ SYSTEM_VPD_FILE_PATH,
+ types::IpzData("UTIL", constants::kwdAMM, l_valToUpdateInVpd)))
+ {
+ logging::logMessage(
+ "Failed to update " + std::string(constants::kwdAMM) +
+ " keyword to VPD");
+ }
+ }
+ else
+ {
+ // TODO: Add PEL
+ logging::logMessage(
+ "Invalid type read for memory mirror mode value from DBus. Skip writing to VPD");
+ }
+}
+
+void IbmBiosHandler::saveAmmToBios(const std::string& i_ammVal)
+{
+ if (i_ammVal.size() != constants::VALUE_1)
+ {
+ logging::logMessage("Bad size for AMM received, Skip writing to BIOS");
+ return;
+ }
+
+ const std::string l_valtoUpdate =
+ (i_ammVal.at(0) == constants::VALUE_2) ? "Enabled" : "Disabled";
+
+ types::PendingBIOSAttrs l_pendingBiosAttribute;
+ l_pendingBiosAttribute.push_back(std::make_pair(
+ "hb_memory_mirror_mode",
+ std::make_tuple(
+ "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.Enumeration",
+ l_valtoUpdate)));
+
+ try
+ {
+ dbusUtility::writeDbusProperty(
+ constants::biosConfigMgrService, constants::biosConfigMgrObjPath,
+ constants::biosConfigMgrInterface, "PendingAttributes",
+ l_pendingBiosAttribute);
+ }
+ catch (const std::exception& l_ex)
+ {
+ // TODO: Should we log informational PEL here as well?
+ logging::logMessage(
+ "DBus call to update AMM value in pending attribute failed. " +
+ std::string(l_ex.what()));
+ }
+}
+
+void IbmBiosHandler::processActiveMemoryMirror()
+{
+ auto l_kwdValueVariant = dbusUtility::readDbusProperty(
+ constants::pimServiceName, constants::systemVpdInvPath,
+ constants::utilInf, constants::kwdAMM);
+
+ if (auto pVal = std::get_if<types::BinaryVector>(&l_kwdValueVariant))
+ {
+ auto l_ammValInVpd = *pVal;
+
+ // Check if active memory mirror value is default in VPD.
+ if (l_ammValInVpd.at(0) == constants::VALUE_0)
+ {
+ types::BiosAttributeCurrentValue l_attrValueVariant =
+ readBiosAttribute("hb_memory_mirror_mode");
+
+ if (auto pVal = std::get_if<std::string>(&l_attrValueVariant))
+ {
+ saveAmmToVpd(*pVal);
+ return;
+ }
+ logging::logMessage(
+ "Invalid type recieved for auto memory mirror mode from BIOS.");
+ return;
+ }
+ else
+ {
+ saveAmmToBios(std::to_string(l_ammValInVpd.at(0)));
+ }
+ return;
+ }
+ logging::logMessage(
+ "Invalid type recieved for auto memory mirror mode from VPD.");
+}
+
+void IbmBiosHandler::saveCreateDefaultLparToVpd(
+ const std::string& i_createDefaultLparVal)
+{
+ if (i_createDefaultLparVal.empty())
+ {
+ logging::logMessage(
+ "Empty value received for Lpar from BIOS. Skip writing in VPD.");
+ return;
+ }
+
+ // Read required keyword from DBus as we need to set only a Bit.
+ auto l_kwdValueVariant = dbusUtility::readDbusProperty(
+ constants::pimServiceName, constants::systemVpdInvPath,
+ constants::utilInf, constants::kwdClearNVRAM_CreateLPAR);
+
+ if (auto l_pVal = std::get_if<types::BinaryVector>(&l_kwdValueVariant))
+ {
+ commonUtility::toLower(
+ const_cast<std::string&>(i_createDefaultLparVal));
+
+ // Check for second bit. Bit set for enabled else disabled.
+ if (((((*l_pVal).at(0) & 0x02) == 0x02) &&
+ (i_createDefaultLparVal.compare("enabled") ==
+ constants::STR_CMP_SUCCESS)) ||
+ ((((*l_pVal).at(0) & 0x02) == 0x00) &&
+ (i_createDefaultLparVal.compare("disabled") ==
+ constants::STR_CMP_SUCCESS)))
+ {
+ // Values are same, Don;t update.
+ return;
+ }
+
+ types::BinaryVector l_valToUpdateInVpd;
+ if (i_createDefaultLparVal.compare("enabled") ==
+ constants::STR_CMP_SUCCESS)
+ {
+ // 2nd Bit is used to store the value.
+ l_valToUpdateInVpd.emplace_back((*l_pVal).at(0) | 0x02);
+ }
+ else
+ {
+ // 2nd Bit is used to store the value.
+ l_valToUpdateInVpd.emplace_back((*l_pVal).at(0) & ~(0x02));
+ }
+
+ if (-1 ==
+ m_manager->updateKeyword(
+ SYSTEM_VPD_FILE_PATH,
+ types::IpzData("UTIL", constants::kwdClearNVRAM_CreateLPAR,
+ l_valToUpdateInVpd)))
+ {
+ logging::logMessage(
+ "Failed to update " +
+ std::string(constants::kwdClearNVRAM_CreateLPAR) +
+ " keyword to VPD");
+ }
+
+ return;
+ }
+ logging::logMessage(
+ "Invalid type recieved for create default Lpar from VPD.");
+}
+
+void IbmBiosHandler::saveCreateDefaultLparToBios(
+ const std::string& i_createDefaultLparVal)
+{
+ // checking for exact length as it is a string and can have garbage value.
+ if (i_createDefaultLparVal.size() != constants::VALUE_1)
+ {
+ logging::logMessage(
+ "Bad size for Create default LPAR in VPD. Skip writing to BIOS.");
+ return;
+ }
+
+ std::string l_valtoUpdate =
+ (i_createDefaultLparVal.at(0) & 0x02) ? "Enabled" : "Disabled";
+
+ types::PendingBIOSAttrs l_pendingBiosAttribute;
+ l_pendingBiosAttribute.push_back(std::make_pair(
+ "pvm_create_default_lpar",
+ std::make_tuple(
+ "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.Enumeration",
+ l_valtoUpdate)));
+
+ try
+ {
+ dbusUtility::writeDbusProperty(
+ constants::biosConfigMgrService, constants::biosConfigMgrObjPath,
+ constants::biosConfigMgrInterface, "PendingAttributes",
+ l_pendingBiosAttribute);
+ }
+ catch (const std::exception& l_ex)
+ {
+ logging::logMessage(
+ "DBus call to update lpar value in pending attribute failed. " +
+ std::string(l_ex.what()));
+ }
+
+ return;
+}
+
+void IbmBiosHandler::processCreateDefaultLpar()
+{
+ // Read required keyword from DBus.
+ auto l_kwdValueVariant = dbusUtility::readDbusProperty(
+ constants::pimServiceName, constants::systemVpdInvPath,
+ constants::utilInf, constants::kwdClearNVRAM_CreateLPAR);
+
+ if (auto l_pVal = std::get_if<types::BinaryVector>(&l_kwdValueVariant))
+ {
+ saveCreateDefaultLparToBios(std::to_string(l_pVal->at(0)));
+ return;
+ }
+ logging::logMessage(
+ "Invalid type recieved for create default Lpar from VPD.");
+}
+
+void IbmBiosHandler::saveClearNvramToVpd(const std::string& i_clearNvramVal)
+{
+ if (i_clearNvramVal.empty())
+ {
+ logging::logMessage(
+ "Empty value received for clear NVRAM from BIOS. Skip updating to VPD.");
+ return;
+ }
+
+ // Read required keyword from DBus as we need to set only a Bit.
+ auto l_kwdValueVariant = dbusUtility::readDbusProperty(
+ constants::pimServiceName, constants::systemVpdInvPath,
+ constants::utilInf, constants::kwdClearNVRAM_CreateLPAR);
+
+ if (auto l_pVal = std::get_if<types::BinaryVector>(&l_kwdValueVariant))
+ {
+ commonUtility::toLower(const_cast<std::string&>(i_clearNvramVal));
+
+ // Check for third bit. Bit set for enabled else disabled.
+ if (((((*l_pVal).at(0) & 0x04) == 0x04) &&
+ (i_clearNvramVal.compare("enabled") ==
+ constants::STR_CMP_SUCCESS)) ||
+ ((((*l_pVal).at(0) & 0x04) == 0x00) &&
+ (i_clearNvramVal.compare("disabled") ==
+ constants::STR_CMP_SUCCESS)))
+ {
+ // Don't update, values are same.
+ return;
+ }
+
+ types::BinaryVector l_valToUpdateInVpd;
+ if (i_clearNvramVal.compare("enabled") == constants::STR_CMP_SUCCESS)
+ {
+ // 3rd bit is used to store the value.
+ l_valToUpdateInVpd.emplace_back(
+ (*l_pVal).at(0) | constants::VALUE_4);
+ }
+ else
+ {
+ // 3rd bit is used to store the value.
+ l_valToUpdateInVpd.emplace_back(
+ (*l_pVal).at(0) & ~(constants::VALUE_4));
+ }
+
+ if (-1 ==
+ m_manager->updateKeyword(
+ SYSTEM_VPD_FILE_PATH,
+ types::IpzData("UTIL", constants::kwdClearNVRAM_CreateLPAR,
+ l_valToUpdateInVpd)))
+ {
+ logging::logMessage(
+ "Failed to update " +
+ std::string(constants::kwdClearNVRAM_CreateLPAR) +
+ " keyword to VPD");
+ }
+
+ return;
+ }
+ logging::logMessage("Invalid type recieved for clear NVRAM from VPD.");
+}
+
+void IbmBiosHandler::saveClearNvramToBios(const std::string& i_clearNvramVal)
+{
+ // Check for the exact length as it is a string and it can have a garbage
+ // value.
+ if (i_clearNvramVal.size() != constants::VALUE_1)
+ {
+ logging::logMessage(
+ "Bad size for clear NVRAM in VPD. Skip writing to BIOS.");
+ return;
+ }
+
+ // 3rd bit is used to store clear NVRAM value.
+ std::string l_valtoUpdate =
+ (i_clearNvramVal.at(0) & constants::VALUE_4) ? "Enabled" : "Disabled";
+
+ types::PendingBIOSAttrs l_pendingBiosAttribute;
+ l_pendingBiosAttribute.push_back(std::make_pair(
+ "pvm_clear_nvram",
+ std::make_tuple(
+ "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.Enumeration",
+ l_valtoUpdate)));
+
+ try
+ {
+ dbusUtility::writeDbusProperty(
+ constants::biosConfigMgrService, constants::biosConfigMgrObjPath,
+ constants::biosConfigMgrInterface, "PendingAttributes",
+ l_pendingBiosAttribute);
+ }
+ catch (const std::exception& l_ex)
+ {
+ logging::logMessage(
+ "DBus call to update NVRAM value in pending attribute failed. " +
+ std::string(l_ex.what()));
+ }
+}
+
+void IbmBiosHandler::processClearNvram()
+{
+ // Read required keyword from VPD.
+ auto l_kwdValueVariant = dbusUtility::readDbusProperty(
+ constants::pimServiceName, constants::systemVpdInvPath,
+ constants::utilInf, constants::kwdClearNVRAM_CreateLPAR);
+
+ if (auto l_pVal = std::get_if<types::BinaryVector>(&l_kwdValueVariant))
+ {
+ saveClearNvramToBios(std::to_string(l_pVal->at(0)));
+ return;
+ }
+ logging::logMessage("Invalid type recieved for clear NVRAM from VPD.");
+}
+
+void IbmBiosHandler::saveKeepAndClearToVpd(const std::string& i_KeepAndClearVal)
+{
+ if (i_KeepAndClearVal.empty())
+ {
+ logging::logMessage(
+ "Empty value received for keep and clear from BIOS. Skip updating to VPD.");
+ return;
+ }
+
+ // Read required keyword from DBus as we need to set only a Bit.
+ auto l_kwdValueVariant = dbusUtility::readDbusProperty(
+ constants::pimServiceName, constants::systemVpdInvPath,
+ constants::utilInf, constants::kwdKeepAndClear);
+
+ if (auto l_pVal = std::get_if<types::BinaryVector>(&l_kwdValueVariant))
+ {
+ commonUtility::toLower(const_cast<std::string&>(i_KeepAndClearVal));
+
+ // Check for first bit. Bit set for enabled else disabled.
+ if (((((*l_pVal).at(0) & 0x01) == 0x01) &&
+ (i_KeepAndClearVal.compare("enabled") ==
+ constants::STR_CMP_SUCCESS)) ||
+ ((((*l_pVal).at(0) & 0x01) == 0x00) &&
+ (i_KeepAndClearVal.compare("disabled") ==
+ constants::STR_CMP_SUCCESS)))
+ {
+ // Don't update, values are same.
+ return;
+ }
+
+ types::BinaryVector l_valToUpdateInVpd;
+ if (i_KeepAndClearVal.compare("enabled") == constants::STR_CMP_SUCCESS)
+ {
+ // 1st bit is used to store the value.
+ l_valToUpdateInVpd.emplace_back(
+ (*l_pVal).at(0) | constants::VALUE_1);
+ }
+ else
+ {
+ // 1st bit is used to store the value.
+ l_valToUpdateInVpd.emplace_back(
+ (*l_pVal).at(0) & ~(constants::VALUE_1));
+ }
+
+ if (-1 == m_manager->updateKeyword(
+ SYSTEM_VPD_FILE_PATH,
+ types::IpzData("UTIL", constants::kwdKeepAndClear,
+ l_valToUpdateInVpd)))
+ {
+ logging::logMessage(
+ "Failed to update " + std::string(constants::kwdKeepAndClear) +
+ " keyword to VPD");
+ }
+
+ return;
+ }
+ logging::logMessage("Invalid type recieved for keep and clear from VPD.");
+}
+
+void IbmBiosHandler::saveKeepAndClearToBios(
+ const std::string& i_KeepAndClearVal)
+{
+ // checking for exact length as it is a string and can have garbage value.
+ if (i_KeepAndClearVal.size() != constants::VALUE_1)
+ {
+ logging::logMessage(
+ "Bad size for keep and clear in VPD. Skip writing to BIOS.");
+ return;
+ }
+
+ // 1st bit is used to store keep and clear value.
+ std::string l_valtoUpdate =
+ (i_KeepAndClearVal.at(0) & constants::VALUE_1) ? "Enabled" : "Disabled";
+
+ types::PendingBIOSAttrs l_pendingBiosAttribute;
+ l_pendingBiosAttribute.push_back(std::make_pair(
+ "pvm_keep_and_clear",
+ std::make_tuple(
+ "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.Enumeration",
+ l_valtoUpdate)));
+
+ try
+ {
+ dbusUtility::writeDbusProperty(
+ constants::biosConfigMgrService, constants::biosConfigMgrObjPath,
+ constants::biosConfigMgrInterface, "PendingAttributes",
+ l_pendingBiosAttribute);
+ }
+ catch (const std::exception& l_ex)
+ {
+ logging::logMessage(
+ "DBus call to update keep and clear value in pending attribute failed. " +
+ std::string(l_ex.what()));
+ }
+}
+
+void IbmBiosHandler::processKeepAndClear()
+{
+ // Read required keyword from VPD.
+ auto l_kwdValueVariant = dbusUtility::readDbusProperty(
+ constants::pimServiceName, constants::systemVpdInvPath,
+ constants::utilInf, constants::kwdKeepAndClear);
+
+ if (auto l_pVal = std::get_if<types::BinaryVector>(&l_kwdValueVariant))
+ {
+ saveKeepAndClearToBios(std::to_string(l_pVal->at(0)));
+ return;
+ }
+ logging::logMessage("Invalid type recieved for keep and clear from VPD.");
+}
+} // namespace vpd
diff --git a/vpd-manager/src/ddimm_parser.cpp b/vpd-manager/src/ddimm_parser.cpp
new file mode 100644
index 0000000..0da4ddc
--- /dev/null
+++ b/vpd-manager/src/ddimm_parser.cpp
@@ -0,0 +1,393 @@
+#include "ddimm_parser.hpp"
+
+#include "constants.hpp"
+#include "exceptions.hpp"
+
+#include <cmath>
+#include <cstdint>
+#include <iostream>
+#include <numeric>
+#include <string>
+
+namespace vpd
+{
+
+static constexpr auto SDRAM_DENSITY_PER_DIE_24GB = 24;
+static constexpr auto SDRAM_DENSITY_PER_DIE_32GB = 32;
+static constexpr auto SDRAM_DENSITY_PER_DIE_48GB = 48;
+static constexpr auto SDRAM_DENSITY_PER_DIE_64GB = 64;
+static constexpr auto SDRAM_DENSITY_PER_DIE_UNDEFINED = 0;
+
+static constexpr auto PRIMARY_BUS_WIDTH_32_BITS = 32;
+static constexpr auto PRIMARY_BUS_WIDTH_UNUSED = 0;
+
+bool DdimmVpdParser::checkValidValue(uint8_t i_ByteValue, uint8_t i_shift,
+ uint8_t i_minValue, uint8_t i_maxValue)
+{
+ bool l_isValid = true;
+ uint8_t l_ByteValue = i_ByteValue >> i_shift;
+ if ((l_ByteValue > i_maxValue) || (l_ByteValue < i_minValue))
+ {
+ logging::logMessage(
+ "Non valid Value encountered value[" + std::to_string(l_ByteValue) +
+ "] range [" + std::to_string(i_minValue) + ".." +
+ std::to_string(i_maxValue) + "] found ");
+ return false;
+ }
+ return l_isValid;
+}
+
+uint8_t DdimmVpdParser::getDdr5DensityPerDie(uint8_t i_ByteValue)
+{
+ uint8_t l_densityPerDie = SDRAM_DENSITY_PER_DIE_UNDEFINED;
+ if (i_ByteValue < constants::VALUE_5)
+ {
+ l_densityPerDie = i_ByteValue * constants::VALUE_4;
+ }
+ else
+ {
+ switch (i_ByteValue)
+ {
+ case constants::VALUE_5:
+ l_densityPerDie = SDRAM_DENSITY_PER_DIE_24GB;
+ break;
+
+ case constants::VALUE_6:
+ l_densityPerDie = SDRAM_DENSITY_PER_DIE_32GB;
+ break;
+
+ case constants::VALUE_7:
+ l_densityPerDie = SDRAM_DENSITY_PER_DIE_48GB;
+ break;
+
+ case constants::VALUE_8:
+ l_densityPerDie = SDRAM_DENSITY_PER_DIE_64GB;
+ break;
+
+ default:
+ logging::logMessage(
+ "default value encountered for density per die");
+ l_densityPerDie = SDRAM_DENSITY_PER_DIE_UNDEFINED;
+ break;
+ }
+ }
+ return l_densityPerDie;
+}
+
+uint8_t DdimmVpdParser::getDdr5DiePerPackage(uint8_t i_ByteValue)
+{
+ uint8_t l_DiePerPackage = constants::VALUE_0;
+ if (i_ByteValue < constants::VALUE_2)
+ {
+ l_DiePerPackage = i_ByteValue + constants::VALUE_1;
+ }
+ else
+ {
+ l_DiePerPackage =
+ pow(constants::VALUE_2, (i_ByteValue - constants::VALUE_1));
+ }
+ return l_DiePerPackage;
+}
+
+size_t DdimmVpdParser::getDdr5BasedDdimmSize(
+ types::BinaryVector::const_iterator i_iterator)
+{
+ size_t l_dimmSize = 0;
+
+ do
+ {
+ if (!checkValidValue(i_iterator[constants::SPD_BYTE_235] &
+ constants::MASK_BYTE_BITS_01,
+ constants::SHIFT_BITS_0, constants::VALUE_1,
+ constants::VALUE_3) ||
+ !checkValidValue(i_iterator[constants::SPD_BYTE_235] &
+ constants::MASK_BYTE_BITS_345,
+ constants::SHIFT_BITS_3, constants::VALUE_1,
+ constants::VALUE_3))
+ {
+ logging::logMessage(
+ "Capacity calculation failed for channels per DIMM. DDIMM Byte "
+ "235 value [" +
+ std::to_string(i_iterator[constants::SPD_BYTE_235]) + "]");
+ break;
+ }
+ uint8_t l_channelsPerPhy =
+ (((i_iterator[constants::SPD_BYTE_235] &
+ constants::MASK_BYTE_BITS_01)
+ ? constants::VALUE_1
+ : constants::VALUE_0) +
+ ((i_iterator[constants::SPD_BYTE_235] &
+ constants::MASK_BYTE_BITS_345)
+ ? constants::VALUE_1
+ : constants::VALUE_0));
+
+ uint8_t l_channelsPerDdimm =
+ (((i_iterator[constants::SPD_BYTE_235] &
+ constants::MASK_BYTE_BIT_6) >>
+ constants::VALUE_6) +
+ ((i_iterator[constants::SPD_BYTE_235] &
+ constants::MASK_BYTE_BIT_7) >>
+ constants::VALUE_7)) *
+ l_channelsPerPhy;
+
+ if (!checkValidValue(i_iterator[constants::SPD_BYTE_235] &
+ constants::MASK_BYTE_BITS_012,
+ constants::SHIFT_BITS_0, constants::VALUE_1,
+ constants::VALUE_3))
+ {
+ logging::logMessage(
+ "Capacity calculation failed for bus width per channel. DDIMM "
+ "Byte 235 value [" +
+ std::to_string(i_iterator[constants::SPD_BYTE_235]) + "]");
+ break;
+ }
+ uint8_t l_busWidthPerChannel =
+ (i_iterator[constants::SPD_BYTE_235] &
+ constants::MASK_BYTE_BITS_012)
+ ? PRIMARY_BUS_WIDTH_32_BITS
+ : PRIMARY_BUS_WIDTH_UNUSED;
+
+ if (!checkValidValue(i_iterator[constants::SPD_BYTE_4] &
+ constants::MASK_BYTE_BITS_567,
+ constants::SHIFT_BITS_5, constants::VALUE_0,
+ constants::VALUE_5))
+ {
+ logging::logMessage(
+ "Capacity calculation failed for die per package. DDIMM Byte 4 "
+ "value [" +
+ std::to_string(i_iterator[constants::SPD_BYTE_4]) + "]");
+ break;
+ }
+ uint8_t l_diePerPackage = getDdr5DiePerPackage(
+ (i_iterator[constants::SPD_BYTE_4] &
+ constants::MASK_BYTE_BITS_567) >>
+ constants::VALUE_5);
+
+ if (!checkValidValue(i_iterator[constants::SPD_BYTE_4] &
+ constants::MASK_BYTE_BITS_01234,
+ constants::SHIFT_BITS_0, constants::VALUE_1,
+ constants::VALUE_8))
+ {
+ logging::logMessage(
+ "Capacity calculation failed for SDRAM Density per Die. DDIMM "
+ "Byte 4 value [" +
+ std::to_string(i_iterator[constants::SPD_BYTE_4]) + "]");
+ break;
+ }
+ uint8_t l_densityPerDie = getDdr5DensityPerDie(
+ i_iterator[constants::SPD_BYTE_4] &
+ constants::MASK_BYTE_BITS_01234);
+
+ uint8_t l_ranksPerChannel = 0;
+
+ if (((i_iterator[constants::SPD_BYTE_234] &
+ constants::MASK_BYTE_BIT_7) >>
+ constants::VALUE_7))
+ {
+ l_ranksPerChannel = ((i_iterator[constants::SPD_BYTE_234] &
+ constants::MASK_BYTE_BITS_345) >>
+ constants::VALUE_3) +
+ constants::VALUE_1;
+ }
+ else if (((i_iterator[constants::SPD_BYTE_235] &
+ constants::MASK_BYTE_BIT_6) >>
+ constants::VALUE_6))
+ {
+ l_ranksPerChannel = (i_iterator[constants::SPD_BYTE_234] &
+ constants::MASK_BYTE_BITS_012) +
+ constants::VALUE_1;
+ }
+
+ if (!checkValidValue(i_iterator[constants::SPD_BYTE_6] &
+ constants::MASK_BYTE_BITS_567,
+ constants::SHIFT_BITS_5, constants::VALUE_0,
+ constants::VALUE_3))
+ {
+ logging::logMessage(
+ "Capacity calculation failed for dram width DDIMM Byte 6 value "
+ "[" +
+ std::to_string(i_iterator[constants::SPD_BYTE_6]) + "]");
+ break;
+ }
+ uint8_t l_dramWidth =
+ constants::VALUE_4 *
+ (constants::VALUE_1 << ((i_iterator[constants::SPD_BYTE_6] &
+ constants::MASK_BYTE_BITS_567) >>
+ constants::VALUE_5));
+
+ // DDIMM size is calculated in GB
+ l_dimmSize = (l_channelsPerDdimm * l_busWidthPerChannel *
+ l_diePerPackage * l_densityPerDie * l_ranksPerChannel) /
+ (8 * l_dramWidth);
+
+ } while (false);
+
+ return constants::CONVERT_GB_TO_KB * l_dimmSize;
+}
+
+size_t DdimmVpdParser::getDdr4BasedDdimmSize(
+ types::BinaryVector::const_iterator i_iterator)
+{
+ size_t l_dimmSize = 0;
+ try
+ {
+ uint8_t l_tmpValue = 0;
+
+ // Calculate SDRAM capacity
+ l_tmpValue = i_iterator[constants::SPD_BYTE_4] &
+ constants::JEDEC_SDRAM_CAP_MASK;
+
+ /* Make sure the bits are not Reserved */
+ if (l_tmpValue > constants::JEDEC_SDRAMCAP_RESERVED)
+ {
+ throw std::runtime_error(
+ "Bad data in VPD byte 4. Can't calculate SDRAM capacity and so "
+ "dimm size.\n ");
+ }
+
+ uint16_t l_sdramCapacity = 1;
+ l_sdramCapacity = (l_sdramCapacity << l_tmpValue) *
+ constants::JEDEC_SDRAMCAP_MULTIPLIER;
+
+ /* Calculate Primary bus width */
+ l_tmpValue = i_iterator[constants::SPD_BYTE_13] &
+ constants::JEDEC_PRI_BUS_WIDTH_MASK;
+
+ if (l_tmpValue > constants::JEDEC_RESERVED_BITS)
+ {
+ throw std::runtime_error(
+ "Bad data in VPD byte 13. Can't calculate primary bus width "
+ "and so dimm size.");
+ }
+
+ uint8_t l_primaryBusWid = 1;
+ l_primaryBusWid = (l_primaryBusWid << l_tmpValue) *
+ constants::JEDEC_PRI_BUS_WIDTH_MULTIPLIER;
+
+ /* Calculate SDRAM width */
+ l_tmpValue = i_iterator[constants::SPD_BYTE_12] &
+ constants::JEDEC_SDRAM_WIDTH_MASK;
+
+ if (l_tmpValue > constants::JEDEC_RESERVED_BITS)
+ {
+ throw std::runtime_error(
+ "Bad data in VPD byte 12. Can't calculate SDRAM width and so "
+ "dimm size.");
+ }
+
+ uint8_t l_sdramWidth = 1;
+ l_sdramWidth = (l_sdramWidth << l_tmpValue) *
+ constants::JEDEC_SDRAM_WIDTH_MULTIPLIER;
+
+ /* Calculate Number of ranks */
+ l_tmpValue = i_iterator[constants::SPD_BYTE_12] &
+ constants::JEDEC_NUM_RANKS_MASK;
+ l_tmpValue >>= constants::JEDEC_RESERVED_BITS;
+
+ if (l_tmpValue > constants::JEDEC_RESERVED_BITS)
+ {
+ throw std::runtime_error(
+ "Bad data in VPD byte 12, can't calculate number of ranks. Invalid data found.");
+ }
+
+ uint8_t l_logicalRanksPerDimm = l_tmpValue + 1;
+
+ // Determine is single load stack (3DS) or not
+ l_tmpValue = i_iterator[constants::SPD_BYTE_6] &
+ constants::JEDEC_SIGNAL_LOADING_MASK;
+
+ if (l_tmpValue == constants::JEDEC_SINGLE_LOAD_STACK)
+ {
+ // Fetch die count
+ l_tmpValue = i_iterator[constants::SPD_BYTE_6] &
+ constants::JEDEC_DIE_COUNT_MASK;
+ l_tmpValue >>= constants::JEDEC_DIE_COUNT_RIGHT_SHIFT;
+
+ uint8_t l_dieCount = l_tmpValue + 1;
+ l_logicalRanksPerDimm *= l_dieCount;
+ }
+
+ l_dimmSize =
+ (l_sdramCapacity / constants::JEDEC_PRI_BUS_WIDTH_MULTIPLIER) *
+ (l_primaryBusWid / l_sdramWidth) * l_logicalRanksPerDimm;
+
+ // Converting dimm size from MB to KB
+ l_dimmSize *= constants::CONVERT_MB_TO_KB;
+ }
+ catch (const std::exception& l_ex)
+ {
+ // TODO:: Need an error log here
+ logging::logMessage("DDR4 DDIMM calculation is failed, reason: " +
+ std::string(l_ex.what()));
+ }
+ return l_dimmSize;
+}
+
+size_t
+ DdimmVpdParser::getDdimmSize(types::BinaryVector::const_iterator i_iterator)
+{
+ size_t l_dimmSize = 0;
+ if (i_iterator[constants::SPD_BYTE_2] == constants::SPD_DRAM_TYPE_DDR5)
+ {
+ l_dimmSize = getDdr5BasedDdimmSize(i_iterator);
+ }
+ else if (i_iterator[constants::SPD_BYTE_2] == constants::SPD_DRAM_TYPE_DDR4)
+ {
+ l_dimmSize = getDdr4BasedDdimmSize(i_iterator);
+ }
+ else
+ {
+ logging::logMessage(
+ "Error: DDIMM is neither DDR4 nor DDR5. DDIMM Byte 2 value [" +
+ std::to_string(i_iterator[constants::SPD_BYTE_2]) + "]");
+ }
+ return l_dimmSize;
+}
+
+void DdimmVpdParser::readKeywords(
+ types::BinaryVector::const_iterator i_iterator)
+{
+ // collect DDIMM size value
+ auto l_dimmSize = getDdimmSize(i_iterator);
+ if (!l_dimmSize)
+ {
+ throw(DataException("Error: Calculated dimm size is 0."));
+ }
+
+ m_parsedVpdMap.emplace("MemorySizeInKB", l_dimmSize);
+ // point the i_iterator to DIMM data and skip "11S"
+ advance(i_iterator, constants::DDIMM_11S_BARCODE_START +
+ constants::DDIMM_11S_FORMAT_LEN);
+ types::BinaryVector l_partNumber(i_iterator,
+ i_iterator + constants::PART_NUM_LEN);
+
+ advance(i_iterator, constants::PART_NUM_LEN);
+ types::BinaryVector l_serialNumber(i_iterator,
+ i_iterator + constants::SERIAL_NUM_LEN);
+
+ advance(i_iterator, constants::SERIAL_NUM_LEN);
+ types::BinaryVector l_ccin(i_iterator, i_iterator + constants::CCIN_LEN);
+
+ m_parsedVpdMap.emplace("FN", l_partNumber);
+ m_parsedVpdMap.emplace("PN", move(l_partNumber));
+ m_parsedVpdMap.emplace("SN", move(l_serialNumber));
+ m_parsedVpdMap.emplace("CC", move(l_ccin));
+}
+
+types::VPDMapVariant DdimmVpdParser::parse()
+{
+ try
+ {
+ // Read the data and return the map
+ auto l_iterator = m_vpdVector.cbegin();
+ readKeywords(l_iterator);
+ return m_parsedVpdMap;
+ }
+ catch (const std::exception& exp)
+ {
+ logging::logMessage(exp.what());
+ throw exp;
+ }
+}
+
+} // namespace vpd
diff --git a/vpd-manager/src/event_logger.cpp b/vpd-manager/src/event_logger.cpp
new file mode 100644
index 0000000..11861d1
--- /dev/null
+++ b/vpd-manager/src/event_logger.cpp
@@ -0,0 +1,294 @@
+#include "event_logger.hpp"
+
+#include "logger.hpp"
+
+#include <systemd/sd-bus.h>
+
+namespace vpd
+{
+const std::unordered_map<types::SeverityType, std::string>
+ EventLogger::m_severityMap = {
+ {types::SeverityType::Notice,
+ "xyz.openbmc_project.Logging.Entry.Level.Notice"},
+ {types::SeverityType::Informational,
+ "xyz.openbmc_project.Logging.Entry.Level.Informational"},
+ {types::SeverityType::Debug,
+ "xyz.openbmc_project.Logging.Entry.Level.Debug"},
+ {types::SeverityType::Warning,
+ "xyz.openbmc_project.Logging.Entry.Level.Warning"},
+ {types::SeverityType::Critical,
+ "xyz.openbmc_project.Logging.Entry.Level.Critical"},
+ {types::SeverityType::Emergency,
+ "xyz.openbmc_project.Logging.Entry.Level.Emergency"},
+ {types::SeverityType::Alert,
+ "xyz.openbmc_project.Logging.Entry.Level.Alert"},
+ {types::SeverityType::Error,
+ "xyz.openbmc_project.Logging.Entry.Level.Error"}};
+
+const std::unordered_map<types::ErrorType, std::string>
+ EventLogger::m_errorMsgMap = {
+ {types::ErrorType::DefaultValue, "com.ibm.VPD.Error.DefaultValue"},
+ {types::ErrorType::InvalidVpdMessage, "com.ibm.VPD.Error.InvalidVPD"},
+ {types::ErrorType::VpdMismatch, "com.ibm.VPD.Error.Mismatch"},
+ {types::ErrorType::InvalidEeprom,
+ "com.ibm.VPD.Error.InvalidEepromPath"},
+ {types::ErrorType::EccCheckFailed, "com.ibm.VPD.Error.EccCheckFailed"},
+ {types::ErrorType::JsonFailure, "com.ibm.VPD.Error.InvalidJson"},
+ {types::ErrorType::DbusFailure, "com.ibm.VPD.Error.DbusFailure"},
+ {types::ErrorType::InvalidSystem,
+ "com.ibm.VPD.Error.UnknownSystemType"},
+ {types::ErrorType::EssentialFru,
+ "com.ibm.VPD.Error.RequiredFRUMissing"},
+ {types::ErrorType::GpioError, "com.ibm.VPD.Error.GPIOError"}};
+
+const std::unordered_map<types::CalloutPriority, std::string>
+ EventLogger::m_priorityMap = {
+ {types::CalloutPriority::High, "H"},
+ {types::CalloutPriority::Medium, "M"},
+ {types::CalloutPriority::MediumGroupA, "A"},
+ {types::CalloutPriority::MediumGroupB, "B"},
+ {types::CalloutPriority::MediumGroupC, "C"},
+ {types::CalloutPriority::Low, "L"}};
+
+void EventLogger::createAsyncPelWithInventoryCallout(
+ const types::ErrorType& i_errorType, const types::SeverityType& i_severity,
+ const std::vector<types::InventoryCalloutData>& i_callouts,
+ const std::string& i_fileName, const std::string& i_funcName,
+ const uint8_t i_internalRc, const std::string& i_description,
+ const std::optional<std::string> i_userData1,
+ const std::optional<std::string> i_userData2,
+ const std::optional<std::string> i_symFru,
+ const std::optional<std::string> i_procedure)
+{
+ (void)i_symFru;
+ (void)i_procedure;
+
+ try
+ {
+ if (i_callouts.empty())
+ {
+ logging::logMessage("Callout information is missing to create PEL");
+ // TODO: Revisit this instead of simpley returning.
+ return;
+ }
+
+ if (m_errorMsgMap.find(i_errorType) == m_errorMsgMap.end())
+ {
+ throw std::runtime_error(
+ "Error type not found in the error message map to create PEL");
+ // TODO: Need to handle, instead of throwing exception. Create
+ // default message in message_registry.json.
+ }
+
+ const std::string& l_message = m_errorMsgMap.at(i_errorType);
+
+ const std::string& l_severity =
+ (m_severityMap.find(i_severity) != m_severityMap.end()
+ ? m_severityMap.at(i_severity)
+ : m_severityMap.at(types::SeverityType::Informational));
+
+ std::string l_description =
+ (!i_description.empty() ? i_description : "VPD generic error");
+
+ std::string l_userData1 = (i_userData1) ? (*i_userData1) : "";
+
+ std::string l_userData2 = (i_userData2) ? (*i_userData2) : "";
+
+ const types::InventoryCalloutData& l_invCallout = i_callouts[0];
+ // TODO: Need to handle multiple inventory path callout's, when multiple
+ // callout's is supported by "Logging" service.
+
+ const types::CalloutPriority& l_priorityEnum = get<1>(l_invCallout);
+
+ const std::string& l_priority =
+ (m_priorityMap.find(l_priorityEnum) != m_priorityMap.end()
+ ? m_priorityMap.at(l_priorityEnum)
+ : m_priorityMap.at(types::CalloutPriority::Low));
+
+ sd_bus* l_sdBus = nullptr;
+ sd_bus_default(&l_sdBus);
+
+ const uint8_t l_additionalDataCount = 8;
+ auto l_rc = sd_bus_call_method_async(
+ l_sdBus, NULL, constants::eventLoggingServiceName,
+ constants::eventLoggingObjectPath, constants::eventLoggingInterface,
+ "Create", NULL, NULL, "ssa{ss}", l_message.c_str(),
+ l_severity.c_str(), l_additionalDataCount, "FileName",
+ i_fileName.c_str(), "FunctionName", i_funcName.c_str(),
+ "InternalRc", std::to_string(i_internalRc).c_str(), "DESCRIPTION",
+ l_description.c_str(), "UserData1", l_userData1.c_str(),
+ "UserData2", l_userData2.c_str(), "CALLOUT_INVENTORY_PATH",
+ get<0>(l_invCallout).c_str(), "CALLOUT_PRIORITY",
+ l_priority.c_str());
+
+ if (l_rc < 0)
+ {
+ logging::logMessage(
+ "Error calling sd_bus_call_method_async, Message = " +
+ std::string(strerror(-l_rc)));
+ }
+ }
+ catch (const std::exception& l_ex)
+ {
+ logging::logMessage(
+ "Create PEL failed with error: " + std::string(l_ex.what()));
+ }
+}
+
+void EventLogger::createAsyncPelWithI2cDeviceCallout(
+ const types::ErrorType i_errorType, const types::SeverityType i_severity,
+ const std::vector<types::DeviceCalloutData>& i_callouts,
+ const std::string& i_fileName, const std::string& i_funcName,
+ const uint8_t i_internalRc,
+ const std::optional<std::pair<std::string, std::string>> i_userData1,
+ const std::optional<std::pair<std::string, std::string>> i_userData2)
+{
+ // TODO, implementation needs to be added.
+ (void)i_errorType;
+ (void)i_severity;
+ (void)i_callouts;
+ (void)i_fileName;
+ (void)i_funcName;
+ (void)i_internalRc;
+ (void)i_userData1;
+ (void)i_userData2;
+}
+
+void EventLogger::createAsyncPelWithI2cBusCallout(
+ const types::ErrorType i_errorType, const types::SeverityType i_severity,
+ const std::vector<types::I2cBusCalloutData>& i_callouts,
+ const std::string& i_fileName, const std::string& i_funcName,
+ const uint8_t i_internalRc,
+ const std::optional<std::pair<std::string, std::string>> i_userData1,
+ const std::optional<std::pair<std::string, std::string>> i_userData2)
+{
+ // TODO, implementation needs to be added.
+ (void)i_errorType;
+ (void)i_severity;
+ (void)i_callouts;
+ (void)i_fileName;
+ (void)i_funcName;
+ (void)i_internalRc;
+ (void)i_userData1;
+ (void)i_userData2;
+}
+
+void EventLogger::createAsyncPel(
+ const types::ErrorType& i_errorType, const types::SeverityType& i_severity,
+ const std::string& i_fileName, const std::string& i_funcName,
+ const uint8_t i_internalRc, const std::string& i_description,
+ const std::optional<std::string> i_userData1,
+ const std::optional<std::string> i_userData2,
+ const std::optional<std::string> i_symFru,
+ const std::optional<std::string> i_procedure)
+{
+ (void)i_symFru;
+ (void)i_procedure;
+ try
+ {
+ if (m_errorMsgMap.find(i_errorType) == m_errorMsgMap.end())
+ {
+ throw std::runtime_error("Unsupported error type received");
+ // TODO: Need to handle, instead of throwing an exception.
+ }
+
+ const std::string& l_message = m_errorMsgMap.at(i_errorType);
+
+ const std::string& l_severity =
+ (m_severityMap.find(i_severity) != m_severityMap.end()
+ ? m_severityMap.at(i_severity)
+ : m_severityMap.at(types::SeverityType::Informational));
+
+ const std::string l_description =
+ ((!i_description.empty() ? i_description : "VPD generic error"));
+
+ const std::string l_userData1 = ((i_userData1) ? (*i_userData1) : "");
+
+ const std::string l_userData2 = ((i_userData2) ? (*i_userData2) : "");
+
+ sd_bus* l_sdBus = nullptr;
+ sd_bus_default(&l_sdBus);
+
+ // VALUE_6 represents the additional data pair count passing to create
+ // PEL. If there any change in additional data, we need to pass the
+ // correct number.
+ auto l_rc = sd_bus_call_method_async(
+ l_sdBus, NULL, constants::eventLoggingServiceName,
+ constants::eventLoggingObjectPath, constants::eventLoggingInterface,
+ "Create", NULL, NULL, "ssa{ss}", l_message.c_str(),
+ l_severity.c_str(), constants::VALUE_6, "FileName",
+ i_fileName.c_str(), "FunctionName", i_funcName.c_str(),
+ "InternalRc", std::to_string(i_internalRc).c_str(), "DESCRIPTION",
+ l_description.c_str(), "UserData1", l_userData1.c_str(),
+ "UserData2", l_userData2.c_str());
+
+ if (l_rc < 0)
+ {
+ logging::logMessage(
+ "Error calling sd_bus_call_method_async, Message = " +
+ std::string(strerror(-l_rc)));
+ }
+ }
+ catch (const sdbusplus::exception::SdBusError& l_ex)
+ {
+ logging::logMessage("Async PEL creation failed with an error: " +
+ std::string(l_ex.what()));
+ }
+}
+
+void EventLogger::createSyncPel(
+ const types::ErrorType& i_errorType, const types::SeverityType& i_severity,
+ const std::string& i_fileName, const std::string& i_funcName,
+ const uint8_t i_internalRc, const std::string& i_description,
+ const std::optional<std::string> i_userData1,
+ const std::optional<std::string> i_userData2,
+ const std::optional<std::string> i_symFru,
+ const std::optional<std::string> i_procedure)
+{
+ (void)i_symFru;
+ (void)i_procedure;
+ try
+ {
+ if (m_errorMsgMap.find(i_errorType) == m_errorMsgMap.end())
+ {
+ throw std::runtime_error("Unsupported error type received");
+ // TODO: Need to handle, instead of throwing an exception.
+ }
+
+ const std::string& l_message = m_errorMsgMap.at(i_errorType);
+
+ const std::string& l_severity =
+ (m_severityMap.find(i_severity) != m_severityMap.end()
+ ? m_severityMap.at(i_severity)
+ : m_severityMap.at(types::SeverityType::Informational));
+
+ const std::string l_description =
+ ((!i_description.empty() ? i_description : "VPD generic error"));
+
+ const std::string l_userData1 = ((i_userData1) ? (*i_userData1) : "");
+
+ const std::string l_userData2 = ((i_userData2) ? (*i_userData2) : "");
+
+ std::map<std::string, std::string> l_additionalData{
+ {"FileName", i_fileName},
+ {"FunctionName", i_funcName},
+ {"DESCRIPTION", l_description},
+ {"InteranlRc", std::to_string(i_internalRc)},
+ {"UserData1", l_userData1.c_str()},
+ {"UserData2", l_userData2.c_str()}};
+
+ auto l_bus = sdbusplus::bus::new_default();
+ auto l_method =
+ l_bus.new_method_call(constants::eventLoggingServiceName,
+ constants::eventLoggingObjectPath,
+ constants::eventLoggingInterface, "Create");
+ l_method.append(l_message, l_severity, l_additionalData);
+ l_bus.call(l_method);
+ }
+ catch (const sdbusplus::exception::SdBusError& l_ex)
+ {
+ logging::logMessage("Sync PEL creation failed with an error: " +
+ std::string(l_ex.what()));
+ }
+}
+} // namespace vpd
diff --git a/vpd-manager/src/gpio_monitor.cpp b/vpd-manager/src/gpio_monitor.cpp
new file mode 100644
index 0000000..521037c
--- /dev/null
+++ b/vpd-manager/src/gpio_monitor.cpp
@@ -0,0 +1,131 @@
+#include "gpio_monitor.hpp"
+
+#include "constants.hpp"
+#include "logger.hpp"
+#include "types.hpp"
+#include "utility/dbus_utility.hpp"
+#include "utility/json_utility.hpp"
+
+#include <boost/asio.hpp>
+#include <boost/bind/bind.hpp>
+#include <gpiod.hpp>
+
+namespace vpd
+{
+void GpioEventHandler::handleChangeInGpioPin(const bool& i_isFruPresent)
+{
+ try
+ {
+ if (i_isFruPresent)
+ {
+ types::VPDMapVariant l_parsedVpd =
+ m_worker->parseVpdFile(m_fruPath);
+
+ if (std::holds_alternative<std::monostate>(l_parsedVpd))
+ {
+ throw std::runtime_error(
+ "VPD parsing failed for " + std::string(m_fruPath));
+ }
+
+ types::ObjectMap l_dbusObjectMap;
+ m_worker->populateDbus(l_parsedVpd, l_dbusObjectMap, m_fruPath);
+
+ if (l_dbusObjectMap.empty())
+ {
+ throw std::runtime_error("Failed to create D-bus object map.");
+ }
+
+ if (!dbusUtility::callPIM(move(l_dbusObjectMap)))
+ {
+ throw std::runtime_error("call PIM failed");
+ }
+ }
+ else
+ {
+ // TODO -- Add implementation to Delete FRU if FRU is not present.
+ }
+ }
+ catch (std::exception& l_ex)
+ {
+ logging::logMessage(std::string(l_ex.what()));
+ }
+}
+
+void GpioEventHandler::handleTimerExpiry(
+ const boost::system::error_code& i_errorCode,
+ const std::shared_ptr<boost::asio::steady_timer>& i_timerObj)
+{
+ if (i_errorCode == boost::asio::error::operation_aborted)
+ {
+ logging::logMessage("Timer aborted for GPIO pin");
+ return;
+ }
+
+ if (i_errorCode)
+ {
+ logging::logMessage("Timer wait failed for gpio pin" +
+ std::string(i_errorCode.message()));
+ return;
+ }
+
+ bool l_currentPresencePinValue = jsonUtility::processGpioPresenceTag(
+ m_worker->getSysCfgJsonObj(), m_fruPath, "pollingRequired",
+ "hotPlugging");
+
+ if (m_prevPresencePinValue != l_currentPresencePinValue)
+ {
+ m_prevPresencePinValue = l_currentPresencePinValue;
+ handleChangeInGpioPin(l_currentPresencePinValue);
+ }
+
+ i_timerObj->expires_at(std::chrono::steady_clock::now() +
+ std::chrono::seconds(constants::VALUE_5));
+ i_timerObj->async_wait(
+ boost::bind(&GpioEventHandler::handleTimerExpiry, this,
+ boost::asio::placeholders::error, i_timerObj));
+}
+
+void GpioEventHandler::setEventHandlerForGpioPresence(
+ const std::shared_ptr<boost::asio::io_context>& i_ioContext)
+{
+ m_prevPresencePinValue = jsonUtility::processGpioPresenceTag(
+ m_worker->getSysCfgJsonObj(), m_fruPath, "pollingRequired",
+ "hotPlugging");
+
+ static std::vector<std::shared_ptr<boost::asio::steady_timer>> l_timers;
+
+ auto l_timerObj = make_shared<boost::asio::steady_timer>(
+ *i_ioContext, std::chrono::seconds(constants::VALUE_5));
+
+ l_timerObj->async_wait(
+ boost::bind(&GpioEventHandler::handleTimerExpiry, this,
+ boost::asio::placeholders::error, l_timerObj));
+
+ l_timers.push_back(l_timerObj);
+}
+
+void GpioMonitor::initHandlerForGpio(
+ const std::shared_ptr<boost::asio::io_context>& i_ioContext,
+ const std::shared_ptr<Worker>& i_worker)
+{
+ try
+ {
+ std::vector<std::string> l_gpioPollingRequiredFrusList =
+ jsonUtility::getListOfGpioPollingFrus(m_sysCfgJsonObj);
+
+ for (const auto& l_fruPath : l_gpioPollingRequiredFrusList)
+ {
+ std::shared_ptr<GpioEventHandler> l_gpioEventHandlerObj =
+ std::make_shared<GpioEventHandler>(l_fruPath, i_worker,
+ i_ioContext);
+
+ m_gpioEventHandlerObjects.push_back(l_gpioEventHandlerObj);
+ }
+ }
+ catch (std::exception& l_ex)
+ {
+ // TODO log PEL for exception.
+ logging::logMessage(l_ex.what());
+ }
+}
+} // namespace vpd
diff --git a/vpd-manager/src/ipz_parser.cpp b/vpd-manager/src/ipz_parser.cpp
new file mode 100644
index 0000000..4300e12
--- /dev/null
+++ b/vpd-manager/src/ipz_parser.cpp
@@ -0,0 +1,842 @@
+#include "config.h"
+
+#include "ipz_parser.hpp"
+
+#include "vpdecc/vpdecc.h"
+
+#include "constants.hpp"
+#include "exceptions.hpp"
+
+#include <nlohmann/json.hpp>
+
+#include <typeindex>
+
+namespace vpd
+{
+
+// Offset of different entries in VPD data.
+enum Offset
+{
+ VHDR = 17,
+ VHDR_TOC_ENTRY = 29,
+ VTOC_PTR = 35,
+ VTOC_REC_LEN = 37,
+ VTOC_ECC_OFF = 39,
+ VTOC_ECC_LEN = 41,
+ VTOC_DATA = 13,
+ VHDR_ECC = 0,
+ VHDR_RECORD = 11
+};
+
+// Length of some specific entries w.r.t VPD data.
+enum Length
+{
+ RECORD_NAME = 4,
+ KW_NAME = 2,
+ RECORD_OFFSET = 2,
+ RECORD_MIN = 44,
+ RECORD_LENGTH = 2,
+ RECORD_ECC_OFFSET = 2,
+ VHDR_ECC_LENGTH = 11,
+ VHDR_RECORD_LENGTH = 44,
+ RECORD_TYPE = 2,
+ SKIP_A_RECORD_IN_PT = 14,
+ JUMP_TO_RECORD_NAME = 6
+}; // enum Length
+
+/**
+ * @brief API to read 2 bytes LE data.
+ *
+ * @param[in] iterator - iterator to VPD vector.
+ * @return read bytes.
+ */
+static uint16_t readUInt16LE(types::BinaryVector::const_iterator iterator)
+{
+ uint16_t lowByte = *iterator;
+ uint16_t highByte = *(iterator + 1);
+ lowByte |= (highByte << 8);
+ return lowByte;
+}
+
+bool IpzVpdParser::vhdrEccCheck()
+{
+ auto vpdPtr = m_vpdVector.cbegin();
+
+ auto l_status = vpdecc_check_data(
+ const_cast<uint8_t*>(&vpdPtr[Offset::VHDR_RECORD]),
+ Length::VHDR_RECORD_LENGTH,
+ const_cast<uint8_t*>(&vpdPtr[Offset::VHDR_ECC]),
+ Length::VHDR_ECC_LENGTH);
+ if (l_status == VPD_ECC_CORRECTABLE_DATA)
+ {
+ try
+ {
+ if (m_vpdFileStream.is_open())
+ {
+ m_vpdFileStream.seekp(m_vpdStartOffset + Offset::VHDR_RECORD,
+ std::ios::beg);
+ m_vpdFileStream.write(reinterpret_cast<const char*>(
+ &m_vpdVector[Offset::VHDR_RECORD]),
+ Length::VHDR_RECORD_LENGTH);
+ }
+ else
+ {
+ logging::logMessage("File not open");
+ return false;
+ }
+ }
+ catch (const std::fstream::failure& e)
+ {
+ logging::logMessage(
+ "Error while operating on file with exception: " +
+ std::string(e.what()));
+ return false;
+ }
+ }
+ else if (l_status != VPD_ECC_OK)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool IpzVpdParser::vtocEccCheck()
+{
+ auto vpdPtr = m_vpdVector.cbegin();
+
+ std::advance(vpdPtr, Offset::VTOC_PTR);
+
+ // The offset to VTOC could be 1 or 2 bytes long
+ auto vtocOffset = readUInt16LE(vpdPtr);
+
+ // Get the VTOC Length
+ std::advance(vpdPtr, sizeof(types::RecordOffset));
+ auto vtocLength = readUInt16LE(vpdPtr);
+
+ // Get the ECC Offset
+ std::advance(vpdPtr, sizeof(types::RecordLength));
+ auto vtocECCOffset = readUInt16LE(vpdPtr);
+
+ // Get the ECC length
+ std::advance(vpdPtr, sizeof(types::ECCOffset));
+ auto vtocECCLength = readUInt16LE(vpdPtr);
+
+ // Reset pointer to start of the vpd,
+ // so that Offset will point to correct address
+ vpdPtr = m_vpdVector.cbegin();
+ auto l_status = vpdecc_check_data(
+ const_cast<uint8_t*>(&m_vpdVector[vtocOffset]), vtocLength,
+ const_cast<uint8_t*>(&m_vpdVector[vtocECCOffset]), vtocECCLength);
+ if (l_status == VPD_ECC_CORRECTABLE_DATA)
+ {
+ try
+ {
+ if (m_vpdFileStream.is_open())
+ {
+ m_vpdFileStream.seekp(m_vpdStartOffset + vtocOffset,
+ std::ios::beg);
+ m_vpdFileStream.write(
+ reinterpret_cast<const char*>(&m_vpdVector[vtocOffset]),
+ vtocLength);
+ }
+ else
+ {
+ logging::logMessage("File not open");
+ return false;
+ }
+ }
+ catch (const std::fstream::failure& e)
+ {
+ logging::logMessage(
+ "Error while operating on file with exception " +
+ std::string(e.what()));
+ return false;
+ }
+ }
+ else if (l_status != VPD_ECC_OK)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool IpzVpdParser::recordEccCheck(types::BinaryVector::const_iterator iterator)
+{
+ auto recordOffset = readUInt16LE(iterator);
+
+ std::advance(iterator, sizeof(types::RecordOffset));
+ auto recordLength = readUInt16LE(iterator);
+
+ if (recordOffset == 0 || recordLength == 0)
+ {
+ throw(DataException("Invalid record offset or length"));
+ }
+
+ std::advance(iterator, sizeof(types::RecordLength));
+ auto eccOffset = readUInt16LE(iterator);
+
+ std::advance(iterator, sizeof(types::ECCOffset));
+ auto eccLength = readUInt16LE(iterator);
+
+ if (eccLength == 0 || eccOffset == 0)
+ {
+ throw(EccException("Invalid ECC length or offset."));
+ }
+
+ auto vpdPtr = m_vpdVector.cbegin();
+
+ if (vpdecc_check_data(
+ const_cast<uint8_t*>(&vpdPtr[recordOffset]), recordLength,
+ const_cast<uint8_t*>(&vpdPtr[eccOffset]), eccLength) == VPD_ECC_OK)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+void IpzVpdParser::checkHeader(types::BinaryVector::const_iterator itrToVPD)
+{
+ if (m_vpdVector.empty() || (Length::RECORD_MIN > m_vpdVector.size()))
+ {
+ throw(DataException("Malformed VPD"));
+ }
+
+ std::advance(itrToVPD, Offset::VHDR);
+ auto stop = std::next(itrToVPD, Length::RECORD_NAME);
+
+ std::string record(itrToVPD, stop);
+ if ("VHDR" != record)
+ {
+ throw(DataException("VHDR record not found"));
+ }
+
+ if (!vhdrEccCheck())
+ {
+ throw(EccException("ERROR: VHDR ECC check Failed"));
+ }
+}
+
+auto IpzVpdParser::readTOC(types::BinaryVector::const_iterator& itrToVPD)
+{
+ // The offset to VTOC could be 1 or 2 bytes long
+ uint16_t vtocOffset =
+ readUInt16LE((itrToVPD + Offset::VTOC_PTR)); // itrToVPD);
+
+ // Got the offset to VTOC, skip past record header and keyword header
+ // to get to the record name.
+ std::advance(itrToVPD, vtocOffset + sizeof(types::RecordId) +
+ sizeof(types::RecordSize) +
+ // Skip past the RT keyword, which contains
+ // the record name.
+ Length::KW_NAME + sizeof(types::KwSize));
+
+ std::string record(itrToVPD, std::next(itrToVPD, Length::RECORD_NAME));
+ if ("VTOC" != record)
+ {
+ throw(DataException("VTOC record not found"));
+ }
+
+ if (!vtocEccCheck())
+ {
+ throw(EccException("ERROR: VTOC ECC check Failed"));
+ }
+
+ // VTOC record name is good, now read through the TOC, stored in the PT
+ // PT keyword; vpdBuffer is now pointing at the first character of the
+ // name 'VTOC', jump to PT data.
+ // Skip past record name and KW name, 'PT'
+ std::advance(itrToVPD, Length::RECORD_NAME + Length::KW_NAME);
+
+ // Note size of PT
+ auto ptLen = *itrToVPD;
+
+ // Skip past PT size
+ std::advance(itrToVPD, sizeof(types::KwSize));
+
+ // length of PT keyword
+ return ptLen;
+}
+
+types::RecordOffsetList IpzVpdParser::readPT(
+ types::BinaryVector::const_iterator& itrToPT, auto ptLength)
+{
+ types::RecordOffsetList recordOffsets;
+
+ auto end = itrToPT;
+ std::advance(end, ptLength);
+
+ // Look at each entry in the PT keyword. In the entry,
+ // we care only about the record offset information.
+ while (itrToPT < end)
+ {
+ std::string recordName(itrToPT, itrToPT + Length::RECORD_NAME);
+ // Skip record name and record type
+ std::advance(itrToPT, Length::RECORD_NAME + sizeof(types::RecordType));
+
+ // Get record offset
+ recordOffsets.push_back(readUInt16LE(itrToPT));
+ try
+ {
+ // Verify the ECC for this Record
+ if (!recordEccCheck(itrToPT))
+ {
+ throw(EccException("ERROR: ECC check failed"));
+ }
+ }
+ catch (const EccException& ex)
+ {
+ logging::logMessage(ex.what());
+
+ /*TODO: uncomment when PEL code goes in */
+
+ /*std::string errMsg =
+ std::string{ex.what()} + " Record: " + recordName;
+
+ inventory::PelAdditionalData additionalData{};
+ additionalData.emplace("DESCRIPTION", errMsg);
+ additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
+ createPEL(additionalData, PelSeverity::WARNING,
+ errIntfForEccCheckFail, nullptr);*/
+ }
+ catch (const DataException& ex)
+ {
+ logging::logMessage(ex.what());
+
+ /*TODO: uncomment when PEL code goes in */
+
+ /*std::string errMsg =
+ std::string{ex.what()} + " Record: " + recordName;
+
+ inventory::PelAdditionalData additionalData{};
+ additionalData.emplace("DESCRIPTION", errMsg);
+ additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
+ createPEL(additionalData, PelSeverity::WARNING,
+ errIntfForInvalidVPD, nullptr);*/
+ }
+
+ // Jump record size, record length, ECC offset and ECC length
+ std::advance(itrToPT,
+ sizeof(types::RecordOffset) + sizeof(types::RecordLength) +
+ sizeof(types::ECCOffset) + sizeof(types::ECCLength));
+ }
+
+ return recordOffsets;
+}
+
+types::IPZVpdMap::mapped_type
+ IpzVpdParser::readKeywords(types::BinaryVector::const_iterator& itrToKwds)
+{
+ types::IPZVpdMap::mapped_type kwdValueMap{};
+ while (true)
+ {
+ // Note keyword name
+ std::string kwdName(itrToKwds, itrToKwds + Length::KW_NAME);
+ if (constants::LAST_KW == kwdName)
+ {
+ // We're done
+ break;
+ }
+ // Check if the Keyword is '#kw'
+ char kwNameStart = *itrToKwds;
+
+ // Jump past keyword name
+ std::advance(itrToKwds, Length::KW_NAME);
+
+ std::size_t kwdDataLength;
+ std::size_t lengthHighByte;
+
+ if (constants::POUND_KW == kwNameStart)
+ {
+ // Note keyword data length
+ kwdDataLength = *itrToKwds;
+ lengthHighByte = *(itrToKwds + 1);
+ kwdDataLength |= (lengthHighByte << 8);
+
+ // Jump past 2Byte keyword length
+ std::advance(itrToKwds, sizeof(types::PoundKwSize));
+ }
+ else
+ {
+ // Note keyword data length
+ kwdDataLength = *itrToKwds;
+
+ // Jump past keyword length
+ std::advance(itrToKwds, sizeof(types::KwSize));
+ }
+
+ // support all the Keywords
+ auto stop = std::next(itrToKwds, kwdDataLength);
+ std::string kwdata(itrToKwds, stop);
+ kwdValueMap.emplace(std::move(kwdName), std::move(kwdata));
+
+ // Jump past keyword data length
+ std::advance(itrToKwds, kwdDataLength);
+ }
+
+ return kwdValueMap;
+}
+
+void IpzVpdParser::processRecord(auto recordOffset)
+{
+ // Jump to record name
+ auto recordNameOffset =
+ recordOffset + sizeof(types::RecordId) + sizeof(types::RecordSize) +
+ // Skip past the RT keyword, which contains
+ // the record name.
+ Length::KW_NAME + sizeof(types::KwSize);
+
+ // Get record name
+ auto itrToVPDStart = m_vpdVector.cbegin();
+ std::advance(itrToVPDStart, recordNameOffset);
+
+ std::string recordName(itrToVPDStart, itrToVPDStart + Length::RECORD_NAME);
+
+ // proceed to find contained keywords and their values.
+ std::advance(itrToVPDStart, Length::RECORD_NAME);
+
+ // Reverse back to RT Kw, in ipz vpd, to Read RT KW & value
+ std::advance(itrToVPDStart, -(Length::KW_NAME + sizeof(types::KwSize) +
+ Length::RECORD_NAME));
+
+ // Add entry for this record (and contained keyword:value pairs)
+ // to the parsed vpd output.
+ m_parsedVPDMap.emplace(std::move(recordName),
+ std::move(readKeywords(itrToVPDStart)));
+}
+
+types::VPDMapVariant IpzVpdParser::parse()
+{
+ try
+ {
+ auto itrToVPD = m_vpdVector.cbegin();
+
+ // Check vaidity of VHDR record
+ checkHeader(itrToVPD);
+
+ // Read the table of contents
+ auto ptLen = readTOC(itrToVPD);
+
+ // Read the table of contents record, to get offsets
+ // to other records.
+ auto recordOffsets = readPT(itrToVPD, ptLen);
+ for (const auto& offset : recordOffsets)
+ {
+ processRecord(offset);
+ }
+
+ return m_parsedVPDMap;
+ }
+ catch (const std::exception& e)
+ {
+ logging::logMessage(e.what());
+ throw e;
+ }
+}
+
+types::BinaryVector IpzVpdParser::getKeywordValueFromRecord(
+ const types::Record& i_recordName, const types::Keyword& i_keywordName,
+ const types::RecordOffset& i_recordDataOffset)
+{
+ auto l_iterator = m_vpdVector.cbegin();
+
+ // Go to the record name in the given record's offset
+ std::ranges::advance(l_iterator,
+ i_recordDataOffset + Length::JUMP_TO_RECORD_NAME,
+ m_vpdVector.cend());
+
+ // Check if the record is present in the given record's offset
+ if (i_recordName !=
+ std::string(l_iterator,
+ std::ranges::next(l_iterator, Length::RECORD_NAME,
+ m_vpdVector.cend())))
+ {
+ throw std::runtime_error(
+ "Given record is not present in the offset provided");
+ }
+
+ std::ranges::advance(l_iterator, Length::RECORD_NAME, m_vpdVector.cend());
+
+ std::string l_kwName = std::string(
+ l_iterator,
+ std::ranges::next(l_iterator, Length::KW_NAME, m_vpdVector.cend()));
+
+ // Iterate through the keywords until the last keyword PF is found.
+ while (l_kwName != constants::LAST_KW)
+ {
+ // First character required for #D keyword check
+ char l_kwNameStart = *l_iterator;
+
+ std::ranges::advance(l_iterator, Length::KW_NAME, m_vpdVector.cend());
+
+ // Get the keyword's data length
+ auto l_kwdDataLength = 0;
+
+ if (constants::POUND_KW == l_kwNameStart)
+ {
+ l_kwdDataLength = readUInt16LE(l_iterator);
+ std::ranges::advance(l_iterator, sizeof(types::PoundKwSize),
+ m_vpdVector.cend());
+ }
+ else
+ {
+ l_kwdDataLength = *l_iterator;
+ std::ranges::advance(l_iterator, sizeof(types::KwSize),
+ m_vpdVector.cend());
+ }
+
+ if (l_kwName == i_keywordName)
+ {
+ // Return keyword's value to the caller
+ return types::BinaryVector(
+ l_iterator, std::ranges::next(l_iterator, l_kwdDataLength,
+ m_vpdVector.cend()));
+ }
+
+ // next keyword search
+ std::ranges::advance(l_iterator, l_kwdDataLength, m_vpdVector.cend());
+
+ // next keyword name
+ l_kwName = std::string(
+ l_iterator,
+ std::ranges::next(l_iterator, Length::KW_NAME, m_vpdVector.cend()));
+ }
+
+ // Keyword not found
+ throw std::runtime_error("Given keyword not found.");
+}
+
+types::RecordData IpzVpdParser::getRecordDetailsFromVTOC(
+ const types::Record& i_recordName, const types::RecordOffset& i_vtocOffset)
+{
+ // Get VTOC's PT keyword value.
+ const auto l_vtocPTKwValue =
+ getKeywordValueFromRecord("VTOC", "PT", i_vtocOffset);
+
+ // Parse through VTOC PT keyword value to find the record which we are
+ // interested in.
+ auto l_vtocPTItr = l_vtocPTKwValue.cbegin();
+
+ types::RecordData l_recordData;
+
+ while (l_vtocPTItr < l_vtocPTKwValue.cend())
+ {
+ if (i_recordName ==
+ std::string(l_vtocPTItr, l_vtocPTItr + Length::RECORD_NAME))
+ {
+ // Record found in VTOC PT keyword. Get offset
+ std::ranges::advance(l_vtocPTItr,
+ Length::RECORD_NAME + Length::RECORD_TYPE,
+ l_vtocPTKwValue.cend());
+ const auto l_recordOffset = readUInt16LE(l_vtocPTItr);
+
+ std::ranges::advance(l_vtocPTItr, Length::RECORD_OFFSET,
+ l_vtocPTKwValue.cend());
+ const auto l_recordLength = readUInt16LE(l_vtocPTItr);
+
+ std::ranges::advance(l_vtocPTItr, Length::RECORD_LENGTH,
+ l_vtocPTKwValue.cend());
+ const auto l_eccOffset = readUInt16LE(l_vtocPTItr);
+
+ std::ranges::advance(l_vtocPTItr, Length::RECORD_ECC_OFFSET,
+ l_vtocPTKwValue.cend());
+ const auto l_eccLength = readUInt16LE(l_vtocPTItr);
+
+ l_recordData = std::make_tuple(l_recordOffset, l_recordLength,
+ l_eccOffset, l_eccLength);
+ break;
+ }
+
+ std::ranges::advance(l_vtocPTItr, Length::SKIP_A_RECORD_IN_PT,
+ l_vtocPTKwValue.cend());
+ }
+
+ return l_recordData;
+}
+
+types::DbusVariantType IpzVpdParser::readKeywordFromHardware(
+ const types::ReadVpdParams i_paramsToReadData)
+{
+ // Extract record and keyword from i_paramsToReadData
+ types::Record l_record;
+ types::Keyword l_keyword;
+
+ if (const types::IpzType* l_ipzData =
+ std::get_if<types::IpzType>(&i_paramsToReadData))
+ {
+ l_record = std::get<0>(*l_ipzData);
+ l_keyword = std::get<1>(*l_ipzData);
+ }
+ else
+ {
+ logging::logMessage(
+ "Input parameter type provided isn't compatible with the given VPD type.");
+ throw types::DbusInvalidArgument();
+ }
+
+ // Read keyword's value from vector
+ auto l_itrToVPD = m_vpdVector.cbegin();
+
+ if (l_record == "VHDR")
+ {
+// Disable providing a way to read keywords from VHDR for the time being.
+#if 0
+ std::ranges::advance(l_itrToVPD, Offset::VHDR_RECORD,
+ m_vpdVector.cend());
+
+ return types::DbusVariantType{getKeywordValueFromRecord(
+ l_record, l_keyword, Offset::VHDR_RECORD)};
+#endif
+
+ logging::logMessage("Read cannot be performed on VHDR record.");
+ throw types::DbusInvalidArgument();
+ }
+
+ // Get VTOC offset
+ std::ranges::advance(l_itrToVPD, Offset::VTOC_PTR, m_vpdVector.cend());
+ auto l_vtocOffset = readUInt16LE(l_itrToVPD);
+
+ if (l_record == "VTOC")
+ {
+ // Disable providing a way to read keywords from VTOC for the time
+ // being.
+#if 0
+ return types::DbusVariantType{
+ getKeywordValueFromRecord(l_record, l_keyword, l_vtocOffset)};
+#endif
+
+ logging::logMessage("Read cannot be performed on VTOC record.");
+ throw types::DbusInvalidArgument();
+ }
+
+ // Get record offset from VTOC's PT keyword value.
+ auto l_recordData = getRecordDetailsFromVTOC(l_record, l_vtocOffset);
+ const auto l_recordOffset = std::get<0>(l_recordData);
+
+ if (l_recordOffset == 0)
+ {
+ throw std::runtime_error("Record not found in VTOC PT keyword.");
+ }
+
+ // Get the given keyword's value
+ return types::DbusVariantType{
+ getKeywordValueFromRecord(l_record, l_keyword, l_recordOffset)};
+}
+
+void IpzVpdParser::updateRecordECC(
+ const auto& i_recordDataOffset, const auto& i_recordDataLength,
+ const auto& i_recordECCOffset, size_t i_recordECCLength,
+ types::BinaryVector& io_vpdVector)
+{
+ auto l_recordDataBegin =
+ std::next(io_vpdVector.begin(), i_recordDataOffset);
+
+ auto l_recordECCBegin = std::next(io_vpdVector.begin(), i_recordECCOffset);
+
+ auto l_eccStatus = vpdecc_create_ecc(
+ const_cast<uint8_t*>(&l_recordDataBegin[0]), i_recordDataLength,
+ const_cast<uint8_t*>(&l_recordECCBegin[0]), &i_recordECCLength);
+
+ if (l_eccStatus != VPD_ECC_OK)
+ {
+ throw(EccException("ECC update failed with error " + l_eccStatus));
+ }
+
+ auto l_recordECCEnd = std::next(l_recordECCBegin, i_recordECCLength);
+
+ m_vpdFileStream.seekp(m_vpdStartOffset + i_recordECCOffset, std::ios::beg);
+
+ std::copy(l_recordECCBegin, l_recordECCEnd,
+ std::ostreambuf_iterator<char>(m_vpdFileStream));
+}
+
+int IpzVpdParser::setKeywordValueInRecord(
+ const types::Record& i_recordName, const types::Keyword& i_keywordName,
+ const types::BinaryVector& i_keywordData,
+ const types::RecordOffset& i_recordDataOffset,
+ types::BinaryVector& io_vpdVector)
+{
+ auto l_iterator = io_vpdVector.begin();
+
+ // Go to the record name in the given record's offset
+ std::ranges::advance(l_iterator,
+ i_recordDataOffset + Length::JUMP_TO_RECORD_NAME,
+ io_vpdVector.end());
+
+ const std::string l_recordFound(
+ l_iterator,
+ std::ranges::next(l_iterator, Length::RECORD_NAME, io_vpdVector.end()));
+
+ // Check if the record is present in the given record's offset
+ if (i_recordName != l_recordFound)
+ {
+ throw(DataException("Given record found at the offset " +
+ std::to_string(i_recordDataOffset) + " is : " +
+ l_recordFound + " and not " + i_recordName));
+ }
+
+ std::ranges::advance(l_iterator, Length::RECORD_NAME, io_vpdVector.end());
+
+ std::string l_kwName = std::string(
+ l_iterator,
+ std::ranges::next(l_iterator, Length::KW_NAME, io_vpdVector.end()));
+
+ // Iterate through the keywords until the last keyword PF is found.
+ while (l_kwName != constants::LAST_KW)
+ {
+ // First character required for #D keyword check
+ char l_kwNameStart = *l_iterator;
+
+ std::ranges::advance(l_iterator, Length::KW_NAME, io_vpdVector.end());
+
+ // Find the keyword's data length
+ size_t l_kwdDataLength = 0;
+
+ if (constants::POUND_KW == l_kwNameStart)
+ {
+ l_kwdDataLength = readUInt16LE(l_iterator);
+ std::ranges::advance(l_iterator, sizeof(types::PoundKwSize),
+ io_vpdVector.end());
+ }
+ else
+ {
+ l_kwdDataLength = *l_iterator;
+ std::ranges::advance(l_iterator, sizeof(types::KwSize),
+ io_vpdVector.end());
+ }
+
+ if (l_kwName == i_keywordName)
+ {
+ // Before writing the keyword's value, get the maximum size that can
+ // be updated.
+ const auto l_lengthToUpdate =
+ i_keywordData.size() <= l_kwdDataLength
+ ? i_keywordData.size()
+ : l_kwdDataLength;
+
+ // Set the keyword's value on vector. This is required to update the
+ // record's ECC based on the new value set.
+ const auto i_keywordDataEnd = std::ranges::next(
+ i_keywordData.cbegin(), l_lengthToUpdate, i_keywordData.cend());
+
+ std::copy(i_keywordData.cbegin(), i_keywordDataEnd, l_iterator);
+
+ // Set the keyword's value on hardware
+ const auto l_kwdDataOffset =
+ std::distance(io_vpdVector.begin(), l_iterator);
+ m_vpdFileStream.seekp(m_vpdStartOffset + l_kwdDataOffset,
+ std::ios::beg);
+
+ std::copy(i_keywordData.cbegin(), i_keywordDataEnd,
+ std::ostreambuf_iterator<char>(m_vpdFileStream));
+
+ // return no of bytes set
+ return l_lengthToUpdate;
+ }
+
+ // next keyword search
+ std::ranges::advance(l_iterator, l_kwdDataLength, io_vpdVector.end());
+
+ // next keyword name
+ l_kwName = std::string(
+ l_iterator,
+ std::ranges::next(l_iterator, Length::KW_NAME, io_vpdVector.end()));
+ }
+
+ // Keyword not found
+ throw(DataException(
+ "Keyword " + i_keywordName + " not found in record " + i_recordName));
+}
+
+int IpzVpdParser::writeKeywordOnHardware(
+ const types::WriteVpdParams i_paramsToWriteData)
+{
+ int l_sizeWritten = -1;
+
+ try
+ {
+ types::Record l_recordName;
+ types::Keyword l_keywordName;
+ types::BinaryVector l_keywordData;
+
+ // Extract record, keyword and value from i_paramsToWriteData
+ if (const types::IpzData* l_ipzData =
+ std::get_if<types::IpzData>(&i_paramsToWriteData))
+ {
+ l_recordName = std::get<0>(*l_ipzData);
+ l_keywordName = std::get<1>(*l_ipzData);
+ l_keywordData = std::get<2>(*l_ipzData);
+ }
+ else
+ {
+ logging::logMessage(
+ "Input parameter type provided isn't compatible with the given FRU's VPD type.");
+ throw types::DbusInvalidArgument();
+ }
+
+ if (l_recordName == "VHDR" || l_recordName == "VTOC")
+ {
+ logging::logMessage(
+ "Write operation not allowed on the given record : " +
+ l_recordName);
+ throw types::DbusNotAllowed();
+ }
+
+ if (l_keywordData.size() == 0)
+ {
+ logging::logMessage(
+ "Write operation not allowed as the given keyword's data length is 0.");
+ throw types::DbusInvalidArgument();
+ }
+
+ auto l_vpdBegin = m_vpdVector.begin();
+
+ // Get VTOC offset
+ std::ranges::advance(l_vpdBegin, Offset::VTOC_PTR, m_vpdVector.end());
+ auto l_vtocOffset = readUInt16LE(l_vpdBegin);
+
+ // Get the details of user given record from VTOC
+ const types::RecordData& l_inputRecordDetails =
+ getRecordDetailsFromVTOC(l_recordName, l_vtocOffset);
+
+ const auto& l_inputRecordOffset = std::get<0>(l_inputRecordDetails);
+
+ if (l_inputRecordOffset == 0)
+ {
+ throw(DataException("Record not found in VTOC PT keyword."));
+ }
+
+ // Create a local copy of m_vpdVector to perform keyword update and ecc
+ // update on filestream.
+ types::BinaryVector l_vpdVector = m_vpdVector;
+
+ // write keyword's value on hardware
+ l_sizeWritten =
+ setKeywordValueInRecord(l_recordName, l_keywordName, l_keywordData,
+ l_inputRecordOffset, l_vpdVector);
+
+ if (l_sizeWritten <= 0)
+ {
+ throw(DataException("Unable to set value on " + l_recordName + ":" +
+ l_keywordName));
+ }
+
+ // Update the record's ECC
+ updateRecordECC(l_inputRecordOffset, std::get<1>(l_inputRecordDetails),
+ std::get<2>(l_inputRecordDetails),
+ std::get<3>(l_inputRecordDetails), l_vpdVector);
+
+ logging::logMessage(std::to_string(l_sizeWritten) +
+ " bytes updated successfully on hardware for " +
+ l_recordName + ":" + l_keywordName);
+ }
+ catch (const std::exception& l_exception)
+ {
+ throw;
+ }
+
+ return l_sizeWritten;
+}
+} // namespace vpd
diff --git a/vpd-manager/src/isdimm_parser.cpp b/vpd-manager/src/isdimm_parser.cpp
new file mode 100644
index 0000000..76e1dea
--- /dev/null
+++ b/vpd-manager/src/isdimm_parser.cpp
@@ -0,0 +1,313 @@
+#include "isdimm_parser.hpp"
+
+#include "constants.hpp"
+#include "logger.hpp"
+
+#include <algorithm>
+#include <iostream>
+#include <numeric>
+#include <optional>
+#include <string>
+#include <unordered_map>
+
+namespace vpd
+{
+
+// Constants
+constexpr auto SPD_JEDEC_DDR4_SDRAM_CAP_MASK = 0x0F;
+constexpr auto SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MASK = 0x07;
+constexpr auto SPD_JEDEC_DDR4_SDRAM_WIDTH_MASK = 0x07;
+constexpr auto SPD_JEDEC_DDR4_NUM_RANKS_MASK = 0x38;
+constexpr auto SPD_JEDEC_DDR4_DIE_COUNT_MASK = 0x70;
+constexpr auto SPD_JEDEC_DDR4_SINGLE_LOAD_STACK = 0x02;
+constexpr auto SPD_JEDEC_DDR4_SIGNAL_LOADING_MASK = 0x03;
+
+constexpr auto SPD_JEDEC_DDR4_SDRAMCAP_MULTIPLIER = 256;
+constexpr auto SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MULTIPLIER = 8;
+constexpr auto SPD_JEDEC_DDR4_SDRAM_WIDTH_MULTIPLIER = 4;
+constexpr auto SPD_JEDEC_DDR4_SDRAMCAP_RESERVED = 8;
+constexpr auto SPD_JEDEC_DDR4_4_RESERVED_BITS = 4;
+constexpr auto SPD_JEDEC_DDR4_3_RESERVED_BITS = 3;
+constexpr auto SPD_JEDEC_DDR4_DIE_COUNT_RIGHT_SHIFT = 4;
+
+constexpr auto SPD_JEDEC_DDR4_MFG_ID_MSB_OFFSET = 321;
+constexpr auto SPD_JEDEC_DDR4_MFG_ID_LSB_OFFSET = 320;
+constexpr auto SPD_JEDEC_DDR4_SN_BYTE0_OFFSET = 325;
+constexpr auto SPD_JEDEC_DDR4_SN_BYTE1_OFFSET = 326;
+constexpr auto SPD_JEDEC_DDR4_SN_BYTE2_OFFSET = 327;
+constexpr auto SPD_JEDEC_DDR4_SN_BYTE3_OFFSET = 328;
+constexpr auto SPD_JEDEC_DDR4_SDRAM_DENSITY_BANK_OFFSET = 4;
+constexpr auto SPD_JEDEC_DDR4_SDRAM_ADDR_OFFSET = 5;
+constexpr auto SPD_JEDEC_DDR4_DRAM_PRI_PACKAGE_OFFSET = 6;
+constexpr auto SPD_JEDEC_DDR4_DRAM_MODULE_ORG_OFFSET = 12;
+
+// Lookup tables
+const std::map<std::tuple<std::string, uint8_t>, std::string> pnFreqFnMap = {
+ {std::make_tuple("8421000", 6), "78P4191"},
+ {std::make_tuple("8421008", 6), "78P4192"},
+ {std::make_tuple("8529000", 6), "78P4197"},
+ {std::make_tuple("8529008", 6), "78P4198"},
+ {std::make_tuple("8529928", 6), "78P4199"},
+ {std::make_tuple("8529B28", 6), "78P4200"},
+ {std::make_tuple("8631928", 6), "78P6925"},
+ {std::make_tuple("8529000", 5), "78P7317"},
+ {std::make_tuple("8529008", 5), "78P7318"},
+ {std::make_tuple("8631008", 5), "78P6815"}};
+
+const std::unordered_map<std::string, std::string> pnCCINMap = {
+ {"78P4191", "324D"}, {"78P4192", "324E"}, {"78P4197", "324E"},
+ {"78P4198", "324F"}, {"78P4199", "325A"}, {"78P4200", "324C"},
+ {"78P6925", "32BC"}, {"78P7317", "331A"}, {"78P7318", "331F"},
+ {"78P6815", "32BB"}};
+
+auto JedecSpdParser::getDDR4DimmCapacity(
+ types::BinaryVector::const_iterator& i_iterator)
+{
+ size_t l_tmp = 0, l_dimmSize = 0;
+
+ size_t l_sdramCap = 1, l_priBusWid = 1, l_sdramWid = 1,
+ l_logicalRanksPerDimm = 1;
+ size_t l_dieCount = 1;
+
+ // NOTE: This calculation is Only for DDR4
+
+ // Calculate SDRAM capacity
+ l_tmp = i_iterator[constants::SPD_BYTE_4] & SPD_JEDEC_DDR4_SDRAM_CAP_MASK;
+
+ /* Make sure the bits are not Reserved */
+ if (l_tmp >= SPD_JEDEC_DDR4_SDRAMCAP_RESERVED)
+ {
+ logging::logMessage(
+ "Bad data in spd byte 4. Can't calculate SDRAM capacity "
+ "and so dimm size.\n ");
+ return l_dimmSize;
+ }
+ l_sdramCap = (l_sdramCap << l_tmp) * SPD_JEDEC_DDR4_SDRAMCAP_MULTIPLIER;
+
+ /* Calculate Primary bus width */
+ l_tmp = i_iterator[constants::SPD_BYTE_13] &
+ SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MASK;
+ if (l_tmp >= SPD_JEDEC_DDR4_4_RESERVED_BITS)
+ {
+ logging::logMessage(
+ "Bad data in spd byte 13. Can't calculate primary bus "
+ "width and so dimm size.\n ");
+ return l_dimmSize;
+ }
+ l_priBusWid = (l_priBusWid << l_tmp) *
+ SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MULTIPLIER;
+
+ /* Calculate SDRAM width */
+ l_tmp = i_iterator[constants::SPD_BYTE_12] &
+ SPD_JEDEC_DDR4_SDRAM_WIDTH_MASK;
+ if (l_tmp >= SPD_JEDEC_DDR4_4_RESERVED_BITS)
+ {
+ logging::logMessage(
+ "Bad data in spd byte 12. Can't calculate SDRAM width and "
+ "so dimm size.\n ");
+ return l_dimmSize;
+ }
+ l_sdramWid = (l_sdramWid << l_tmp) * SPD_JEDEC_DDR4_SDRAM_WIDTH_MULTIPLIER;
+
+ l_tmp = i_iterator[constants::SPD_BYTE_6] &
+ SPD_JEDEC_DDR4_SIGNAL_LOADING_MASK;
+ if (l_tmp == SPD_JEDEC_DDR4_SINGLE_LOAD_STACK)
+ {
+ // Fetch die count
+ l_tmp = i_iterator[constants::SPD_BYTE_6] &
+ SPD_JEDEC_DDR4_DIE_COUNT_MASK;
+ l_tmp >>= SPD_JEDEC_DDR4_DIE_COUNT_RIGHT_SHIFT;
+ l_dieCount = l_tmp + 1;
+ }
+
+ /* Calculate Number of ranks */
+ l_tmp = i_iterator[constants::SPD_BYTE_12] & SPD_JEDEC_DDR4_NUM_RANKS_MASK;
+ l_tmp >>= SPD_JEDEC_DDR4_3_RESERVED_BITS;
+
+ if (l_tmp >= SPD_JEDEC_DDR4_4_RESERVED_BITS)
+ {
+ logging::logMessage(
+ "Can't calculate number of ranks. Invalid data found.\n ");
+ return l_dimmSize;
+ }
+ l_logicalRanksPerDimm = (l_tmp + 1) * l_dieCount;
+
+ l_dimmSize = (l_sdramCap / SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MULTIPLIER) *
+ (l_priBusWid / l_sdramWid) * l_logicalRanksPerDimm;
+
+ return l_dimmSize;
+}
+
+std::string_view JedecSpdParser::getDDR4PartNumber(
+ types::BinaryVector::const_iterator& i_iterator)
+{
+ char l_tmpPN[constants::PART_NUM_LEN + 1] = {'\0'};
+ sprintf(l_tmpPN, "%02X%02X%02X%X",
+ i_iterator[SPD_JEDEC_DDR4_SDRAM_DENSITY_BANK_OFFSET],
+ i_iterator[SPD_JEDEC_DDR4_SDRAM_ADDR_OFFSET],
+ i_iterator[SPD_JEDEC_DDR4_DRAM_PRI_PACKAGE_OFFSET],
+ i_iterator[SPD_JEDEC_DDR4_DRAM_MODULE_ORG_OFFSET] & 0x0F);
+ std::string l_partNumber(l_tmpPN, sizeof(l_tmpPN) - 1);
+ return l_partNumber;
+}
+
+std::string JedecSpdParser::getDDR4SerialNumber(
+ types::BinaryVector::const_iterator& i_iterator)
+{
+ char l_tmpSN[constants::SERIAL_NUM_LEN + 1] = {'\0'};
+ sprintf(l_tmpSN, "%02X%02X%02X%02X%02X%02X",
+ i_iterator[SPD_JEDEC_DDR4_MFG_ID_MSB_OFFSET],
+ i_iterator[SPD_JEDEC_DDR4_MFG_ID_LSB_OFFSET],
+ i_iterator[SPD_JEDEC_DDR4_SN_BYTE0_OFFSET],
+ i_iterator[SPD_JEDEC_DDR4_SN_BYTE1_OFFSET],
+ i_iterator[SPD_JEDEC_DDR4_SN_BYTE2_OFFSET],
+ i_iterator[SPD_JEDEC_DDR4_SN_BYTE3_OFFSET]);
+ std::string l_serialNumber(l_tmpSN, sizeof(l_tmpSN) - 1);
+ return l_serialNumber;
+}
+
+std::string_view JedecSpdParser::getDDR4FruNumber(
+ const std::string& i_partNumber,
+ types::BinaryVector::const_iterator& i_iterator)
+{
+ // check for 128GB ISRDIMM not implemented
+ //(128GB 2RX4(8GX72) IS RDIMM 36*(16GBIT, 2H),1.2V 288PIN,1.2" ROHS) - NA
+
+ // MTB Units is used in deciding the frequency of the DIMM
+ // This is applicable only for DDR4 specification
+ // 10 - DDR4-1600
+ // 9 - DDR4-1866
+ // 8 - DDR4-2133
+ // 7 - DDR4-2400
+ // 6 - DDR4-2666
+ // 5 - DDR4-3200
+ // pnFreqFnMap < tuple <partNumber, MTBUnits>, fruNumber>
+ uint8_t l_mtbUnits = i_iterator[constants::SPD_BYTE_18] &
+ constants::SPD_BYTE_MASK;
+ std::string l_fruNumber = "FFFFFFF";
+ auto it = pnFreqFnMap.find({i_partNumber, l_mtbUnits});
+ if (it != pnFreqFnMap.end())
+ {
+ l_fruNumber = it->second;
+ }
+
+ return l_fruNumber;
+}
+
+std::string_view JedecSpdParser::getDDR4CCIN(const std::string& i_fruNumber)
+{
+ auto it = pnCCINMap.find(i_fruNumber);
+ if (it != pnCCINMap.end())
+ {
+ return it->second;
+ }
+ return "XXXX"; // Return default value as XXXX
+}
+
+auto JedecSpdParser::getDDR5DimmCapacity(
+ types::BinaryVector::const_iterator& i_iterator)
+{
+ // dummy implementation to be updated when required
+ size_t dimmSize = 0;
+ (void)i_iterator;
+ return dimmSize;
+}
+
+auto JedecSpdParser::getDDR5PartNumber(
+ types::BinaryVector::const_iterator& i_iterator)
+{
+ // dummy implementation to be updated when required
+ std::string l_partNumber;
+ (void)i_iterator;
+ l_partNumber = "0123456";
+ return l_partNumber;
+}
+
+auto JedecSpdParser::getDDR5SerialNumber(
+ types::BinaryVector::const_iterator& i_iterator)
+{
+ // dummy implementation to be updated when required
+ std::string l_serialNumber;
+ (void)i_iterator;
+ l_serialNumber = "444444444444";
+ return l_serialNumber;
+}
+
+auto JedecSpdParser::getDDR5FruNumber(const std::string& i_partNumber)
+{
+ // dummy implementation to be updated when required
+ static std::unordered_map<std::string, std::string> pnFruMap = {
+ {"1234567", "XXXXXXX"}};
+
+ std::string l_fruNumber;
+ auto itr = pnFruMap.find(i_partNumber);
+ if (itr != pnFruMap.end())
+ {
+ l_fruNumber = itr->second;
+ }
+ else
+ {
+ l_fruNumber = "FFFFFFF";
+ }
+ return l_fruNumber;
+}
+
+auto JedecSpdParser::getDDR5CCIN(const std::string& i_partNumber)
+{
+ // dummy implementation to be updated when required
+ static std::unordered_map<std::string, std::string> pnCCINMap = {
+ {"1234567", "XXXX"}};
+
+ std::string ccin = "XXXX";
+ auto itr = pnCCINMap.find(i_partNumber);
+ if (itr != pnCCINMap.end())
+ {
+ ccin = itr->second;
+ }
+ return ccin;
+}
+
+types::JedecSpdMap JedecSpdParser::readKeywords(
+ types::BinaryVector::const_iterator& i_iterator)
+{
+ types::JedecSpdMap l_keywordValueMap{};
+ size_t dimmSize = getDDR4DimmCapacity(i_iterator);
+ if (!dimmSize)
+ {
+ logging::logMessage("Error: Calculated dimm size is 0.");
+ }
+ else
+ {
+ l_keywordValueMap.emplace("MemorySizeInKB",
+ dimmSize * constants::CONVERT_MB_TO_KB);
+ }
+
+ auto l_partNumber = getDDR4PartNumber(i_iterator);
+ auto l_fruNumber = getDDR4FruNumber(
+ std::string(l_partNumber.begin(), l_partNumber.end()), i_iterator);
+ auto l_serialNumber = getDDR4SerialNumber(i_iterator);
+ auto ccin =
+ getDDR4CCIN(std::string(l_fruNumber.begin(), l_fruNumber.end()));
+ // PN value is made same as FN value
+ auto l_displayPartNumber = l_fruNumber;
+ l_keywordValueMap.emplace("PN",
+ move(std::string(l_displayPartNumber.begin(),
+ l_displayPartNumber.end())));
+ l_keywordValueMap.emplace(
+ "FN", move(std::string(l_fruNumber.begin(), l_fruNumber.end())));
+ l_keywordValueMap.emplace("SN", move(l_serialNumber));
+ l_keywordValueMap.emplace("CC",
+ move(std::string(ccin.begin(), ccin.end())));
+
+ return l_keywordValueMap;
+}
+
+types::VPDMapVariant JedecSpdParser::parse()
+{
+ // Read the data and return the map
+ auto l_iterator = m_memSpd.cbegin();
+ auto l_spdDataMap = readKeywords(l_iterator);
+ return l_spdDataMap;
+}
+
+} // namespace vpd
diff --git a/vpd-manager/src/keyword_vpd_parser.cpp b/vpd-manager/src/keyword_vpd_parser.cpp
new file mode 100644
index 0000000..9997f94
--- /dev/null
+++ b/vpd-manager/src/keyword_vpd_parser.cpp
@@ -0,0 +1,136 @@
+#include "keyword_vpd_parser.hpp"
+
+#include "constants.hpp"
+#include "exceptions.hpp"
+#include "logger.hpp"
+
+#include <iostream>
+#include <numeric>
+#include <string>
+
+namespace vpd
+{
+
+types::VPDMapVariant KeywordVpdParser::parse()
+{
+ if (m_keywordVpdVector.empty())
+ {
+ throw(DataException("Vector for Keyword format VPD is empty"));
+ }
+ m_vpdIterator = m_keywordVpdVector.begin();
+
+ if (*m_vpdIterator != constants::KW_VPD_START_TAG)
+ {
+ throw(DataException("Invalid Large resource type Identifier String"));
+ }
+
+ checkNextBytesValidity(sizeof(constants::KW_VPD_START_TAG));
+ std::advance(m_vpdIterator, sizeof(constants::KW_VPD_START_TAG));
+
+ uint16_t l_dataSize = getKwDataSize();
+
+ checkNextBytesValidity(constants::TWO_BYTES + l_dataSize);
+ std::advance(m_vpdIterator, constants::TWO_BYTES + l_dataSize);
+
+ // Check for invalid vendor defined large resource type
+ if (*m_vpdIterator != constants::KW_VPD_PAIR_START_TAG)
+ {
+ if (*m_vpdIterator != constants::ALT_KW_VPD_PAIR_START_TAG)
+ {
+ throw(DataException("Invalid Keyword Vpd Start Tag"));
+ }
+ }
+ types::BinaryVector::const_iterator l_checkSumStart = m_vpdIterator;
+ auto l_kwValMap = populateVpdMap();
+
+ // Do these validations before returning parsed data.
+ // Check for small resource type end tag
+ if (*m_vpdIterator != constants::KW_VAL_PAIR_END_TAG)
+ {
+ throw(DataException("Invalid Small resource type End"));
+ }
+
+ types::BinaryVector::const_iterator l_checkSumEnd = m_vpdIterator;
+ validateChecksum(l_checkSumStart, l_checkSumEnd);
+
+ checkNextBytesValidity(constants::TWO_BYTES);
+ std::advance(m_vpdIterator, constants::TWO_BYTES);
+
+ // Check VPD end Tag.
+ if (*m_vpdIterator != constants::KW_VPD_END_TAG)
+ {
+ throw(DataException("Invalid Small resource type."));
+ }
+
+ return l_kwValMap;
+}
+
+types::KeywordVpdMap KeywordVpdParser::populateVpdMap()
+{
+ checkNextBytesValidity(constants::ONE_BYTE);
+ std::advance(m_vpdIterator, constants::ONE_BYTE);
+
+ auto l_totalSize = getKwDataSize();
+ if (l_totalSize == 0)
+ {
+ throw(DataException("Data size is 0, badly formed keyword VPD"));
+ }
+
+ checkNextBytesValidity(constants::TWO_BYTES);
+ std::advance(m_vpdIterator, constants::TWO_BYTES);
+
+ types::KeywordVpdMap l_kwValMap;
+
+ // Parse the keyword-value and store the pairs in map
+ while (l_totalSize > 0)
+ {
+ checkNextBytesValidity(constants::TWO_BYTES);
+ std::string l_keywordName(m_vpdIterator,
+ m_vpdIterator + constants::TWO_BYTES);
+ std::advance(m_vpdIterator, constants::TWO_BYTES);
+
+ size_t l_kwSize = *m_vpdIterator;
+ checkNextBytesValidity(constants::ONE_BYTE + l_kwSize);
+ m_vpdIterator++;
+ std::vector<uint8_t> l_valueBytes(m_vpdIterator,
+ m_vpdIterator + l_kwSize);
+ std::advance(m_vpdIterator, l_kwSize);
+
+ l_kwValMap.emplace(
+ std::make_pair(std::move(l_keywordName), std::move(l_valueBytes)));
+
+ l_totalSize -= constants::TWO_BYTES + constants::ONE_BYTE + l_kwSize;
+ }
+
+ return l_kwValMap;
+}
+
+void KeywordVpdParser::validateChecksum(
+ types::BinaryVector::const_iterator i_checkSumStart,
+ types::BinaryVector::const_iterator i_checkSumEnd)
+{
+ uint8_t l_checkSumCalculated = 0;
+
+ // Checksum calculation
+ l_checkSumCalculated =
+ std::accumulate(i_checkSumStart, i_checkSumEnd, l_checkSumCalculated);
+ l_checkSumCalculated = ~l_checkSumCalculated + 1;
+ uint8_t l_checksumVpdValue = *(m_vpdIterator + constants::ONE_BYTE);
+
+ if (l_checkSumCalculated != l_checksumVpdValue)
+ {
+ throw(DataException("Invalid Checksum"));
+ }
+}
+
+void KeywordVpdParser::checkNextBytesValidity(uint8_t i_numberOfBytes)
+{
+ if ((std::distance(m_keywordVpdVector.begin(),
+ m_vpdIterator + i_numberOfBytes)) >
+ std::distance(m_keywordVpdVector.begin(), m_keywordVpdVector.end()))
+ {
+ throw(DataException("Truncated VPD data"));
+ }
+}
+
+} // namespace vpd
diff --git a/vpd-manager/src/logger.cpp b/vpd-manager/src/logger.cpp
new file mode 100644
index 0000000..19959a1
--- /dev/null
+++ b/vpd-manager/src/logger.cpp
@@ -0,0 +1,23 @@
+#include "logger.hpp"
+
+#include <sstream>
+
+namespace vpd
+{
+namespace logging
+{
+void logMessage(std::string_view message, const std::source_location& location)
+{
+ std::ostringstream log;
+ log << "FileName: " << location.file_name() << ","
+ << " Line: " << location.line() << " " << message;
+
+ /* TODO: Check on this later.
+ log << "FileName: " << location.file_name() << ","
+ << " Line: " << location.line() << ","
+ << " Func: " << location.function_name() << ", " << message;*/
+
+ std::cout << log.str() << std::endl;
+}
+} // namespace logging
+} // namespace vpd
diff --git a/vpd-manager/src/manager.cpp b/vpd-manager/src/manager.cpp
new file mode 100644
index 0000000..51eb4af
--- /dev/null
+++ b/vpd-manager/src/manager.cpp
@@ -0,0 +1,915 @@
+#include "config.h"
+
+#include "manager.hpp"
+
+#include "backup_restore.hpp"
+#include "constants.hpp"
+#include "exceptions.hpp"
+#include "logger.hpp"
+#include "parser.hpp"
+#include "parser_factory.hpp"
+#include "parser_interface.hpp"
+#include "types.hpp"
+#include "utility/dbus_utility.hpp"
+#include "utility/json_utility.hpp"
+#include "utility/vpd_specific_utility.hpp"
+
+#include <boost/asio/steady_timer.hpp>
+#include <sdbusplus/bus/match.hpp>
+#include <sdbusplus/message.hpp>
+
+namespace vpd
+{
+Manager::Manager(
+ const std::shared_ptr<boost::asio::io_context>& ioCon,
+ const std::shared_ptr<sdbusplus::asio::dbus_interface>& iFace,
+ const std::shared_ptr<sdbusplus::asio::connection>& asioConnection) :
+ m_ioContext(ioCon), m_interface(iFace), m_asioConnection(asioConnection)
+{
+ try
+ {
+#ifdef IBM_SYSTEM
+ m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT);
+
+ // Set up minimal things that is needed before bus name is claimed.
+ m_worker->performInitialSetup();
+
+ // set callback to detect any asset tag change
+ registerAssetTagChangeCallback();
+
+ // set async timer to detect if system VPD is published on D-Bus.
+ SetTimerToDetectSVPDOnDbus();
+
+ // set async timer to detect if VPD collection is done.
+ SetTimerToDetectVpdCollectionStatus();
+
+ // Instantiate GpioMonitor class
+ m_gpioMonitor = std::make_shared<GpioMonitor>(
+ m_worker->getSysCfgJsonObj(), m_worker, m_ioContext);
+
+#endif
+ // set callback to detect host state change.
+ registerHostStateChangeCallback();
+
+ // For backward compatibility. Should be depricated.
+ iFace->register_method(
+ "WriteKeyword",
+ [this](const sdbusplus::message::object_path i_path,
+ const std::string i_recordName, const std::string i_keyword,
+ const types::BinaryVector i_value) -> int {
+ return this->updateKeyword(
+ i_path, std::make_tuple(i_recordName, i_keyword, i_value));
+ });
+
+ // Register methods under com.ibm.VPD.Manager interface
+ iFace->register_method(
+ "UpdateKeyword",
+ [this](const types::Path i_vpdPath,
+ const types::WriteVpdParams i_paramsToWriteData) -> int {
+ return this->updateKeyword(i_vpdPath, i_paramsToWriteData);
+ });
+
+ iFace->register_method(
+ "WriteKeywordOnHardware",
+ [this](const types::Path i_fruPath,
+ const types::WriteVpdParams i_paramsToWriteData) -> int {
+ return this->updateKeywordOnHardware(i_fruPath,
+ i_paramsToWriteData);
+ });
+
+ iFace->register_method(
+ "ReadKeyword",
+ [this](const types::Path i_fruPath,
+ const types::ReadVpdParams i_paramsToReadData)
+ -> types::DbusVariantType {
+ return this->readKeyword(i_fruPath, i_paramsToReadData);
+ });
+
+ iFace->register_method(
+ "CollectFRUVPD",
+ [this](const sdbusplus::message::object_path& i_dbusObjPath) {
+ this->collectSingleFruVpd(i_dbusObjPath);
+ });
+
+ iFace->register_method(
+ "deleteFRUVPD",
+ [this](const sdbusplus::message::object_path& i_dbusObjPath) {
+ this->deleteSingleFruVpd(i_dbusObjPath);
+ });
+
+ iFace->register_method(
+ "GetExpandedLocationCode",
+ [this](const std::string& i_unexpandedLocationCode,
+ uint16_t& i_nodeNumber) -> std::string {
+ return this->getExpandedLocationCode(i_unexpandedLocationCode,
+ i_nodeNumber);
+ });
+
+ iFace->register_method("GetFRUsByExpandedLocationCode",
+ [this](const std::string& i_expandedLocationCode)
+ -> types::ListOfPaths {
+ return this->getFrusByExpandedLocationCode(
+ i_expandedLocationCode);
+ });
+
+ iFace->register_method(
+ "GetFRUsByUnexpandedLocationCode",
+ [this](const std::string& i_unexpandedLocationCode,
+ uint16_t& i_nodeNumber) -> types::ListOfPaths {
+ return this->getFrusByUnexpandedLocationCode(
+ i_unexpandedLocationCode, i_nodeNumber);
+ });
+
+ iFace->register_method(
+ "GetHardwarePath",
+ [this](const sdbusplus::message::object_path& i_dbusObjPath)
+ -> std::string { return this->getHwPath(i_dbusObjPath); });
+
+ iFace->register_method("PerformVPDRecollection", [this]() {
+ this->performVpdRecollection();
+ });
+
+ // Indicates FRU VPD collection for the system has not started.
+ iFace->register_property_rw<std::string>(
+ "CollectionStatus", sdbusplus::vtable::property_::emits_change,
+ [this](const std::string l_currStatus, const auto&) {
+ m_vpdCollectionStatus = l_currStatus;
+ return 0;
+ },
+ [this](const auto&) { return m_vpdCollectionStatus; });
+ }
+ catch (const std::exception& e)
+ {
+ logging::logMessage(
+ "VPD-Manager service failed. " + std::string(e.what()));
+ throw;
+ }
+}
+
+#ifdef IBM_SYSTEM
+void Manager::registerAssetTagChangeCallback()
+{
+ static std::shared_ptr<sdbusplus::bus::match_t> l_assetMatch =
+ std::make_shared<sdbusplus::bus::match_t>(
+ *m_asioConnection,
+ sdbusplus::bus::match::rules::propertiesChanged(
+ constants::systemInvPath, constants::assetTagInf),
+ [this](sdbusplus::message_t& l_msg) {
+ processAssetTagChangeCallback(l_msg);
+ });
+}
+
+void Manager::processAssetTagChangeCallback(sdbusplus::message_t& i_msg)
+{
+ try
+ {
+ if (i_msg.is_method_error())
+ {
+ throw std::runtime_error(
+ "Error reading callback msg for asset tag.");
+ }
+
+ std::string l_objectPath;
+ types::PropertyMap l_propMap;
+ i_msg.read(l_objectPath, l_propMap);
+
+ const auto& l_itrToAssetTag = l_propMap.find("AssetTag");
+ if (l_itrToAssetTag != l_propMap.end())
+ {
+ if (auto l_assetTag =
+ std::get_if<std::string>(&(l_itrToAssetTag->second)))
+ {
+ // Call Notify to persist the AssetTag
+ types::ObjectMap l_objectMap = {
+ {sdbusplus::message::object_path(constants::systemInvPath),
+ {{constants::assetTagInf, {{"AssetTag", *l_assetTag}}}}}};
+
+ // Notify PIM
+ if (!dbusUtility::callPIM(move(l_objectMap)))
+ {
+ throw std::runtime_error(
+ "Call to PIM failed for asset tag update.");
+ }
+ }
+ }
+ else
+ {
+ throw std::runtime_error(
+ "Could not find asset tag in callback message.");
+ }
+ }
+ catch (const std::exception& l_ex)
+ {
+ // TODO: Log PEL with below description.
+ logging::logMessage("Asset tag callback update failed with error: " +
+ std::string(l_ex.what()));
+ }
+}
+
+void Manager::SetTimerToDetectSVPDOnDbus()
+{
+ static boost::asio::steady_timer timer(*m_ioContext);
+
+ // timer for 2 seconds
+ auto asyncCancelled = timer.expires_after(std::chrono::seconds(2));
+
+ (asyncCancelled == 0) ? logging::logMessage("Timer started")
+ : logging::logMessage("Timer re-started");
+
+ timer.async_wait([this](const boost::system::error_code& ec) {
+ if (ec == boost::asio::error::operation_aborted)
+ {
+ throw std::runtime_error(
+ "Timer to detect system VPD collection status was aborted");
+ }
+
+ if (ec)
+ {
+ throw std::runtime_error(
+ "Timer to detect System VPD collection failed");
+ }
+
+ if (m_worker->isSystemVPDOnDBus())
+ {
+ // cancel the timer
+ timer.cancel();
+
+ // Triggering FRU VPD collection. Setting status to "In
+ // Progress".
+ m_interface->set_property("CollectionStatus",
+ std::string("InProgress"));
+ m_worker->collectFrusFromJson();
+ }
+ });
+}
+
+void Manager::SetTimerToDetectVpdCollectionStatus()
+{
+ // Keeping max retry for 2 minutes. TODO: Make it cinfigurable based on
+ // system type.
+ static constexpr auto MAX_RETRY = 40;
+
+ static boost::asio::steady_timer l_timer(*m_ioContext);
+ static uint8_t l_timerRetry = 0;
+
+ auto l_asyncCancelled = l_timer.expires_after(std::chrono::seconds(3));
+
+ (l_asyncCancelled == 0)
+ ? logging::logMessage("Collection Timer started")
+ : logging::logMessage("Collection Timer re-started");
+
+ l_timer.async_wait([this](const boost::system::error_code& ec) {
+ if (ec == boost::asio::error::operation_aborted)
+ {
+ throw std::runtime_error(
+ "Timer to detect thread collection status was aborted");
+ }
+
+ if (ec)
+ {
+ throw std::runtime_error(
+ "Timer to detect thread collection failed");
+ }
+
+ if (m_worker->isAllFruCollectionDone())
+ {
+ // cancel the timer
+ l_timer.cancel();
+ m_interface->set_property("CollectionStatus",
+ std::string("Completed"));
+
+ const nlohmann::json& l_sysCfgJsonObj =
+ m_worker->getSysCfgJsonObj();
+ if (jsonUtility::isBackupAndRestoreRequired(l_sysCfgJsonObj))
+ {
+ BackupAndRestore l_backupAndRestoreObj(l_sysCfgJsonObj);
+ l_backupAndRestoreObj.backupAndRestore();
+ }
+ }
+ else
+ {
+ auto l_threadCount = m_worker->getActiveThreadCount();
+ if (l_timerRetry == MAX_RETRY)
+ {
+ l_timer.cancel();
+ logging::logMessage("Taking too long. Active thread = " +
+ std::to_string(l_threadCount));
+ }
+ else
+ {
+ l_timerRetry++;
+ logging::logMessage("Waiting... active thread = " +
+ std::to_string(l_threadCount) + "After " +
+ std::to_string(l_timerRetry) + " re-tries");
+
+ SetTimerToDetectVpdCollectionStatus();
+ }
+ }
+ });
+}
+#endif
+
+int Manager::updateKeyword(const types::Path i_vpdPath,
+ const types::WriteVpdParams i_paramsToWriteData)
+{
+ if (i_vpdPath.empty())
+ {
+ logging::logMessage("Given VPD path is empty.");
+ return -1;
+ }
+
+ types::Path l_fruPath;
+ nlohmann::json l_sysCfgJsonObj{};
+
+ if (m_worker.get() != nullptr)
+ {
+ l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
+
+ // Get the EEPROM path
+ if (!l_sysCfgJsonObj.empty())
+ {
+ try
+ {
+ l_fruPath =
+ jsonUtility::getFruPathFromJson(l_sysCfgJsonObj, i_vpdPath);
+ }
+ catch (const std::exception& l_exception)
+ {
+ logging::logMessage(
+ "Error while getting FRU path, Path: " + i_vpdPath +
+ ", error: " + std::string(l_exception.what()));
+ return -1;
+ }
+ }
+ }
+
+ if (l_fruPath.empty())
+ {
+ l_fruPath = i_vpdPath;
+ }
+
+ try
+ {
+ std::shared_ptr<Parser> l_parserObj =
+ std::make_shared<Parser>(l_fruPath, l_sysCfgJsonObj);
+ return l_parserObj->updateVpdKeyword(i_paramsToWriteData);
+ }
+ catch (const std::exception& l_exception)
+ {
+ // TODO:: error log needed
+ logging::logMessage("Update keyword failed for file[" + i_vpdPath +
+ "], reason: " + std::string(l_exception.what()));
+ return -1;
+ }
+}
+
+int Manager::updateKeywordOnHardware(
+ const types::Path i_fruPath,
+ const types::WriteVpdParams i_paramsToWriteData) noexcept
+{
+ try
+ {
+ if (i_fruPath.empty())
+ {
+ throw std::runtime_error("Given FRU path is empty");
+ }
+
+ nlohmann::json l_sysCfgJsonObj{};
+
+ if (m_worker.get() != nullptr)
+ {
+ l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
+ }
+
+ std::shared_ptr<Parser> l_parserObj =
+ std::make_shared<Parser>(i_fruPath, l_sysCfgJsonObj);
+ return l_parserObj->updateVpdKeywordOnHardware(i_paramsToWriteData);
+ }
+ catch (const std::exception& l_exception)
+ {
+ EventLogger::createAsyncPel(
+ types::ErrorType::InvalidEeprom, types::SeverityType::Informational,
+ __FILE__, __FUNCTION__, 0,
+ "Update keyword on hardware failed for file[" + i_fruPath +
+ "], reason: " + std::string(l_exception.what()),
+ std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+
+ return constants::FAILURE;
+ }
+}
+
+types::DbusVariantType Manager::readKeyword(
+ const types::Path i_fruPath, const types::ReadVpdParams i_paramsToReadData)
+{
+ try
+ {
+ nlohmann::json l_jsonObj{};
+
+ if (m_worker.get() != nullptr)
+ {
+ l_jsonObj = m_worker->getSysCfgJsonObj();
+ }
+
+ std::error_code ec;
+
+ // Check if given path is filesystem path
+ if (!std::filesystem::exists(i_fruPath, ec) && (ec))
+ {
+ throw std::runtime_error(
+ "Given file path " + i_fruPath + " not found.");
+ }
+
+ logging::logMessage("Performing VPD read on " + i_fruPath);
+
+ std::shared_ptr<vpd::Parser> l_parserObj =
+ std::make_shared<vpd::Parser>(i_fruPath, l_jsonObj);
+
+ std::shared_ptr<vpd::ParserInterface> l_vpdParserInstance =
+ l_parserObj->getVpdParserInstance();
+
+ return (
+ l_vpdParserInstance->readKeywordFromHardware(i_paramsToReadData));
+ }
+ catch (const std::exception& e)
+ {
+ logging::logMessage(
+ e.what() + std::string(". VPD manager read operation failed for ") +
+ i_fruPath);
+ throw types::DeviceError::ReadFailure();
+ }
+}
+
+void Manager::collectSingleFruVpd(
+ const sdbusplus::message::object_path& i_dbusObjPath)
+{
+ try
+ {
+ if (m_vpdCollectionStatus != "Completed")
+ {
+ throw std::runtime_error(
+ "Currently VPD CollectionStatus is not completed. Cannot perform single FRU VPD collection for " +
+ std::string(i_dbusObjPath));
+ }
+
+ // Get system config JSON object from worker class
+ nlohmann::json l_sysCfgJsonObj{};
+
+ if (m_worker.get() != nullptr)
+ {
+ l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
+ }
+
+ // Check if system config JSON is present
+ if (l_sysCfgJsonObj.empty())
+ {
+ throw std::runtime_error(
+ "System config JSON object not present. Single FRU VPD collection failed for " +
+ std::string(i_dbusObjPath));
+ }
+
+ // Get FRU path for the given D-bus object path from JSON
+ const std::string& l_fruPath =
+ jsonUtility::getFruPathFromJson(l_sysCfgJsonObj, i_dbusObjPath);
+
+ if (l_fruPath.empty())
+ {
+ throw std::runtime_error(
+ "D-bus object path not present in JSON. Single FRU VPD collection failed for " +
+ std::string(i_dbusObjPath));
+ }
+
+ // Check if host is up and running
+ if (dbusUtility::isHostRunning())
+ {
+ if (!jsonUtility::isFruReplaceableAtRuntime(l_sysCfgJsonObj,
+ l_fruPath))
+ {
+ throw std::runtime_error(
+ "Given FRU is not replaceable at host runtime. Single FRU VPD collection failed for " +
+ std::string(i_dbusObjPath));
+ }
+ }
+ else if (dbusUtility::isBMCReady())
+ {
+ if (!jsonUtility::isFruReplaceableAtStandby(l_sysCfgJsonObj,
+ l_fruPath) &&
+ (!jsonUtility::isFruReplaceableAtRuntime(l_sysCfgJsonObj,
+ l_fruPath)))
+ {
+ throw std::runtime_error(
+ "Given FRU is neither replaceable at standby nor replaceable at runtime. Single FRU VPD collection failed for " +
+ std::string(i_dbusObjPath));
+ }
+ }
+
+ // Parse VPD
+ types::VPDMapVariant l_parsedVpd = m_worker->parseVpdFile(l_fruPath);
+
+ // If l_parsedVpd is pointing to std::monostate
+ if (l_parsedVpd.index() == 0)
+ {
+ throw std::runtime_error(
+ "VPD parsing failed for " + std::string(i_dbusObjPath));
+ }
+
+ // Get D-bus object map from worker class
+ types::ObjectMap l_dbusObjectMap;
+ m_worker->populateDbus(l_parsedVpd, l_dbusObjectMap, l_fruPath);
+
+ if (l_dbusObjectMap.empty())
+ {
+ throw std::runtime_error(
+ "Failed to create D-bus object map. Single FRU VPD collection failed for " +
+ std::string(i_dbusObjPath));
+ }
+
+ // Call PIM's Notify method
+ if (!dbusUtility::callPIM(move(l_dbusObjectMap)))
+ {
+ throw std::runtime_error(
+ "Notify PIM failed. Single FRU VPD collection failed for " +
+ std::string(i_dbusObjPath));
+ }
+ }
+ catch (const std::exception& l_error)
+ {
+ // TODO: Log PEL
+ logging::logMessage(std::string(l_error.what()));
+ }
+}
+
+void Manager::deleteSingleFruVpd(
+ const sdbusplus::message::object_path& i_dbusObjPath)
+{
+ try
+ {
+ if (std::string(i_dbusObjPath).empty())
+ {
+ throw std::runtime_error(
+ "Given DBus object path is empty. Aborting FRU VPD deletion.");
+ }
+
+ if (m_worker.get() == nullptr)
+ {
+ throw std::runtime_error(
+ "Worker object not found, can't perform FRU VPD deletion for: " +
+ std::string(i_dbusObjPath));
+ }
+
+ m_worker->deleteFruVpd(std::string(i_dbusObjPath));
+ }
+ catch (const std::exception& l_ex)
+ {
+ // TODO: Log PEL
+ logging::logMessage(l_ex.what());
+ }
+}
+
+bool Manager::isValidUnexpandedLocationCode(
+ const std::string& i_unexpandedLocationCode)
+{
+ if ((i_unexpandedLocationCode.length() <
+ constants::UNEXP_LOCATION_CODE_MIN_LENGTH) ||
+ ((i_unexpandedLocationCode.compare(0, 4, "Ufcs") !=
+ constants::STR_CMP_SUCCESS) &&
+ (i_unexpandedLocationCode.compare(0, 4, "Umts") !=
+ constants::STR_CMP_SUCCESS)) ||
+ ((i_unexpandedLocationCode.length() >
+ constants::UNEXP_LOCATION_CODE_MIN_LENGTH) &&
+ (i_unexpandedLocationCode.find("-") != 4)))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+std::string Manager::getExpandedLocationCode(
+ const std::string& i_unexpandedLocationCode,
+ [[maybe_unused]] const uint16_t i_nodeNumber)
+{
+ if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
+ {
+ phosphor::logging::elog<types::DbusInvalidArgument>(
+ types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
+ types::InvalidArgument::ARGUMENT_VALUE(
+ i_unexpandedLocationCode.c_str()));
+ }
+
+ const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
+ if (!l_sysCfgJsonObj.contains("frus"))
+ {
+ logging::logMessage("Missing frus tag in system config JSON");
+ }
+
+ const nlohmann::json& l_listOfFrus =
+ l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
+
+ for (const auto& l_frus : l_listOfFrus.items())
+ {
+ for (const auto& l_aFru : l_frus.value())
+ {
+ if (l_aFru["extraInterfaces"].contains(
+ constants::locationCodeInf) &&
+ l_aFru["extraInterfaces"][constants::locationCodeInf].value(
+ "LocationCode", "") == i_unexpandedLocationCode)
+ {
+ return std::get<std::string>(dbusUtility::readDbusProperty(
+ l_aFru["serviceName"], l_aFru["inventoryPath"],
+ constants::locationCodeInf, "LocationCode"));
+ }
+ }
+ }
+ phosphor::logging::elog<types::DbusInvalidArgument>(
+ types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
+ types::InvalidArgument::ARGUMENT_VALUE(
+ i_unexpandedLocationCode.c_str()));
+}
+
+types::ListOfPaths Manager::getFrusByUnexpandedLocationCode(
+ const std::string& i_unexpandedLocationCode,
+ [[maybe_unused]] const uint16_t i_nodeNumber)
+{
+ types::ListOfPaths l_inventoryPaths;
+
+ if (!isValidUnexpandedLocationCode(i_unexpandedLocationCode))
+ {
+ phosphor::logging::elog<types::DbusInvalidArgument>(
+ types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
+ types::InvalidArgument::ARGUMENT_VALUE(
+ i_unexpandedLocationCode.c_str()));
+ }
+
+ const nlohmann::json& l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
+ if (!l_sysCfgJsonObj.contains("frus"))
+ {
+ logging::logMessage("Missing frus tag in system config JSON");
+ }
+
+ const nlohmann::json& l_listOfFrus =
+ l_sysCfgJsonObj["frus"].get_ref<const nlohmann::json::object_t&>();
+
+ for (const auto& l_frus : l_listOfFrus.items())
+ {
+ for (const auto& l_aFru : l_frus.value())
+ {
+ if (l_aFru["extraInterfaces"].contains(
+ constants::locationCodeInf) &&
+ l_aFru["extraInterfaces"][constants::locationCodeInf].value(
+ "LocationCode", "") == i_unexpandedLocationCode)
+ {
+ l_inventoryPaths.push_back(
+ l_aFru.at("inventoryPath")
+ .get_ref<const nlohmann::json::string_t&>());
+ }
+ }
+ }
+
+ if (l_inventoryPaths.empty())
+ {
+ phosphor::logging::elog<types::DbusInvalidArgument>(
+ types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
+ types::InvalidArgument::ARGUMENT_VALUE(
+ i_unexpandedLocationCode.c_str()));
+ }
+
+ return l_inventoryPaths;
+}
+
+std::string
+ Manager::getHwPath(const sdbusplus::message::object_path& i_dbusObjPath)
+{
+ // Dummy code to supress unused variable warning. To be removed.
+ logging::logMessage(std::string(i_dbusObjPath));
+
+ return std::string{};
+}
+
+std::tuple<std::string, uint16_t> Manager::getUnexpandedLocationCode(
+ const std::string& i_expandedLocationCode)
+{
+ /**
+ * Location code should always start with U and fulfil minimum length
+ * criteria.
+ */
+ if (i_expandedLocationCode[0] != 'U' ||
+ i_expandedLocationCode.length() <
+ constants::EXP_LOCATION_CODE_MIN_LENGTH)
+ {
+ phosphor::logging::elog<types::DbusInvalidArgument>(
+ types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
+ types::InvalidArgument::ARGUMENT_VALUE(
+ i_expandedLocationCode.c_str()));
+ }
+
+ std::string l_fcKwd;
+
+ auto l_fcKwdValue = dbusUtility::readDbusProperty(
+ "xyz.openbmc_project.Inventory.Manager",
+ "/xyz/openbmc_project/inventory/system/chassis/motherboard",
+ "com.ibm.ipzvpd.VCEN", "FC");
+
+ if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_fcKwdValue))
+ {
+ l_fcKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
+ }
+
+ // Get the first part of expanded location code to check for FC or TM.
+ std::string l_firstKwd = i_expandedLocationCode.substr(1, 4);
+
+ std::string l_unexpandedLocationCode{};
+ uint16_t l_nodeNummber = constants::INVALID_NODE_NUMBER;
+
+ // Check if this value matches the value of FC keyword.
+ if (l_fcKwd.substr(0, 4) == l_firstKwd)
+ {
+ /**
+ * Period(.) should be there in expanded location code to seggregate
+ * FC, node number and SE values.
+ */
+ size_t l_nodeStartPos = i_expandedLocationCode.find('.');
+ if (l_nodeStartPos == std::string::npos)
+ {
+ phosphor::logging::elog<types::DbusInvalidArgument>(
+ types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
+ types::InvalidArgument::ARGUMENT_VALUE(
+ i_expandedLocationCode.c_str()));
+ }
+
+ size_t l_nodeEndPos =
+ i_expandedLocationCode.find('.', l_nodeStartPos + 1);
+ if (l_nodeEndPos == std::string::npos)
+ {
+ phosphor::logging::elog<types::DbusInvalidArgument>(
+ types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
+ types::InvalidArgument::ARGUMENT_VALUE(
+ i_expandedLocationCode.c_str()));
+ }
+
+ // Skip 3 bytes for '.ND'
+ l_nodeNummber = std::stoi(i_expandedLocationCode.substr(
+ l_nodeStartPos + 3, (l_nodeEndPos - l_nodeStartPos - 3)));
+
+ /**
+ * Confirm if there are other details apart FC, node number and SE
+ * in location code
+ */
+ if (i_expandedLocationCode.length() >
+ constants::EXP_LOCATION_CODE_MIN_LENGTH)
+ {
+ l_unexpandedLocationCode =
+ i_expandedLocationCode[0] + std::string("fcs") +
+ i_expandedLocationCode.substr(
+ l_nodeEndPos + 1 + constants::SE_KWD_LENGTH,
+ std::string::npos);
+ }
+ else
+ {
+ l_unexpandedLocationCode = "Ufcs";
+ }
+ }
+ else
+ {
+ std::string l_tmKwd;
+ // Read TM keyword value.
+ auto l_tmKwdValue = dbusUtility::readDbusProperty(
+ "xyz.openbmc_project.Inventory.Manager",
+ "/xyz/openbmc_project/inventory/system/chassis/motherboard",
+ "com.ibm.ipzvpd.VSYS", "TM");
+
+ if (auto l_kwdValue = std::get_if<types::BinaryVector>(&l_tmKwdValue))
+ {
+ l_tmKwd.assign(l_kwdValue->begin(), l_kwdValue->end());
+ }
+
+ // Check if the substr matches to TM keyword value.
+ if (l_tmKwd.substr(0, 4) == l_firstKwd)
+ {
+ /**
+ * System location code will not have node number and any other
+ * details.
+ */
+ l_unexpandedLocationCode = "Umts";
+ }
+ // The given location code is neither "fcs" or "mts".
+ else
+ {
+ phosphor::logging::elog<types::DbusInvalidArgument>(
+ types::InvalidArgument::ARGUMENT_NAME("LOCATIONCODE"),
+ types::InvalidArgument::ARGUMENT_VALUE(
+ i_expandedLocationCode.c_str()));
+ }
+ }
+
+ return std::make_tuple(l_unexpandedLocationCode, l_nodeNummber);
+}
+
+types::ListOfPaths Manager::getFrusByExpandedLocationCode(
+ const std::string& i_expandedLocationCode)
+{
+ std::tuple<std::string, uint16_t> l_locationAndNodePair =
+ getUnexpandedLocationCode(i_expandedLocationCode);
+
+ return getFrusByUnexpandedLocationCode(std::get<0>(l_locationAndNodePair),
+ std::get<1>(l_locationAndNodePair));
+}
+
+void Manager::registerHostStateChangeCallback()
+{
+ static std::shared_ptr<sdbusplus::bus::match_t> l_hostState =
+ std::make_shared<sdbusplus::bus::match_t>(
+ *m_asioConnection,
+ sdbusplus::bus::match::rules::propertiesChanged(
+ constants::hostObjectPath, constants::hostInterface),
+ [this](sdbusplus::message_t& i_msg) {
+ hostStateChangeCallBack(i_msg);
+ });
+}
+
+void Manager::hostStateChangeCallBack(sdbusplus::message_t& i_msg)
+{
+ try
+ {
+ if (i_msg.is_method_error())
+ {
+ throw std::runtime_error(
+ "Error reading callback message for host state");
+ }
+
+ std::string l_objectPath;
+ types::PropertyMap l_propMap;
+ i_msg.read(l_objectPath, l_propMap);
+
+ const auto l_itr = l_propMap.find("CurrentHostState");
+
+ if (l_itr == l_propMap.end())
+ {
+ throw std::runtime_error(
+ "CurrentHostState field is missing in callback message");
+ }
+
+ if (auto l_hostState = std::get_if<std::string>(&(l_itr->second)))
+ {
+ // implies system is moving from standby to power on state
+ if (*l_hostState == "xyz.openbmc_project.State.Host.HostState."
+ "TransitioningToRunning")
+ {
+ // TODO: check for all the essential FRUs in the system.
+
+ // Perform recollection.
+ performVpdRecollection();
+ return;
+ }
+ }
+ else
+ {
+ throw std::runtime_error(
+ "Invalid type recieved in variant for host state.");
+ }
+ }
+ catch (const std::exception& l_ex)
+ {
+ // TODO: Log PEL.
+ logging::logMessage(l_ex.what());
+ }
+}
+
+void Manager::performVpdRecollection()
+{
+ try
+ {
+ if (m_worker.get() != nullptr)
+ {
+ nlohmann::json l_sysCfgJsonObj = m_worker->getSysCfgJsonObj();
+
+ // Check if system config JSON is present
+ if (l_sysCfgJsonObj.empty())
+ {
+ throw std::runtime_error(
+ "System config json object is empty, can't process recollection.");
+ }
+
+ const auto& l_frusReplaceableAtStandby =
+ jsonUtility::getListOfFrusReplaceableAtStandby(l_sysCfgJsonObj);
+
+ for (const auto& l_fruInventoryPath : l_frusReplaceableAtStandby)
+ {
+ // ToDo: Add some logic/trace to know the flow to
+ // collectSingleFruVpd has been directed via
+ // performVpdRecollection.
+ collectSingleFruVpd(l_fruInventoryPath);
+ }
+ return;
+ }
+
+ throw std::runtime_error(
+ "Worker object not found can't process recollection");
+ }
+ catch (const std::exception& l_ex)
+ {
+ // TODO Log PEL
+ logging::logMessage(
+ "VPD recollection failed with error: " + std::string(l_ex.what()));
+ }
+}
+} // namespace vpd
diff --git a/vpd-manager/src/manager_main.cpp b/vpd-manager/src/manager_main.cpp
new file mode 100644
index 0000000..a1d61e4
--- /dev/null
+++ b/vpd-manager/src/manager_main.cpp
@@ -0,0 +1,94 @@
+#include "config.h"
+
+#include "bios_handler.hpp"
+#include "event_logger.hpp"
+#include "exceptions.hpp"
+#include "logger.hpp"
+#include "manager.hpp"
+#include "types.hpp"
+
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+#include <iostream>
+
+/**
+ * @brief Main function for VPD parser application.
+ */
+int main(int, char**)
+{
+ try
+ {
+ auto io_con = std::make_shared<boost::asio::io_context>();
+ auto connection =
+ std::make_shared<sdbusplus::asio::connection>(*io_con);
+ auto server = sdbusplus::asio::object_server(connection);
+
+ std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
+ server.add_interface(OBJPATH, IFACE);
+
+ auto vpdManager =
+ std::make_shared<vpd::Manager>(io_con, interface, connection);
+
+ // TODO: Take this under conditional compilation for IBM
+ auto biosHandler =
+ std::make_shared<vpd::BiosHandler<vpd::IbmBiosHandler>>(
+ connection, vpdManager);
+
+ interface->initialize();
+
+ vpd::logging::logMessage("Start VPD-Manager event loop");
+
+ // Grab the bus name
+ connection->request_name(BUSNAME);
+
+ // Start event loop.
+ io_con->run();
+
+ exit(EXIT_SUCCESS);
+ }
+ catch (const std::exception& l_ex)
+ {
+ if (typeid(l_ex) == typeid(vpd::JsonException))
+ {
+ // ToDo: Severity needs to be revisited.
+ vpd::EventLogger::createAsyncPel(
+ vpd::types::ErrorType::JsonFailure,
+ vpd::types::SeverityType::Informational, __FILE__, __FUNCTION__,
+ 0,
+ std::string("VPD Manager service failed with : ") + l_ex.what(),
+ std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+ }
+ else if (typeid(l_ex) == typeid(vpd::GpioException))
+ {
+ // ToDo: Severity needs to be revisited.
+ vpd::EventLogger::createAsyncPel(
+ vpd::types::ErrorType::GpioError,
+ vpd::types::SeverityType::Informational, __FILE__, __FUNCTION__,
+ 0,
+ std::string("VPD Manager service failed with : ") + l_ex.what(),
+ std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+ }
+ else if (typeid(l_ex) == typeid(sdbusplus::exception::SdBusError))
+ {
+ // ToDo: Severity needs to be revisited.
+ vpd::EventLogger::createAsyncPel(
+ vpd::types::ErrorType::DbusFailure,
+ vpd::types::SeverityType::Informational, __FILE__, __FUNCTION__,
+ 0,
+ std::string("VPD Manager service failed with : ") + l_ex.what(),
+ std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+ }
+ else
+ {
+ // ToDo: Severity needs to be revisited.
+ vpd::EventLogger::createAsyncPel(
+ vpd::types::ErrorType::InvalidVpdMessage,
+ vpd::types::SeverityType::Informational, __FILE__, __FUNCTION__,
+ 0,
+ std::string("VPD Manager service failed with : ") + l_ex.what(),
+ "BMC0001", std::nullopt, std::nullopt, std::nullopt);
+ }
+ }
+ exit(EXIT_FAILURE);
+}
diff --git a/vpd-manager/src/parser.cpp b/vpd-manager/src/parser.cpp
new file mode 100644
index 0000000..d6266cb
--- /dev/null
+++ b/vpd-manager/src/parser.cpp
@@ -0,0 +1,342 @@
+#include "parser.hpp"
+
+#include "constants.hpp"
+#include "event_logger.hpp"
+
+#include <utility/dbus_utility.hpp>
+#include <utility/json_utility.hpp>
+#include <utility/vpd_specific_utility.hpp>
+
+#include <fstream>
+
+namespace vpd
+{
+Parser::Parser(const std::string& vpdFilePath, nlohmann::json parsedJson) :
+ m_vpdFilePath(vpdFilePath), m_parsedJson(parsedJson)
+{
+ std::error_code l_errCode;
+
+ // ToDo: Add minimum file size check in all the concert praser classes,
+ // depends on their VPD type.
+ if (!std::filesystem::exists(m_vpdFilePath, l_errCode))
+ {
+ std::string l_message{"Parser object creation failed, file [" +
+ m_vpdFilePath + "] doesn't exists."};
+
+ if (l_errCode)
+ {
+ l_message += " Error message: " + l_errCode.message();
+ }
+
+ throw std::runtime_error(l_message);
+ }
+
+ // Read VPD offset if applicable.
+ if (!m_parsedJson.empty())
+ {
+ m_vpdStartOffset = jsonUtility::getVPDOffset(m_parsedJson, vpdFilePath);
+ }
+}
+
+std::shared_ptr<vpd::ParserInterface> Parser::getVpdParserInstance()
+{
+ // Read the VPD data into a vector.
+ vpdSpecificUtility::getVpdDataInVector(m_vpdFilePath, m_vpdVector,
+ m_vpdStartOffset);
+
+ // This will detect the type of parser required.
+ std::shared_ptr<vpd::ParserInterface> l_parser =
+ ParserFactory::getParser(m_vpdVector, m_vpdFilePath, m_vpdStartOffset);
+
+ return l_parser;
+}
+
+types::VPDMapVariant Parser::parse()
+{
+ std::shared_ptr<vpd::ParserInterface> l_parser = getVpdParserInstance();
+ return l_parser->parse();
+}
+
+int Parser::updateVpdKeyword(const types::WriteVpdParams& i_paramsToWriteData)
+{
+ int l_bytesUpdatedOnHardware = constants::FAILURE;
+
+ // A lambda to extract Record : Keyword string from i_paramsToWriteData
+ auto l_keyWordIdentifier =
+ [](const types::WriteVpdParams& i_paramsToWriteData) -> std::string {
+ std::string l_keywordString{};
+ if (const types::IpzData* l_ipzData =
+ std::get_if<types::IpzData>(&i_paramsToWriteData))
+ {
+ l_keywordString =
+ std::get<0>(*l_ipzData) + ":" + std::get<1>(*l_ipzData);
+ }
+ else if (const types::KwData* l_kwData =
+ std::get_if<types::KwData>(&i_paramsToWriteData))
+ {
+ l_keywordString = std::get<0>(*l_kwData);
+ }
+ return l_keywordString;
+ };
+
+ try
+ {
+ // Enable Reboot Guard
+ if (constants::FAILURE == dbusUtility::EnableRebootGuard())
+ {
+ EventLogger::createAsyncPel(
+ types::ErrorType::DbusFailure,
+ types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
+ std::string(
+ "Failed to enable BMC Reboot Guard while updating " +
+ l_keyWordIdentifier(i_paramsToWriteData)),
+ std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+
+ return constants::FAILURE;
+ }
+
+ // Update keyword's value on hardware
+ try
+ {
+ std::shared_ptr<ParserInterface> l_vpdParserInstance =
+ getVpdParserInstance();
+ l_bytesUpdatedOnHardware =
+ l_vpdParserInstance->writeKeywordOnHardware(
+ i_paramsToWriteData);
+ }
+ catch (const std::exception& l_exception)
+ {
+ std::string l_errMsg(
+ "Error while updating keyword's value on hardware path " +
+ m_vpdFilePath + ", error: " + std::string(l_exception.what()));
+
+ // TODO : Log PEL
+
+ throw std::runtime_error(l_errMsg);
+ }
+
+ auto [l_fruPath, l_inventoryObjPath, l_redundantFruPath] =
+ jsonUtility::getAllPathsToUpdateKeyword(m_parsedJson,
+ m_vpdFilePath);
+
+ // If inventory D-bus object path is present, update keyword's value on
+ // DBus
+ if (!l_inventoryObjPath.empty())
+ {
+ types::Record l_recordName;
+ std::string l_interfaceName;
+ std::string l_propertyName;
+ types::DbusVariantType l_keywordValue;
+
+ if (const types::IpzData* l_ipzData =
+ std::get_if<types::IpzData>(&i_paramsToWriteData))
+ {
+ l_recordName = std::get<0>(*l_ipzData);
+ l_interfaceName = constants::ipzVpdInf + l_recordName;
+ l_propertyName = std::get<1>(*l_ipzData);
+
+ try
+ {
+ // Read keyword's value from hardware to write the same on
+ // D-bus.
+ std::shared_ptr<ParserInterface> l_vpdParserInstance =
+ getVpdParserInstance();
+
+ logging::logMessage(
+ "Performing VPD read on " + m_vpdFilePath);
+
+ l_keywordValue =
+ l_vpdParserInstance->readKeywordFromHardware(
+ types::ReadVpdParams(
+ std::make_tuple(l_recordName, l_propertyName)));
+ }
+ catch (const std::exception& l_exception)
+ {
+ // Unable to read keyword's value from hardware.
+ std::string l_errMsg(
+ "Error while reading keyword's value from hadware path " +
+ m_vpdFilePath +
+ ", error: " + std::string(l_exception.what()));
+
+ // TODO: Log PEL
+
+ throw std::runtime_error(l_errMsg);
+ }
+ }
+ else
+ {
+ // Input parameter type provided isn't compatible to perform
+ // update.
+ std::string l_errMsg(
+ "Input parameter type isn't compatible to update keyword's value on DBus for object path: " +
+ l_inventoryObjPath);
+ throw std::runtime_error(l_errMsg);
+ }
+
+ // Get D-bus name for the given keyword
+ l_propertyName =
+ vpdSpecificUtility::getDbusPropNameForGivenKw(l_propertyName);
+
+ // Create D-bus object map
+ types::ObjectMap l_dbusObjMap = {std::make_pair(
+ l_inventoryObjPath,
+ types::InterfaceMap{std::make_pair(
+ l_interfaceName, types::PropertyMap{std::make_pair(
+ l_propertyName, l_keywordValue)})})};
+
+ // Call PIM's Notify method to perform update
+ if (!dbusUtility::callPIM(std::move(l_dbusObjMap)))
+ {
+ // Call to PIM's Notify method failed.
+ std::string l_errMsg("Notify PIM is failed for object path: " +
+ l_inventoryObjPath);
+ throw std::runtime_error(l_errMsg);
+ }
+ }
+
+ // Update keyword's value on redundant hardware if present
+ if (!l_redundantFruPath.empty())
+ {
+ if (updateVpdKeywordOnRedundantPath(l_redundantFruPath,
+ i_paramsToWriteData) < 0)
+ {
+ std::string l_errMsg(
+ "Error while updating keyword's value on redundant path " +
+ l_redundantFruPath);
+ throw std::runtime_error(l_errMsg);
+ }
+ }
+
+ // TODO: Check if revert is required when any of the writes fails.
+ // TODO: Handle error logging
+ }
+ catch (const std::exception& l_ex)
+ {
+ logging::logMessage("Update VPD Keyword failed for : " +
+ l_keyWordIdentifier(i_paramsToWriteData) +
+ " failed due to error: " + l_ex.what());
+
+ // update failed, set return value to failure
+ l_bytesUpdatedOnHardware = constants::FAILURE;
+ }
+
+ // Disable Reboot Guard
+ if (constants::FAILURE == dbusUtility::DisableRebootGuard())
+ {
+ EventLogger::createAsyncPel(
+ types::ErrorType::DbusFailure, types::SeverityType::Critical,
+ __FILE__, __FUNCTION__, 0,
+ std::string("Failed to disable BMC Reboot Guard while updating " +
+ l_keyWordIdentifier(i_paramsToWriteData)),
+ std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+ }
+
+ return l_bytesUpdatedOnHardware;
+}
+
+int Parser::updateVpdKeywordOnRedundantPath(
+ const std::string& i_fruPath,
+ const types::WriteVpdParams& i_paramsToWriteData)
+{
+ try
+ {
+ std::shared_ptr<Parser> l_parserObj =
+ std::make_shared<Parser>(i_fruPath, m_parsedJson);
+
+ std::shared_ptr<ParserInterface> l_vpdParserInstance =
+ l_parserObj->getVpdParserInstance();
+
+ return l_vpdParserInstance->writeKeywordOnHardware(i_paramsToWriteData);
+ }
+ catch (const std::exception& l_exception)
+ {
+ EventLogger::createSyncPel(
+ types::ErrorType::InvalidVpdMessage,
+ types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
+ "Error while updating keyword's value on redundant path " +
+ i_fruPath + ", error: " + std::string(l_exception.what()),
+ std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+ return -1;
+ }
+}
+
+int Parser::updateVpdKeywordOnHardware(
+ const types::WriteVpdParams& i_paramsToWriteData)
+{
+ int l_bytesUpdatedOnHardware = constants::FAILURE;
+
+ // A lambda to extract Record : Keyword string from i_paramsToWriteData
+ auto l_keyWordIdentifier =
+ [](const types::WriteVpdParams& i_paramsToWriteData) -> std::string {
+ std::string l_keywordString{};
+ if (const types::IpzData* l_ipzData =
+ std::get_if<types::IpzData>(&i_paramsToWriteData))
+ {
+ l_keywordString =
+ std::get<0>(*l_ipzData) + ":" + std::get<1>(*l_ipzData);
+ }
+ else if (const types::KwData* l_kwData =
+ std::get_if<types::KwData>(&i_paramsToWriteData))
+ {
+ l_keywordString = std::get<0>(*l_kwData);
+ }
+ return l_keywordString;
+ };
+
+ try
+ {
+ // Enable Reboot Guard
+ if (constants::FAILURE == dbusUtility::EnableRebootGuard())
+ {
+ EventLogger::createAsyncPel(
+ types::ErrorType::DbusFailure,
+ types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
+ std::string(
+ "Failed to enable BMC Reboot Guard while updating " +
+ l_keyWordIdentifier(i_paramsToWriteData)),
+ std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+
+ return constants::FAILURE;
+ }
+
+ std::shared_ptr<ParserInterface> l_vpdParserInstance =
+ getVpdParserInstance();
+ l_bytesUpdatedOnHardware =
+ l_vpdParserInstance->writeKeywordOnHardware(i_paramsToWriteData);
+ }
+ catch (const std::exception& l_exception)
+ {
+ types::ErrorType l_errorType;
+
+ if (typeid(l_exception) == typeid(EccException))
+ {
+ l_errorType = types::ErrorType::EccCheckFailed;
+ }
+ else
+ {
+ l_errorType = types::ErrorType::InvalidVpdMessage;
+ }
+
+ EventLogger::createAsyncPel(
+ l_errorType, types::SeverityType::Informational, __FILE__,
+ __FUNCTION__, 0,
+ "Error while updating keyword's value on hardware path [" +
+ m_vpdFilePath + "], error: " + std::string(l_exception.what()),
+ std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+ }
+
+ // Disable Reboot Guard
+ if (constants::FAILURE == dbusUtility::DisableRebootGuard())
+ {
+ EventLogger::createAsyncPel(
+ types::ErrorType::DbusFailure, types::SeverityType::Critical,
+ __FILE__, __FUNCTION__, 0,
+ std::string("Failed to disable BMC Reboot Guard while updating " +
+ l_keyWordIdentifier(i_paramsToWriteData)),
+ std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+ }
+
+ return l_bytesUpdatedOnHardware;
+}
+
+} // namespace vpd
diff --git a/vpd-manager/src/parser_factory.cpp b/vpd-manager/src/parser_factory.cpp
new file mode 100644
index 0000000..546e67b
--- /dev/null
+++ b/vpd-manager/src/parser_factory.cpp
@@ -0,0 +1,140 @@
+#include "parser_factory.hpp"
+
+#include "constants.hpp"
+#include "ddimm_parser.hpp"
+#include "exceptions.hpp"
+#include "ipz_parser.hpp"
+#include "isdimm_parser.hpp"
+#include "keyword_vpd_parser.hpp"
+
+namespace vpd
+{
+
+/**
+ * @brief Type of VPD formats.
+ */
+enum vpdType
+{
+ IPZ_VPD, /**< IPZ VPD type */
+ KEYWORD_VPD, /**< Keyword VPD type */
+ DDR4_DDIMM_MEMORY_VPD, /**< DDR4 DDIMM Memory VPD type */
+ DDR5_DDIMM_MEMORY_VPD, /**< DDR5 DDIMM Memory VPD type */
+ DDR4_ISDIMM_MEMORY_VPD, /**< DDR4 ISDIMM Memory VPD type */
+ DDR5_ISDIMM_MEMORY_VPD, /**< DDR5 ISDIMM Memory VPD type */
+ INVALID_VPD_FORMAT /**< Invalid VPD type */
+};
+
+/**
+ * @brief API to get the type of VPD.
+ *
+ * @param[in] i_vpdVector - VPD file content
+ *
+ * @return Type of VPD data, "INVALID_VPD_FORMAT" in case of unknown type.
+ */
+static vpdType vpdTypeCheck(const types::BinaryVector& i_vpdVector)
+{
+ if (i_vpdVector[constants::IPZ_DATA_START] == constants::IPZ_DATA_START_TAG)
+ {
+ return vpdType::IPZ_VPD;
+ }
+ else if (i_vpdVector[constants::KW_VPD_DATA_START] ==
+ constants::KW_VPD_START_TAG)
+ {
+ return vpdType::KEYWORD_VPD;
+ }
+ else if (((i_vpdVector[constants::SPD_BYTE_3] &
+ constants::SPD_BYTE_BIT_0_3_MASK) ==
+ constants::SPD_MODULE_TYPE_DDIMM))
+ {
+ std::string l_is11SFormat;
+ if (i_vpdVector.size() > (constants::DDIMM_11S_BARCODE_START +
+ constants::DDIMM_11S_BARCODE_LEN))
+ {
+ // Read first 3 Bytes to check the 11S bar code format
+ for (uint8_t l_index = 0; l_index < constants::DDIMM_11S_FORMAT_LEN;
+ l_index++)
+ {
+ l_is11SFormat +=
+ i_vpdVector[constants::DDIMM_11S_BARCODE_START + l_index];
+ }
+ }
+
+ if (l_is11SFormat.compare(constants::DDIMM_11S_BARCODE_START_TAG) == 0)
+ {
+ // DDIMM memory VPD format
+ if ((i_vpdVector[constants::SPD_BYTE_2] &
+ constants::SPD_BYTE_MASK) == constants::SPD_DRAM_TYPE_DDR5)
+ {
+ return vpdType::DDR5_DDIMM_MEMORY_VPD;
+ }
+
+ if ((i_vpdVector[constants::SPD_BYTE_2] &
+ constants::SPD_BYTE_MASK) == constants::SPD_DRAM_TYPE_DDR4)
+ {
+ return vpdType::DDR4_DDIMM_MEMORY_VPD;
+ }
+ }
+
+ logging::logMessage("11S format is not found in the DDIMM VPD.");
+ return vpdType::INVALID_VPD_FORMAT;
+ }
+ else if ((i_vpdVector[constants::SPD_BYTE_2] & constants::SPD_BYTE_MASK) ==
+ constants::SPD_DRAM_TYPE_DDR5)
+ {
+ // ISDIMM memory VPD format
+ return vpdType::DDR5_ISDIMM_MEMORY_VPD;
+ }
+ else if ((i_vpdVector[constants::SPD_BYTE_2] & constants::SPD_BYTE_MASK) ==
+ constants::SPD_DRAM_TYPE_DDR4)
+ {
+ // ISDIMM memory VPD format
+ return vpdType::DDR4_ISDIMM_MEMORY_VPD;
+ }
+
+ return vpdType::INVALID_VPD_FORMAT;
+}
+
+std::shared_ptr<ParserInterface> ParserFactory::getParser(
+ const types::BinaryVector& i_vpdVector, const std::string& i_vpdFilePath,
+ size_t i_vpdStartOffset)
+{
+ if (i_vpdVector.empty())
+ {
+ throw std::runtime_error("Empty VPD vector passed to parser factory");
+ }
+
+ vpdType l_type = vpdTypeCheck(i_vpdVector);
+
+ switch (l_type)
+ {
+ case vpdType::IPZ_VPD:
+ {
+ return std::make_shared<IpzVpdParser>(i_vpdVector, i_vpdFilePath,
+ i_vpdStartOffset);
+ }
+
+ case vpdType::KEYWORD_VPD:
+ {
+ return std::make_shared<KeywordVpdParser>(i_vpdVector);
+ }
+
+ case vpdType::DDR5_DDIMM_MEMORY_VPD:
+ case vpdType::DDR4_DDIMM_MEMORY_VPD:
+ {
+ return std::make_shared<DdimmVpdParser>(i_vpdVector);
+ }
+
+ case vpdType::DDR4_ISDIMM_MEMORY_VPD:
+ case vpdType::DDR5_ISDIMM_MEMORY_VPD:
+ {
+ // return shared pointer to class object.
+ logging::logMessage(
+ "ISDIMM parser selected for VPD path: " + i_vpdFilePath);
+ return std::make_shared<JedecSpdParser>(i_vpdVector);
+ }
+
+ default:
+ throw DataException("Unable to determine VPD format");
+ }
+}
+} // namespace vpd
diff --git a/vpd-manager/src/vpd_parser_main.cpp b/vpd-manager/src/vpd_parser_main.cpp
new file mode 100644
index 0000000..0c434d8
--- /dev/null
+++ b/vpd-manager/src/vpd_parser_main.cpp
@@ -0,0 +1,101 @@
+#include "logger.hpp"
+#include "parser.hpp"
+#include "parser_interface.hpp"
+#include "types.hpp"
+#include "worker.hpp"
+
+#include <CLI/CLI.hpp>
+#include <nlohmann/json.hpp>
+#include <parser_factory.hpp>
+
+#include <filesystem>
+#include <iostream>
+
+/**
+ * @brief This file implements a generic parser APP.
+ *
+ * It recieves path of the VPD file(mandatory) and path to a config
+ * file(optional) as arguments. It will parse the data and return parsed data in
+ * a required format.
+ *
+ * Steps to get parsed VPD.
+ * - Pass VPD file path and config file (if applicable).
+ * - Read VPD file to vector.
+ * - Pass that to parser_factory to get the parser and call parse API on that
+ * parser object to get the Parsed VPD map.
+ * - If VPD format is other than the existing formats. Follow the steps
+ * - a) Add logic in parser_factory.cpp, vpdTypeCheck API to detect the format.
+ * - b) Implement a custom parser class.
+ * - c) Override parse API in the newly added parser class.
+ * - d) Add type of parsed data returned by parse API into types.hpp,
+ * "VPDMapVariant".
+ *
+ */
+
+int main(int argc, char** argv)
+{
+ try
+ {
+ std::string vpdFilePath{};
+ CLI::App app{"VPD-parser-app - APP to parse VPD. "};
+
+ app.add_option("-f, --file", vpdFilePath, "VPD file path")->required();
+
+ std::string configFilePath{};
+
+ app.add_option("-c,--config", configFilePath, "Path to JSON config");
+
+ CLI11_PARSE(app, argc, argv);
+
+ vpd::logging::logMessage("VPD file path recieved" + vpdFilePath);
+
+ // VPD file path is a mandatory parameter to execute any parser.
+ if (vpdFilePath.empty())
+ {
+ throw std::runtime_error("Empty VPD file path");
+ }
+
+ nlohmann::json json;
+ vpd::types::VPDMapVariant parsedVpdDataMap;
+
+ // Below are two different ways of parsing the VPD.
+ if (!configFilePath.empty())
+ {
+ vpd::logging::logMessage(
+ "Processing with config file - " + configFilePath);
+
+ std::shared_ptr<vpd::Worker> objWorker =
+ std::make_shared<vpd::Worker>(configFilePath);
+ parsedVpdDataMap = objWorker->parseVpdFile(vpdFilePath);
+
+ // Based on requirement, call appropriate public API of worker class
+ /*If required to publish the FRU data on Dbus*/
+ // objWorker->publishFruDataOnDbus(parsedVpdDataMap);
+ }
+ else
+ {
+ // Will work with empty JSON
+ std::shared_ptr<vpd::Parser> vpdParser =
+ std::make_shared<vpd::Parser>(vpdFilePath, json);
+ parsedVpdDataMap = vpdParser->parse();
+ }
+
+ // If custom handling is required then custom logic to be implemented
+ // based on the type of variant,
+ // eg: for IPZ VPD format
+ if (auto ipzVpdMap =
+ std::get_if<vpd::types::IPZVpdMap>(&parsedVpdDataMap))
+ {
+ // get rid of unused variable warning/error
+ (void)ipzVpdMap;
+ // implement code that needs to handle parsed IPZ VPD.
+ }
+ }
+ catch (const std::exception& ex)
+ {
+ vpd::logging::logMessage(ex.what());
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/vpd-manager/src/worker.cpp b/vpd-manager/src/worker.cpp
new file mode 100644
index 0000000..2cf8cb5
--- /dev/null
+++ b/vpd-manager/src/worker.cpp
@@ -0,0 +1,1677 @@
+#include "config.h"
+
+#include "worker.hpp"
+
+#include "backup_restore.hpp"
+#include "configuration.hpp"
+#include "constants.hpp"
+#include "event_logger.hpp"
+#include "exceptions.hpp"
+#include "logger.hpp"
+#include "parser.hpp"
+#include "parser_factory.hpp"
+#include "parser_interface.hpp"
+
+#include <utility/dbus_utility.hpp>
+#include <utility/json_utility.hpp>
+#include <utility/vpd_specific_utility.hpp>
+
+#include <filesystem>
+#include <fstream>
+#include <future>
+#include <typeindex>
+#include <unordered_set>
+
+namespace vpd
+{
+
+Worker::Worker(std::string pathToConfigJson) :
+ m_configJsonPath(pathToConfigJson)
+{
+ // Implies the processing is based on some config JSON
+ if (!m_configJsonPath.empty())
+ {
+ // Check if symlink is already there to confirm fresh boot/factory
+ // reset.
+ if (std::filesystem::exists(INVENTORY_JSON_SYM_LINK))
+ {
+ logging::logMessage("Sym Link already present");
+ m_configJsonPath = INVENTORY_JSON_SYM_LINK;
+ m_isSymlinkPresent = true;
+ }
+
+ try
+ {
+ m_parsedJson = jsonUtility::getParsedJson(m_configJsonPath);
+
+ // check for mandatory fields at this point itself.
+ if (!m_parsedJson.contains("frus"))
+ {
+ throw std::runtime_error("Mandatory tag(s) missing from JSON");
+ }
+ }
+ catch (const std::exception& ex)
+ {
+ throw(JsonException(ex.what(), m_configJsonPath));
+ }
+ }
+ else
+ {
+ logging::logMessage("Processing in not based on any config JSON");
+ }
+}
+
+void Worker::enableMuxChips()
+{
+ if (m_parsedJson.empty())
+ {
+ // config JSON should not be empty at this point of execution.
+ throw std::runtime_error("Config JSON is empty. Can't enable muxes");
+ return;
+ }
+
+ if (!m_parsedJson.contains("muxes"))
+ {
+ logging::logMessage("No mux defined for the system in config JSON");
+ return;
+ }
+
+ // iterate over each MUX detail and enable them.
+ for (const auto& item : m_parsedJson["muxes"])
+ {
+ if (item.contains("holdidlepath"))
+ {
+ std::string cmd = "echo 0 > ";
+ cmd += item["holdidlepath"];
+
+ logging::logMessage("Enabling mux with command = " + cmd);
+
+ commonUtility::executeCmd(cmd);
+ continue;
+ }
+
+ logging::logMessage(
+ "Mux Entry does not have hold idle path. Can't enable the mux");
+ }
+}
+
+#ifdef IBM_SYSTEM
+void Worker::primeSystemBlueprint()
+{
+ if (m_parsedJson.empty())
+ {
+ return;
+ }
+
+ const nlohmann::json& l_listOfFrus =
+ m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>();
+
+ for (const auto& l_itemFRUS : l_listOfFrus.items())
+ {
+ const std::string& l_vpdFilePath = l_itemFRUS.key();
+
+ if (l_vpdFilePath == SYSTEM_VPD_FILE_PATH)
+ {
+ continue;
+ }
+
+ // Prime the inventry for FRUs which
+ // are not present/processing had some error.
+ if (!primeInventory(l_vpdFilePath))
+ {
+ logging::logMessage(
+ "Priming of inventory failed for FRU " + l_vpdFilePath);
+ }
+ }
+}
+
+void Worker::performInitialSetup()
+{
+ try
+ {
+ if (!dbusUtility::isChassisPowerOn())
+ {
+ logging::logMessage("Chassis is in Off state.");
+ setDeviceTreeAndJson();
+ primeSystemBlueprint();
+ }
+
+ // Enable all mux which are used for connecting to the i2c on the
+ // pcie slots for pcie cards. These are not enabled by kernel due to
+ // an issue seen with Castello cards, where the i2c line hangs on a
+ // probe.
+ enableMuxChips();
+
+ // Nothing needs to be done. Service restarted or BMC re-booted for
+ // some reason at system power on.
+ return;
+ }
+ catch (const std::exception& ex)
+ {
+ if (typeid(ex) == std::type_index(typeid(DataException)))
+ {
+ // TODO:Catch logic to be implemented once PEL code goes in.
+ }
+ else if (typeid(ex) == std::type_index(typeid(EccException)))
+ {
+ // TODO:Catch logic to be implemented once PEL code goes in.
+ }
+ else if (typeid(ex) == std::type_index(typeid(JsonException)))
+ {
+ // TODO:Catch logic to be implemented once PEL code goes in.
+ }
+
+ logging::logMessage(ex.what());
+ throw;
+ }
+}
+#endif
+
+static std::string readFitConfigValue()
+{
+ std::vector<std::string> output =
+ commonUtility::executeCmd("/sbin/fw_printenv");
+ std::string fitConfigValue;
+
+ for (const auto& entry : output)
+ {
+ auto pos = entry.find("=");
+ auto key = entry.substr(0, pos);
+ if (key != "fitconfig")
+ {
+ continue;
+ }
+
+ if (pos + 1 < entry.size())
+ {
+ fitConfigValue = entry.substr(pos + 1);
+ }
+ }
+
+ return fitConfigValue;
+}
+
+bool Worker::isSystemVPDOnDBus() const
+{
+ const std::string& mboardPath =
+ m_parsedJson["frus"][SYSTEM_VPD_FILE_PATH].at(0).value(
+ "inventoryPath", "");
+
+ if (mboardPath.empty())
+ {
+ throw JsonException("System vpd file path missing in JSON",
+ INVENTORY_JSON_SYM_LINK);
+ }
+
+ std::array<const char*, 1> interfaces = {
+ "xyz.openbmc_project.Inventory.Item.Board.Motherboard"};
+
+ const types::MapperGetObject& objectMap =
+ dbusUtility::getObjectMap(mboardPath, interfaces);
+
+ if (objectMap.empty())
+ {
+ return false;
+ }
+ return true;
+}
+
+std::string Worker::getIMValue(const types::IPZVpdMap& parsedVpd) const
+{
+ if (parsedVpd.empty())
+ {
+ throw std::runtime_error("Empty VPD map. Can't Extract IM value");
+ }
+
+ const auto& itrToVSBP = parsedVpd.find("VSBP");
+ if (itrToVSBP == parsedVpd.end())
+ {
+ throw DataException("VSBP record missing.");
+ }
+
+ const auto& itrToIM = (itrToVSBP->second).find("IM");
+ if (itrToIM == (itrToVSBP->second).end())
+ {
+ throw DataException("IM keyword missing.");
+ }
+
+ types::BinaryVector imVal;
+ std::copy(itrToIM->second.begin(), itrToIM->second.end(),
+ back_inserter(imVal));
+
+ std::ostringstream imData;
+ for (auto& aByte : imVal)
+ {
+ imData << std::setw(2) << std::setfill('0') << std::hex
+ << static_cast<int>(aByte);
+ }
+
+ return imData.str();
+}
+
+std::string Worker::getHWVersion(const types::IPZVpdMap& parsedVpd) const
+{
+ if (parsedVpd.empty())
+ {
+ throw std::runtime_error("Empty VPD map. Can't Extract HW value");
+ }
+
+ const auto& itrToVINI = parsedVpd.find("VINI");
+ if (itrToVINI == parsedVpd.end())
+ {
+ throw DataException("VINI record missing.");
+ }
+
+ const auto& itrToHW = (itrToVINI->second).find("HW");
+ if (itrToHW == (itrToVINI->second).end())
+ {
+ throw DataException("HW keyword missing.");
+ }
+
+ types::BinaryVector hwVal;
+ std::copy(itrToHW->second.begin(), itrToHW->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& aByte : hwVal)
+ {
+ hwString << std::setw(2) << std::setfill('0') << std::hex
+ << static_cast<int>(aByte);
+ }
+
+ return hwString.str();
+}
+
+void Worker::fillVPDMap(const std::string& vpdFilePath,
+ types::VPDMapVariant& vpdMap)
+{
+ logging::logMessage(std::string("Parsing file = ") + vpdFilePath);
+
+ if (vpdFilePath.empty())
+ {
+ throw std::runtime_error("Invalid file path passed to fillVPDMap API.");
+ }
+
+ if (!std::filesystem::exists(vpdFilePath))
+ {
+ throw std::runtime_error("Can't Find physical file");
+ }
+
+ try
+ {
+ std::shared_ptr<Parser> vpdParser =
+ std::make_shared<Parser>(vpdFilePath, m_parsedJson);
+ vpdMap = vpdParser->parse();
+ }
+ catch (const std::exception& ex)
+ {
+ if (typeid(ex) == std::type_index(typeid(DataException)))
+ {
+ // TODO: Do what needs to be done in case of Data exception.
+ // Uncomment when PEL implementation goes in.
+ /* string errorMsg =
+ "VPD file is either empty or invalid. Parser failed for [";
+ errorMsg += m_vpdFilePath;
+ errorMsg += "], with error = " + std::string(ex.what());
+
+ additionalData.emplace("DESCRIPTION", errorMsg);
+ additionalData.emplace("CALLOUT_INVENTORY_PATH",
+ INVENTORY_PATH + baseFruInventoryPath);
+ createPEL(additionalData, pelSeverity, errIntfForInvalidVPD,
+ nullptr);*/
+
+ // throw generic error from here to inform main caller about
+ // failure.
+ logging::logMessage(ex.what());
+ throw std::runtime_error(
+ "Data Exception occurred for file path = " + vpdFilePath);
+ }
+
+ if (typeid(ex) == std::type_index(typeid(EccException)))
+ {
+ // TODO: Do what needs to be done in case of ECC exception.
+ // Uncomment when PEL implementation goes in.
+ /* additionalData.emplace("DESCRIPTION", "ECC check failed");
+ additionalData.emplace("CALLOUT_INVENTORY_PATH",
+ INVENTORY_PATH + baseFruInventoryPath);
+ createPEL(additionalData, pelSeverity, errIntfForEccCheckFail,
+ nullptr);
+ */
+
+ logging::logMessage(ex.what());
+ // Need to decide once all error handling is implemented.
+ // vpdSpecificUtility::dumpBadVpd(vpdFilePath,vpdVector);
+
+ // throw generic error from here to inform main caller about
+ // failure.
+ throw std::runtime_error(
+ "Ecc Exception occurred for file path = " + vpdFilePath);
+ }
+ }
+}
+
+void Worker::getSystemJson(std::string& systemJson,
+ const types::VPDMapVariant& parsedVpdMap)
+{
+ if (auto pVal = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
+ {
+ std::string hwKWdValue = getHWVersion(*pVal);
+ if (hwKWdValue.empty())
+ {
+ throw DataException("HW value fetched is empty.");
+ }
+
+ const std::string& imKwdValue = getIMValue(*pVal);
+ if (imKwdValue.empty())
+ {
+ throw DataException("IM value fetched is empty.");
+ }
+
+ auto itrToIM = config::systemType.find(imKwdValue);
+ if (itrToIM == config::systemType.end())
+ {
+ throw DataException("IM keyword does not map to any system type");
+ }
+
+ const types::HWVerList hwVersionList = itrToIM->second.second;
+ if (!hwVersionList.empty())
+ {
+ transform(hwKWdValue.begin(), hwKWdValue.end(), hwKWdValue.begin(),
+ ::toupper);
+
+ auto itrToHW =
+ std::find_if(hwVersionList.begin(), hwVersionList.end(),
+ [&hwKWdValue](const auto& aPair) {
+ return aPair.first == hwKWdValue;
+ });
+
+ if (itrToHW != hwVersionList.end())
+ {
+ if (!(*itrToHW).second.empty())
+ {
+ systemJson += (*itrToIM).first + "_" + (*itrToHW).second +
+ ".json";
+ }
+ else
+ {
+ systemJson += (*itrToIM).first + ".json";
+ }
+ return;
+ }
+ }
+ systemJson += itrToIM->second.first + ".json";
+ return;
+ }
+
+ throw DataException("Invalid VPD type returned from Parser");
+}
+
+static void setEnvAndReboot(const std::string& key, const std::string& value)
+{
+ // set env and reboot and break.
+ commonUtility::executeCmd("/sbin/fw_setenv", key, value);
+ logging::logMessage("Rebooting BMC to pick up new device tree");
+
+ // make dbus call to reboot
+ auto bus = sdbusplus::bus::new_default_system();
+ auto method = bus.new_method_call(
+ "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager", "Reboot");
+ bus.call_noreply(method);
+}
+
+void Worker::setJsonSymbolicLink(const std::string& i_systemJson)
+{
+ std::error_code l_ec;
+ l_ec.clear();
+ if (!std::filesystem::exists(VPD_SYMLIMK_PATH, l_ec))
+ {
+ if (l_ec)
+ {
+ throw std::runtime_error(
+ "File system call to exist failed with error = " +
+ l_ec.message());
+ }
+
+ // implies it is a fresh boot/factory reset.
+ // Create the directory for hosting the symlink
+ if (!std::filesystem::create_directories(VPD_SYMLIMK_PATH, l_ec))
+ {
+ if (l_ec)
+ {
+ throw std::runtime_error(
+ "File system call to create directory failed with error = " +
+ l_ec.message());
+ }
+ }
+ }
+
+ // create a new symlink based on the system
+ std::filesystem::create_symlink(i_systemJson, INVENTORY_JSON_SYM_LINK,
+ l_ec);
+
+ if (l_ec)
+ {
+ throw std::runtime_error(
+ "create_symlink system call failed with error: " + l_ec.message());
+ }
+
+ // If the flow is at this point implies the symlink was not present there.
+ // Considering this as factory reset.
+ m_isFactoryResetDone = true;
+}
+
+void Worker::setDeviceTreeAndJson()
+{
+ // JSON is madatory for processing of this API.
+ if (m_parsedJson.empty())
+ {
+ throw std::runtime_error("JSON is empty");
+ }
+
+ types::VPDMapVariant parsedVpdMap;
+ fillVPDMap(SYSTEM_VPD_FILE_PATH, parsedVpdMap);
+
+ // Implies it is default JSON.
+ std::string systemJson{JSON_ABSOLUTE_PATH_PREFIX};
+
+ // ToDo: Need to check if INVENTORY_JSON_SYM_LINK pointing to correct system
+ // This is required to support movement from rainier to Blue Ridge on the
+ // fly.
+
+ // Do we have the entry for device tree in parsed JSON?
+ if (m_parsedJson.find("devTree") == m_parsedJson.end())
+ {
+ getSystemJson(systemJson, parsedVpdMap);
+
+ if (!systemJson.compare(JSON_ABSOLUTE_PATH_PREFIX))
+ {
+ // TODO: Log a PEL saying that "System type not supported"
+ throw DataException("Error in getting system JSON.");
+ }
+
+ // re-parse the JSON once appropriate JSON has been selected.
+ try
+ {
+ m_parsedJson = jsonUtility::getParsedJson(systemJson);
+ }
+ catch (const nlohmann::json::parse_error& ex)
+ {
+ throw(JsonException("Json parsing failed", systemJson));
+ }
+ }
+
+ std::string devTreeFromJson;
+ if (m_parsedJson.contains("devTree"))
+ {
+ devTreeFromJson = m_parsedJson["devTree"];
+
+ if (devTreeFromJson.empty())
+ {
+ // TODO:: Log a predictive PEL
+ logging::logMessage(
+ "Mandatory value for device tree missing from JSON[" +
+ std::string(INVENTORY_JSON_SYM_LINK) + "]");
+ }
+ }
+
+ auto fitConfigVal = readFitConfigValue();
+
+ if (devTreeFromJson.empty() ||
+ fitConfigVal.find(devTreeFromJson) != std::string::npos)
+ { // Skipping setting device tree as either devtree info is missing from
+ // Json or it is rightly set.
+
+ // avoid setting symlink on every reboot.
+ if (!m_isSymlinkPresent)
+ {
+ setJsonSymbolicLink(systemJson);
+ }
+
+ if (isSystemVPDOnDBus() &&
+ jsonUtility::isBackupAndRestoreRequired(m_parsedJson))
+ {
+ performBackupAndRestore(parsedVpdMap);
+ }
+
+ // proceed to publish system VPD.
+ publishSystemVPD(parsedVpdMap);
+ return;
+ }
+
+ setEnvAndReboot("fitconfig", devTreeFromJson);
+ exit(EXIT_SUCCESS);
+}
+
+void Worker::populateIPZVPDpropertyMap(
+ types::InterfaceMap& interfacePropMap,
+ const types::IPZKwdValueMap& keyordValueMap,
+ const std::string& interfaceName)
+{
+ types::PropertyMap propertyValueMap;
+ for (const auto& kwdVal : keyordValueMap)
+ {
+ auto kwd = kwdVal.first;
+
+ if (kwd[0] == '#')
+ {
+ kwd = std::string("PD_") + kwd[1];
+ }
+ else if (isdigit(kwd[0]))
+ {
+ kwd = std::string("N_") + kwd;
+ }
+
+ types::BinaryVector value(kwdVal.second.begin(), kwdVal.second.end());
+ propertyValueMap.emplace(move(kwd), move(value));
+ }
+
+ if (!propertyValueMap.empty())
+ {
+ interfacePropMap.emplace(interfaceName, propertyValueMap);
+ }
+}
+
+void Worker::populateKwdVPDpropertyMap(const types::KeywordVpdMap& keyordVPDMap,
+ types::InterfaceMap& interfaceMap)
+{
+ for (const auto& kwdValMap : keyordVPDMap)
+ {
+ types::PropertyMap propertyValueMap;
+ auto kwd = kwdValMap.first;
+
+ if (kwd[0] == '#')
+ {
+ kwd = std::string("PD_") + kwd[1];
+ }
+ else if (isdigit(kwd[0]))
+ {
+ kwd = std::string("N_") + kwd;
+ }
+
+ if (auto keywordValue = get_if<types::BinaryVector>(&kwdValMap.second))
+ {
+ types::BinaryVector value((*keywordValue).begin(),
+ (*keywordValue).end());
+ propertyValueMap.emplace(move(kwd), move(value));
+ }
+ else if (auto keywordValue = get_if<std::string>(&kwdValMap.second))
+ {
+ types::BinaryVector value((*keywordValue).begin(),
+ (*keywordValue).end());
+ propertyValueMap.emplace(move(kwd), move(value));
+ }
+ else if (auto keywordValue = get_if<size_t>(&kwdValMap.second))
+ {
+ if (kwd == "MemorySizeInKB")
+ {
+ types::PropertyMap memProp;
+ memProp.emplace(move(kwd), ((*keywordValue)));
+ interfaceMap.emplace("xyz.openbmc_project.Inventory.Item.Dimm",
+ move(memProp));
+ continue;
+ }
+ else
+ {
+ logging::logMessage(
+ "Unknown Keyword =" + kwd + " found in keyword VPD map");
+ continue;
+ }
+ }
+ else
+ {
+ logging::logMessage(
+ "Unknown variant type found in keyword VPD map.");
+ continue;
+ }
+
+ if (!propertyValueMap.empty())
+ {
+ vpdSpecificUtility::insertOrMerge(
+ interfaceMap, constants::kwdVpdInf, move(propertyValueMap));
+ }
+ }
+}
+
+void Worker::populateInterfaces(const nlohmann::json& interfaceJson,
+ types::InterfaceMap& interfaceMap,
+ const types::VPDMapVariant& parsedVpdMap)
+{
+ for (const auto& interfacesPropPair : interfaceJson.items())
+ {
+ const std::string& interface = interfacesPropPair.key();
+ types::PropertyMap propertyMap;
+
+ for (const auto& propValuePair : interfacesPropPair.value().items())
+ {
+ const std::string property = propValuePair.key();
+
+ if (propValuePair.value().is_boolean())
+ {
+ propertyMap.emplace(property,
+ propValuePair.value().get<bool>());
+ }
+ else if (propValuePair.value().is_string())
+ {
+ if (property.compare("LocationCode") == 0 &&
+ interface.compare("com.ibm.ipzvpd.Location") == 0)
+ {
+ std::string value =
+ vpdSpecificUtility::getExpandedLocationCode(
+ propValuePair.value().get<std::string>(),
+ parsedVpdMap);
+ propertyMap.emplace(property, value);
+
+ auto l_locCodeProperty = propertyMap;
+ vpdSpecificUtility::insertOrMerge(
+ interfaceMap,
+ std::string(constants::xyzLocationCodeInf),
+ move(l_locCodeProperty));
+ }
+ else
+ {
+ propertyMap.emplace(
+ property, propValuePair.value().get<std::string>());
+ }
+ }
+ else if (propValuePair.value().is_array())
+ {
+ try
+ {
+ propertyMap.emplace(
+ property,
+ propValuePair.value().get<types::BinaryVector>());
+ }
+ catch (const nlohmann::detail::type_error& e)
+ {
+ std::cerr << "Type exception: " << e.what() << "\n";
+ }
+ }
+ else if (propValuePair.value().is_number())
+ {
+ // For now assume the value is a size_t. In the future it would
+ // be nice to come up with a way to get the type from the JSON.
+ propertyMap.emplace(property,
+ propValuePair.value().get<size_t>());
+ }
+ else if (propValuePair.value().is_object())
+ {
+ const std::string& record =
+ propValuePair.value().value("recordName", "");
+ const std::string& keyword =
+ propValuePair.value().value("keywordName", "");
+ const std::string& encoding =
+ propValuePair.value().value("encoding", "");
+
+ if (auto ipzVpdMap =
+ std::get_if<types::IPZVpdMap>(&parsedVpdMap))
+ {
+ if (!record.empty() && !keyword.empty() &&
+ (*ipzVpdMap).count(record) &&
+ (*ipzVpdMap).at(record).count(keyword))
+ {
+ auto encoded = vpdSpecificUtility::encodeKeyword(
+ ((*ipzVpdMap).at(record).at(keyword)), encoding);
+ propertyMap.emplace(property, encoded);
+ }
+ }
+ else if (auto kwdVpdMap =
+ std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
+ {
+ if (!keyword.empty() && (*kwdVpdMap).count(keyword))
+ {
+ if (auto kwValue = std::get_if<types::BinaryVector>(
+ &(*kwdVpdMap).at(keyword)))
+ {
+ auto encodedValue =
+ vpdSpecificUtility::encodeKeyword(
+ std::string((*kwValue).begin(),
+ (*kwValue).end()),
+ encoding);
+
+ propertyMap.emplace(property, encodedValue);
+ }
+ else if (auto kwValue = std::get_if<std::string>(
+ &(*kwdVpdMap).at(keyword)))
+ {
+ auto encodedValue =
+ vpdSpecificUtility::encodeKeyword(
+ std::string((*kwValue).begin(),
+ (*kwValue).end()),
+ encoding);
+
+ propertyMap.emplace(property, encodedValue);
+ }
+ else if (auto uintValue = std::get_if<size_t>(
+ &(*kwdVpdMap).at(keyword)))
+ {
+ propertyMap.emplace(property, *uintValue);
+ }
+ else
+ {
+ logging::logMessage(
+ "Unknown keyword found, Keywrod = " + keyword);
+ }
+ }
+ }
+ }
+ }
+ vpdSpecificUtility::insertOrMerge(interfaceMap, interface,
+ move(propertyMap));
+ }
+}
+
+bool Worker::isCPUIOGoodOnly(const std::string& i_pgKeyword)
+{
+ const unsigned char l_io[] = {
+ 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF,
+ 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF, 0xE7, 0xF9, 0xFF};
+
+ // EQ0 index (in PG keyword) starts at 97 (with offset starting from 0).
+ // Each EQ carries 3 bytes of data. Totally there are 8 EQs. If all EQs'
+ // value equals 0xE7F9FF, then the cpu has no good cores and its treated as
+ // IO.
+ if (memcmp(l_io, i_pgKeyword.data() + constants::INDEX_OF_EQ0_IN_PG,
+ constants::SIZE_OF_8EQ_IN_PG) == 0)
+ {
+ return true;
+ }
+
+ // The CPU is not an IO
+ return false;
+}
+
+bool Worker::primeInventory(const std::string& i_vpdFilePath)
+{
+ if (i_vpdFilePath.empty())
+ {
+ logging::logMessage("Empty VPD file path given");
+ return false;
+ }
+
+ if (m_parsedJson.empty())
+ {
+ logging::logMessage("Empty JSON detected for " + i_vpdFilePath);
+ return false;
+ }
+ else if (!m_parsedJson["frus"].contains(i_vpdFilePath))
+ {
+ logging::logMessage("File " + i_vpdFilePath +
+ ", is not found in the system config JSON file.");
+ return false;
+ }
+
+ types::ObjectMap l_objectInterfaceMap;
+ for (const auto& l_Fru : m_parsedJson["frus"][i_vpdFilePath])
+ {
+ types::InterfaceMap l_interfaces;
+ sdbusplus::message::object_path l_fruObjectPath(l_Fru["inventoryPath"]);
+
+ if (l_Fru.contains("ccin"))
+ {
+ continue;
+ }
+
+ if (l_Fru.contains("noprime") && l_Fru.value("noprime", false))
+ {
+ continue;
+ }
+
+ // Clear data under PIM if already exists.
+ vpdSpecificUtility::resetDataUnderPIM(
+ std::string(l_Fru["inventoryPath"]), l_interfaces);
+
+ // Add extra interfaces mentioned in the Json config file
+ if (l_Fru.contains("extraInterfaces"))
+ {
+ populateInterfaces(l_Fru["extraInterfaces"], l_interfaces,
+ std::monostate{});
+ }
+
+ types::PropertyMap l_propertyValueMap;
+ l_propertyValueMap.emplace("Present", false);
+ if (std::filesystem::exists(i_vpdFilePath))
+ {
+ l_propertyValueMap["Present"] = true;
+ }
+
+ vpdSpecificUtility::insertOrMerge(l_interfaces,
+ "xyz.openbmc_project.Inventory.Item",
+ move(l_propertyValueMap));
+
+ if (l_Fru.value("inherit", true) &&
+ m_parsedJson.contains("commonInterfaces"))
+ {
+ populateInterfaces(m_parsedJson["commonInterfaces"], l_interfaces,
+ std::monostate{});
+ }
+
+ processFunctionalProperty(l_Fru["inventoryPath"], l_interfaces);
+ processEnabledProperty(l_Fru["inventoryPath"], l_interfaces);
+
+ l_objectInterfaceMap.emplace(std::move(l_fruObjectPath),
+ std::move(l_interfaces));
+ }
+
+ // Notify PIM
+ if (!dbusUtility::callPIM(move(l_objectInterfaceMap)))
+ {
+ logging::logMessage("Call to PIM failed for VPD file " + i_vpdFilePath);
+ return false;
+ }
+
+ return true;
+}
+
+void Worker::processEmbeddedAndSynthesizedFrus(const nlohmann::json& singleFru,
+ types::InterfaceMap& interfaces)
+{
+ // embedded property(true or false) says whether the subfru is embedded
+ // into the parent fru (or) not. VPD sets Present property only for
+ // embedded frus. If the subfru is not an embedded FRU, the subfru may
+ // or may not be physically present. Those non embedded frus will always
+ // have Present=false irrespective of its physical presence or absence.
+ // Eg: nvme drive in nvme slot is not an embedded FRU. So don't set
+ // Present to true for such sub frus.
+ // Eg: ethernet port is embedded into bmc card. So set Present to true
+ // for such sub frus. Also donot populate present property for embedded
+ // subfru which is synthesized. Currently there is no subfru which are
+ // both embedded and synthesized. But still the case is handled here.
+
+ // Check if its required to handle presence for this FRU.
+ if (singleFru.value("handlePresence", true))
+ {
+ types::PropertyMap presProp;
+ presProp.emplace("Present", true);
+ vpdSpecificUtility::insertOrMerge(
+ interfaces, "xyz.openbmc_project.Inventory.Item", move(presProp));
+ }
+}
+
+void Worker::processExtraInterfaces(const nlohmann::json& singleFru,
+ types::InterfaceMap& interfaces,
+ const types::VPDMapVariant& parsedVpdMap)
+{
+ populateInterfaces(singleFru["extraInterfaces"], interfaces, parsedVpdMap);
+ if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
+ {
+ if (singleFru["extraInterfaces"].contains(
+ "xyz.openbmc_project.Inventory.Item.Cpu"))
+ {
+ auto itrToRec = (*ipzVpdMap).find("CP00");
+ if (itrToRec == (*ipzVpdMap).end())
+ {
+ return;
+ }
+
+ std::string pgKeywordValue;
+ vpdSpecificUtility::getKwVal(itrToRec->second, "PG",
+ pgKeywordValue);
+ if (!pgKeywordValue.empty())
+ {
+ if (isCPUIOGoodOnly(pgKeywordValue))
+ {
+ interfaces["xyz.openbmc_project.Inventory.Item"]
+ ["PrettyName"] = "IO Module";
+ }
+ }
+ }
+ }
+}
+
+void Worker::processCopyRecordFlag(const nlohmann::json& singleFru,
+ const types::VPDMapVariant& parsedVpdMap,
+ types::InterfaceMap& interfaces)
+{
+ if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
+ {
+ for (const auto& record : singleFru["copyRecords"])
+ {
+ const std::string& recordName = record;
+ if ((*ipzVpdMap).find(recordName) != (*ipzVpdMap).end())
+ {
+ populateIPZVPDpropertyMap(interfaces,
+ (*ipzVpdMap).at(recordName),
+ constants::ipzVpdInf + recordName);
+ }
+ }
+ }
+}
+
+void Worker::processInheritFlag(const types::VPDMapVariant& parsedVpdMap,
+ types::InterfaceMap& interfaces)
+{
+ if (auto ipzVpdMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
+ {
+ for (const auto& [recordName, kwdValueMap] : *ipzVpdMap)
+ {
+ populateIPZVPDpropertyMap(interfaces, kwdValueMap,
+ constants::ipzVpdInf + recordName);
+ }
+ }
+ else if (auto kwdVpdMap = std::get_if<types::KeywordVpdMap>(&parsedVpdMap))
+ {
+ populateKwdVPDpropertyMap(*kwdVpdMap, interfaces);
+ }
+
+ if (m_parsedJson.contains("commonInterfaces"))
+ {
+ populateInterfaces(m_parsedJson["commonInterfaces"], interfaces,
+ parsedVpdMap);
+ }
+}
+
+bool Worker::processFruWithCCIN(const nlohmann::json& singleFru,
+ const types::VPDMapVariant& parsedVpdMap)
+{
+ if (auto ipzVPDMap = std::get_if<types::IPZVpdMap>(&parsedVpdMap))
+ {
+ auto itrToRec = (*ipzVPDMap).find("VINI");
+ if (itrToRec == (*ipzVPDMap).end())
+ {
+ return false;
+ }
+
+ std::string ccinFromVpd;
+ vpdSpecificUtility::getKwVal(itrToRec->second, "CC", ccinFromVpd);
+ if (ccinFromVpd.empty())
+ {
+ return false;
+ }
+
+ transform(ccinFromVpd.begin(), ccinFromVpd.end(), ccinFromVpd.begin(),
+ ::toupper);
+
+ std::vector<std::string> ccinList;
+ for (std::string ccin : singleFru["ccin"])
+ {
+ transform(ccin.begin(), ccin.end(), ccin.begin(), ::toupper);
+ ccinList.push_back(ccin);
+ }
+
+ if (ccinList.empty())
+ {
+ return false;
+ }
+
+ if (find(ccinList.begin(), ccinList.end(), ccinFromVpd) ==
+ ccinList.end())
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+void Worker::processFunctionalProperty(const std::string& i_inventoryObjPath,
+ types::InterfaceMap& io_interfaces)
+{
+ if (!dbusUtility::isChassisPowerOn())
+ {
+ std::array<const char*, 1> l_operationalStatusInf = {
+ constants::operationalStatusInf};
+
+ auto mapperObjectMap = dbusUtility::getObjectMap(
+ i_inventoryObjPath, l_operationalStatusInf);
+
+ // If the object has been found. Check if it is under PIM.
+ if (mapperObjectMap.size() != 0)
+ {
+ for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
+ {
+ if (l_serviceName == constants::pimServiceName)
+ {
+ // The object is already under PIM. No need to process
+ // again. Retain the old value.
+ return;
+ }
+ }
+ }
+
+ // Implies value is not there in D-Bus. Populate it with default
+ // value "true".
+ types::PropertyMap l_functionalProp;
+ l_functionalProp.emplace("Functional", true);
+ vpdSpecificUtility::insertOrMerge(io_interfaces,
+ constants::operationalStatusInf,
+ move(l_functionalProp));
+ }
+
+ // if chassis is power on. Functional property should be there on D-Bus.
+ // Don't process.
+ return;
+}
+
+void Worker::processEnabledProperty(const std::string& i_inventoryObjPath,
+ types::InterfaceMap& io_interfaces)
+{
+ if (!dbusUtility::isChassisPowerOn())
+ {
+ std::array<const char*, 1> l_enableInf = {constants::enableInf};
+
+ auto mapperObjectMap =
+ dbusUtility::getObjectMap(i_inventoryObjPath, l_enableInf);
+
+ // If the object has been found. Check if it is under PIM.
+ if (mapperObjectMap.size() != 0)
+ {
+ for (const auto& [l_serviceName, l_interfaceLsit] : mapperObjectMap)
+ {
+ if (l_serviceName == constants::pimServiceName)
+ {
+ // The object is already under PIM. No need to process
+ // again. Retain the old value.
+ return;
+ }
+ }
+ }
+
+ // Implies value is not there in D-Bus. Populate it with default
+ // value "true".
+ types::PropertyMap l_enabledProp;
+ l_enabledProp.emplace("Enabled", true);
+ vpdSpecificUtility::insertOrMerge(io_interfaces, constants::enableInf,
+ move(l_enabledProp));
+ }
+
+ // if chassis is power on. Enabled property should be there on D-Bus.
+ // Don't process.
+ return;
+}
+
+void Worker::populateDbus(const types::VPDMapVariant& parsedVpdMap,
+ types::ObjectMap& objectInterfaceMap,
+ const std::string& vpdFilePath)
+{
+ if (vpdFilePath.empty())
+ {
+ throw std::runtime_error(
+ "Invalid parameter passed to populateDbus API.");
+ }
+
+ // JSON config is mandatory for processing of "if". Add "else" for any
+ // processing without config JSON.
+ if (!m_parsedJson.empty())
+ {
+ types::InterfaceMap interfaces;
+
+ for (const auto& aFru : m_parsedJson["frus"][vpdFilePath])
+ {
+ const auto& inventoryPath = aFru["inventoryPath"];
+ sdbusplus::message::object_path fruObjectPath(inventoryPath);
+ if (aFru.contains("ccin"))
+ {
+ if (!processFruWithCCIN(aFru, parsedVpdMap))
+ {
+ continue;
+ }
+ }
+
+ if (aFru.value("inherit", true))
+ {
+ processInheritFlag(parsedVpdMap, interfaces);
+ }
+
+ // If specific record needs to be copied.
+ if (aFru.contains("copyRecords"))
+ {
+ processCopyRecordFlag(aFru, parsedVpdMap, interfaces);
+ }
+
+ if (aFru.contains("extraInterfaces"))
+ {
+ // Process extra interfaces w.r.t a FRU.
+ processExtraInterfaces(aFru, interfaces, parsedVpdMap);
+ }
+
+ // Process FRUS which are embedded in the parent FRU and whose VPD
+ // will be synthesized.
+ if ((aFru.value("embedded", true)) &&
+ (!aFru.value("synthesized", false)))
+ {
+ processEmbeddedAndSynthesizedFrus(aFru, interfaces);
+ }
+
+ processFunctionalProperty(inventoryPath, interfaces);
+ processEnabledProperty(inventoryPath, interfaces);
+
+ objectInterfaceMap.emplace(std::move(fruObjectPath),
+ std::move(interfaces));
+ }
+ }
+}
+
+std::string
+ Worker::createAssetTagString(const types::VPDMapVariant& i_parsedVpdMap)
+{
+ std::string l_assetTag;
+
+ // system VPD will be in IPZ format.
+ if (auto l_parsedVpdMap = std::get_if<types::IPZVpdMap>(&i_parsedVpdMap))
+ {
+ auto l_itrToVsys = (*l_parsedVpdMap).find(constants::recVSYS);
+ if (l_itrToVsys != (*l_parsedVpdMap).end())
+ {
+ std::string l_tmKwdValue;
+ vpdSpecificUtility::getKwVal(l_itrToVsys->second, constants::kwdTM,
+ l_tmKwdValue);
+
+ std::string l_seKwdValue;
+ vpdSpecificUtility::getKwVal(l_itrToVsys->second, constants::kwdSE,
+ l_seKwdValue);
+
+ l_assetTag = std::string{"Server-"} + l_tmKwdValue +
+ std::string{"-"} + l_seKwdValue;
+ }
+ else
+ {
+ throw std::runtime_error(
+ "VSYS record not found in parsed VPD map to create Asset tag.");
+ }
+ }
+ else
+ {
+ throw std::runtime_error(
+ "Invalid VPD type recieved to create Asset tag.");
+ }
+
+ return l_assetTag;
+}
+
+void Worker::publishSystemVPD(const types::VPDMapVariant& parsedVpdMap)
+{
+ types::ObjectMap objectInterfaceMap;
+
+ if (std::get_if<types::IPZVpdMap>(&parsedVpdMap))
+ {
+ populateDbus(parsedVpdMap, objectInterfaceMap, SYSTEM_VPD_FILE_PATH);
+
+ try
+ {
+ if (m_isFactoryResetDone)
+ {
+ const auto& l_assetTag = createAssetTagString(parsedVpdMap);
+
+ auto l_itrToSystemPath = objectInterfaceMap.find(
+ sdbusplus::message::object_path(constants::systemInvPath));
+ if (l_itrToSystemPath == objectInterfaceMap.end())
+ {
+ throw std::runtime_error(
+ "System Path not found in object map.");
+ }
+
+ types::PropertyMap l_assetTagProperty;
+ l_assetTagProperty.emplace("AssetTag", l_assetTag);
+
+ (l_itrToSystemPath->second)
+ .emplace(constants::assetTagInf,
+ std::move(l_assetTagProperty));
+ }
+ }
+ catch (const std::exception& l_ex)
+ {
+ EventLogger::createSyncPel(
+ types::ErrorType::InvalidVpdMessage,
+ types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
+ "Asset tag update failed with following error: " +
+ std::string(l_ex.what()),
+ std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+ }
+
+ // Notify PIM
+ if (!dbusUtility::callPIM(move(objectInterfaceMap)))
+ {
+ throw std::runtime_error("Call to PIM failed for system VPD");
+ }
+ }
+ else
+ {
+ throw DataException("Invalid format of parsed VPD map.");
+ }
+}
+
+bool Worker::processPreAction(const std::string& i_vpdFilePath,
+ const std::string& i_flagToProcess)
+{
+ if (i_vpdFilePath.empty() || i_flagToProcess.empty())
+ {
+ logging::logMessage(
+ "Invalid input parameter. Abort processing pre action");
+ return false;
+ }
+
+ if ((!jsonUtility::executeBaseAction(m_parsedJson, "preAction",
+ i_vpdFilePath, i_flagToProcess)) &&
+ (i_flagToProcess.compare("collection") == constants::STR_CMP_SUCCESS))
+ {
+ // TODO: Need a way to delete inventory object from Dbus and persisted
+ // data section in case any FRU is not present or there is any
+ // problem in collecting it. Once it has been deleted, it can be
+ // re-created in the flow of priming the inventory. This needs to be
+ // done either here or in the exception section of "parseAndPublishVPD"
+ // API. Any failure in the process of collecting FRU will land up in the
+ // excpetion of "parseAndPublishVPD".
+
+ // If the FRU is not there, clear the VINI/CCIN data.
+ // Enity manager probes for this keyword to look for this
+ // FRU, now if the data is persistent on BMC and FRU is
+ // removed this can lead to ambiguity. Hence clearing this
+ // Keyword if FRU is absent.
+ const auto& inventoryPath =
+ m_parsedJson["frus"][i_vpdFilePath].at(0).value("inventoryPath",
+ "");
+
+ if (!inventoryPath.empty())
+ {
+ types::ObjectMap l_pimObjMap{
+ {inventoryPath,
+ {{constants::kwdVpdInf,
+ {{constants::kwdCCIN, types::BinaryVector{}}}}}}};
+
+ if (!dbusUtility::callPIM(std::move(l_pimObjMap)))
+ {
+ logging::logMessage(
+ "Call to PIM failed for file " + i_vpdFilePath);
+ }
+ }
+ else
+ {
+ logging::logMessage(
+ "Inventory path is empty in Json for file " + i_vpdFilePath);
+ }
+
+ return false;
+ }
+ return true;
+}
+
+bool Worker::processPostAction(
+ const std::string& i_vpdFruPath, const std::string& i_flagToProcess,
+ const std::optional<types::VPDMapVariant> i_parsedVpd)
+{
+ if (i_vpdFruPath.empty() || i_flagToProcess.empty())
+ {
+ logging::logMessage(
+ "Invalid input parameter. Abort processing post action");
+ return false;
+ }
+
+ // Check if post action tag is to be triggered in the flow of collection
+ // based on some CCIN value?
+ if (m_parsedJson["frus"][i_vpdFruPath]
+ .at(0)["postAction"][i_flagToProcess]
+ .contains("ccin"))
+ {
+ if (!i_parsedVpd.has_value())
+ {
+ logging::logMessage("Empty VPD Map");
+ return false;
+ }
+
+ // CCIN match is required to process post action for this FRU as it
+ // contains the flag.
+ if (!vpdSpecificUtility::findCcinInVpd(
+ m_parsedJson["frus"][i_vpdFruPath].at(
+ 0)["postAction"]["collection"],
+ i_parsedVpd.value()))
+ {
+ // If CCIN is not found, implies post action processing is not
+ // required for this FRU. Let the flow continue.
+ return true;
+ }
+ }
+
+ if (!jsonUtility::executeBaseAction(m_parsedJson, "postAction",
+ i_vpdFruPath, i_flagToProcess))
+ {
+ logging::logMessage(
+ "Execution of post action failed for path: " + i_vpdFruPath);
+
+ // If post action was required and failed only in that case return
+ // false. In all other case post action is considered passed.
+ return false;
+ }
+
+ return true;
+}
+
+types::VPDMapVariant Worker::parseVpdFile(const std::string& i_vpdFilePath)
+{
+ if (i_vpdFilePath.empty())
+ {
+ throw std::runtime_error(
+ "Empty VPD file path passed to Worker::parseVpdFile. Abort processing");
+ }
+
+ try
+ {
+ if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
+ "preAction", "collection"))
+ {
+ if (!processPreAction(i_vpdFilePath, "collection"))
+ {
+ throw std::runtime_error("Pre-Action failed");
+ }
+ }
+
+ if (!std::filesystem::exists(i_vpdFilePath))
+ {
+ throw std::runtime_error(
+ "Could not find file path " + i_vpdFilePath +
+ "Skipping parser trigger for the EEPROM");
+ }
+
+ std::shared_ptr<Parser> vpdParser =
+ std::make_shared<Parser>(i_vpdFilePath, m_parsedJson);
+
+ types::VPDMapVariant l_parsedVpd = vpdParser->parse();
+
+ // Before returning, as collection is over, check if FRU qualifies for
+ // any post action in the flow of collection.
+ // Note: Don't change the order, post action needs to be processed only
+ // after collection for FRU is successfully done.
+ if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
+ "postAction", "collection"))
+ {
+ if (!processPostAction(i_vpdFilePath, "collection", l_parsedVpd))
+ {
+ // TODO: Log PEL
+ logging::logMessage("Required post action failed for path [" +
+ i_vpdFilePath + "]");
+ }
+ }
+
+ return l_parsedVpd;
+ }
+ catch (std::exception& l_ex)
+ {
+ // If post fail action is required, execute it.
+ if (jsonUtility::isActionRequired(m_parsedJson, i_vpdFilePath,
+ "PostFailAction", "collection"))
+ {
+ if (!jsonUtility::executePostFailAction(m_parsedJson, i_vpdFilePath,
+ "collection"))
+ {
+ // TODO: Log PEL
+ throw std::runtime_error(
+ "VPD parsing failed for " + i_vpdFilePath +
+ " due to error: " + l_ex.what() +
+ ". Post Fail Action also failed, aborting collection for this FRU");
+ }
+ }
+
+ // TODO: Log PEL
+ throw std::runtime_error("VPD parsing failed for " + i_vpdFilePath +
+ " due to error: " + l_ex.what());
+ }
+}
+
+std::tuple<bool, std::string>
+ Worker::parseAndPublishVPD(const std::string& i_vpdFilePath)
+{
+ try
+ {
+ m_semaphore.acquire();
+
+ // Thread launched.
+ m_mutex.lock();
+ m_activeCollectionThreadCount++;
+ m_mutex.unlock();
+
+ const types::VPDMapVariant& parsedVpdMap = parseVpdFile(i_vpdFilePath);
+
+ types::ObjectMap objectInterfaceMap;
+ populateDbus(parsedVpdMap, objectInterfaceMap, i_vpdFilePath);
+
+ // logging::logMessage("Dbus sucessfully populated for FRU " +
+ // i_vpdFilePath);
+
+ // Notify PIM
+ if (!dbusUtility::callPIM(move(objectInterfaceMap)))
+ {
+ throw std::runtime_error(
+ "Call to PIM failed while publishing VPD.");
+ }
+ }
+ catch (const std::exception& ex)
+ {
+ // handle all the exceptions internally. Return only true/false
+ // based on status of execution.
+ if (typeid(ex) == std::type_index(typeid(DataException)))
+ {
+ // TODO: Add custom handling
+ logging::logMessage(ex.what());
+ }
+ else if (typeid(ex) == std::type_index(typeid(EccException)))
+ {
+ // TODO: Add custom handling
+ logging::logMessage(ex.what());
+ }
+ else if (typeid(ex) == std::type_index(typeid(JsonException)))
+ {
+ // TODO: Add custom handling
+ logging::logMessage(ex.what());
+ }
+ else
+ {
+ logging::logMessage(ex.what());
+ }
+
+ // TODO: Figure out a way to clear data in case of any failure at
+ // runtime.
+ // Prime the inventry for FRUs which
+ // are not present/processing had some error.
+ /* if (!primeInventory(i_vpdFilePath))
+ {
+ logging::logMessage("Priming of inventory failed for FRU " +
+ i_vpdFilePath);
+ }*/
+ m_semaphore.release();
+ return std::make_tuple(false, i_vpdFilePath);
+ }
+ m_semaphore.release();
+ return std::make_tuple(true, i_vpdFilePath);
+}
+
+void Worker::collectFrusFromJson()
+{
+ // A parsed JSON file should be present to pick FRUs EEPROM paths
+ if (m_parsedJson.empty())
+ {
+ throw std::runtime_error(
+ "A config JSON is required for processing of FRUs");
+ }
+
+ const nlohmann::json& listOfFrus =
+ m_parsedJson["frus"].get_ref<const nlohmann::json::object_t&>();
+
+ for (const auto& itemFRUS : listOfFrus.items())
+ {
+ const std::string& vpdFilePath = itemFRUS.key();
+
+ // skip processing of system VPD again as it has been already collected.
+ // Also, if chassis is powered on, skip collecting FRUs which are
+ // powerOffOnly.
+ // TODO: Need to revisit for P-Future to reduce code update time.
+ if (vpdFilePath == SYSTEM_VPD_FILE_PATH ||
+ (jsonUtility::isFruPowerOffOnly(m_parsedJson, vpdFilePath) &&
+ dbusUtility::isChassisPowerOn()))
+ {
+ continue;
+ }
+
+ std::thread{[vpdFilePath, this]() {
+ auto l_futureObject =
+ std::async(&Worker::parseAndPublishVPD, this, vpdFilePath);
+
+ std::tuple<bool, std::string> l_threadInfo = l_futureObject.get();
+
+ // thread returned.
+ m_mutex.lock();
+ m_activeCollectionThreadCount--;
+ m_mutex.unlock();
+
+ if (!m_activeCollectionThreadCount)
+ {
+ m_isAllFruCollected = true;
+ }
+ }}.detach();
+ }
+}
+
+// ToDo: Move the API under IBM_SYSTEM
+void Worker::performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap)
+{
+ try
+ {
+ std::string l_backupAndRestoreCfgFilePath =
+ m_parsedJson.value("backupRestoreConfigPath", "");
+
+ nlohmann::json l_backupAndRestoreCfgJsonObj =
+ jsonUtility::getParsedJson(l_backupAndRestoreCfgFilePath);
+
+ // check if either of "source" or "destination" has inventory path.
+ // this indicates that this sytem has System VPD on hardware
+ // and other copy on D-Bus (BMC cache).
+ if (!l_backupAndRestoreCfgJsonObj.empty() &&
+ ((l_backupAndRestoreCfgJsonObj.contains("source") &&
+ l_backupAndRestoreCfgJsonObj["source"].contains(
+ "inventoryPath")) ||
+ (l_backupAndRestoreCfgJsonObj.contains("destination") &&
+ l_backupAndRestoreCfgJsonObj["destination"].contains(
+ "inventoryPath"))))
+ {
+ BackupAndRestore l_backupAndRestoreObj(m_parsedJson);
+ auto [l_srcVpdVariant,
+ l_dstVpdVariant] = l_backupAndRestoreObj.backupAndRestore();
+
+ // ToDo: Revisit is this check is required or not.
+ if (auto l_srcVpdMap =
+ std::get_if<types::IPZVpdMap>(&l_srcVpdVariant);
+ l_srcVpdMap && !(*l_srcVpdMap).empty())
+ {
+ io_srcVpdMap = std::move(l_srcVpdVariant);
+ }
+ }
+ }
+ catch (const std::exception& l_ex)
+ {
+ EventLogger::createSyncPel(
+ types::ErrorType::InvalidVpdMessage,
+ types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
+ std::string(
+ "Exception caught while backup and restore VPD keyword's.") +
+ l_ex.what(),
+ std::nullopt, std::nullopt, std::nullopt, std::nullopt);
+ }
+}
+
+void Worker::deleteFruVpd(const std::string& i_dbusObjPath)
+{
+ if (i_dbusObjPath.empty())
+ {
+ throw std::runtime_error("Given DBus object path is empty.");
+ }
+
+ const std::string& l_fruPath =
+ jsonUtility::getFruPathFromJson(m_parsedJson, i_dbusObjPath);
+
+ try
+ {
+ auto l_presentPropValue = dbusUtility::readDbusProperty(
+ constants::pimServiceName, i_dbusObjPath,
+ constants::inventoryItemInf, "Present");
+
+ if (auto l_value = std::get_if<bool>(&l_presentPropValue))
+ {
+ if (!(*l_value))
+ {
+ throw std::runtime_error("Given FRU is not present");
+ }
+ else
+ {
+ if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
+ "preAction", "deletion"))
+ {
+ if (!processPreAction(l_fruPath, "deletion"))
+ {
+ throw std::runtime_error("Pre action failed");
+ }
+ }
+
+ std::vector<std::string> l_interfaceList{
+ constants::operationalStatusInf};
+
+ types::MapperGetSubTree l_subTreeMap =
+ dbusUtility::getObjectSubTree(i_dbusObjPath, 0,
+ l_interfaceList);
+
+ types::ObjectMap l_objectMap;
+
+ // Updates VPD specific interfaces property value under PIM for
+ // sub FRUs.
+ for (const auto& [l_objectPath, l_serviceInterfaceMap] :
+ l_subTreeMap)
+ {
+ types::InterfaceMap l_interfaceMap;
+ vpdSpecificUtility::resetDataUnderPIM(l_objectPath,
+ l_interfaceMap);
+ l_objectMap.emplace(l_objectPath,
+ std::move(l_interfaceMap));
+ }
+
+ types::InterfaceMap l_interfaceMap;
+ vpdSpecificUtility::resetDataUnderPIM(i_dbusObjPath,
+ l_interfaceMap);
+
+ l_objectMap.emplace(i_dbusObjPath, std::move(l_interfaceMap));
+
+ if (!dbusUtility::callPIM(std::move(l_objectMap)))
+ {
+ throw std::runtime_error("Call to PIM failed.");
+ }
+
+ if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
+ "postAction", "deletion"))
+ {
+ if (!processPostAction(l_fruPath, "deletion"))
+ {
+ throw std::runtime_error("Post action failed");
+ }
+ }
+ }
+ }
+ else
+ {
+ logging::logMessage(
+ "Can't process delete VPD for FRU [" + i_dbusObjPath +
+ "] as unable to read present property");
+ return;
+ }
+
+ logging::logMessage(
+ "Successfully completed deletion of FRU VPD for " + i_dbusObjPath);
+ }
+ catch (const std::exception& l_ex)
+ {
+ if (jsonUtility::isActionRequired(m_parsedJson, l_fruPath,
+ "postFailAction", "deletion"))
+ {
+ if (!jsonUtility::executePostFailAction(m_parsedJson, l_fruPath,
+ "deletion"))
+ {
+ logging::logMessage(
+ "Post fail action failed for: " + i_dbusObjPath);
+ }
+ }
+
+ logging::logMessage("Failed to delete VPD for FRU : " + i_dbusObjPath +
+ " error: " + std::string(l_ex.what()));
+ }
+}
+} // namespace vpd