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/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