pldm: Add support for OCC reset

BMC initiated OCC reset is translated to a SetStateEffecterStates
request for state effecter corresponding to the OCC instance.

Tested:

Invoked resetOCC path explicitly and verified that the
SetStateEffecterStates request message for OCCReset is accurate.

Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
Change-Id: I4e0d211b51151d24bf0112819fc9f201698bfac0
diff --git a/occ_manager.cpp b/occ_manager.cpp
index f59683d..8ead2a4 100644
--- a/occ_manager.cpp
+++ b/occ_manager.cpp
@@ -53,7 +53,13 @@
     statusObjects.emplace_back(std::make_unique<Status>(
         bus, event, path.c_str(), *this,
         std::bind(std::mem_fn(&Manager::statusCallBack), this,
-                  std::placeholders::_1)));
+                  std::placeholders::_1)
+#ifdef PLDM
+            ,
+        std::bind(std::mem_fn(&pldm::Interface::resetOCC), pldmHandle.get(),
+                  std::placeholders::_1)
+#endif
+            ));
 
     // Create the power cap monitor object for master occ (0)
     if (!pcap)
diff --git a/occ_status.cpp b/occ_status.cpp
index 6b19b76..502783d 100644
--- a/occ_status.cpp
+++ b/occ_status.cpp
@@ -93,6 +93,12 @@
 // Sends message to host control command handler to reset OCC
 void Status::resetOCC()
 {
+#ifdef PLDM
+    if (resetCallBack)
+    {
+        this->resetCallBack(instance);
+    }
+#else
     using namespace phosphor::logging;
     constexpr auto CONTROL_HOST_PATH = "/org/open_power/control/host0";
     constexpr auto CONTROL_HOST_INTF = "org.open_power.Control.Host";
@@ -109,6 +115,7 @@
     method.append(std::variant<uint8_t>(std::get<0>(sensorMap.at(instance))));
     bus.call_noreply(method);
     return;
+#endif
 }
 
 // Handler called by Host control command handler to convey the
diff --git a/occ_status.hpp b/occ_status.hpp
index 1617d12..24dcb0e 100644
--- a/occ_status.hpp
+++ b/occ_status.hpp
@@ -62,10 +62,18 @@
      *  @param[in] manager  - OCC manager instance
      *  @param[in] callBack - Callback handler to invoke during
      *                        property change
+     *  @param[in] resetCallBack - callback handler to invoke for resetting the
+     *                             OCC if PLDM is the host communication
+     *                             protocol
      */
     Status(sdbusplus::bus::bus& bus, EventPtr& event, const char* path,
-           const Manager& manager,
-           std::function<void(bool)> callBack = nullptr) :
+           const Manager& manager, std::function<void(bool)> callBack = nullptr
+#ifdef PLDM
+           ,
+           std::function<void(instanceID)> resetCallBack = nullptr
+#endif
+           ) :
+
         Interface(bus, getDbusPath(path).c_str(), true),
         bus(bus), path(path), callBack(callBack), instance(getInstance(path)),
         device(event,
@@ -87,6 +95,10 @@
                                        Control::Host::Command::OCCReset)),
             std::bind(std::mem_fn(&Status::hostControlEvent), this,
                       std::placeholders::_1))
+#ifdef PLDM
+        ,
+        resetCallBack(resetCallBack)
+#endif
     {
         // Check to see if we have OCC already bound.  If so, just set it
         if (device.bound())
@@ -207,6 +219,9 @@
 
         return estimatedPath;
     }
+#ifdef PLDM
+    std::function<void(instanceID)> resetCallBack = nullptr;
+#endif
 };
 
 } // namespace occ
diff --git a/pldm.cpp b/pldm.cpp
index c520340..22beea9 100644
--- a/pldm.cpp
+++ b/pldm.cpp
@@ -1,5 +1,7 @@
 #include "pldm.hpp"

 

+#include "file.hpp"

+

 #include <libpldm/entity.h>

 #include <libpldm/platform.h>

 #include <libpldm/state_set.h>

