BIOS: Implement attribute table with type string

Construct attribute table with type string based on string table.

Tested:
Following are the tables constructed from the sample json file present at
"test/bios_jsons/

$ hexdump -C stringTable
00000000  00 00 07 00 41 6c 6c 6f  77 65 64 01 00 10 00 43  |....Allowed....C|
00000010  6f 64 65 55 70 64 61 74  65 50 6f 6c 69 63 79 02  |odeUpdatePolicy.|
00000020  00 0a 00 43 6f 6e 63 75  72 72 65 6e 74 03 00 0a  |...Concurrent...|
00000030  00 44 69 73 72 75 70 74  69 76 65 04 00 0a 00 46  |.Disruptive....F|
00000040  57 42 6f 6f 74 53 69 64  65 05 00 0f 00 48 4d 43  |WBootSide....HMC|
00000050  4d 61 6e 61 67 65 64 53  74 61 74 65 06 00 10 00  |ManagedState....|
00000060  49 6e 62 61 6e 64 43 6f  64 65 55 70 64 61 74 65  |InbandCodeUpdate|
00000070  07 00 0a 00 4e 6f 74 41  6c 6c 6f 77 65 64 08 00  |....NotAllowed..|
00000080  03 00 4f 66 66 09 00 02  00 4f 6e 0a 00 04 00 50  |..Off....On....P|
00000090  65 72 6d 0b 00 04 00 54  65 6d 70 0c 00 0c 00 73  |erm....Temp....s|
000000a0  74 72 5f 65 78 61 6d 70  6c 65 31 0d 00 0c 00 73  |tr_example1....s|
000000b0  74 72 5f 65 78 61 6d 70  6c 65 32 0e 00 0c 00 73  |tr_example2....s|
000000c0  74 72 5f 65 78 61 6d 70  6c 65 33 00 6c 11 89 d4  |tr_example3.l...|

$ hexdump -C attributeTable
00000000  00 00 00 01 00 02 02 00  03 00 01 00 01 00 00 04  |................|
00000010  00 02 0a 00 0b 00 01 00  02 00 00 05 00 02 08 00  |................|
00000020  09 00 01 01 03 00 00 06  00 02 00 00 07 00 01 00  |................|
00000030  04 00 01 0c 00 01 01 00  64 00 03 00 61 62 63 05  |........d...abc.|
00000040  00 01 0d 00 02 00 00 64  00 00 00 06 00 01 0e 00  |.......d........|
00000050  00 01 00 64 00 02 00 65  66 00 00 00 72 ef 0c 2a  |...d...ef...r..*|

