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:
     /**