bios: Implement BIOSEnumAttribute

Implement BIOSEnumAttribute, most of the code is copied from
bios/bios_parser.cpp.

Implement SetAttrValueOnDbus and constructEntry for enum attribute

Signed-off-by: John Wang <wangzqbj@inspur.com>
Change-Id: Ia6230485fd2d6d05f9f2ddb3a80b81f6556aee9f
diff --git a/libpldm/bios_table.c b/libpldm/bios_table.c
index 8c04f36..4af4661 100644
--- a/libpldm/bios_table.c
+++ b/libpldm/bios_table.c
@@ -625,7 +625,7 @@
 
 void pldm_bios_table_attr_value_entry_encode_enum(
     void *entry, size_t entry_length, uint16_t attr_handle, uint8_t attr_type,
-    uint8_t count, uint8_t *handles)
+    uint8_t count, const uint8_t *handles)
 {
 	size_t length =
 	    pldm_bios_table_attr_value_entry_encode_enum_length(count);
diff --git a/libpldm/bios_table.h b/libpldm/bios_table.h
index d0e06a4..66d620f 100644
--- a/libpldm/bios_table.h
+++ b/libpldm/bios_table.h
@@ -509,7 +509,7 @@
  */
 void pldm_bios_table_attr_value_entry_encode_enum(
     void *entry, size_t entry_length, uint16_t attr_handle, uint8_t attr_type,
-    uint8_t count, uint8_t *handle_indexes);
+    uint8_t count, const uint8_t *handle_indexes);
 
 /** @brief Get number of current values for the enum entry
  *  @param[in] entry - Pointer to bios attribute value table entry
diff --git a/libpldmresponder/bios_config.cpp b/libpldmresponder/bios_config.cpp
index 90b2ad2..e742ee6 100644
--- a/libpldmresponder/bios_config.cpp
+++ b/libpldmresponder/bios_config.cpp
@@ -1,5 +1,6 @@
 #include "bios_config.hpp"
 
+#include "bios_enum_attribute.hpp"
 #include "bios_integer_attribute.hpp"
 #include "bios_string_attribute.hpp"
 
@@ -66,9 +67,12 @@
     load(jsonDir / stringJsonFile, [this](const Json& entry) {
         constructAttribute<BIOSStringAttribute>(entry);
     });
-    load(jsonDir / stringJsonFile, [this](const Json& entry) {
+    load(jsonDir / integerJsonFile, [this](const Json& entry) {
         constructAttribute<BIOSIntegerAttribute>(entry);
     });
+    load(jsonDir / enumJsonFile, [this](const Json& entry) {
+        constructAttribute<BIOSEnumAttribute>(entry);
+    });
 }
 
 void BIOSConfig::buildAndStoreAttrTables(const Table& stringTable)
diff --git a/libpldmresponder/bios_enum_attribute.cpp b/libpldmresponder/bios_enum_attribute.cpp
new file mode 100644
index 0000000..7d7cba4
--- /dev/null
+++ b/libpldmresponder/bios_enum_attribute.cpp
@@ -0,0 +1,204 @@
+#include "bios_enum_attribute.hpp"
+
+#include "utils.hpp"
+
+#include <iostream>
+
+namespace pldm
+{
+namespace responder
+{
+namespace bios
+{
+
+BIOSEnumAttribute::BIOSEnumAttribute(const Json& entry,
+                                     DBusHandler* const dbusHandler) :
+    BIOSAttribute(entry, dbusHandler)
+{
+    std::string attrName = entry.at("attribute_name");
+    Json pv = entry.at("possible_values");
+    for (auto& val : pv)
+    {
+        possibleValues.emplace_back(val);
+    }
+
+    std::vector<std::string> defaultValues;
+    Json dv = entry.at("default_values");
+    for (auto& val : dv)
+    {
+        defaultValues.emplace_back(val);
+    }
+    assert(defaultValues.size() == 1);
+    defaultValue = defaultValues[0];
+    if (!readOnly)
+    {
+        auto dbusValues = entry.at("dbus").at("property_values");
+        buildValMap(dbusValues);
+    }
+}
+
+uint8_t BIOSEnumAttribute::getValueIndex(const std::string& value,
+                                         const std::vector<std::string>& pVs)
+{
+    auto iter = std::find_if(pVs.begin(), pVs.end(),
+                             [&value](const auto& v) { return v == value; });
+    if (iter == pVs.end())
+    {
+        throw std::invalid_argument("value must be one of possible value");
+    }
+    return iter - pVs.begin();
+}
+
+std::vector<uint16_t> BIOSEnumAttribute::getPossibleValuesHandle(
+    const BIOSStringTable& stringTable, const std::vector<std::string>& pVs)
+{
+    std::vector<uint16_t> possibleValuesHandle;
+    for (const auto& pv : pVs)
+    {
+        auto handle = stringTable.findHandle(pv);
+        possibleValuesHandle.push_back(handle);
+    }
+
+    return possibleValuesHandle;
+}
+
+void BIOSEnumAttribute::buildValMap(const Json& dbusVals)
+{
+    PropertyValue value;
+    size_t pos = 0;
+    for (auto it = dbusVals.begin(); it != dbusVals.end(); ++it, ++pos)
+    {
+        if (dBusMap->propertyType == "uint8_t")
+        {
+            value = static_cast<uint8_t>(it.value());
+        }
+        else if (dBusMap->propertyType == "uint16_t")
+        {
+            value = static_cast<uint16_t>(it.value());
+        }
+        else if (dBusMap->propertyType == "uint32_t")
+        {
+            value = static_cast<uint32_t>(it.value());
+        }
+        else if (dBusMap->propertyType == "uint64_t")
+        {
+            value = static_cast<uint64_t>(it.value());
+        }
+        else if (dBusMap->propertyType == "int16_t")
+        {
+            value = static_cast<int16_t>(it.value());
+        }
+        else if (dBusMap->propertyType == "int32_t")
+        {
+            value = static_cast<int32_t>(it.value());
+        }
+        else if (dBusMap->propertyType == "int64_t")
+        {
+            value = static_cast<int64_t>(it.value());
+        }
+        else if (dBusMap->propertyType == "bool")
+        {
+            value = static_cast<bool>(it.value());
+        }
+        else if (dBusMap->propertyType == "double")
+        {
+            value = static_cast<double>(it.value());
+        }
+        else if (dBusMap->propertyType == "string")
+        {
+            value = static_cast<std::string>(it.value());
+        }
+        else
+        {
+            std::cerr << "Unknown D-Bus property type, TYPE="
+                      << dBusMap->propertyType << "\n";
+            throw std::invalid_argument("Unknown D-BUS property type");
+        }
+        valMap.emplace(value, possibleValues[pos]);
+    }
+}
+
+uint8_t BIOSEnumAttribute::getAttrValueIndex()
+{
+    auto defaultValueIndex = getValueIndex(defaultValue, possibleValues);
+    if (readOnly)
+    {
+        return defaultValueIndex;
+    }
+
+    try
+    {
+        auto propValue = dbusHandler->getDbusPropertyVariant(
+            dBusMap->objectPath.c_str(), dBusMap->propertyName.c_str(),
+            dBusMap->interface.c_str());
+        auto iter = valMap.find(propValue);
+        if (iter == valMap.end())
+        {
+            return defaultValueIndex;
+        }
+        auto currentValue = iter->second;
+        return getValueIndex(currentValue, possibleValues);
+    }
+    catch (const std::exception& e)
+    {
+        return defaultValueIndex;
+    }
+}
+
+void BIOSEnumAttribute::setAttrValueOnDbus(
+    const pldm_bios_attr_val_table_entry* attrValueEntry,
+    const pldm_bios_attr_table_entry* attrEntry,
+    const BIOSStringTable& stringTable)
+{
+    if (readOnly)
+    {
+        return;
+    }
+    auto [pvHdls, _] = table::attribute::decodeEnumEntry(attrEntry);
+    auto currHdls = table::attribute_value::decodeEnumEntry(attrValueEntry);
+
+    assert(currHdls.size() == 1);
+
+    auto valueString = stringTable.findString(pvHdls[currHdls[0]]);
+
+    auto it = std::find_if(valMap.begin(), valMap.end(),
+                           [&valueString](const auto& typePair) {
+                               return typePair.second == valueString;
+                           });
+    if (it == valMap.end())
+    {
+        return;
+    }
+
+    dbusHandler->setDbusProperty(*dBusMap, it->first);
+}
+
+void BIOSEnumAttribute::constructEntry(const BIOSStringTable& stringTable,
+                                       Table& attrTable, Table& attrValueTable)
+{
+    auto possibleValuesHandle =
+        getPossibleValuesHandle(stringTable, possibleValues);
+    std::vector<uint8_t> defaultIndices(1, 0);
+    defaultIndices[0] = getValueIndex(defaultValue, possibleValues);
+
+    pldm_bios_table_attr_entry_enum_info info = {
+        stringTable.findHandle(name),         readOnly,
+        (uint8_t)possibleValuesHandle.size(), possibleValuesHandle.data(),
+        (uint8_t)defaultIndices.size(),       defaultIndices.data(),
+    };
+
+    auto attrTableEntry =
+        table::attribute::constructEnumEntry(attrTable, &info);
+    auto [attrHandle, attrType, _] =
+        table::attribute::decodeHeader(attrTableEntry);
+
+    std::vector<uint8_t> currValueIndices(1, 0);
+    currValueIndices[0] = getAttrValueIndex();
+
+    table::attribute_value::constructEnumEntry(attrValueTable, attrHandle,
+                                               attrType, currValueIndices);
+}
+
+} // namespace bios
+} // namespace responder
+} // namespace pldm
\ No newline at end of file
diff --git a/libpldmresponder/bios_enum_attribute.hpp b/libpldmresponder/bios_enum_attribute.hpp
new file mode 100644
index 0000000..2d8667a
--- /dev/null
+++ b/libpldmresponder/bios_enum_attribute.hpp
@@ -0,0 +1,93 @@
+#pragma once
+
+#include "bios_attribute.hpp"
+
+#include <map>
+#include <set>
+#include <string>
+#include <variant>
+
+class TestBIOSEnumAttribute;
+
+namespace pldm
+{
+namespace responder
+{
+namespace bios
+{
+
+/** @class BIOSEnumAttribute
+ *  @brief Associate enum entry(attr table and attribute value table) and
+ *         dbus attribute
+ */
+class BIOSEnumAttribute : public BIOSAttribute
+{
+  public:
+    friend class ::TestBIOSEnumAttribute;
+    /** @brief Construct a bios enum attribute
+     *  @param[in] entry - Json Object
+     *  @param[in] dbusHandler - Dbus Handler
+     */
+    BIOSEnumAttribute(const Json& entry, DBusHandler* const dbusHandler);
+
+    /** @brief Set Attribute value On Dbus according to the attribute value
+     *         entry
+     *  @param[in] attrValueEntry - The attribute value entry
+     *  @param[in] attrEntry - The attribute entry corresponding to the
+     *                         attribute value entry
+     *  @param[in] stringTable - The string table
+     */
+    void
+        setAttrValueOnDbus(const pldm_bios_attr_val_table_entry* attrValueEntry,
+                           const pldm_bios_attr_table_entry* attrEntry,
+                           const BIOSStringTable& stringTable) override;
+
+    /** @brief Construct corresponding entries at the end of the attribute table
+     *         and attribute value tables
+     *  @param[in] stringTable - The string Table
+     *  @param[in,out] attrTable - The attribute table
+     *  @param[in,out] attrValueTable - The attribute value table
+     */
+    void constructEntry(const BIOSStringTable& stringTable, Table& attrTable,
+                        Table& attrValueTable) override;
+
+  private:
+    std::vector<std::string> possibleValues;
+    std::string defaultValue;
+
+    /** @brief Get index of the given value in possible values
+     *  @param[in] value - The given value
+     *  @param[in] pVs - The possible values
+     *  @return Index of the given value in possible values
+     */
+    uint8_t getValueIndex(const std::string& value,
+                          const std::vector<std::string>& pVs);
+
+    /** @brief Get handles of possible values
+     *  @param[in] stringTable - The bios string table
+     *  @param[in] pVs - The possible values
+     *  @return The handles
+     */
+    std::vector<uint16_t>
+        getPossibleValuesHandle(const BIOSStringTable& stringTable,
+                                const std::vector<std::string>& pVs);
+
+    using ValMap = std::map<PropertyValue, std::string>;
+
+    /** @brief Map of value on dbus and pldm */
+    ValMap valMap;
+
+    /** @brief Build the map of dbus value to pldm enum value
+     *  @param[in] dbusVals - The dbus values in the json's dbus section
+     */
+    void buildValMap(const Json& dbusVals);
+
+    /** @brief Get index of the current value in possible values
+     *  @return The index of the current value in possible values
+     */
+    uint8_t getAttrValueIndex();
+};
+
+} // namespace bios
+} // namespace responder
+} // namespace pldm
\ No newline at end of file
diff --git a/libpldmresponder/bios_table.cpp b/libpldmresponder/bios_table.cpp
index 67dfb0e..3f5d532 100644
--- a/libpldmresponder/bios_table.cpp
+++ b/libpldmresponder/bios_table.cpp
@@ -193,6 +193,33 @@
     return {lower, upper, scalar, def};
 }
 