Change-Id: I272c59b96b5aa14571d01eeeaccd504ca7ac168b
Signed-off-by: Carol Wang <wangkair@cn.ibm.com>
diff --git a/libpldmresponder/bios.cpp b/libpldmresponder/bios.cpp
index 6669688..90d86c8 100644
--- a/libpldmresponder/bios.cpp
+++ b/libpldmresponder/bios.cpp
@@ -591,6 +591,9 @@
 
 namespace bios_type_string
 {
+
+using namespace bios_parser::bios_string;
+
 /** @brief Construct the attibute table for BIOS type String and
  *         String ReadOnly
  *  @param[in] BIOSStringTable - the string table
@@ -598,10 +601,63 @@
  *  @param[in,out] attributeTable - the attribute table
  *
  */
-void constructAttrTable(const BIOSTable& /*BIOSStringTable*/,
-                        const char* /*biosJsonDir*/, Table& /*attributeTable*/)
+void constructAttrTable(const BIOSTable& BIOSStringTable,
+                        const char* biosJsonDir, Table& attributeTable)
 {
-    // TODO
+    auto rc = setupValueLookup(biosJsonDir);
+    if (rc == -1)
+    {
+        log<level::ERR>("Failed to parse entries in Json file");
+        return;
+    }
+    const auto& attributeMap = getValues();
+    StringHandle strHandle;
+
+    for (const auto& [key, value] : attributeMap)
+    {
+        try
+        {
+            strHandle = findStringHandle(key, BIOSStringTable);
+        }
+        catch (InternalFailure& e)
+        {
+            log<level::ERR>("Could not find handle for BIOS string",
+                            entry("ATTRIBUTE=%s", key.c_str()));
+            continue;
+        }
+
+        const auto& [type, strType, minStrLen, maxStrLen, defaultStrLen,
+                     defaultStr] = value;
+        uint8_t typeOfAttr =
+            type ? PLDM_BIOS_STRING_READ_ONLY : PLDM_BIOS_STRING;
+
+        BIOSTableRow stringAttrTable(bios_parser::bios_string::attrTableSize +
+                                     defaultStr.size());
+        BIOSTableRow::iterator it = stringAttrTable.begin();
+        auto attrPtr = reinterpret_cast<struct pldm_bios_attr_table_entry*>(
+            stringAttrTable.data());
+        attrPtr->attr_handle = nextAttributeHandle();
+        attrPtr->attr_type = typeOfAttr;
+        attrPtr->string_handle = strHandle;
+
+        std::advance(it, (sizeof(struct pldm_bios_attr_table_entry) - 1));
+        std::copy_n(&strType, sizeof(uint8_t), it);
+        std::advance(it, sizeof(uint8_t));
+        std::copy_n(reinterpret_cast<const uint8_t*>(&minStrLen),
+                    sizeof(uint16_t), it);
+        std::advance(it, sizeof(uint16_t));
+        std::copy_n(reinterpret_cast<const uint8_t*>(&maxStrLen),
+                    sizeof(uint16_t), it);
+        std::advance(it, sizeof(uint16_t));
+        std::copy_n(reinterpret_cast<const uint8_t*>(&defaultStrLen),
+                    sizeof(uint16_t), it);
+        std::advance(it, sizeof(uint16_t));
+        std::copy_n(defaultStr.data(), defaultStr.size(), it);
+        std::advance(it, defaultStr.size());
+
+        attributeTable.insert(attributeTable.end(), stringAttrTable.begin(),
+                              stringAttrTable.end());
+    }
 }
 } // end namespace bios_type_string
 
diff --git a/libpldmresponder/bios_parser.cpp b/libpldmresponder/bios_parser.cpp
index 3933ad4..a6f07d8 100644
--- a/libpldmresponder/bios_parser.cpp
+++ b/libpldmresponder/bios_parser.cpp
@@ -15,6 +15,43 @@
 namespace fs = std::filesystem;
 using namespace phosphor::logging;
 
+namespace
+{
+
+const std::vector<Json> emptyJsonList{};
+const Json emptyJson{};
+
+} // namespace
+
+int parseBiosJsonFile(const char* dirPath, const std::string& fileName,
+                      Json& fileData)
+{
+    int rc = 0;
+
+    fs::path filePath(dirPath);
+    filePath /= fileName;
+
+    std::ifstream jsonFile(filePath);
+    if (!jsonFile.is_open())
+    {
+        log<level::ERR>("BIOS config file does not exist",
+                        entry("FILE=%s", filePath.c_str()));
+        rc = -1;
+    }
+    else
+    {
+        fileData = Json::parse(jsonFile, nullptr, false);
+        if (fileData.is_discarded())
+        {
+            log<level::ERR>("Parsing config file failed",
+                            entry("FILE=%s", filePath.c_str()));
+            rc = -1;
+        }
+    }
+
+    return rc;
+}
+
 namespace bios_enum
 {
 
@@ -165,8 +202,7 @@
         return rc;
     }
 
-    static const std::vector<Json> emptyList{};
-    auto entries = fileData.value("entries", emptyList);
+    auto entries = fileData.value("entries", emptyJsonList);
     // Iterate through each JSON object in the config file
     for (const auto& entry : entries)
     {
@@ -187,10 +223,10 @@
         }
 
         std::optional<internal::DBusMapping> dBusMap = std::nullopt;
-        static const Json empty{};
+
         if (entry.count("dbus") != 0)
         {
-            auto dBusEntry = entry.value("dbus", empty);
+            auto dBusEntry = entry.value("dbus", emptyJson);
             dBusMap = std::make_optional<internal::DBusMapping>();
             dBusMap.value().objectPath = dBusEntry.value("object_path", "");
             dBusMap.value().interface = dBusEntry.value("interface", "");
@@ -251,6 +287,154 @@
 
 } // namespace bios_enum
 