@@ -147,8 +149,203 @@
         if (propVal == "xyz.openbmc_project.State.Host.HostState.Off")

         {

             sensorToOCCInstance.clear();

+            occInstanceToEffecter.clear();

         }

     }

 }

 

+void Interface::fetchOCCEffecterInfo(

+    const PdrList& pdrs, OccInstanceToEffecter& instanceToEffecterMap,

+    CompositeEffecterCount& count, uint8_t& bootRestartPos)

+{

+    bool offsetFound = false;

+    auto pdr =

+        reinterpret_cast<const pldm_state_effecter_pdr*>(pdrs.front().data());

+    auto possibleStatesPtr = pdr->possible_states;

+    for (auto offset = 0; offset < pdr->composite_effecter_count; offset++)

+    {

+        auto possibleStates =

+            reinterpret_cast<const state_effecter_possible_states*>(

+                possibleStatesPtr);

+

+        if (possibleStates->state_set_id == PLDM_STATE_SET_BOOT_RESTART_CAUSE)

+        {

+            bootRestartPos = offset;

+            effecterCount = pdr->composite_effecter_count;

+            offsetFound = true;

+            break;

+        }

+        possibleStatesPtr += sizeof(possibleStates->state_set_id) +

+                             sizeof(possibleStates->possible_states_size) +

+                             possibleStates->possible_states_size;

+    }

+

+    if (!offsetFound)

+    {

+        return;

+    }

+

+    std::map<EntityInstance, EffecterID> entityInstMap{};

+    for (auto& pdr : pdrs)

+    {

+        auto pdrPtr =

+            reinterpret_cast<const pldm_state_effecter_pdr*>(pdr.data());

+        entityInstMap.emplace(

+            static_cast<EntityInstance>(pdrPtr->entity_instance),

+            static_cast<SensorID>(pdrPtr->effecter_id));

+    }

+

+    open_power::occ::instanceID position = start;

+    for (auto const& pair : entityInstMap)

+    {

+        occInstanceToEffecter.emplace(position, pair.second);

+        position++;

+    }

+}

+

+std::vector<uint8_t>

+    Interface::prepareSetEffecterReq(uint8_t instanceId, EffecterID effecterId,

+                                     CompositeEffecterCount effecterCount,

+                                     uint8_t bootRestartPos)

+{

+    std::vector<uint8_t> request(

+        sizeof(pldm_msg_hdr) + sizeof(effecterId) + sizeof(effecterCount) +

+        (effecterCount * sizeof(set_effecter_state_field)));

+    auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());

+    std::vector<set_effecter_state_field> stateField;

+

+    for (uint8_t effecterPos = 0; effecterPos < effecterCount; effecterPos++)

+    {

+        if (effecterPos == bootRestartPos)

+        {

+            stateField.emplace_back(set_effecter_state_field{

+                PLDM_REQUEST_SET,

+                PLDM_STATE_SET_BOOT_RESTART_CAUSE_WARM_RESET});

+        }

+        else

+        {

+            stateField.emplace_back(

+                set_effecter_state_field{PLDM_NO_CHANGE, 0});

+        }

+    }

+    auto rc = encode_set_state_effecter_states_req(

+        instanceId, effecterId, effecterCount, stateField.data(), requestMsg);

+    if (rc != PLDM_SUCCESS)

+    {

+        log<level::ERR>("encode set effecter states request returned error ",

+                        entry("RC=%d", rc));

+        request.clear();

+    }

+    return request;

+}

+

+void Interface::resetOCC(open_power::occ::instanceID occInstanceId)

