Restore system VPD if EEPROM data is blank.
This commit implement changes which enables restoring system VPD.
If at the time of VPD parsing for backplane it is found that data
related to system VPD is blank on EEPROM but available on cache,
then the data from Dbus will be used to restore data in EEPROM.
There are another scenarios in restoring the system VPD but this
commit only implement changes to restore system VPD when the data
is available on cache but blank on EEPROM.
This commit also implements creation and logging of PEL in case blank
system VPD is found both on EEPROM and Dbus in the process of
system VPD restore.
Meson Build: OK.
Tested on Simics: OK.
Signed-off-by: Sunny Srivastava <sunnsr25@in.ibm.com>
Change-Id: I32a872b3c3a74b79a9b8173c712b50f72fd7588c
diff --git a/const.hpp b/const.hpp
index 75fc63f..9eafc4b 100644
--- a/const.hpp
+++ b/const.hpp
@@ -82,6 +82,8 @@
constexpr auto errIntfForEccCheckFail = "com.ibm.VPD.Error.EccCheckFailed";
constexpr auto errIntfForJsonFailure = "com.ibm.VPD.Error.InvalidJson";
constexpr auto errIntfForBusFailure = "com.ibm.VPD.Error.DbusFailure";
+constexpr auto motherBoardInterface =
+ "xyz.openbmc_project.Inventory.Item.Board.Motherboard";
namespace lengths
{
diff --git a/ibm_vpd_app.cpp b/ibm_vpd_app.cpp
index b09ed79..f723613 100644
--- a/ibm_vpd_app.cpp
+++ b/ibm_vpd_app.cpp
@@ -8,6 +8,7 @@
#include "utils.hpp"
#include "vpd_exceptions.hpp"
+#include <assert.h>
#include <ctype.h>
#include <CLI/CLI.hpp>
@@ -580,6 +581,154 @@
}
/**
+ * @brief API to call VPD manager to write VPD to EEPROM.
+ * @param[in] Object path.
+ * @param[in] record to be updated.
+ * @param[in] keyword to be updated.
+ * @param[in] keyword data to be updated
+ */
+void updateHardware(const string& objectName, const string& recName,
+ const string& kwdName, const Binary& data)
+{
+ try
+ {
+ auto bus = sdbusplus::bus::new_default();
+ auto properties =
+ bus.new_method_call(BUSNAME, OBJPATH, IFACE, "WriteKeyword");
+ properties.append(
+ static_cast<sdbusplus::message::object_path>(objectName));
+ properties.append(recName);
+ properties.append(kwdName);
+ properties.append(data);
+ bus.call(properties);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ std::string what =
+ "VPDManager WriteKeyword api failed for inventory path " +
+ objectName;
+ what += " record " + recName;
+ what += " keyword " + kwdName;
+ what += " with bus error = " + std::string(e.what());
+
+ // map to hold additional data in case of logging pel
+ PelAdditionalData additionalData{};
+ additionalData.emplace("CALLOUT_INVENTORY_PATH", objectName);
+ additionalData.emplace("DESCRIPTION", what);
+ createPEL(additionalData, errIntfForBusFailure);
+ }
+}
+
+/**
+ * @brief API to check if we need to restore system VPD
+ * This functionality is only applicable for IPZ VPD data.
+ * @param[in] vpdMap - IPZ vpd map
+ * @param[in] objectPath - Object path for the FRU
+ * @return EEPROMs with records and keywords updated at standby
+ */
+std::vector<RestoredEeproms> restoreSystemVPD(Parsed& vpdMap,
+ const string& objectPath)
+{
+ // the list of keywords for VSYS record is as per the S0 system. Should be
+ // updated for another type of systems
+ static std::unordered_map<std::string, std::vector<std::string>> svpdKwdMap{
+ {"VSYS", {"BR", "TM", "SE", "SU", "RB"}},
+ {"VCEN", {"FC", "SE"}},
+ {"LXR0", {"LX"}}};
+
+ // vector to hold all the EEPROMs updated at standby
+ std::vector<RestoredEeproms> updatedEeproms = {};
+
+ 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& keyword : kwdListForRecord)
+ {
+ 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);
+
+ if (busValue.find_first_not_of(' ') != string::npos)
+ {
+ if (kwdValue.find_first_not_of(' ') != string::npos)
+ {
+ // both the data are present, check for mismatch
+ if (busValue != kwdValue)
+ {
+ string errMsg = "VPD data mismatch on cache "
+ "and hardware for record: ";
+ errMsg += (*it).first;
+ errMsg += " and keyword: ";
+ errMsg += keyword;
+
+ // data mismatch
+ PelAdditionalData additionalData;
+ additionalData.emplace("CALLOUT_INVENTORY_PATH",
+ objectPath);
+
+ additionalData.emplace("DESCRIPTION", errMsg);
+
+ createPEL(additionalData, errIntfForInvalidVPD);
+ }
+ }
+ else
+ {
+ // implies hardware data is blank
+ // update the map
+ Binary busData(busValue.begin(), busValue.end());
+
+ updatedEeproms.push_back(std::make_tuple(
+ objectPath, recordName, keyword, busData));
+ }
+
+ // update the map as well, so that cache data is not
+ // updated as blank while populating VPD map on Dbus in
+ // populateDBus Api
+ kwdValue = busValue;
+ continue;
+ }
+ else if (kwdValue.find_first_not_of(' ') == string::npos)
+ {
+ string errMsg = "VPD is blank on both cache and "
+ "hardware for record: ";
+ errMsg += (*it).first;
+ errMsg += " and keyword: ";
+ errMsg += keyword;
+ errMsg += ". SSR need to update hardware VPD.";
+
+ // both the data are blanks, log PEL
+ PelAdditionalData additionalData;
+ additionalData.emplace("CALLOUT_INVENTORY_PATH",
+ objectPath);
+
+ additionalData.emplace("DESCRIPTION", errMsg);
+
+ // log PEL TODO: Block IPL
+ createPEL(additionalData, errIntfForBlankSystemVPD);
+ continue;
+ }
+ }
+ }
+ }
+ }
+
+ return updatedEeproms;
+}
+
+/**
* @brief Populate Dbus.
* This method invokes all the populateInterface functions
* and notifies PIM about dbus object.
@@ -590,19 +739,22 @@
* @param[in] preIntrStr - Interface string
*/
template <typename T>
-static void populateDbus(const T& vpdMap, nlohmann::json& js,
- const string& filePath)
+static void populateDbus(T& vpdMap, nlohmann::json& js, const string& filePath)
{
inventory::InterfaceMap interfaces;
inventory::ObjectMap objects;
inventory::PropertyMap prop;
+ // map to hold all the keywords whose value has been changed at standby
+ vector<RestoredEeproms> updatedEeproms = {};
+
bool isSystemVpd = false;
for (const auto& item : js["frus"][filePath])
{
const auto& objectPath = item["inventoryPath"];
sdbusplus::message::object_path object(objectPath);
isSystemVpd = item.value("isSystemVpd", false);
+
// Populate the VPD keywords and the common interfaces only if we
// are asked to inherit that data from the VPD, else only add the
// extraInterfaces.
@@ -610,6 +762,30 @@
{
if constexpr (is_same<T, Parsed>::value)
{
+ if (isSystemVpd)
+ {
+ std::vector<std::string> interfaces = {
+ motherBoardInterface};
+ // call mapper to check for object path creation
+ MapperResponse subTree =
+ getObjectSubtreeForInterfaces(pimPath, 0, interfaces);
+
+ // Skip system vpd restore if object path is not generated
+ // for motherboard, Implies first boot.
+ if (subTree.size() != 0)
+ {
+ assert(
+ (subTree.find(pimPath + std::string(objectPath)) !=
+ subTree.end()));
+
+ updatedEeproms = restoreSystemVPD(vpdMap, objectPath);
+ }
+ else
+ {
+ log<level::ERR>("No object path found");
+ }
+ }
+
// Each record in the VPD becomes an interface and all
// keyword within the record are properties under that
// interface.
@@ -649,7 +825,6 @@
}
}
}
-
if (item.value("inheritEI", true))
{
// Populate interfaces and properties that are common to every FRU
@@ -721,6 +896,13 @@
// set the U-boot environment variable for device-tree
setDevTreeEnv(imValStr);
+
+ // if system VPD has been restored at standby, update the EEPROM
+ for (const auto& item : updatedEeproms)
+ {
+ updateHardware(get<0>(item), get<1>(item), get<2>(item),
+ get<3>(item));
+ }
}
// Notify PIM
diff --git a/store.hpp b/store.hpp
index a8326a7..6cf1a01 100644
--- a/store.hpp
+++ b/store.hpp
@@ -46,7 +46,7 @@
*
* @returns VPD as a Parsed object
*/
- inline const Parsed& getVpdMap() const
+ inline Parsed& getVpdMap()
{
return vpd;
}
diff --git a/types.hpp b/types.hpp
index 5f24bd0..c297bf5 100644
--- a/types.hpp
+++ b/types.hpp
@@ -40,15 +40,20 @@
using ListOfPaths = std::vector<sdbusplus::message::object_path>;
using NodeNumber = uint16_t;
using namespace std::string_literals;
-constexpr auto pimPath = "/xyz/openbmc_project/inventory";
-constexpr auto pimIntf = "xyz.openbmc_project.Inventory.Manager";
using KeywordVpdMap = std::unordered_map<std::string, Binary>;
using systemType = std::string;
using deviceTree = std::string;
using deviceTreeMap = std::unordered_map<systemType, deviceTree>;
using PelAdditionalData = std::map<std::string, std::string>;
-
+using Keyword = std::string;
+using KeywordData = std::string;
+using DbusPropertyMap = std::unordered_map<Keyword, KeywordData>;
+using PelAdditionalData = std::map<std::string, std::string>;
+using Service = std::string;
+using MapperResponse =
+ std::map<Path, std::map<Service, std::vector<Interface>>>;
+using RestoredEeproms = std::tuple<Path, std::string, Keyword, Binary>;
} // namespace inventory
} // namespace vpd
diff --git a/utils.cpp b/utils.cpp
index 8b33305..b9d07e4 100644
--- a/utils.cpp
+++ b/utils.cpp
@@ -4,8 +4,10 @@
#include "defines.hpp"
+#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/log.hpp>
#include <sdbusplus/server.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
namespace openpower
{
@@ -14,6 +16,7 @@
using namespace openpower::vpd::constants;
using namespace inventory;
using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
namespace inventory
{
@@ -66,11 +69,38 @@
}
catch (const std::runtime_error& e)
{
- using namespace phosphor::logging;
log<level::ERR>(e.what());
}
}
+MapperResponse
+ getObjectSubtreeForInterfaces(const std::string& root, const int32_t depth,
+ const std::vector<std::string>& interfaces)
+{
+ auto bus = sdbusplus::bus::new_default();
+ auto mapperCall = bus.new_method_call(mapperDestination, mapperObjectPath,
+ mapperInterface, "GetSubTree");
+ mapperCall.append(root);
+ mapperCall.append(depth);
+ mapperCall.append(interfaces);
+
+ MapperResponse result = {};
+
+ try
+ {
+ auto response = bus.call(mapperCall);
+
+ response.read(result);
+ }
+ catch (const sdbusplus::exception::SdBusError& e)
+ {
+ log<level::ERR>("Error in mapper GetSubTree",
+ entry("ERROR=%s", e.what()));
+ }
+
+ return result;
+}
+
} // namespace inventory
vpdType vpdTypeCheck(const Binary& vpdVector)
@@ -186,7 +216,6 @@
try
{
auto bus = sdbusplus::bus::new_default();
-
auto service = getService(bus, loggerObjectPath, loggerCreateInterface);
auto method = bus.new_method_call(service.c_str(), loggerObjectPath,
loggerCreateInterface, "Create");
diff --git a/utils.hpp b/utils.hpp
index d531842..2c748ee 100644
--- a/utils.hpp
+++ b/utils.hpp
@@ -6,6 +6,7 @@
#include <iostream>
using namespace std;
+
namespace openpower
{
namespace vpd
@@ -45,9 +46,14 @@
namespace inventory
{
-/** @brief Get inventory-manager's d-bus service
+/** @brief Api to Get d-bus service for given interface
+ * @param[in] - Bus object
+ * @param[in] - object path of the service
+ * @param[in] - interface under the object path
+ * @return service name
*/
-auto getPIMService();
+std::string getService(sdbusplus::bus::bus& bus, const std::string& path,
+ const std::string& interface);
/** @brief Call inventory-manager to add objects
*
@@ -55,6 +61,20 @@
*/
void callPIM(ObjectMap&& objects);
+/** @brief Api to obtain a dictionary of path -> services
+ * where path is in subtree and services is of the type
+ * returned by the GetObject method.
+ *
+ * @param [in] root - Root path for object subtree
+ * @param [in] depth - Maximum subtree depth required
+ * @param [in] interfaces - Array to interfaces for which
+ * result is required.
+ * @return A dictionary of Path -> services
+ */
+MapperResponse
+ getObjectSubtreeForInterfaces(const std::string& root, const int32_t depth,
+ const std::vector<std::string>& interfaces);
+
} // namespace inventory
/**@brief This API reads 2 Bytes of data and swap the read data
@@ -87,5 +107,6 @@
*/
void createPEL(const std::map<std::string, std::string>& additionalData,
const std::string& errIntf);
+
} // namespace vpd
} // namespace openpower