blob: 73dd2292d84a4dbbffb60c2a91587816597b4eb0 [file] [log] [blame] [edit]
#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
{
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<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))));
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::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 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::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(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)));
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::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)));
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::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)));
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::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