+{

+    if (!isPDREffecterCacheValid())

+    {

+        PdrList pdrs{};

+

+        try

+        {

+            auto method = bus.new_method_call(

+                "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",

+                "xyz.openbmc_project.PLDM.PDR", "FindStateEffecterPDR");

+            method.append(tid, (uint16_t)PLDM_ENTITY_PROC_MODULE,

+                          (uint16_t)PLDM_STATE_SET_BOOT_RESTART_CAUSE);

+

+            auto responseMsg = bus.call(method);

+            responseMsg.read(pdrs);

+        }

+        catch (const SdBusError& e)

+        {

+            log<level::ERR>("pldm: Failed to fetch the OCC state effecter PDRs",

+                            entry("ERROR=%s", e.what()));

+        }

+

+        if (!pdrs.size())

+        {

+            log<level::ERR>("pldm: OCC state effecter PDRs not present");

+            return;

+        }

+

+        fetchOCCEffecterInfo(pdrs, occInstanceToEffecter, effecterCount,

+                             bootRestartPosition);

+    }

+

+    // Find the matching effecter for the OCC instance

+    auto effecterEntry = occInstanceToEffecter.find(occInstanceId);

+    if (effecterEntry == occInstanceToEffecter.end())

+    {

+        log<level::ERR>(

+            "pldm: Failed to find a matching effecter for OCC instance",

+            entry("OCC_INSTANCE_ID=%d", occInstanceId));

+

+        return;

+    }

+

+    uint8_t instanceId{};

+

+    try

+    {

+        auto method = bus.new_method_call(

+            "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",

+            "xyz.openbmc_project.PLDM.Requester", "GetInstanceId");

+        method.append(mctpEid);

+        auto reply = bus.call(method);

+        reply.read(instanceId);

+    }

+    catch (const SdBusError& e)

+    {

+        log<level::ERR>("pldm: GetInstanceId returned error",

+                        entry("ERROR=%s", e.what()));

+        return;

+    }

+

+    // Prepare the SetStateEffecterStates request to reset the OCC

+    auto request = prepareSetEffecterReq(instanceId, effecterEntry->second,

+                                         effecterCount, bootRestartPosition);

+

+    if (request.empty())

+    {

+        log<level::ERR>("pldm: SetStateEffecterStates request message empty");

+        return;

+    }

+

+    // Connect to MCTP scoket

+    int fd = pldm_open();

+    if (fd == -1)

+    {

+        log<level::ERR>("pldm: Failed to connect to MCTP socket");

+        return;

+    }

+    open_power::occ::FileDescriptor fileFd(fd);

+

+    // Send the PLDM request message to HBRT

+    uint8_t* response = nullptr;

+    size_t responseSize{};

+    auto rc = pldm_send_recv(mctpEid, fileFd(), request.data(), request.size(),

+                             &response, &responseSize);

+    std::unique_ptr<uint8_t, decltype(std::free)*> responsePtr{response,

+                                                               std::free};

+    if (rc)

+    {

+        log<level::ERR>("pldm: pldm_send_recv failed for OCC reset",

+                        entry("RC=%d", rc));

+    }

+

+    uint8_t completionCode{};

+    auto responseMsg = reinterpret_cast<const pldm_msg*>(responsePtr.get());

+    auto rcDecode = decode_set_state_effecter_states_resp(

+        responseMsg, responseSize - sizeof(pldm_msg_hdr), &completionCode);

+    if (rcDecode || completionCode)

+    {

+        log<level::ERR>(

+            "pldm: decode_set_state_effecter_states_resp returned error",

+            entry("RC=%d", rcDecode),

+            entry("COMPLETION_CODE=%d", completionCode));

+    }

+

+    return;

+}

+

 } // namespace pldm
\ No newline at end of file
diff --git a/pldm.hpp b/pldm.hpp
index 76d7e44..48f80c7 100644
--- a/pldm.hpp
+++ b/pldm.hpp
@@ -2,6 +2,8 @@
 

 #include "occ_status.hpp"

 

+#include <libpldm/pldm.h>

+

 #include <sdbusplus/bus/match.hpp>

 

 namespace pldm

@@ -9,20 +11,26 @@
 

 namespace MatchRules = sdbusplus::bus::match::rules;

 

+using CompositeEffecterCount = uint8_t;