+const pldm_bios_attr_table_entry*
+    constructEnumEntry(Table& table, pldm_bios_table_attr_entry_enum_info* info)
+{
+    auto entryLength = pldm_bios_table_attr_entry_enum_encode_length(
+        info->pv_num, info->def_num);
+
+    auto tableSize = table.size();
+    table.resize(tableSize + entryLength, 0);
+    pldm_bios_table_attr_entry_enum_encode(table.data() + tableSize,
+                                           entryLength, info);
+
+    return reinterpret_cast<pldm_bios_attr_table_entry*>(table.data() +
+                                                         tableSize);
+}
+
+EnumField decodeEnumEntry(const pldm_bios_attr_table_entry* entry)
+{
+    uint8_t pvNum = pldm_bios_table_attr_entry_enum_decode_pv_num(entry);
+    std::vector<uint16_t> pvHdls(pvNum, 0);
+    pldm_bios_table_attr_entry_enum_decode_pv_hdls(entry, pvHdls.data(), pvNum);
+    auto defNum = pldm_bios_table_attr_entry_enum_decode_def_num(entry);
+    std::vector<uint8_t> defIndices(defNum, 0);
+    pldm_bios_table_attr_entry_enum_decode_def_indices(entry, defIndices.data(),
+                                                       defIndices.size());
+    return {pvHdls, defIndices};
+}
+
 } // namespace attribute
 
 namespace attribute_value
