blob: 8341a9e04a62229b2fbcb6a60b03191558378d90 [file] [log] [blame]
#include "config.h"
#include "manager.hpp"
#include "constants.hpp"
#include "exceptions.hpp"
#include "logger.hpp"
#include "parser.hpp"
#include "parser_factory.hpp"
#include "parser_interface.hpp"
#include "single_fab.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)
{
#ifdef IBM_SYSTEM
if (!dbusUtility::isChassisPowerOn())
{
SingleFab l_singleFab;
const int& l_rc = l_singleFab.singleFabImOverride();
if (l_rc == constants::FAILURE)
{
throw std::runtime_error(
std::string(__FUNCTION__) +
" : Found an invalid system configuration. Needs manual intervention. BMC is being quiesced.");
}
}
#endif
try
{
// 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; });
// If required, instantiate OEM specific handler here.
#ifdef IBM_SYSTEM
m_ibmHandler = std::make_shared<IbmHandler>(
m_worker, m_backupAndRestoreObj, m_interface, m_ioContext,
m_asioConnection);
#else
m_worker = std::make_shared<Worker>(INVENTORY_JSON_DEFAULT);
m_interface->set_property("CollectionStatus", std::string("Completed"));
#endif
}
catch (const std::exception& e)
{
logging::logMessage(
"Manager class instantiation failed. " + std::string(e.what()));
vpd::EventLogger::createSyncPel(
vpd::EventLogger::getErrorType(e), vpd::types::SeverityType::Error,
__FILE__, __FUNCTION__, 0, vpd::EventLogger::getErrorMsg(e),
std::nullopt, std::nullopt, std::nullopt, std::nullopt);
}
}
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())
{
l_fruPath =
jsonUtility::getFruPathFromJson(l_sysCfgJsonObj, i_vpdPath);
}
}
if (l_fruPath.empty())
{
l_fruPath = i_vpdPath;
}
try
{
std::shared_ptr<Parser> l_parserObj =
std::make_shared<Parser>(l_fruPath, l_sysCfgJsonObj);
auto l_rc = l_parserObj->updateVpdKeyword(i_paramsToWriteData);
if (l_rc != constants::FAILURE && m_backupAndRestoreObj)
{
if (m_backupAndRestoreObj->updateKeywordOnPrimaryOrBackupPath(
l_fruPath, i_paramsToWriteData) < constants::VALUE_0)
{
logging::logMessage(
"Write success, but backup and restore failed for file[" +
l_fruPath + "]");
}
}
// update keyword in inherited FRUs
if (l_rc != constants::FAILURE)
{
vpdSpecificUtility::updateKwdOnInheritedFrus(
l_fruPath, i_paramsToWriteData, l_sysCfgJsonObj);
}
// update common interface(s) properties
if (l_rc != constants::FAILURE)
{
vpdSpecificUtility::updateCiPropertyOfInheritedFrus(
l_fruPath, i_paramsToWriteData, l_sysCfgJsonObj);
}
return l_rc;
}
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)
{
if (m_vpdCollectionStatus != "Completed")
{
logging::logMessage(
"Currently VPD CollectionStatus is not completed. Cannot perform single FRU VPD collection for " +
std::string(i_dbusObjPath));
return;
}
if (m_worker.get() != nullptr)
{
m_worker->collectSingleFruVpd(i_dbusObjPath);
}
}
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::performVpdRecollection()
{
if (m_worker.get() != nullptr)
{
m_worker->performVpdRecollection();
}
}
} // namespace vpd