vpd-tool mfgClean: implement sync BIOS attributes
This commit implements --syncBiosAttributes feature in vpd-tool
--mfgClean. When --syncBiosAttributes is selected along with --mfgClean,
the VPD keywords which are used for backing up BIOS attributes are
updated with the BIOS attribute values read from BIOS Config Manager
service.
Test:
```
- Install BMC image on rainier_2s2u.
- Reboot and wait for BMC to reach Ready state.
- Using vpd-tool, check the value of all the System VPD keywords used
for backing up BIOS attributes both on EEPROM and on D-Bus.
- Run vpd-tool --mfgClean --syncBiosAttributes/-s --yes
- Check return code
- Use pldmtool to check the current values of respective BIOS attributes
- Use vpd-tool, check the value of all the System VPD keywords used for
backing up BIOS attributes, both on EEPROM and on D-Bus.
- The VPD values should correspond with the BIOS attribute values.
- For all other keywords for which --mfgClean applies, the value on
EEPROM and D-Bus should be the default value from the respective
backup_restore JSON file.
```
Change-Id: I57c5df6ac0b3c4c91533d1ef22e69efcd4c87162
Signed-off-by: Souvik Roy <souvikroyofficial10@gmail.com>
diff --git a/vpd-tool/include/tool_constants.hpp b/vpd-tool/include/tool_constants.hpp
index 7fe0190..dbc4eb2 100644
--- a/vpd-tool/include/tool_constants.hpp
+++ b/vpd-tool/include/tool_constants.hpp
@@ -46,5 +46,20 @@
constexpr auto i2cDeviceInf =
"xyz.openbmc_project.Inventory.Decorator.I2CDevice";
constexpr auto vpdManagerProcessName = "vpd-manager";
+constexpr auto biosConfigMgrObjPath =
+ "/xyz/openbmc_project/bios_config/manager";
+constexpr auto biosConfigMgrInterface =
+ "xyz.openbmc_project.BIOSConfig.Manager";
+
+static constexpr auto VALUE_0 = 0;
+static constexpr auto VALUE_1 = 1;
+static constexpr auto VALUE_2 = 2;
+static constexpr auto VALUE_3 = 3;
+static constexpr auto VALUE_4 = 4;
+static constexpr auto VALUE_5 = 5;
+static constexpr auto VALUE_6 = 6;
+static constexpr auto VALUE_7 = 7;
+static constexpr auto VALUE_8 = 8;
+static constexpr auto VALUE_32 = 32;
} // namespace constants
} // namespace vpd
diff --git a/vpd-tool/include/tool_types.hpp b/vpd-tool/include/tool_types.hpp
index 86e942c..56088b1 100644
--- a/vpd-tool/include/tool_types.hpp
+++ b/vpd-tool/include/tool_types.hpp
@@ -4,6 +4,7 @@
#include <cstdint>
#include <tuple>
+#include <unordered_map>
#include <variant>
#include <vector>
@@ -82,5 +83,35 @@
SkipCurrent
};
+using BiosAttributeCurrentValue =
+ std::variant<std::monostate, int64_t, std::string>;
+using BiosAttributePendingValue = std::variant<int64_t, std::string>;
+using BiosGetAttrRetType = std::tuple<std::string, BiosAttributeCurrentValue,
+ BiosAttributePendingValue>;
+
+// VPD keyword to BIOS attribute map
+struct IpzKeyHash
+{
+ std::size_t operator()(const IpzType& i_key) const
+ {
+ return std::hash<std::string>()(std::get<0>(i_key)) ^ std::hash<std::string>()(std::get<1>(i_key));
+ }
+};
+
+struct IpzKeyEqual
+{
+ bool operator()(const IpzType& i_leftKey, const IpzType& i_rightKey) const
+ {
+ return std::get<0>(i_leftKey) == std::get<0>(i_rightKey) && std::get<1>(i_leftKey) == std::get<1>(i_rightKey);
+ }
+};
+
+// Bios attribute metadata container : {attribute name, number of bits, starting bit position, enabled value, disabled value}
+using BiosAttributeMetaData = std::tuple<std::string, uint8_t, std::optional<uint8_t>, std::optional<uint8_t>, std::optional<uint8_t>>;
+
+// IPZ keyword to BIOS attribute map
+//{Record, Keyword} -> {attribute name, number of bits in keyword, starting bit
+// position, enabled value, disabled value}
+using BiosAttributeKeywordMap = std::unordered_map<IpzType,std::vector<BiosAttributeMetaData>,IpzKeyHash,IpzKeyEqual>;
} // namespace types
} // namespace vpd
diff --git a/vpd-tool/include/tool_utils.hpp b/vpd-tool/include/tool_utils.hpp
index d4bde8a..16253ee 100644
--- a/vpd-tool/include/tool_utils.hpp
+++ b/vpd-tool/include/tool_utils.hpp
@@ -903,5 +903,98 @@
return l_retVal;
}
+/**
+ * @brief API to call "GetAttribute" method under BIOS Config Manager.
+ *
+ * The API reads the given attribute from BIOS Config Manager and returns a
+ * variant containing current value for that attribute if the value is found.
+ * API returns an empty variant of type BiosAttributeCurrentValue in case of any
+ * error.
+ *
+ * @param[in] i_attributeName - Attribute to be read.
+ *
+ * @return Tuple of PLDM attribute Type, current attribute value and pending
+ * attribute value.
+ */
+inline types::BiosAttributeCurrentValue biosGetAttributeMethodCall(
+ const std::string& i_attributeName) noexcept
+{
+ types::BiosGetAttrRetType l_attributeVal;
+
+ try
+ {
+ auto l_bus = sdbusplus::bus::new_default();
+ auto l_method = l_bus.new_method_call(
+ constants::biosConfigMgrService, constants::biosConfigMgrObjPath,
+ constants::biosConfigMgrInterface, "GetAttribute");
+ l_method.append(i_attributeName);
+
+ auto l_result = l_bus.call(l_method);
+ l_result.read(std::get<0>(l_attributeVal), std::get<1>(l_attributeVal),
+ std::get<2>(l_attributeVal));
+ }
+ catch (const sdbusplus::exception::SdBusError& l_ex)
+ {
+ // TODO : enable logging when verbose is implemented
+ std::cerr << "Failed to read BIOS Attribute: " + i_attributeName +
+ " due to error " + std::string(l_ex.what())
+ << std::endl;
+ }
+
+ return std::get<1>(l_attributeVal);
+}
+
+/**
+ * @brief Converts string to lower case.
+ *
+ * @param [in,out] io_string - Input string.
+ *
+ * @throw std::terminate, std::bad_alloc
+ */
+inline void toLower(std::string& io_string)
+{
+ std::transform(io_string.begin(), io_string.end(), io_string.begin(),
+ [](const unsigned char& l_char) {
+ return std::tolower(l_char);
+ });
+}
+
+/**
+ * @brief Converts an integral data value to a vector of bytes.
+ * The LSB of integer is copied to MSB of the vector.
+ *
+ * @param[in] i_integralData - Input integral data.
+ * @param[in] i_numBytesCopy - Number of bytes to copy.
+ *
+ * @return - On success, returns the Binary vector representation of the
+ * integral data, empty binary vector otherwise.
+ *
+ * @throw std::length_error
+ */
+template <typename T>
+ requires std::integral<T>
+inline types::BinaryVector convertIntegralTypeToBytes(
+ const T& i_integralData, size_t i_numBytesCopy = constants::VALUE_1)
+{
+ types::BinaryVector l_result;
+ constexpr auto l_byteMask{0xFF};
+
+ l_result.resize(i_numBytesCopy, constants::VALUE_0);
+
+ // sanitize number of bytes to copy
+ if (i_numBytesCopy > sizeof(T))
+ {
+ i_numBytesCopy = sizeof(T);
+ }
+
+ // LSB of source -> MSB of result
+ for (size_t l_byte = 0; l_byte < i_numBytesCopy; ++l_byte)
+ {
+ l_result[l_result.size() - (l_byte + constants::VALUE_1)] =
+ (i_integralData >> (l_byte * constants::VALUE_8)) & l_byteMask;
+ }
+ return l_result;
+}
+
} // namespace utils
} // namespace vpd
diff --git a/vpd-tool/include/vpd_tool.hpp b/vpd-tool/include/vpd_tool.hpp
index 0004949..ed2aa00 100644
--- a/vpd-tool/include/vpd_tool.hpp
+++ b/vpd-tool/include/vpd_tool.hpp
@@ -217,8 +217,19 @@
* @throw std::terminate, std::bad_alloc
*/
types::BinaryVector getVpdValueInBiosConfigManager(
- [[maybe_unused]] const std::string& i_recordName,
- [[maybe_unused]] const std::string& i_keywordName) const;
+ const std::string& i_recordName,
+ const std::string& i_keywordName) const;
+
+ /**
+ * @brief VPD keyword to BIOS attribute map
+ *
+ * This map specifies which VPD keyword is used to backup which BIOS
+ * attribute.
+ * {Record, Keyword} -> {attribute name, number of bits in keyword, starting
+ * bit position, enabled value, disabled value}
+ *
+ */
+ static const types::BiosAttributeKeywordMap m_biosAttributeVpdKeywordMap;
public:
/**
diff --git a/vpd-tool/src/vpd_tool.cpp b/vpd-tool/src/vpd_tool.cpp
index 9e2df42..7108942 100644
--- a/vpd-tool/src/vpd_tool.cpp
+++ b/vpd-tool/src/vpd_tool.cpp
@@ -12,6 +12,24 @@
#include <tuple>
namespace vpd
{
+// {Record, Keyword} -> {attribute name, number of bits in keyword, starting bit
+// position, enabled value, disabled value}
+// Note: we do not care about min/max value for the BIOS attribute here.
+const types::BiosAttributeKeywordMap VpdTool::m_biosAttributeVpdKeywordMap = {
+ {{"UTIL", "D0"},
+ {{"hb_memory_mirror_mode", constants::VALUE_8, std::nullopt,
+ constants::VALUE_2, constants::VALUE_1}}},
+ {{"UTIL", "D1"},
+ {{"pvm_keep_and_clear", constants::VALUE_1, constants::VALUE_0,
+ constants::VALUE_1, constants::VALUE_0},
+ {"pvm_create_default_lpar", constants::VALUE_1, constants::VALUE_1,
+ constants::VALUE_1, constants::VALUE_0},
+ {"pvm_clear_nvram", constants::VALUE_1, constants::VALUE_2,
+ constants::VALUE_1, constants::VALUE_0}}},
+ {{"VSYS", "RG"},
+ {{"hb_field_core_override", constants::VALUE_32, std::nullopt,
+ std::nullopt, std::nullopt}}}};
+
int VpdTool::readKeyword(
const std::string& i_vpdPath, const std::string& i_recordName,
const std::string& i_keywordName, const bool i_onHardware,
@@ -1428,13 +1446,93 @@
}
types::BinaryVector VpdTool::getVpdValueInBiosConfigManager(
- [[maybe_unused]] const std::string& i_recordName,
- [[maybe_unused]] const std::string& i_keywordName) const
+ const std::string& i_recordName, const std::string& i_keywordName) const
{
types::BinaryVector l_result;
- // TODO: Use Record name, Keyword name to identify BIOS attribute.
- // Get BIOS attribute value from BIOS Config Manager.
- // Convert BIOS attribute value to VPD format value in binary.
+ const auto l_itrToBiosAttributeKeywordMap =
+ m_biosAttributeVpdKeywordMap.find(
+ types::IpzType(i_recordName, i_keywordName));
+
+ if (l_itrToBiosAttributeKeywordMap != m_biosAttributeVpdKeywordMap.end())
+ {
+ const auto& l_biosAttributeList =
+ l_itrToBiosAttributeKeywordMap->second;
+ for (const auto& l_biosAttributeEntry : l_biosAttributeList)
+ {
+ // get the attribute name
+ const std::string l_attributeName =
+ std::get<0>(l_biosAttributeEntry);
+
+ // get the number of bits used to store the value in VPD
+ const size_t l_numBitsKeyword = std::get<1>(l_biosAttributeEntry);
+
+ auto l_attrValueVariant =
+ utils::biosGetAttributeMethodCall(l_attributeName);
+
+ if (auto l_attrVal = std::get_if<int64_t>(&l_attrValueVariant))
+ {
+ // multiple bytes update
+
+ size_t l_numBytesKeyword =
+ l_numBitsKeyword / constants::VALUE_8;
+
+ // convert to VPD format
+ l_result = utils::convertIntegralTypeToBytes(*l_attrVal,
+ l_numBytesKeyword);
+ }
+ else if (auto l_attrVal =
+ std::get_if<std::string>(&l_attrValueVariant))
+ {
+ utils::toLower(*l_attrVal);
+
+ // Since we are doing mfgClean, we do not
+ // care about reading the current VPD keyword value before
+ // writing to it.
+ if (l_numBitsKeyword == constants::VALUE_1)
+ {
+ // single bit update.
+
+ // get the bit position
+ const uint8_t l_bitPosition =
+ std::get<2>(l_biosAttributeEntry).has_value()
+ ? std::get<2>(l_biosAttributeEntry).value()
+ : constants::VALUE_0;
+
+ l_result.resize(constants::VALUE_1, constants::VALUE_0);
+
+ if (l_attrVal->compare("enabled") ==
+ constants::STR_CMP_SUCCESS)
+ {
+ l_result.at(constants::VALUE_0) |=
+ (constants::VALUE_1 << l_bitPosition);
+ }
+ else
+ {
+ l_result.at(constants::VALUE_0) &=
+ ~(constants::VALUE_1 << l_bitPosition);
+ }
+ }
+ else
+ {
+ // single byte update
+ const auto l_enabledValue =
+ std::get<3>(l_biosAttributeEntry).has_value()
+ ? std::get<3>(l_biosAttributeEntry).value()
+ : constants::VALUE_1;
+
+ const auto l_disabledValue =
+ std::get<4>(l_biosAttributeEntry).has_value()
+ ? std::get<4>(l_biosAttributeEntry).value()
+ : constants::VALUE_0;
+
+ l_result.emplace_back((l_attrVal->compare("enabled") ==
+ constants::STR_CMP_SUCCESS)
+ ? l_enabledValue
+ : l_disabledValue);
+ }
+ }
+ } // BIOS attribute loop end
+ }
return l_result;
}
} // namespace vpd