@@ -220,6 +247,16 @@
     return pldm_bios_table_attr_value_entry_integer_decode_cv(entry);
 }
 
+std::vector<uint8_t>
+    decodeEnumEntry(const pldm_bios_attr_val_table_entry* entry)
+{
+    auto number = pldm_bios_table_attr_value_entry_enum_decode_number(entry);
+    std::vector<uint8_t> currHdls(number, 0);
+    pldm_bios_table_attr_value_entry_enum_decode_handles(entry, currHdls.data(),
+                                                         currHdls.size());
+    return currHdls;
+}
+
 const pldm_bios_attr_val_table_entry*
     constructStringEntry(Table& table, uint16_t attrHandle, uint8_t attrType,
                          const std::string& str)
@@ -251,6 +288,21 @@
                                                              tableSize);
 }
 
+const pldm_bios_attr_val_table_entry*
+    constructEnumEntry(Table& table, uint16_t attrHandle, uint8_t attrType,
+                       const std::vector<uint8_t>& handleIndices)
+{
+    auto entryLength = pldm_bios_table_attr_value_entry_encode_enum_length(
+        handleIndices.size());
+    auto tableSize = table.size();
+    table.resize(tableSize + entryLength);
+    pldm_bios_table_attr_value_entry_encode_enum(
+        table.data() + tableSize, entryLength, attrHandle, attrType,
+        handleIndices.size(), handleIndices.data());
+    return reinterpret_cast<pldm_bios_attr_val_table_entry*>(table.data() +
+                                                             tableSize);
+}
+
 std::optional<Table> updateTable(const Table& table, const void* entry,
                                  size_t size)
 {
diff --git a/libpldmresponder/bios_table.hpp b/libpldmresponder/bios_table.hpp
index aab6f53..cb1873c 100644
--- a/libpldmresponder/bios_table.hpp
+++ b/libpldmresponder/bios_table.hpp
@@ -252,6 +252,31 @@
  */
 IntegerField decodeIntegerEntry(const pldm_bios_attr_table_entry* entry);
 
+/** @struct EnumField
+ *  @brief Enum field of attribute table
+ */
+struct EnumField
+{
+    std::vector<uint16_t> possibleValueStringHandle;
+    std::vector<uint8_t> defaultValueIndex;
+};
+
+/** @brief decode enum entry of attribute table
+ *  @param[in] entry - Pointer to an attribute table entry
+ *  @return Enum field of the entry
+ */
+EnumField decodeEnumEntry(const pldm_bios_attr_table_entry* entry);
+
+/** @brief construct enum entry of attribute table at the end of the
+ *         given table
+ *  @param[in,out] table - The given table
+ *  @param[in] info - enum info
+ *  @return pointer to the constructed entry
+ */
+const pldm_bios_attr_table_entry*
+    constructEnumEntry(Table& table,
+                       pldm_bios_table_attr_entry_enum_info* info);
+
 } // namespace attribute
 
 namespace attribute_value
