BIOS: Implement attribute value table with type string

Construct attribute value table with type string based on string table
and attribute table.

Change-Id: I40c782ae8753340c964cf067e53e7947b18e9e35
Signed-off-by: Carol Wang <wangkair@cn.ibm.com>
diff --git a/libpldmresponder/bios.cpp b/libpldmresponder/bios.cpp
index 90d86c8..6454052 100644
--- a/libpldmresponder/bios.cpp
+++ b/libpldmresponder/bios.cpp
@@ -479,13 +479,13 @@
  *
  *  @param[in] BIOSAttributeTable - the attribute table
  *  @param[in] BIOSStringTable - the string table
+ *  @param[in, out] attributeValueTable - the attribute value table
  *
- *  @return - Table - the attribute value table
  */
-Table constructAttrValueTable(const BIOSTable& BIOSAttributeTable,
-                              const BIOSTable& BIOSStringTable)
+void constructAttrValueTable(const BIOSTable& BIOSAttributeTable,
+                             const BIOSTable& BIOSStringTable,
+                             Table& attributeValueTable)
 {
-    Table attributeValueTable;
     Response response;
     BIOSAttributeTable.load(response);
 
@@ -518,7 +518,7 @@
             {
                 log<level::ERR>("Did not find string name for handle",
                                 entry("STRING_HANDLE=%d", stringHdl));
-                return attributeValueTable;
+                return;
             }
             attrPtr =
                 reinterpret_cast<struct pldm_bios_attr_table_entry*>(tableData);
@@ -538,7 +538,7 @@
             if (std::distance(tableData, response.data() + tableLen) <=
                 padChksumMax)
             {
-                return attributeValueTable;
+                return;
             }
 
             attrPtr =
@@ -583,8 +583,6 @@
         attrPtr =
             reinterpret_cast<struct pldm_bios_attr_table_entry*>(tableData);
     }
-
-    return attributeValueTable;
 }
 
 } // end namespace bios_type_enum
@@ -659,6 +657,101 @@
                               stringAttrTable.end());
     }
 }
+
+/** @brief Construct the attibute value table for BIOS type String and
+ *  String ReadOnly
+ *
+ *  @param[in] BIOSAttributeTable - the attribute table
+ *  @param[in] BIOSStringTable - the string table
+ *  @param[in, out] attributeValueTable - the attribute value table
+ *
+ */
+void constructAttrValueTable(const BIOSTable& BIOSAttributeTable,
+                             const BIOSTable& BIOSStringTable,
+                             Table& attributeValueTable)
+{
+    Response response;
+    BIOSAttributeTable.load(response);
+
+    auto dataPtr = response.data();
+    size_t tableLen = response.size();
+
+    while (true)
+    {
+        auto attrPtr =
+            reinterpret_cast<struct pldm_bios_attr_table_entry*>(dataPtr);
+        uint16_t attrHdl = attrPtr->attr_handle;
+        uint8_t attrType = attrPtr->attr_type;
+        uint16_t stringHdl = attrPtr->string_handle;
+        dataPtr += (sizeof(struct pldm_bios_attr_table_entry) - 1);
+        // pass number of StringType, MinimumStringLength, MaximumStringLength
+        dataPtr += sizeof(uint8_t) + sizeof(uint16_t) + sizeof(uint16_t);
+        auto sizeDefaultStr = *(reinterpret_cast<uint16_t*>(dataPtr));
+        // pass number of DefaultStringLength, DefaultString
+        dataPtr += sizeof(uint16_t) + sizeDefaultStr;
+
+        auto attrName = findStringName(stringHdl, BIOSStringTable);
+        if (attrName.empty())
+        {
+            if (std::distance(dataPtr, response.data() + tableLen) <=
+                padChksumMax)
+            {
+                log<level::ERR>("Did not find string name for handle",
+                                entry("STRING_HANDLE=%d", stringHdl));
+                return;
+            }
+            continue;
+        }
+
+        uint16_t currStrLen = 0;
+        std::string currStr;
+        try
+        {
+            currStr = getAttrValue(attrName);
+            currStrLen = currStr.size();
+        }
+        catch (const std::exception& e)
+        {
+            log<level::ERR>("getAttrValue returned error for attribute",
+                            entry("NAME=%s", attrName.c_str()),
+                            entry("ERROR=%s", e.what()));
+            if (std::distance(dataPtr, response.data() + tableLen) <=
+                padChksumMax)
+            {
+                return;
+            }
+            continue;
+        }
+
+        BIOSTableRow strAttrValTable(
+            bios_parser::bios_string::attrValueTableSize + currStrLen, 0);
+        BIOSTableRow::iterator it = strAttrValTable.begin();
+        auto attrValPtr =
+            reinterpret_cast<struct pldm_bios_attr_val_table_entry*>(
+                strAttrValTable.data());
+        attrValPtr->attr_handle = attrHdl;
+        attrValPtr->attr_type = attrType;
+        std::advance(it, (sizeof(pldm_bios_attr_val_table_entry) - 1));
+        std::copy_n(reinterpret_cast<uint8_t*>(&currStrLen), sizeof(uint16_t),
+                    it);
+        std::advance(it, sizeof(uint16_t));
+        if (currStrLen)
+        {
+            std::copy_n(currStr.cbegin(), currStrLen, it);
+            std::advance(it, currStrLen);
+        }
+
+        attributeValueTable.insert(attributeValueTable.end(),
+                                   strAttrValTable.begin(),
+                                   strAttrValTable.end());
+
+        if (std::distance(dataPtr, response.data() + tableLen) <= padChksumMax)
+        {
+            break;
+        }
+    }
+}
+
 } // end namespace bios_type_string
 
 using typeHandler =
