#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 #include #include namespace vpd { Manager::Manager( const std::shared_ptr& ioCon, const std::shared_ptr& iFace, const std::shared_ptr& progressiFace, const std::shared_ptr& asioConnection) : m_ioContext(ioCon), m_interface(iFace), m_progressInterface(progressiFace), 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(); }); iFace->register_method("CollectAllFRUVPD", [this]() -> bool { return this->collectAllFruVpd(); }); // Indicates FRU VPD collection for the system has not started. progressiFace->register_property_rw( "Status", sdbusplus::vtable::property_::emits_change, [this](const std::string& l_currStatus, const auto&) { if (m_vpdCollectionStatus != l_currStatus) { m_vpdCollectionStatus = l_currStatus; m_interface->signal_property("Status"); } return true; }, [this](const auto&) { return m_vpdCollectionStatus; }); // If required, instantiate OEM specific handler here. #ifdef IBM_SYSTEM m_ibmHandler = std::make_shared( m_worker, m_backupAndRestoreObj, m_interface, m_progressInterface, m_ioContext, m_asioConnection); #else m_worker = std::make_shared(INVENTORY_JSON_DEFAULT); m_progressInterface->set_property( "Status", std::string(constants::vpdCollectionCompleted)); #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; } uint16_t l_errCode = 0; 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, l_errCode); } } if (l_fruPath.empty()) { if (l_errCode) { logging::logMessage( "Failed to get FRU path from JSON for [" + i_vpdPath + "], error : " + commonUtility::getErrCodeMsg(l_errCode)); } l_fruPath = i_vpdPath; } try { std::shared_ptr l_parserObj = std::make_shared(l_fruPath, l_sysCfgJsonObj); types::DbusVariantType l_updatedValue; auto l_rc = l_parserObj->updateVpdKeyword(i_paramsToWriteData, l_updatedValue); 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 + "]"); } } types::WriteVpdParams l_writeParams; types::BinaryVector l_valueToUpdate; if (const types::IpzData* l_ipzData = std::get_if(&i_paramsToWriteData)) { if (const types::BinaryVector* l_val = std::get_if(&l_updatedValue)) { l_valueToUpdate = *l_val; } else { l_valueToUpdate = std::get<2>(*l_ipzData); } l_writeParams = std::make_tuple(std::get<0>(*l_ipzData), std::get<1>(*l_ipzData), l_valueToUpdate); } else if (const types::KwData* l_kwData = std::get_if(&i_paramsToWriteData)) { if (const types::BinaryVector* l_val = std::get_if(&l_updatedValue)) { l_valueToUpdate = *l_val; } else { l_valueToUpdate = std::get<1>(*l_kwData); } l_writeParams = std::make_tuple(std::get<0>(*l_kwData), l_valueToUpdate); } // update keyword in inherited FRUs if (l_rc != constants::FAILURE) { vpdSpecificUtility::updateKwdOnInheritedFrus( l_fruPath, l_writeParams, l_sysCfgJsonObj); } // update common interface(s) properties if (l_rc != constants::FAILURE) { vpdSpecificUtility::updateCiPropertyOfInheritedFrus( l_fruPath, l_writeParams, l_sysCfgJsonObj); } // log VPD write success or failure auto l_logger = Logger::getLoggerInstance(); uint16_t l_errorCode; l_logger->logMessage( "VPD write " + std::string( (l_rc != constants::FAILURE) ? "successful" : "failed") + " on path[" + i_vpdPath + "] : " + vpdSpecificUtility::convertWriteVpdParamsToString(l_writeParams, l_errorCode), PlaceHolder::VPD_WRITE); 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 l_parserObj = std::make_shared(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."); } std::shared_ptr l_parserObj = std::make_shared(i_fruPath, l_jsonObj); std::shared_ptr 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 != constants::vpdCollectionCompleted) { 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::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(); 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(dbusUtility::readDbusProperty( l_aFru["serviceName"], l_aFru["inventoryPath"], constants::locationCodeInf, "LocationCode")); } } } phosphor::logging::elog( 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::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(); 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()); } } } if (l_inventoryPaths.empty()) { phosphor::logging::elog( 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 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::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(&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::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::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(&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::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 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(); } } bool Manager::collectAllFruVpd() const noexcept { try { types::SeverityType l_severityType; if (m_vpdCollectionStatus == constants::vpdCollectionNotStarted) { l_severityType = types::SeverityType::Informational; } else if (m_vpdCollectionStatus == constants::vpdCollectionCompleted || m_vpdCollectionStatus == constants::vpdCollectionFailed) { l_severityType = types::SeverityType::Warning; } else { throw std::runtime_error( "Invalid collection status " + m_vpdCollectionStatus + ". Aborting all FRUs VPD collection."); } EventLogger::createSyncPel( types::ErrorType::FirmwareError, l_severityType, __FILE__, __FUNCTION__, 0, "Collect all FRUs VPD is requested.", std::nullopt, std::nullopt, std::nullopt, std::nullopt); // ToDo: Handle with OEM interface #ifdef IBM_SYSTEM if (m_ibmHandler.get() != nullptr) { m_ibmHandler->collectAllFruVpd(); return true; } else { throw std::runtime_error( "Not found any OEM handler to collect all FRUs VPD."); } #endif } catch (const std::exception& l_ex) { EventLogger::createSyncPel( EventLogger::getErrorType(l_ex), types::SeverityType::Warning, __FILE__, __FUNCTION__, 0, std::string(l_ex.what()), std::nullopt, std::nullopt, std::nullopt, std::nullopt); } return false; } } // namespace vpd