@@ -284,6 +309,13 @@
  */
 uint64_t decodeIntegerEntry(const pldm_bios_attr_val_table_entry* entry);
 
+/** @brief Decode enum entry of attribute value table
+ *  @param[in] entry - Pointer to an attribute value table entry
+ *  @return Current value string handle indices
+ */
+std::vector<uint8_t>
+    decodeEnumEntry(const pldm_bios_attr_val_table_entry* entry);
+
 /** @brief Construct string entry of attribute value table at the end of the
  *         given table
  *  @param[in] table - The given table
@@ -309,6 +341,18 @@
                                                             uint8_t attrType,
                                                             uint64_t value);
 
+/** @brief Construct enum entry of attribute value table at the end of
+ *         the given table
+ *  @param[in] table - The given table
+ *  @param[in] attrHandle - attribute handle
+ *  @param[in] attrType - attribute type
+ *  @param[in] handleIndices -  handle indices
+ *  @return Pointer to the constructed entry
+ */
+const pldm_bios_attr_val_table_entry*
+    constructEnumEntry(Table& table, uint16_t attrHandle, uint8_t attrType,
+                       const std::vector<uint8_t>& handleIndices);
+
 /** @brief construct a table with an new entry
  *  @param[in] table - the table need to be updated
  *  @param[in] entry - the new attribute value entry
diff --git a/libpldmresponder/meson.build b/libpldmresponder/meson.build
index 9e073eb..88dfdab 100644
--- a/libpldmresponder/meson.build
+++ b/libpldmresponder/meson.build
@@ -13,6 +13,7 @@
   'bios_attribute.cpp',
   'bios_string_attribute.cpp',
   'bios_integer_attribute.cpp',
+  'bios_enum_attribute.cpp',
   'bios_config.cpp',
   'pdr_utils.cpp',
   'pdr.cpp',
diff --git a/test/libpldmresponder_bios_config_test.cpp b/test/libpldmresponder_bios_config_test.cpp
index 05a3ced..b6ec9a0 100644
--- a/test/libpldmresponder_bios_config_test.cpp
+++ b/test/libpldmresponder_bios_config_test.cpp
@@ -29,6 +29,7 @@
         std::vector<fs::path> paths = {
             "./bios_jsons/string_attrs.json",
             "./bios_jsons/integer_attrs.json",
+            "./bios_jsons/enum_attrs.json",
         };
 
         for (auto& path : paths)
@@ -163,6 +164,29 @@
                           jsonEntry->at("default_value").get<uint64_t>());
                 break;
             }
+            case PLDM_BIOS_ENUMERATION:
+            case PLDM_BIOS_ENUMERATION_READ_ONLY:
+            {
+                auto [pvHdls, defInds] =
+                    table::attribute::decodeEnumEntry(entry);
+                auto possibleValues = jsonEntry->at("possible_values")
+                                          .get<std::vector<std::string>>();
+                std::vector<std::string> strings;
+                for (auto pv : pvHdls)
+                {
+                    auto s = biosStringTable.findString(pv);
+                    strings.emplace_back(s);
+                }
+                EXPECT_EQ(strings, possibleValues);
+                EXPECT_EQ(defInds.size(), 1);
+
+                auto defValue = biosStringTable.findString(pvHdls[defInds[0]]);
+                auto defaultValues = jsonEntry->at("default_values")
+                                         .get<std::vector<std::string>>();
+                EXPECT_EQ(defValue, defaultValues[0]);
+
+                break;
+            }
             default:
                 EXPECT_TRUE(false);
                 break;
@@ -198,6 +222,19 @@
                 EXPECT_EQ(value, defValue);
                 break;
             }
+            case PLDM_BIOS_ENUMERATION:
+            case PLDM_BIOS_ENUMERATION_READ_ONLY:
+            {
+                auto indices = table::attribute_value::decodeEnumEntry(entry);
+                EXPECT_EQ(indices.size(), 1);
+                auto possibleValues = jsonEntry->at("possible_values")
+                                          .get<std::vector<std::string>>();
+
+                auto defValues = jsonEntry->at("default_values")
+                                     .get<std::vector<std::string>>();
+                EXPECT_EQ(possibleValues[indices[0]], defValues[0]);
+                break;
+            }
             default:
                 EXPECT_TRUE(false);
                 break;
diff --git a/test/libpldmresponder_bios_enum_attribute_test.cpp b/test/libpldmresponder_bios_enum_attribute_test.cpp
new file mode 100644
index 0000000..4cd5809
--- /dev/null
+++ b/test/libpldmresponder_bios_enum_attribute_test.cpp
@@ -0,0 +1,212 @@
+#include "libpldmresponder/bios_enum_attribute.hpp"
+#include "mocked_bios.hpp"
+#include "mocked_utils.hpp"
+
+#include <memory>
+#include <nlohmann/json.hpp>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using ::testing::_;
+using ::testing::ElementsAreArray;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::Throw;
+
+class TestBIOSEnumAttribute : public ::testing::Test
+{
+  public:
+    const auto& getPossibleValues(const BIOSEnumAttribute& attribute)
+    {
+        return attribute.possibleValues;
+    }
+
+    const auto& getDefaultValue(const BIOSEnumAttribute& attribute)
+    {
+        return attribute.defaultValue;
+    }
+};
+
+TEST_F(TestBIOSEnumAttribute, CtorTest)
+{
+    auto jsonEnumReadOnly = R"({
+         "attribute_name" : "CodeUpdatePolicy",
+         "possible_values" : [ "Concurrent", "Disruptive" ],
+         "default_values" : [ "Concurrent" ]
+      })"_json;
+
+    BIOSEnumAttribute enumReadOnly{jsonEnumReadOnly, nullptr};
+    EXPECT_EQ(enumReadOnly.name, "CodeUpdatePolicy");
+    EXPECT_TRUE(enumReadOnly.readOnly);
+    EXPECT_THAT(getPossibleValues(enumReadOnly),
+                ElementsAreArray({"Concurrent", "Disruptive"}));
+    EXPECT_EQ(getDefaultValue(enumReadOnly), "Concurrent");
+
+    auto jsonEnumReadOnlyError = R"({
+         "attribute_name" : "CodeUpdatePolicy",
+         "possible_value" : [ "Concurrent", "Disruptive" ],
+         "default_values" : [ "Concurrent" ]
+      })"_json; // possible_value -> possible_values
+    EXPECT_THROW((BIOSEnumAttribute{jsonEnumReadOnlyError, nullptr}),
+                 Json::exception);
+
+    auto jsonEnumReadWrite = R"({
+         "attribute_name" : "FWBootSide",
+         "possible_values" : [ "Perm", "Temp" ],
+         "default_values" : [ "Perm" ],
+         "dbus":
+            {
+               "object_path" : "/xyz/abc/def",
+               "interface" : "xyz.openbmc.FWBoot.Side",
+               "property_name" : "Side",
+               "property_type" : "bool",
+               "property_values" : [true, false]
+            }
+      })"_json;
+
+    BIOSEnumAttribute enumReadWrite{jsonEnumReadWrite, nullptr};
+    EXPECT_EQ(enumReadWrite.name, "FWBootSide");
+    EXPECT_TRUE(!enumReadWrite.readOnly);
+}
+
+TEST_F(TestBIOSEnumAttribute, ConstructEntry)
+{
+    MockBIOSStringTable biosStringTable;
+    MockdBusHandler dbusHandler;
+
+    auto jsonEnumReadOnly = R"({
+         "attribute_name" : "CodeUpdatePolicy",
+         "possible_values" : [ "Concurrent", "Disruptive" ],
+         "default_values" : [ "Disruptive" ]
+      })"_json;
+
+    std::vector<uint8_t> expectedAttrEntry{
+        0,    0, /* attr handle */
+        0x80,    /* attr type enum read-only*/
+        4,    0, /* attr name handle */
+        2,       /* number of possible value */
+        2,    0, /* possible value handle */
+        3,    0, /* possible value handle */
+        1,       /* number of default value */
+        1        /* defaut value string handle index */
+    };
+
+    std::vector<uint8_t> expectedAttrValueEntry{
+        0, 0, /* attr handle */
+        0x80, /* attr type enum read-only*/
+        1,    /* number of current value */
+        1     /* current value string handle index */
+    };
+
+    BIOSEnumAttribute enumReadOnly{jsonEnumReadOnly, nullptr};
+
+    ON_CALL(biosStringTable, findHandle(StrEq("Concurrent")))
+        .WillByDefault(Return(2));
+    ON_CALL(biosStringTable, findHandle(StrEq("Disruptive")))
+        .WillByDefault(Return(3));
+    ON_CALL(biosStringTable, findHandle(StrEq("CodeUpdatePolicy")))
+        .WillByDefault(Return(4));
+
+    checkConstructEntry(enumReadOnly, biosStringTable, expectedAttrEntry,
+                        expectedAttrValueEntry);
+
+    auto jsonEnumReadWrite = R"({
+         "attribute_name" : "CodeUpdatePolicy",
+         "possible_values" : [ "Concurrent", "Disruptive" ],
+         "default_values" : [ "Disruptive" ],
+         "dbus":
+            {
+               "object_path" : "/xyz/abc/def",
+               "interface" : "xyz.openbmc.abc.def",
+               "property_name" : "Policy",
+               "property_type" : "bool",
+               "property_values" : [true, false]
+          }
+      })"_json;
+
+    BIOSEnumAttribute enumReadWrite{jsonEnumReadWrite, &dbusHandler};
+
+    EXPECT_CALL(dbusHandler,
+                getDbusPropertyVariant(StrEq("/xyz/abc/def"), StrEq("Policy"),
+                                       StrEq("xyz.openbmc.abc.def")))
+        .WillOnce(Throw(std::exception()));
+
+    /* Set expected attr type to read-write */
+    expectedAttrEntry[2] = PLDM_BIOS_ENUMERATION;
+    expectedAttrValueEntry[2] = PLDM_BIOS_ENUMERATION;
+
+    checkConstructEntry(enumReadWrite, biosStringTable, expectedAttrEntry,
+                        expectedAttrValueEntry);
+
+    EXPECT_CALL(dbusHandler,
+                getDbusPropertyVariant(StrEq("/xyz/abc/def"), StrEq("Policy"),
+                                       StrEq("xyz.openbmc.abc.def")))
+        .WillOnce(Return(PropertyValue(true)));
+
+    expectedAttrValueEntry = {
+        0, 0, /* attr handle */
+        0,    /* attr type enum read-write*/
+        1,    /* number of current value */
+        0     /* current value string handle index */
+    };
+
+    checkConstructEntry(enumReadWrite, biosStringTable, expectedAttrEntry,
+                        expectedAttrValueEntry);
+}
+
+TEST_F(TestBIOSEnumAttribute, setAttrValueOnDbus)
+{
+    MockBIOSStringTable biosStringTable;
+    MockdBusHandler dbusHandler;
+
+    auto jsonEnumReadWrite = R"({
+         "attribute_name" : "CodeUpdatePolicy",
+         "possible_values" : [ "Concurrent", "Disruptive" ],
+         "default_values" : [ "Disruptive" ],
+         "dbus":
+            {
+               "object_path" : "/xyz/abc/def",
+               "interface" : "xyz.openbmc.abc.def",
+               "property_name" : "Policy",
+               "property_type" : "bool",
+               "property_values" : [true, false]
+          }
+      })"_json;
+    DBusMapping dbusMapping{"/xyz/abc/def", "xyz.openbmc.abc.def", "Policy",
+                            "bool"};
+
+    BIOSEnumAttribute enumReadWrite{jsonEnumReadWrite, &dbusHandler};
+
+    std::vector<uint8_t> attrEntry{
+        0, 0, /* attr handle */
+        0,    /* attr type enum read-only*/
+        4, 0, /* attr name handle */
+        2,    /* number of possible value */
+        2, 0, /* possible value handle */
+        3, 0, /* possible value handle */
+        1,    /* number of default value */
+        1     /* defaut value string handle index */
+    };
+
+    ON_CALL(biosStringTable, findString(2))
+        .WillByDefault(Return(std::string("Concurrent")));
+    ON_CALL(biosStringTable, findString(3))
+        .WillByDefault(Return(std::string("Disruptive")));
+
+    std::vector<uint8_t> attrValueEntry{
+        0, 0, /* attr handle */
+        0,    /* attr type enum read-only*/
+        1,    /* number of current value */
+        0     /* current value string handle index */
+    };
+
+    EXPECT_CALL(dbusHandler,
+                setDbusProperty(dbusMapping, PropertyValue{bool(true)}))
+        .Times(1);
+    enumReadWrite.setAttrValueOnDbus(
+        reinterpret_cast<pldm_bios_attr_val_table_entry*>(
+            attrValueEntry.data()),
+        reinterpret_cast<pldm_bios_attr_table_entry*>(attrEntry.data()),
+        biosStringTable);
+}
\ No newline at end of file
diff --git a/test/meson.build b/test/meson.build
index 0210833..159cfc2 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -23,6 +23,7 @@
   'libpldmresponder_bios_string_attribute_test',
   'libpldmresponder_bios_config_test',
   'libpldmresponder_bios_integer_attribute_test',
+  'libpldmresponder_bios_enum_attribute_test',
   'libpldmresponder_pdr_state_effecter_test',
   'libpldmresponder_bios_table_test',
   'libpldmresponder_platform_test',
diff --git a/test/mocked_bios.hpp b/test/mocked_bios.hpp
index eb95c33..568fad9 100644
--- a/test/mocked_bios.hpp
+++ b/test/mocked_bios.hpp
@@ -14,6 +14,8 @@
     }
 
     MOCK_METHOD(uint16_t, findHandle, (const std::string&), (const override));
+
+    MOCK_METHOD(std::string, findString, (const uint16_t), (const override));
 };
 
 void checkHeader(const Table& attrEntry, const Table& attrValueEntry)