#include "config.h" #include "bios_handler.hpp" #include "constants.hpp" #include "logger.hpp" #include #include #include #include namespace vpd { // Template declaration to define APIs. template class BiosHandler; template void BiosHandler::checkAndListenPldmService() { // Setup a call back match on NameOwnerChanged to determine when PLDM is // up. static std::shared_ptr l_nameOwnerMatch = std::make_shared( *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 void BiosHandler::listenBiosAttributes() { static std::shared_ptr l_biosMatch = std::make_shared( *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>( &(l_property.second))) { for (const auto& l_attribute : *l_attributeList) { if (auto l_val = std::get_if( &(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( &(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 { logging::logMessage("Invalid type received for BIOS table."); EventLogger::createSyncPel( types::ErrorType::FirmwareError, types::SeverityType::Warning, __FILE__, __FUNCTION__, 0, std::string("Invalid type received for BIOS table."), std::nullopt, std::nullopt, std::nullopt, std::nullopt); 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(&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(&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(&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(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(constants::vsysInf, 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)))); if (!dbusUtility::writeDbusProperty( constants::biosConfigMgrService, constants::biosConfigMgrObjPath, constants::biosConfigMgrInterface, "PendingAttributes", l_pendingBiosAttribute)) { // TODO: Should we log informational PEL here as well? logging::logMessage( "DBus call to update FCO value in pending attribute failed. "); } } 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::vsysInf, constants::kwdAMM); if (auto l_pVal = std::get_if(&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(constants::vsysInf, 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 uint8_t& i_ammVal) { const std::string l_valtoUpdate = (i_ammVal == 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))); if (!dbusUtility::writeDbusProperty( constants::biosConfigMgrService, constants::biosConfigMgrObjPath, constants::biosConfigMgrInterface, "PendingAttributes", l_pendingBiosAttribute)) { // TODO: Should we log informational PEL here as well? logging::logMessage( "DBus call to update AMM value in pending attribute failed."); } } void IbmBiosHandler::processActiveMemoryMirror() { auto l_kwdValueVariant = dbusUtility::readDbusProperty( constants::pimServiceName, constants::systemVpdInvPath, constants::vsysInf, constants::kwdAMM); if (auto pVal = std::get_if(&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(&l_attrValueVariant)) { saveAmmToVpd(*pVal); return; } logging::logMessage( "Invalid type recieved for auto memory mirror mode from BIOS."); return; } else { saveAmmToBios(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::vsysInf, constants::kwdClearNVRAM_CreateLPAR); if (auto l_pVal = std::get_if(&l_kwdValueVariant)) { commonUtility::toLower( const_cast(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(constants::vsysInf, 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))); if (!dbusUtility::writeDbusProperty( constants::biosConfigMgrService, constants::biosConfigMgrObjPath, constants::biosConfigMgrInterface, "PendingAttributes", l_pendingBiosAttribute)) { logging::logMessage( "DBus call to update lpar value in pending attribute failed."); } return; } void IbmBiosHandler::processCreateDefaultLpar() { // Read required keyword from DBus. auto l_kwdValueVariant = dbusUtility::readDbusProperty( constants::pimServiceName, constants::systemVpdInvPath, constants::vsysInf, constants::kwdClearNVRAM_CreateLPAR); if (auto l_pVal = std::get_if(&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::vsysInf, constants::kwdClearNVRAM_CreateLPAR); if (auto l_pVal = std::get_if(&l_kwdValueVariant)) { commonUtility::toLower(const_cast(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(constants::vsysInf, 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))); if (!dbusUtility::writeDbusProperty( constants::biosConfigMgrService, constants::biosConfigMgrObjPath, constants::biosConfigMgrInterface, "PendingAttributes", l_pendingBiosAttribute)) { logging::logMessage( "DBus call to update NVRAM value in pending attribute failed."); } } void IbmBiosHandler::processClearNvram() { // Read required keyword from VPD. auto l_kwdValueVariant = dbusUtility::readDbusProperty( constants::pimServiceName, constants::systemVpdInvPath, constants::vsysInf, constants::kwdClearNVRAM_CreateLPAR); if (auto l_pVal = std::get_if(&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::vsysInf, constants::kwdKeepAndClear); if (auto l_pVal = std::get_if(&l_kwdValueVariant)) { commonUtility::toLower(const_cast(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(constants::vsysInf, 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))); if (!dbusUtility::writeDbusProperty( constants::biosConfigMgrService, constants::biosConfigMgrObjPath, constants::biosConfigMgrInterface, "PendingAttributes", l_pendingBiosAttribute)) { logging::logMessage( "DBus call to update keep and clear value in pending attribute failed."); } } void IbmBiosHandler::processKeepAndClear() { // Read required keyword from VPD. auto l_kwdValueVariant = dbusUtility::readDbusProperty( constants::pimServiceName, constants::systemVpdInvPath, constants::vsysInf, constants::kwdKeepAndClear); if (auto l_pVal = std::get_if(&l_kwdValueVariant)) { saveKeepAndClearToBios(std::to_string(l_pVal->at(0))); return; } logging::logMessage("Invalid type recieved for keep and clear from VPD."); } } // namespace vpd