+using EffecterID = uint16_t;

 using EntityType = uint16_t;

 using EntityInstance = uint16_t;

 using EventState = uint8_t;

+using OccInstanceToEffecter = std::map<open_power::occ::instanceID, EffecterID>;

 using PdrList = std::vector<std::vector<uint8_t>>;

 using SensorID = uint16_t;

 using SensorOffset = uint8_t;

 using SensorToOCCInstance = std::map<SensorID, open_power::occ::instanceID>;

 using TerminusID = uint8_t;

 

+/** @brief Hardcoded TID */

+constexpr TerminusID tid = 0;

+

 /** @brief OCC instance starts with 0 for example "occ0" */

 constexpr open_power::occ::instanceID start = 0;

 

-/** @brief Hardcoded TID */

-constexpr TerminusID tid = 0;

+/** @brief Hardcoded mctpEid for HBRT */

+constexpr mctp_eid_t mctpEid = 10;

 

 /** @class Interface

  *

@@ -78,6 +86,41 @@
                             SensorToOCCInstance& sensorInstanceMap,

                             SensorOffset& sensorOffset);

 

+    /** @brief Fetch the OCC state effecter PDRs and populate the cache with

+     *         OCC instance to EffecterID information.

+     *

+     *  @param[in] pdrs - OCC state effecter PDRs

+     *  @param[out] instanceToEffecterMap - map of OCC instance to effecterID

+     *  @param[out] count - sensor offset of interested state set ID

+     *  @param[out] bootRestartPos - position of Boot/Restart Cause stateSetID

+     */

+    void fetchOCCEffecterInfo(const PdrList& pdrs,

+                              OccInstanceToEffecter& instanceToEffecterMap,

+                              CompositeEffecterCount& count,

+                              uint8_t& bootRestartPos);

+

+    /** @brief Prepare the request for SetStateEffecterStates command

+     *

+     *  @param[in] instanceId - PLDM instanceID

+     *  @param[in] instanceToEffecterMap - map of OCC instance to effecterID

+     *  @param[in] count - compositeEffecterCount for OCC reset effecter PDR

+     *  @param[in] bootRestartPos - position of Boot/Restart Cause stateSetID

+     *

+     *  @return PLDM request message to be sent to host for OCC reset, empty

+     *          response in the case of failure.

+     */

+    std::vector<uint8_t>

+        prepareSetEffecterReq(uint8_t instanceId, EffecterID effecterId,

+                              CompositeEffecterCount effecterCount,

+                              uint8_t bootRestartPos);

+

+    /** @brief Send the PLDM message to reset the OCC

+     *

+     *  @param[in] instanceId - OCC instance to reset

+     *

+     */

+    void resetOCC(open_power::occ::instanceID occInstanceId);

+

   private:

     /** @brief reference to the systemd bus*/

     sdbusplus::bus::bus& bus;

@@ -104,6 +147,18 @@
      */

     SensorOffset sensorOffset;

 

+    /** @brief OCC Instance mapping to PLDM Effecter ID

+     */

+    OccInstanceToEffecter occInstanceToEffecter;

+

+    /** @brief compositeEffecterCount for OCC reset state effecter PDR */

+    CompositeEffecterCount effecterCount = 0;

+

+    /** @brief Position of Boot/Restart Cause stateSetID in OCC state

+     *         effecter PDR

+     */

+    uint8_t bootRestartPosition = 0;

+

     /** @brief When the OCC state changes host sends PlatformEventMessage

      *         StateSensorEvent, this function processes the D-Bus signal

      *         with the sensor event information and invokes the callback

@@ -130,6 +185,16 @@
     {

         return (sensorToOCCInstance.empty() ? false : true);

     }

+

+    /** @brief Check if the PDR cache for PLDM OCC effecters is valid

+     *

+     *  @return true if cache is populated and false if the cache is not

+     *          populated.

+     */

+    auto isPDREffecterCacheValid()

+    {

+        return (occInstanceToEffecter.empty() ? false : true);

+    }

 };

 

 } // namespace pldm