+namespace bios_string
+{
+
+/** @brief BIOS string types
+ */
+enum BiosStringEncoding
+{
+    UNKNOWN = 0x00,
+    ASCII = 0x01,
+    HEX = 0x02,
+    UTF_8 = 0x03,
+    UTF_16LE = 0x04,
+    UTF_16BE = 0x05,
+    VENDOR_SPECIFIC = 0xFF
+};
+
+const std::map<std::string, uint8_t> strTypeMap{
+    {"Unknown", UNKNOWN},
+    {"ASCII", ASCII},
+    {"Hex", HEX},
+    {"UTF-8", UTF_8},
+    {"UTF-16LE", UTF_16LE},
+    {"UTF-16LE", UTF_16LE},
+    {"Vendor Specific", VENDOR_SPECIFIC}};
+
+namespace internal
+{
+
+/** @struct DBusMapping
+ *
+ *  Data structure for storing information regarding BIOS string attribute
+ *  and the D-Bus object for the attribute.
+ */
+struct DBusMapping
+{
+    std::string objectPath;   //!< D-Bus object path
+    std::string interface;    //!< D-Bus interface
+    std::string propertyName; //!< D-Bus property name
+};
+
+/** @brief Map containing the possible and the default values for the BIOS
+ *         string type attributes.
+ */
+AttrValuesMap valueMap;
+
+/** @brief Map containing the optional D-Bus property information about the
+ *         BIOS string type attributes.
+ */
+std::map<AttrName, std::optional<DBusMapping>> attrLookup;
+
+} // namespace internal
+
+int setupValueLookup(const char* dirPath)
+{
+    int rc = 0;
+
+    if (!internal::valueMap.empty() && !internal::attrLookup.empty())
+    {
+        return rc;
+    }
+
+    Json fileData;
+    rc = parseBiosJsonFile(dirPath, bIOSStrJson, fileData);
+    if (rc != 0)
+    {
+        return rc;
+    }
+
+    auto entries = fileData.value("entries", emptyJsonList);
+    // Iterate through each JSON object in the config file
+    for (const auto& entry : entries)
+    {
+        std::string attr = entry.value("attribute_name", "");
+        // Transfer string type from string to enum
+        std::string strTypeTmp = entry.value("string_type", "Unknown");
+        auto iter = strTypeMap.find(strTypeTmp);
+        if (iter == strTypeMap.end())
+        {
+            log<level::ERR>(
+                "Wrong string type",
+                phosphor::logging::entry("STRING_TYPE=%s", strTypeTmp.c_str()),
+                phosphor::logging::entry("ATTRIBUTE_NAME=%s", attr.c_str()));
+            return -1;
+        }
+        uint8_t strType = iter->second;
+
+        uint16_t minStrLen = entry.value("minimum_string_length", 0);
+        uint16_t maxStrLen = entry.value("maximum_string_length", 0);
+        if (maxStrLen - minStrLen < 0)
+        {
+            log<level::ERR>(
+                "Maximum string length is smaller than minimum string length",
+                phosphor::logging::entry("ATTRIBUTE_NAME=%s", attr.c_str()));
+            return -1;
+        }
+        uint16_t defaultStrLen = entry.value("default_string_length", 0);
+        std::string defaultStr = entry.value("default_string", "");
+        if ((defaultStrLen == 0) && (defaultStr.size() > 0))
+        {
+            log<level::ERR>(
+                "Default string length is 0, but default string is existing",
+                phosphor::logging::entry("ATTRIBUTE_NAME=%s", attr.c_str()));
+            return -1;
+        }
+
+        // dbus handling
+        std::optional<internal::DBusMapping> dBusMap;
+
+        if (entry.count("dbus") != 0)
+        {
+            auto dBusEntry = entry.value("dbus", emptyJson);
+            dBusMap = std::make_optional<internal::DBusMapping>();
+            dBusMap->objectPath = dBusEntry.value("object_path", "");
+            dBusMap->interface = dBusEntry.value("interface", "");
+            dBusMap->propertyName = dBusEntry.value("property_name", "");
+            if (dBusMap->objectPath.empty() || dBusMap->interface.empty() ||
+                dBusMap->propertyName.empty())
+            {
+                log<level::ERR>(
+                    "Invalid dbus config",
+                    phosphor::logging::entry("OBJPATH=%s",
+                                             dBusMap->objectPath.c_str()),
+                    phosphor::logging::entry("INTERFACE=%s",
+                                             dBusMap->interface.c_str()),
+                    phosphor::logging::entry("PROPERTY_NAME=%s",
+                                             dBusMap->propertyName.c_str()));
+                return -1;
+            }
+        }
+
+        internal::attrLookup.emplace(attr, std::move(dBusMap));
+
+        // Defaulting all the types of attributes to BIOSString
+        internal::valueMap.emplace(
+            std::move(attr),
+            std::make_tuple(entry.count("dbus") == 0, strType, minStrLen,
+                            maxStrLen, defaultStrLen, std::move(defaultStr)));
+    }
+
+    return rc;
+}
+
+const AttrValuesMap& getValues()
+{
+    return internal::valueMap;
+}
+} // namespace bios_string
+
 Strings getStrings(const char* dirPath)
 {
     Strings biosStrings{};
@@ -279,8 +463,7 @@
             continue;
         }
 
