oem ibm: infrastructure for oem handlers

1. This commit adds the framework for an oem handler
which can be used by specific oem use-cases
for implementing various commands.

2. This commit adds implementation for getStateSensorReadings
and setStateEffecterStates commands for oem state sets.

3. Also adds implementation for inband code update.

Change-Id: Ib38a66ee381dd06b93f6a9313d51de1c23e6ee65
Signed-off-by: Sampa Misra <sampmisr@in.ibm.com>
diff --git a/oem/ibm/libpldmresponder/file_io.hpp b/oem/ibm/libpldmresponder/file_io.hpp
index 771d038..9f6492c 100644
--- a/oem/ibm/libpldmresponder/file_io.hpp
+++ b/oem/ibm/libpldmresponder/file_io.hpp
@@ -7,6 +7,7 @@
 #include "oem/ibm/libpldm/host.h"
 
 #include "common/utils.hpp"
+#include "oem_ibm_handler.hpp"
 #include "pldmd/handler.hpp"
 
 #include <fcntl.h>
@@ -157,7 +158,8 @@
 class Handler : public CmdHandler
 {
   public:
-    Handler()
+    Handler(oem_platform::Handler* oemPlatformHandler) :
+        oemPlatformHandler(oemPlatformHandler)
     {
         handlers.emplace(PLDM_READ_FILE_INTO_MEMORY,
                          [this](const pldm_msg* request, size_t payloadLength) {
@@ -312,6 +314,9 @@
      *  @return PLDM response message
      */
     Response newFileAvailable(const pldm_msg* request, size_t payloadLength);
+
+  private:
+    oem_platform::Handler* oemPlatformHandler;
 };
 
 } // namespace oem_ibm
diff --git a/oem/ibm/libpldmresponder/inband_code_update.cpp b/oem/ibm/libpldmresponder/inband_code_update.cpp
new file mode 100644
index 0000000..f17b209
--- /dev/null
+++ b/oem/ibm/libpldmresponder/inband_code_update.cpp
@@ -0,0 +1,217 @@
+#include "inband_code_update.hpp"
+
+#include "oem_ibm_handler.hpp"
+#include "xyz/openbmc_project/Common/error.hpp"
+
+#include <sdbusplus/server.hpp>
+#include <xyz/openbmc_project/Dump/NewDump/server.hpp>
+
+#include <exception>
+
+namespace pldm
+{
+
+namespace responder
+{
+using namespace oem_ibm_platform;
+
+std::string CodeUpdate::fetchCurrentBootSide()
+{
+    return currBootSide;
+}
+
+std::string CodeUpdate::fetchNextBootSide()
+{
+    return nextBootSide;
+}
+
+int CodeUpdate::setCurrentBootSide(const std::string& currSide)
+{
+    currBootSide = currSide;
+    return PLDM_SUCCESS;
+}
+
+int CodeUpdate::setNextBootSide(const std::string& nextSide)
+{
+    nextBootSide = nextSide;
+    std::string objPath{};
+    if (nextBootSide == currBootSide)
+    {
+        objPath = runningVersion;
+    }
+    else
+    {
+        objPath = nonRunningVersion;
+    }
+    if (objPath.empty())
+    {
+        std::cerr << "no nonRunningVersion present \n";
+        return PLDM_PLATFORM_INVALID_STATE_VALUE;
+    }
+
+    pldm::utils::DBusMapping dbusMapping{objPath, redundancyIntf, "Priority",
+                                         "uint8_t"};
+    uint8_t val = 0;
+    pldm::utils::PropertyValue value = static_cast<uint8_t>(val);
+    try
+    {
+        dBusIntf->setDbusProperty(dbusMapping, value);
+    }
+    catch (const std::exception& e)
+    {
+        std::cerr << "failed to set the next boot side to " << objPath.c_str()
+                  << " ERROR=" << e.what() << "\n";
+        return PLDM_ERROR;
+    }
+    return PLDM_SUCCESS;
+}
+
+void CodeUpdate::setVersions()
+{
+    static constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper";
+    static constexpr auto functionalObjPath =
+        "/xyz/openbmc_project/software/functional";
+    static constexpr auto activeObjPath =
+        "/xyz/openbmc_project/software/active";
+    static constexpr auto propIntf = "org.freedesktop.DBus.Properties";
+
+    auto& bus = dBusIntf->getBus();
+
+    try
+    {
+        auto method = bus.new_method_call(mapperService, functionalObjPath,
+                                          propIntf, "Get");
+        method.append("xyz.openbmc_project.Association", "endpoints");
+        std::variant<std::vector<std::string>> paths;
+
+        auto reply = bus.call(method);
+        reply.read(paths);
+
+        runningVersion = std::get<std::vector<std::string>>(paths)[0];
+
+        auto method1 =
+            bus.new_method_call(mapperService, activeObjPath, propIntf, "Get");
+        method1.append("xyz.openbmc_project.Association", "endpoints");
+
+        auto reply1 = bus.call(method1);
+        reply1.read(paths);
+        for (const auto& path : std::get<std::vector<std::string>>(paths))
+        {
+            if (path != runningVersion)
+            {
+                nonRunningVersion = path;
+                break;
+            }
+        }
+    }
+    catch (const std::exception& e)
+    {
+        std::cerr << "failed to make a d-bus call to Object Mapper "
+                     "Association, ERROR="
+                  << e.what() << "\n";
+        return;
+    }
+
+    using namespace sdbusplus::bus::match::rules;
+    captureNextBootSideChange.push_back(
+        std::make_unique<sdbusplus::bus::match::match>(
+            pldm::utils::DBusHandler::getBus(),
+            propertiesChanged(runningVersion, redundancyIntf),
+            [this](sdbusplus::message::message& msg) {
+                DbusChangedProps props;
+                std::string iface;
+                msg.read(iface, props);
+                processPriorityChangeNotification(props);
+            }));
+    fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>(
+        pldm::utils::DBusHandler::getBus(),
+        "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
+        "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
+        [this](sdbusplus::message::message& msg) {
+            DBusInterfaceAdded interfaces;
+            sdbusplus::message::object_path path;
+            msg.read(path, interfaces);
+            for (auto& interface : interfaces)
+            {
+                if (interface.first ==
+                    "xyz.openbmc_project.Software.Activation")
+                {
+                    newImageId = path.str;
+                    break;
+                }
+            }
+        });
+}
+
+void CodeUpdate::processPriorityChangeNotification(
+    const DbusChangedProps& chProperties)
+{
+    static constexpr auto propName = "Priority";
+    const auto it = chProperties.find(propName);
+    if (it == chProperties.end())
+    {
+        return;
+    }
+    uint8_t newVal = std::get<uint8_t>(it->second);
+    nextBootSide = (newVal == 0) ? currBootSide
+                                 : ((currBootSide == Tside) ? Pside : Tside);
+}
+
+void CodeUpdate::setOemPlatformHandler(
+    pldm::responder::oem_platform::Handler* handler)
+{
+    oemPlatformHandler = handler;
+}
+
+uint8_t fetchBootSide(uint16_t entityInstance, CodeUpdate* codeUpdate)
+{
+    uint8_t sensorOpState = tSideNum;
+
+    if (entityInstance == 0)
+    {
+        auto currSide = codeUpdate->fetchCurrentBootSide();
+        if (currSide == Pside)
+        {
+            sensorOpState = pSideNum;
+        }
+    }
+    else if (entityInstance == 1)
+    {
+        auto nextSide = codeUpdate->fetchNextBootSide();
+        if (nextSide == Pside)
+        {
+            sensorOpState = pSideNum;
+        }
+    }
+    else
+    {
+        sensorOpState = PLDM_SENSOR_UNKNOWN;
+    }
+
+    return sensorOpState;
+}
+
+int setBootSide(uint16_t entityInstance, uint8_t currState,
+                const std::vector<set_effecter_state_field>& stateField,
+                CodeUpdate* codeUpdate)
+{
+    int rc = PLDM_SUCCESS;
+    auto side = (stateField[currState].effecter_state == pSideNum) ? "P" : "T";
+
+    if (entityInstance == 0)
+    {
+        rc = codeUpdate->setCurrentBootSide(side);
+    }
+    else if (entityInstance == 1)
+    {
+        rc = codeUpdate->setNextBootSide(side);
+    }
+    else
+    {
+        rc = PLDM_PLATFORM_INVALID_STATE_VALUE;
+    }
+    return rc;
+}
+
+} // namespace responder
+} // namespace pldm
diff --git a/oem/ibm/libpldmresponder/inband_code_update.hpp b/oem/ibm/libpldmresponder/inband_code_update.hpp
new file mode 100644
index 0000000..6607de0
--- /dev/null
+++ b/oem/ibm/libpldmresponder/inband_code_update.hpp
@@ -0,0 +1,128 @@
+#pragma once
+
+#include "common/utils.hpp"
+#include "libpldmresponder/platform.hpp"
+
+#include <string>
+
+using namespace pldm::utils;
+namespace pldm
+{
+namespace responder
+{
+
+static constexpr uint8_t pSideNum = 1;
+static constexpr uint8_t tSideNum = 2;
+static constexpr auto Pside = "P";
+static constexpr auto Tside = "T";
+
+static constexpr auto redundancyIntf =
+    "xyz.openbmc_project.Software.RedundancyPriority";
+
+/** @class CodeUpdate
+ *
+ *  @brief This class performs the necessary operation in pldm for
+ *         inband code update. That includes taking actions on the
+ *         setStateEffecterStates calls from Host and also sending
+ *         notification to phosphor-software-manager app
+ */
+class CodeUpdate
+{
+  public:
+    /** @brief Constructor to create an inband codeupdate object
+     *  @param[in] dBusIntf - D-Bus handler pointer
+     */
+    CodeUpdate(const pldm::utils::DBusHandler* dBusIntf) : dBusIntf(dBusIntf)
+    {
+        currBootSide = Tside;
+        nextBootSide = Tside;
+    }
+
+    /* @brief Method to return the current boot side
+     */
+    std::string fetchCurrentBootSide();
+
+    /* @brief Method to return the next boot side
+     */
+    std::string fetchNextBootSide();
+
+    /* @brief Method to set the current boot side or
+     *        perform a rename operation on current boot side
+     * @param[in] currSide - current side to be set to
+     * @return PLDM_SUCCESS codes
+     */
+    int setCurrentBootSide(const std::string& currSide);
+
+    /* @brief Method to set the next boot side
+     * @param[in] nextSide - next boot side to be set to
+     * @return PLDM_SUCCESS codes
+     */
+    int setNextBootSide(const std::string& nextSide);
+
+    /* @brief Method to set the running and non-running
+     *        images
+     */
+    virtual void setVersions();
+
+    /* @brief Method to return the newly upoaded image id in
+     *        /tmp
+     */
+    std::string fetchnewImageId()
+    {
+        return newImageId;
+    }
+
+    /* @brief Method to set the oem platform handler in CodeUpdate class */
+    void setOemPlatformHandler(pldm::responder::oem_platform::Handler* handler);
+
+    virtual ~CodeUpdate()
+    {}
+
+  private:
+    std::string currBootSide;      //!< current boot side
+    std::string nextBootSide;      //!< next boot side
+    std::string runningVersion;    //!< currently running image
+    std::string nonRunningVersion; //!< alternate image
+    std::string newImageId;        //!< new image id
+    bool codeUpdateInProgress =
+        false; //!< indicates whether codeupdate is going on
+    const pldm::utils::DBusHandler* dBusIntf; //!< D-Bus handler
+    std::vector<std::unique_ptr<sdbusplus::bus::match::match>>
+        captureNextBootSideChange; //!< vector to catch the D-Bus property
+                                   //!< change for next boot side
+    std::unique_ptr<sdbusplus::bus::match::match>
+        fwUpdateMatcher; //!< pointer to capture the interface added signal for
+                         //!< new image
+    pldm::responder::oem_platform::Handler*
+        oemPlatformHandler; //!< oem platform handler
+
+    /* @brief Method to take action when the subscribed D-Bus property is
+     *        changed
+     * @param[in] chProperties - list of properties which have changed
+     * @return - none
+     */
+
+    void
+        processPriorityChangeNotification(const DbusChangedProps& chProperties);
+};
+
+/* @brief Method to fetch current or next boot side
+ * @param[in] entityInstance - entity instance for the sensor
+ * @param[in] codeUpdate - pointer to the CodeUpdate object
+ *
+ * @return - boot side
+ */
+uint8_t fetchBootSide(uint16_t entityInstance, CodeUpdate* codeUpdate);
+
+/* @brief Method to set current or next  boot side
+ * @param[in] entityInstance - entity instance for the effecter
+ * @param[in] currState - state to be set
+ * @param[in] stateField - state field set as sent by Host
+ * @return - PLDM_SUCCESS codes
+ */
+int setBootSide(uint16_t entityInstance, uint8_t currState,
+                const std::vector<set_effecter_state_field>& stateField,
+                CodeUpdate* codeUpdate);
+
+} // namespace responder
+} // namespace pldm
diff --git a/oem/ibm/libpldmresponder/oem_ibm_handler.cpp b/oem/ibm/libpldmresponder/oem_ibm_handler.cpp
new file mode 100644
index 0000000..2e6bd4c
--- /dev/null
+++ b/oem/ibm/libpldmresponder/oem_ibm_handler.cpp
@@ -0,0 +1,83 @@
+#include "oem_ibm_handler.hpp"
+
+#include "libpldm/entity.h"
+
+namespace pldm
+{
+
+namespace responder
+{
+
+namespace oem_ibm_platform
+{
+
+int pldm::responder::oem_ibm_platform::Handler::
+    getOemStateSensorReadingsHandler(
+        EntityType entityType, EntityInstance entityInstance,
+        StateSetId stateSetId, CompositeCount compSensorCnt,
+        std::vector<get_sensor_state_field>& stateField)
+{
+    int rc = PLDM_SUCCESS;
+    stateField.clear();
+
+    for (size_t i = 0; i < compSensorCnt; i++)
+    {
+        uint8_t sensorOpState{};
+        if (entityType == PLDM_ENTITY_VIRTUAL_MACHINE_MANAGER &&
+            stateSetId == PLDM_OEM_IBM_BOOT_STATE)
+        {
+            sensorOpState = fetchBootSide(entityInstance, codeUpdate);
+        }
+        else
+        {
+            rc = PLDM_PLATFORM_INVALID_STATE_VALUE;
+            break;
+        }
+        stateField.push_back({PLDM_SENSOR_ENABLED, PLDM_SENSOR_UNKNOWN,
+                              PLDM_SENSOR_UNKNOWN, sensorOpState});
+    }
+    return rc;
+}
+
+int pldm::responder::oem_ibm_platform::Handler::
+    oemSetStateEffecterStatesHandler(
+        EntityType entityType, EntityInstance entityInstance,
+        StateSetId stateSetId, CompositeCount compEffecterCnt,
+        const std::vector<set_effecter_state_field>& stateField)
+{
+    int rc = PLDM_SUCCESS;
+
+    for (uint8_t currState = 0; currState < compEffecterCnt; ++currState)
+    {
+        if (stateField[currState].set_request == PLDM_REQUEST_SET)
+        {
+            if (entityType == PLDM_ENTITY_VIRTUAL_MACHINE_MANAGER &&
+                stateSetId == PLDM_OEM_IBM_BOOT_STATE)
+            {
+                rc = setBootSide(entityInstance, currState, stateField,
+                                 codeUpdate);
+            }
+            else
+            {
+                rc = PLDM_PLATFORM_SET_EFFECTER_UNSUPPORTED_SENSORSTATE;
+            }
+        }
+        if (rc != PLDM_SUCCESS)
+        {
+            break;
+        }
+    }
+    return rc;
+}
+
+void pldm::responder::oem_ibm_platform::Handler::setPlatformHandler(
+    pldm::responder::platform::Handler* handler)
+{
+    platformHandler = handler;
+}
+
+} // namespace oem_ibm_platform
+
+} // namespace responder
+
+} // namespace pldm
diff --git a/oem/ibm/libpldmresponder/oem_ibm_handler.hpp b/oem/ibm/libpldmresponder/oem_ibm_handler.hpp
new file mode 100644
index 0000000..2d294f0
--- /dev/null
+++ b/oem/ibm/libpldmresponder/oem_ibm_handler.hpp
@@ -0,0 +1,57 @@
+#pragma once
+
+#include "inband_code_update.hpp"
+#include "libpldmresponder/oem_handler.hpp"
+#include "libpldmresponder/platform.hpp"
+
+namespace pldm
+{
+
+namespace responder
+{
+
+namespace oem_ibm_platform
+{
+
+#define PLDM_OEM_IBM_FIRMWARE_UPDATE_STATE 32768
+#define PLDM_OEM_IBM_BOOT_STATE 32769
+
+class Handler : public oem_platform::Handler
+{
+  public:
+    Handler(const pldm::utils::DBusHandler* dBusIntf,
+            pldm::responder::CodeUpdate* codeUpdate) :
+        oem_platform::Handler(dBusIntf),
+        codeUpdate(codeUpdate), platformHandler(nullptr)
+    {
+        codeUpdate->setVersions();
+    }
+
+    int getOemStateSensorReadingsHandler(
+        EntityType entityType, EntityInstance entityInstance,
+        StateSetId stateSetId, CompositeCount compSensorCnt,
+        std::vector<get_sensor_state_field>& stateField);
+
+    int oemSetStateEffecterStatesHandler(
+        EntityType entityType, EntityInstance entityInstance,
+        StateSetId stateSetId, CompositeCount compEffecterCnt,
+        const std::vector<set_effecter_state_field>& stateField);
+
+    /** @brief Method to set the platform handler in the
+     *         oem_ibm_handler class
+     *  @param[in] handler - pointer to PLDM platform handler
+     */
+    void setPlatformHandler(pldm::responder::platform::Handler* handler);
+
+    ~Handler() = default;
+
+    pldm::responder::CodeUpdate* codeUpdate; //!< pointer to CodeUpdate object
+    pldm::responder::platform::Handler*
+        platformHandler; //!< pointer to PLDM platform handler
+};
+
+} // namespace oem_ibm_platform
+
+} // namespace responder
+
+} // namespace pldm