@@ -668,6 +761,16 @@
     {bios_parser::bIOSEnumJson, bios_type_enum::constructAttrTable},
     {bios_parser::bIOSStrJson, bios_type_string::constructAttrTable}};
 
+using valueHandler = void (*)(const BIOSTable& BIOSAttributeTable,
+
+                              const BIOSTable& BIOSStringTable,
+
+                              Table& attributeTable);
+
+std::map<std::string, valueHandler> attrValueHandlers{
+    {bios_parser::bIOSEnumJson, bios_type_enum::constructAttrValueTable},
+    {bios_parser::bIOSStrJson, bios_type_string::constructAttrValueTable}};
+
 /** @brief Construct the BIOS attribute table
  *
  *  @param[in] BIOSAttributeTable - the attribute table
@@ -768,8 +871,7 @@
                                     const BIOSTable& BIOSStringTable,
                                     uint32_t& /*transferHandle*/,
                                     uint8_t& /*transferOpFlag*/,
-                                    uint8_t instanceID,
-                                    const char* /*biosJsonDir*/)
+                                    uint8_t instanceID, const char* biosJsonDir)
 {
     Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES,
                       0);
@@ -780,8 +882,27 @@
 
     if (BIOSAttributeValueTable.isEmpty())
     { // no persisted table, constructing fresh table and data
-        Table attributeValueTable = bios_type_enum::constructAttrValueTable(
-            BIOSAttributeTable, BIOSStringTable);
+        Table attributeValueTable;
+        fs::path dir(biosJsonDir);
+
+        for (auto it = attrValueHandlers.begin(); it != attrValueHandlers.end();
+             it++)
+        {
+            fs::path file = dir / it->first;
+            if (fs::exists(file))
+            {
+                it->second(BIOSAttributeTable, BIOSStringTable,
+                           attributeValueTable);
+            }
+        }
+
+        if (attributeValueTable.empty())
+        { // no available json file is found
+            encode_get_bios_table_resp(instanceID, PLDM_BIOS_TABLE_UNAVAILABLE,
+                                       nxtTransferHandle, transferFlag, nullptr,
+                                       response.size(), responsePtr);
+            return response;
+        }
         // calculate pad
         uint8_t padSize = utils::getNumPadBytes(attributeValueTable.size());
         std::vector<uint8_t> pad(padSize, 0);
diff --git a/libpldmresponder/bios_parser.cpp b/libpldmresponder/bios_parser.cpp
index a6f07d8..99c1149 100644
--- a/libpldmresponder/bios_parser.cpp
+++ b/libpldmresponder/bios_parser.cpp
@@ -433,6 +433,31 @@
 {
     return internal::valueMap;
 }
+
+std::string getAttrValue(const AttrName& attrName)
+{
+    const auto& dBusMap = internal::attrLookup.at(attrName);
+    std::variant<std::string> propValue;
+
+    if (dBusMap == std::nullopt)
+    { // return default string
+        const auto& valueEntry = internal::valueMap.at(attrName);
+        return std::get<DefaultStr>(valueEntry);
+    }
+
+    auto bus = sdbusplus::bus::new_default();
+    auto service = pldm::responder::getService(bus, dBusMap->objectPath,
+                                               dBusMap->interface);
+    auto method =
+        bus.new_method_call(service.c_str(), dBusMap->objectPath.c_str(),
+                            "org.freedesktop.DBus.Properties", "Get");
+    method.append(dBusMap->interface, dBusMap->propertyName);
+    auto reply = bus.call(method);
+    reply.read(propValue);
+
+    return std::get<std::string>(propValue);
+}
+
 } // namespace bios_string
 
 Strings getStrings(const char* dirPath)
diff --git a/libpldmresponder/bios_parser.hpp b/libpldmresponder/bios_parser.hpp
index 2edbc95..8b0c46e 100644
--- a/libpldmresponder/bios_parser.hpp
+++ b/libpldmresponder/bios_parser.hpp
@@ -124,6 +124,12 @@
                                    sizeof(uint16_t) + sizeof(uint8_t) +
                                    sizeof(uint16_t) + sizeof(uint16_t) +
                                    sizeof(uint16_t));
+/* attrValueTableSize is the sum of fixed length of members which construct a
+ * string attribute value table, including attr_handle(uint16_t),
+ * attr_type(uint8_t), CurrentStringLength(uint16_t)*/
+constexpr auto attrValueTableSize = 5;
+static_assert(attrValueTableSize ==
+              sizeof(uint16_t) + sizeof(uint8_t) + sizeof(uint16_t));
 
 /** @brief Get the string related values and the default values for the
  *         BIOSString and BIOSStringReadOnly types
@@ -133,6 +139,14 @@
  */
 const AttrValuesMap& getValues();
 
+/** @brief Get the current values for the BIOS Attribute
+ *
+ *  @param[in] attrName - BIOS attribute name
+ *
+ *  @return BIOS attribute value
+ */
+std::string getAttrValue(const AttrName& attrName);
+
 } // namespace bios_string
 
 } // namespace bios_parser