-        static const std::vector<Json> emptyList{};
-        auto entries = fileData.value("entries", emptyList);
+        auto entries = fileData.value("entries", emptyJsonList);
 
         // Iterate through each entry in the config file
         for (auto& entry : entries)
diff --git a/libpldmresponder/bios_parser.hpp b/libpldmresponder/bios_parser.hpp
index 1f13f94..2edbc95 100644
--- a/libpldmresponder/bios_parser.hpp
+++ b/libpldmresponder/bios_parser.hpp
@@ -89,4 +89,50 @@
 
 } // namespace bios_enum
 
+namespace bios_string
+{
+
+/** @brief Parse the JSON file specific to BIOSString and
+ *         BIOSStringReadOnly types and populate the data structure for
+ *         the corresponding possible values and the default value. Setup the
+ *         data structure to lookup the current value of the BIOS string
+ *         attribute. JSON is parsed once and the information is cached.
+ *
+ *  @param[in] dirPath - directory path where all the BIOS configuration JSON
+ *                      exist
+ *
+ *  @return 0 for success and negative return code for failure
+ */
+int setupValueLookup(const char* dirPath);
+
+using AttrName = std::string;
+using IsReadOnly = bool;
+using StrType = uint8_t;
+using MinStrLen = uint16_t;
+using MaxStrLen = uint16_t;
+using DefaultStrLen = uint16_t;
+using DefaultStr = std::string;
+using AttrValuesMap =
+    std::map<AttrName, std::tuple<IsReadOnly, StrType, MinStrLen, MaxStrLen,
+                                  DefaultStrLen, DefaultStr>>;
+/* attrTableSize is the sum of fixed length of members which construct a string
+ * attribute table, including attr_handle(uint16_t), attr_type(uint8_t),
+ * string_handle(uint16_t), strType(uint8_t), minStrLen(uint16_t),
+ * MaxStrLen(uint16_t), DefaultStrLen(uint16_t) */
+constexpr auto attrTableSize = 12;
+static_assert(attrTableSize == sizeof(uint16_t) + sizeof(uint8_t) +
+                                   sizeof(uint16_t) + sizeof(uint8_t) +
+                                   sizeof(uint16_t) + sizeof(uint16_t) +
+                                   sizeof(uint16_t));
+
+/** @brief Get the string related values and the default values for the
+ *         BIOSString and BIOSStringReadOnly types
+ *
+ *  @return information needed to build the BIOS attribute table specific to
+ *         BIOSString and BIOSStringReadOnly types
+ */
+const AttrValuesMap& getValues();
+
+} // namespace bios_string
+
 } // namespace bios_parser