bios: Add SetBIOSAttributeCurrentValue

Update the persisted attribute value table and the d-bus property
corresponding to the attribute.

Tested:

Tested on fp5280g2, with json files:
https://gist.github.com/wangzqbj/b24558331cb35d14fca3b555ef03e458

Build Tables:

pldmtool raw --data 0x80 0x03 0x01 0x00 0x00 0x00 0x00 0x00 0x00
pldmtool raw --data 0x80 0x03 0x01 0x00 0x00 0x00 0x00 0x00 0x01
pldmtool raw --data 0x80 0x03 0x01 0x00 0x00 0x00 0x00 0x00 0x02

enum attribute:

Get Attribute Value By Handle:
root@fp5280g2:/tmp# pldmtool raw --data 0x80 0x03 0x08 0x00 0x00 0x00 0x00 0x00 0x01 0x00
...
...
Response Message:
08 01 00 03 08 00 00 00 00 00 05 01 00

Check Dbus Property:
root@fp5280g2:/tmp# busctl get-property xyz.openbmc_project.LED.Controller.front_memory /xyz/openbmc_project/led/physical/front_memory xyz.openbmc_project.Led.Physical State
s "xyz.openbmc_project.Led.Physical.Action.Off"

Set Attribute Value:
root@fp5280g2:/tmp# pldmtool raw --data 0x80 0x03 0x07 0x00 0x00 0x00 0x00 0x05 0x01 0x00 0x00 0x01 0x01
...
...
Response Message:
08 01 00 03 07 00 //exec successfully

Get Attribute Value By Handle:
root@fp5280g2:/tmp# pldmtool raw --data 0x80 0x03 0x08 0x00 0x00 0x00 0x00 0x00 0x01 0x00
...
...
Response Message:
08 01 00 03 08 00 00 00 00 00 05 01 01  // attribute value table was updated.

Check Dbus Property:
root@fp5280g2:/tmp# busctl get-property xyz.openbmc_project.LED.Controller.front_memory /xyz/openbmc_project/led/physical/front_memory xyz.openbmc_project.Led.Physical State
s "xyz.openbmc_project.Led.Physical.Action.On" // dbus property was updated

Verified that string/integer attributes can be updated as expected.

Signed-off-by: John Wang <wangzqbj@inspur.com>
Change-Id: I8dab1870396061037284c1683de2502c8da68c98
diff --git a/libpldmresponder/bios.cpp b/libpldmresponder/bios.cpp
index 082ac0f..fe20140 100644
--- a/libpldmresponder/bios.cpp
+++ b/libpldmresponder/bios.cpp
@@ -130,6 +130,11 @@
                          return this->getBIOSAttributeCurrentValueByHandle(
                              request, payloadLength);
                      });
+    handlers.emplace(PLDM_SET_BIOS_ATTRIBUTE_CURRENT_VALUE,
+                     [this](const pldm_msg* request, size_t payloadLength) {
+                         return this->setBIOSAttributeCurrentValue(
+                             request, payloadLength);
+                     });
 }
 
 Response Handler::getDateTime(const pldm_msg* request, size_t /*payloadLength*/)
@@ -955,6 +960,66 @@
     return response;
 }
 
+Response Handler::setBIOSAttributeCurrentValue(const pldm_msg* request,
+                                               size_t payloadLength)
+{
+    uint32_t transferHandle;
+    uint8_t transferOpFlag;
+    variable_field attributeField;
+
+    auto rc = decode_set_bios_attribute_current_value_req(
+        request, payloadLength, &transferHandle, &transferOpFlag,
+        &attributeField);
+    if (rc != PLDM_SUCCESS)
+    {
+        return ccOnlyResponse(request, rc);
+    }
+
+    fs::path tablesPath(BIOS_TABLES_DIR);
+    auto stringTablePath = tablesPath / stringTableFile;
+    BIOSStringTable biosStringTable(stringTablePath.c_str());
+    auto attrTablePath = tablesPath / attrTableFile;
+    BIOSTable biosAttributeTable(attrTablePath.c_str());
+    auto attrValueTablePath = tablesPath / attrValTableFile;
+    BIOSTable biosAttributeValueTable(attrValueTablePath.c_str());
+    // TODO: Construct attribute value table if it's empty. (another commit)
+
+    Response srcTable;
+    biosAttributeValueTable.load(srcTable);
+
+    // Replace the old attribute with the new attribute, the size of table will
+    // change:
+    //   sizeof(newTableBuffer) = srcTableSize + sizeof(newAttribute) -
+    //                      sizeof(oldAttribute) + pad(4-byte alignment, max =
+    //                      3)
+    // For simplicity, we use
+    //   sizeof(newTableBuffer) = srcTableSize + sizeof(newAttribute) + 3
+    size_t destBufferLength = srcTable.size() + attributeField.length + 3;
+    Response destTable(destBufferLength);
+    size_t destTableLen = destTable.size();
+
+    rc = pldm_bios_table_attr_value_copy_and_update(
+        srcTable.data(), srcTable.size(), destTable.data(), &destTableLen,
+        attributeField.ptr, attributeField.length);
+    destTable.resize(destTableLen);
+
+    if (rc != PLDM_SUCCESS)
+    {
+        return ccOnlyResponse(request, rc);
+    }
+
+    rc = setAttributeValueOnDbus(&attributeField, biosAttributeTable,
+                                 biosStringTable);
+    if (rc != PLDM_SUCCESS)
+    {
+        return ccOnlyResponse(request, rc);
+    }
+
+    biosAttributeValueTable.store(destTable);
+
+    return ccOnlyResponse(request, PLDM_SUCCESS);
+}
+
 namespace internal
 {
 
diff --git a/libpldmresponder/bios.hpp b/libpldmresponder/bios.hpp
index 4a42bf4..d023ce9 100644
--- a/libpldmresponder/bios.hpp
+++ b/libpldmresponder/bios.hpp
@@ -85,6 +85,15 @@
      *  @return Response - PLDM Response message
      */
     Response setDateTime(const pldm_msg* request, size_t payloadLength);
+
+    /** @brief Handler for setBIOSAttributeCurrentValue
+     *
+     *  @param[in] request - Request message
+     *  @param[in] payloadLength - Request message payload length
+     *  @return Response - PLDM Response message
+     */
+    Response setBIOSAttributeCurrentValue(const pldm_msg* request,
+                                          size_t payloadLength);
 };
 
 } // namespace bios