oem-ibm: Access key update support

An “Update Access Key” (UAK) is checked when applying system
firmware updates. Entitled updates are applied if the embedded
expiration date of the UAK is later than the firmware release date.

Host provides us the Firmware User Access Key (FW UAK) Date to BMC in
the genesis boot as it will have license information and only Host
has the knowledge of it. Also the BMC can use this value even when
host is not running.

The commit implements the support for access key
update in PLDM. The host sends a setFruRecordTable command with
the value to be updated.
PLDM uses the WriteKeyword method [1] since that will update both the
D-Bus cache and the VPD backplane.

[1] https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/yaml/com/ibm/VPD/Manager.interface.yaml

Signed-off-by: Pavithra Barithaya <pavithra.b@ibm.com>
Change-Id: Ib089787db3a3c42eedb833852dc48b8fa87be7e4
diff --git a/oem/ibm/libpldmresponder/fru_oem_ibm.cpp b/oem/ibm/libpldmresponder/fru_oem_ibm.cpp
index 805d0ec..8cc35d2 100644
--- a/oem/ibm/libpldmresponder/fru_oem_ibm.cpp
+++ b/oem/ibm/libpldmresponder/fru_oem_ibm.cpp
@@ -1,5 +1,6 @@
 #include "fru_oem_ibm.hpp"
 
+#include <com/ibm/VPD/Manager/client.hpp>
 #include <phosphor-logging/lg2.hpp>
 
 #include <ranges>
@@ -80,6 +81,13 @@
                                    deviceId, revisionId, classCode,
                                    subSystemVendorId, subSystemId);
             }
+
+            if (tlv->type == PLDM_OEM_IBM_FRU_FIELD_TYPE_FIRMWARE_UAK)
+            {
+                std::vector<uint8_t> value(&tlv->value[0],
+                                           &tlv->value[tlv->length]);
+                setFirmwareUAK(value);
+            }
             // length of tlv is removed from the structure pldm_fru_record_tlv
             // and the new tlv length is added back.
             dataSize += sizeof(pldm_fru_record_tlv) - sizeof(uint8_t) +
@@ -147,6 +155,32 @@
               propertyName, "ERROR", e);
     }
 }
+
+void Handler::setFirmwareUAK(const std::vector<uint8_t>& data)
+{
+    using VPDManager = sdbusplus::client::com::ibm::vpd::Manager<>;
+
+    static constexpr auto uakObjPath = "/com/ibm/VPD/Manager";
+    static constexpr auto fruPath =
+        "/xyz/openbmc_project/inventory/system/chassis/motherboard";
+
+    auto& bus = pldm::utils::DBusHandler::getBus();
+    try
+    {
+        auto service = pldm::utils::DBusHandler().getService(
+            uakObjPath, VPDManager::interface);
+        auto method = bus.new_method_call(
+            service.c_str(), uakObjPath, VPDManager::interface, "WriteKeyword");
+        method.append(static_cast<sdbusplus::message::object_path>(fruPath),
+                      "UTIL", "D8", data);
+        bus.call_noreply(method);
+    }
+    catch (const std::exception& e)
+    {
+        error("Failed to make a DBus call to VPD manager: {ERROR}", "ERROR", e);
+    }
+}
+
 } // namespace oem_ibm_fru
 } // namespace responder
 } // namespace pldm