Add SBE timeout handling

Add an Error instance to detect SBE timeouts in the driver.
Refactor the PLDM code to handle another requeset type (the HRESET) and
another type of sensor event (success or failure of the HRESET).
Handle the timeout and the PLDM event state change in the Manager by
changing the SBE state and requesting an SBE dump if necessary.

Signed-off-by: Eddie James <eajames@linux.ibm.com>
Change-Id: I30d8f9de1914d9808ddb7b547cc57085a5e5779e
diff --git a/meson.build b/meson.build
index e6b6c6c..7d48b5d 100644
--- a/meson.build
+++ b/meson.build
@@ -10,6 +10,8 @@
     ]
 )
 
+cxx = meson.get_compiler('cpp')
+
 conf_data = configuration_data()
 conf_data.set_quoted('OCC_CONTROL_BUSNAME', 'org.open_power.OCC.Control')
 conf_data.set_quoted('OCC_CONTROL_ROOT', '/org/open_power/control')
@@ -155,6 +157,8 @@
         )
         deps += [
             libpldm_dep,
+            cxx.find_library('pdbg'),
+            cxx.find_library('phal'),
         ]
         sources += [
             'pldm.cpp',
diff --git a/occ_device.cpp b/occ_device.cpp
index 2ee5f34..c95c0f3 100644
--- a/occ_device.cpp
+++ b/occ_device.cpp
@@ -1,5 +1,6 @@
 #include "occ_device.hpp"
 
+#include "occ_manager.hpp"
 #include "occ_status.hpp"
 
 #include <iostream>
@@ -48,6 +49,24 @@
     return (master != 0);
 }
 
+void Device::errorCallback(bool error)
+{
+    if (error)
+    {
+        statusObject.deviceError();
+    }
+}
+
+#ifdef PLDM
+void Device::timeoutCallback(bool error)
+{
+    if (error)
+    {
+        managerObject.sbeTimeout(instance);
+    }
+}
+#endif
+
 void Device::throttleProcTempCallback(bool error)
 {
     statusObject.throttleProcTemp(error);
diff --git a/occ_device.hpp b/occ_device.hpp
index ef2fb06..106604e 100644
--- a/occ_device.hpp
+++ b/occ_device.hpp
@@ -42,16 +42,30 @@
      *  @param[in] manager  - OCC manager instance
      *  @param[in] status   - Status instance
      *  @param[in] instance - OCC instance number
-     *  @param[in] callback - Optional callback on errors
      */
-    Device(EventPtr& event, const fs::path& path, const Manager& manager,
-           Status& status, unsigned int instance = 0,
-           std::function<void(bool)> callBack = nullptr) :
+    Device(EventPtr& event, const fs::path& path, Manager& manager,
+           Status& status, unsigned int instance = 0) :
         config(getPathBack(path)),
-        devPath(path), statusObject(status),
-        error(event, path / "occ_error", callBack),
+        devPath(path), instance(instance), statusObject(status),
+        managerObject(manager),
+        error(event, path / "occ_error",
+              std::bind(std::mem_fn(&Device::errorCallback), this,
+                        std::placeholders::_1)),
+        timeout(event,
+                path /
+                    fs::path("../../sbefifo" + std::to_string(instance + 1)) /
+                    "timeout",
+#ifdef PLDM
+                std::bind(std::mem_fn(&Device::timeoutCallback), this,
+                          std::placeholders::_1)
+#else
+                nullptr
+#endif
+                    ),
         ffdc(event, path / "ffdc", instance),
-        presence(event, path / "occs_present", manager, callBack),
+        presence(event, path / "occs_present", manager,
+                 std::bind(std::mem_fn(&Device::errorCallback), this,
+                           std::placeholders::_1)),
         throttleProcTemp(
             event, path / "occ_dvfs_overtemp",
             std::bind(std::mem_fn(&Device::throttleProcTempCallback), this,
@@ -126,6 +140,15 @@
             // nothing to do if there is no FFDC file
         }
 
+        try
+        {
+            timeout.addWatch(poll);
+        }
+        catch (const std::exception& e)
+        {
+            // nothing to do if there is no SBE timeout file
+        }
+
         error.addWatch(poll);
     }
 
@@ -136,6 +159,7 @@
         presence.removeWatch();
         ffdc.removeWatch();
         error.removeWatch();
+        timeout.removeWatch();
         throttleMemTemp.removeWatch();
         throttleProcPower.removeWatch();
         throttleProcTemp.removeWatch();
@@ -167,6 +191,9 @@
     /** @brief This directory contains the error files */
     const fs::path devPath;
 
+    /** @brief OCC instance ID */
+    const unsigned int instance;
+
     /**  @brief To bind the device to the OCC driver, do:
      *
      *    Write occ<#>-dev0 to: /sys/bus/platform/drivers/occ-hwmon/bind
@@ -181,9 +208,15 @@
     /**  Store the associated Status instance */
     Status& statusObject;
 
+    /** Store the parent Manager instance */
+    Manager& managerObject;
+
     /** Abstraction of error monitoring */
     Error error;
 
+    /** Abstraction of SBE timeout monitoring */
+    Error timeout;
+
     /** SBE FFDC monitoring */
     FFDC ffdc;
 
@@ -210,6 +243,20 @@
         return;
     }
 
+    /** @brief callback for OCC error and presence monitoring
+     *
+     * @param[in] error - True if an error is reported, false otherwise
+     */
+    void errorCallback(bool error);
+
+#ifdef PLDM
+    /** @brief callback for SBE timeout monitoring
+     *
+     * @param[in] error - True if an error is reported, false otherwise
+     */
+    void timeoutCallback(bool error);
+#endif
+
     /** @brief callback for the proc temp throttle event
      *
      *  @param[in] error - True if an error is reported, false otherwise
diff --git a/occ_manager.cpp b/occ_manager.cpp
index b83feb3..ed69274 100644
--- a/occ_manager.cpp
+++ b/occ_manager.cpp
@@ -248,10 +248,163 @@
 #endif
 
 #ifdef PLDM
+void Manager::sbeTimeout(unsigned int instance)
+{
+    log<level::INFO>("SBE timeout, requesting HRESET",
+                     entry("SBE=%d", instance));
+
+    setSBEState(instance, SBE_STATE_NOT_USABLE);
+
+    pldmHandle->sendHRESET(instance);
+}
+
 bool Manager::updateOCCActive(instanceID instance, bool status)
 {
     return (statusObjects[instance])->occActive(status);
 }
+
+void Manager::sbeHRESETResult(instanceID instance, bool success)
+{
+    if (success)
+    {
+        log<level::INFO>("HRESET succeeded", entry("SBE=%d", instance));
+
+        setSBEState(instance, SBE_STATE_BOOTED);
+
+        return;
+    }
+
+    setSBEState(instance, SBE_STATE_FAILED);
+
+    if (sbeCanDump(instance))
+    {
+        constexpr auto path = "/org/openpower/dump";
+        constexpr auto interface = "xyz.openbmc_project.Dump.Create";
+        constexpr auto function = "CreateDump";
+
+        log<level::INFO>("HRESET failed, triggering SBE dump",
+                         entry("SBE=%d", instance));
+
+        auto& bus = utils::getBus();
+        uint32_t src6 = instance << 16;
+        uint32_t logId =
+            FFDC::createPEL("org.open_power.Processor.Error.SbeChipOpTimeout",
+                            src6, "SBE command timeout");
+
+        try
+        {
+            std::string service = utils::getService(path, interface);
+            auto method =
+                bus.new_method_call(service.c_str(), path, interface, function);
+
+            std::map<std::string, std::variant<std::string, uint64_t>>
+                createParams{
+                    {"com.ibm.Dump.Create.CreateParameters.ErrorLogId",
+                     uint64_t(logId)},
+                    {"com.ibm.Dump.Create.CreateParameters.DumpType",
+                     "com.ibm.Dump.Create.DumpType.SBE"},
+                    {"com.ibm.Dump.Create.CreateParameters.FailingUnitId",
+                     uint64_t(instance)},
+                };
+
+            method.append(createParams);
+
+            auto response = bus.call(method);
+        }
+        catch (const sdbusplus::exception::exception& e)
+        {
+            constexpr auto ERROR_DUMP_DISABLED =
+                "xyz.openbmc_project.Dump.Create.Error.Disabled";
+            if (e.name() == ERROR_DUMP_DISABLED)
+            {
+                log<level::INFO>("Dump is disabled, skipping");
+            }
+            else
+            {
+                log<level::ERR>("Dump failed");
+            }
+        }
+    }
+}
+
+bool Manager::sbeCanDump(unsigned int instance)
+{
+    struct pdbg_target* proc = getPdbgTarget(instance);
+
+    if (!proc)
+    {
+        // allow the dump in the error case
+        return true;
+    }
+
+    try
+    {
+        if (!openpower::phal::sbe::isDumpAllowed(proc))
+        {
+            return false;
+        }
+
+        if (openpower::phal::pdbg::isSbeVitalAttnActive(proc))
+        {
+            return false;
+        }
+    }
+    catch (openpower::phal::exception::SbeError& e)
+    {
+        log<level::INFO>("Failed to query SBE state");
+    }
+
+    // allow the dump in the error case
+    return true;
+}
+
+void Manager::setSBEState(unsigned int instance, enum sbe_state state)
+{
+    struct pdbg_target* proc = getPdbgTarget(instance);
+
+    if (!proc)
+    {
+        return;
+    }
+
+    try
+    {
+        openpower::phal::sbe::setState(proc, state);
+    }
+    catch (const openpower::phal::exception::SbeError& e)
+    {
+        log<level::ERR>("Failed to set SBE state");
+    }
+}
+
+struct pdbg_target* Manager::getPdbgTarget(unsigned int instance)
+{
+    if (!pdbgInitialized)
+    {
+        try
+        {
+            openpower::phal::pdbg::init();
+            pdbgInitialized = true;
+        }
+        catch (const openpower::phal::exception::PdbgError& e)
+        {
+            log<level::ERR>("pdbg initialization failed");
+            return nullptr;
+        }
+    }
+
+    struct pdbg_target* proc = nullptr;
+    pdbg_for_each_class_target("proc", proc)
+    {
+        if (pdbg_target_index(proc) == instance)
+        {
+            return proc;
+        }
+    }
+
+    log<level::ERR>("Failed to get pdbg target");
+    return nullptr;
+}
 #endif
 
 void Manager::pollerTimerExpired()
@@ -591,6 +744,5 @@
     return;
 }
 #endif
-
 } // namespace occ
 } // namespace open_power
diff --git a/occ_manager.hpp b/occ_manager.hpp
index 5b97abc..e6e7a18 100644
--- a/occ_manager.hpp
+++ b/occ_manager.hpp
@@ -4,6 +4,8 @@
 #include "occ_status.hpp"
 #ifdef PLDM
 #include "pldm.hpp"
+
+#include <libphal.H>
 #endif
 #include "powercap.hpp"
 #include "utils.hpp"
@@ -74,6 +76,8 @@
         ,
         pldmHandle(std::make_unique<pldm::Interface>(
             std::bind(std::mem_fn(&Manager::updateOCCActive), this,
+                      std::placeholders::_1, std::placeholders::_2),
+            std::bind(std::mem_fn(&Manager::sbeHRESETResult), this,
                       std::placeholders::_1, std::placeholders::_2)))
 #endif
 #ifdef POWER10
@@ -98,6 +102,15 @@
         return activeCount;
     }
 
+#ifdef PLDM
+    /** @brief Called by a Device to report that the SBE timed out
+     *         and appropriate action should be taken
+     *
+     * @param[in] instance - the OCC instance id
+     */
+    void sbeTimeout(unsigned int instance);
+#endif
+
   private:
     /** @brief Creates the OCC D-Bus objects.
      */
@@ -195,6 +208,42 @@
      */
     bool updateOCCActive(instanceID instance, bool status);
 
+    /** @brief Callback handler invoked by PLDM sensor change when
+     *         the HRESET succeeds or fails.
+     *
+     *  @param[in] instance - the SBE instance id
+     *  @param[in] success - true if the HRESET succeeded, otherwise false
+     */
+    void sbeHRESETResult(instanceID instance, bool success);
+
+    /** @brief Helper function to check whether an SBE dump should be collected
+     *         now.
+     *
+     *  @param[in] instance - the SBE instance id
+     *
+     *  @return true if an SBE dump should be collected and false if not
+     */
+    bool sbeCanDump(unsigned int instance);
+
+    /** @brief Helper function to set the SBE state through PDBG/PHAL
+     *
+     * @param[in] instance - instance of the SBE
+     * @param[in] state - the state to which the SBE should be set
+     *
+     */
+    void setSBEState(unsigned int instance, enum sbe_state state);
+
+    /** @brief Helper function to get the SBE instance PDBG processor target
+     *
+     * @param[in] instance - the SBE instance id
+     *
+     * @return a pointer to the PDBG target
+     */
+    struct pdbg_target* getPdbgTarget(unsigned int instance);
+
+    /** @brief Whether pdbg_targets_init has been called */
+    bool pdbgInitialized = false;
+
     std::unique_ptr<pldm::Interface> pldmHandle = nullptr;
 #endif
 
diff --git a/occ_status.cpp b/occ_status.cpp
index b1893fd..eaa6080 100644
--- a/occ_status.cpp
+++ b/occ_status.cpp
@@ -92,17 +92,13 @@
 }
 
 // Callback handler when a device error is reported.
-void Status::deviceErrorHandler(bool error)
+void Status::deviceError()
 {
-    // Make sure we have an error
-    if (error)
-    {
-        // This would deem OCC inactive
-        this->occActive(false);
+    // This would deem OCC inactive
+    this->occActive(false);
 
-        // Reset the OCC
-        this->resetOCC();
-    }
+    // Reset the OCC
+    this->resetOCC();
 }
 
 // Sends message to host control command handler to reset OCC
diff --git a/occ_status.hpp b/occ_status.hpp
index 606a890..7356add 100644
--- a/occ_status.hpp
+++ b/occ_status.hpp
@@ -69,7 +69,7 @@
      *                             OCC if PLDM is the host communication
      *                             protocol
      */
-    Status(EventPtr& event, const char* path, const Manager& manager,
+    Status(EventPtr& event, const char* path, Manager& manager,
            std::function<void(bool)> callBack = nullptr
 #ifdef PLDM
            ,
@@ -86,9 +86,7 @@
                fs::path(DEV_PATH) /
                    fs::path(sysfsName + "." + std::to_string(instance + 1)),
 #endif
-               manager, *this, instance,
-               std::bind(std::mem_fn(&Status::deviceErrorHandler), this,
-                         std::placeholders::_1)),
+               manager, *this, instance),
         hostControlSignal(
             utils::getBus(),
             sdbusRule::type::signal() + sdbusRule::member("CommandComplete") +
@@ -163,6 +161,9 @@
     /** @brief Read OCC state (will trigger kernel to poll the OCC) */
     void readOccState();
 
+    /** @brief Called when device errors are detected */
+    void deviceError();
+
 #ifdef POWER10
     /** @brief Handle additional tasks when the OCCs reach active state */
     void occsWentActive();
@@ -210,12 +211,6 @@
     /** @brief Command object to send commands to the OCC */
     OccCommand occCmd;
 
-    /** @brief Callback handler when device errors are detected
-     *
-     *  @param[in]  error - True if an error is reported, false otherwise
-     */
-    void deviceErrorHandler(bool error);
-
     /** @brief Callback function on host control signals
      *
      *  @param[in]  msg - Data associated with subscribed signal
diff --git a/pldm.cpp b/pldm.cpp
index 093869d..085e19e 100644
--- a/pldm.cpp
+++ b/pldm.cpp
@@ -9,15 +9,48 @@
 
 #include <phosphor-logging/log.hpp>
 
+#define PLDM_OEM_IBM_SBE_MAINTENANCE_STATE 32772
+#define SBE_RETRY_REQUIRED 0x2
+
+#define PLDM_OEM_IBM_SBE_HRESET_STATE 32775
+#define SBE_HRESET_NOT_READY 0x1
+#define SBE_HRESET_READY 0x2
+#define SBE_HRESET_FAILED 0x3
+
 namespace pldm
 {
 
 using namespace phosphor::logging;
 
-void Interface::fetchOCCSensorInfo(const PdrList& pdrs,
-                                   SensorToOCCInstance& sensorInstanceMap,
-                                   SensorOffset& sensorOffset)
+void Interface::fetchSensorInfo(uint16_t stateSetId,
+                                SensorToInstance& sensorInstanceMap,
+                                SensorOffset& sensorOffset)
 {
+    PdrList pdrs{};
+
+    auto& bus = open_power::occ::utils::getBus();
+    try
+    {
+        auto method = bus.new_method_call(
+            "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
+            "xyz.openbmc_project.PLDM.PDR", "FindStateSensorPDR");
+        method.append(tid, (uint16_t)PLDM_ENTITY_PROC, stateSetId);
+
+        auto responseMsg = bus.call(method);
+        responseMsg.read(pdrs);
+    }
+    catch (const sdbusplus::exception::exception& e)
+    {
+        log<level::ERR>("pldm: Failed to fetch the state sensor PDRs",
+                        entry("ERROR=%s", e.what()));
+    }
+
+    if (pdrs.empty())
+    {
+        log<level::ERR>("pldm: state sensor PDRs not present");
+        return;
+    }
+
     bool offsetFound = false;
     auto pdr =
         reinterpret_cast<const pldm_state_sensor_pdr*>(pdrs.front().data());
@@ -28,8 +61,7 @@
             reinterpret_cast<const state_sensor_possible_states*>(
                 possibleStatesPtr);
 
-        if (possibleStates->state_set_id ==
-            PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS)
+        if (possibleStates->state_set_id == stateSetId)
         {
             sensorOffset = offset;
             offsetFound = true;
@@ -42,8 +74,7 @@
 
     if (!offsetFound)
     {
-        log<level::ERR>(
-            "pldm: OCC state sensor PDR with StateSetId PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS not found");
+        log<level::ERR>("pldm: state sensor PDR not found");
         return;
     }
 
@@ -72,33 +103,14 @@
 {
     if (!isOCCSensorCacheValid())
     {
-        PdrList pdrs{};
+        fetchSensorInfo(PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS,
+                        sensorToOCCInstance, OCCSensorOffset);
+    }
 
-        auto& bus = open_power::occ::utils::getBus();
-        try
-        {
-            auto method = bus.new_method_call(
-                "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
-                "xyz.openbmc_project.PLDM.PDR", "FindStateSensorPDR");
-            method.append(tid, (uint16_t)PLDM_ENTITY_PROC,
-                          (uint16_t)PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS);
-
-            auto responseMsg = bus.call(method);
-            responseMsg.read(pdrs);
-        }
-        catch (const sdbusplus::exception::exception& e)
-        {
-            log<level::ERR>("pldm: Failed to fetch the OCC state sensor PDRs",
-                            entry("ERROR=%s", e.what()));
-        }
-
-        if (!pdrs.size())
-        {
-            log<level::ERR>("pldm: OCC state sensor PDRs not present");
-            return;
-        }
-
-        fetchOCCSensorInfo(pdrs, sensorToOCCInstance, sensorOffset);
+    if (sensorToSBEInstance.empty())
+    {
+        fetchSensorInfo(PLDM_OEM_IBM_SBE_HRESET_STATE, sensorToSBEInstance,
+                        SBESensorOffset);
     }
 
     TerminusID tid{};
@@ -109,32 +121,57 @@
 
     msg.read(tid, sensorId, msgSensorOffset, eventState, previousEventState);
 
-    auto sensorEntry = sensorToOCCInstance.find(sensorId);
-    if (sensorEntry == sensorToOCCInstance.end() ||
-        (msgSensorOffset != sensorOffset))
+    if (msgSensorOffset == OCCSensorOffset)
     {
-        // No action for non matching sensorEvents
-        return;
-    }
+        auto sensorEntry = sensorToOCCInstance.find(sensorId);
 
-    if (eventState == static_cast<EventState>(
-                          PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_IN_SERVICE))
-    {
-        log<level::INFO>(
-            fmt::format("PLDM: OCC{} is RUNNING", sensorEntry->second).c_str());
-        callBack(sensorEntry->second, true);
-    }
-    else if (eventState ==
-             static_cast<EventState>(
-                 PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_STOPPED))
-    {
-        log<level::INFO>(
-            fmt::format("PLDM: OCC{} has now STOPPED", sensorEntry->second)
-                .c_str());
-        callBack(sensorEntry->second, false);
-    }
+        if (sensorEntry == sensorToOCCInstance.end())
+        {
+            return;
+        }
 
-    return;
+        if (eventState ==
+            static_cast<EventState>(
+                PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_IN_SERVICE))
+        {
+            log<level::INFO>(
+                fmt::format("PLDM: OCC{} is RUNNING", sensorEntry->second)
+                    .c_str());
+            callBack(sensorEntry->second, true);
+        }
+        else if (eventState ==
+                 static_cast<EventState>(
+                     PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_STOPPED))
+        {
+            log<level::INFO>(
+                fmt::format("PLDM: OCC{} has now STOPPED", sensorEntry->second)
+                    .c_str());
+            callBack(sensorEntry->second, false);
+        }
+    }
+    else if (msgSensorOffset == SBESensorOffset)
+    {
+        auto sensorEntry = sensorToSBEInstance.find(sensorId);
+
+        if (sensorEntry == sensorToSBEInstance.end())
+        {
+            return;
+        }
+
+        if (eventState == static_cast<EventState>(SBE_HRESET_NOT_READY))
+        {
+            log<level::INFO>("pldm: HRESET is NOT READY",
+                             entry("SBE=%d", sensorEntry->second));
+        }
+        else if (eventState == static_cast<EventState>(SBE_HRESET_READY))
+        {
+            sbeCallBack(sensorEntry->second, true);
+        }
+        else if (eventState == static_cast<EventState>(SBE_HRESET_FAILED))
+        {
+            sbeCallBack(sensorEntry->second, false);
+        }
+    }
 }
 
 void Interface::hostStateEvent(sdbusplus::message::message& msg)
@@ -151,14 +188,43 @@
         {
             sensorToOCCInstance.clear();
             occInstanceToEffecter.clear();
+
+            sensorToSBEInstance.clear();
+            sbeInstanceToEffecter.clear();
         }
     }
 }
 
-void Interface::fetchOCCEffecterInfo(
-    const PdrList& pdrs, OccInstanceToEffecter& /*instanceToEffecterMap*/,
-    CompositeEffecterCount& /*count*/, uint8_t& bootRestartPos)
+void Interface::fetchEffecterInfo(uint16_t entityId, uint16_t stateSetId,
+                                  InstanceToEffecter& instanceToEffecterMap,
+                                  CompositeEffecterCount& effecterCount,
+                                  uint8_t& stateIdPos)
 {
+    PdrList pdrs{};
+
+    auto& bus = open_power::occ::utils::getBus();
+    try
+    {
+        auto method = bus.new_method_call(
+            "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
+            "xyz.openbmc_project.PLDM.PDR", "FindStateEffecterPDR");
+        method.append(tid, entityId, stateSetId);
+
+        auto responseMsg = bus.call(method);
+        responseMsg.read(pdrs);
+    }
+    catch (const sdbusplus::exception::exception& e)
+    {
+        log<level::ERR>("pldm: Failed to fetch the state effecter PDRs",
+                        entry("ERROR=%s", e.what()));
+    }
+
+    if (!pdrs.size())
+    {
+        log<level::ERR>("pldm: state effecter PDRs not present");
+        return;
+    }
+
     bool offsetFound = false;
     auto pdr =
         reinterpret_cast<const pldm_state_effecter_pdr*>(pdrs.front().data());
@@ -169,9 +235,9 @@
             reinterpret_cast<const state_effecter_possible_states*>(
                 possibleStatesPtr);
 
-        if (possibleStates->state_set_id == PLDM_STATE_SET_BOOT_RESTART_CAUSE)
+        if (possibleStates->state_set_id == stateSetId)
         {
-            bootRestartPos = offset;
+            stateIdPos = offset;
             effecterCount = pdr->composite_effecter_count;
             offsetFound = true;
             break;
@@ -199,7 +265,7 @@
     open_power::occ::instanceID position = start;
     for (auto const& pair : entityInstMap)
     {
-        occInstanceToEffecter.emplace(position, pair.second);
+        instanceToEffecterMap.emplace(position, pair.second);
         position++;
     }
 }
@@ -207,7 +273,7 @@
 std::vector<uint8_t>
     Interface::prepareSetEffecterReq(uint8_t instanceId, EffecterID effecterId,
                                      CompositeEffecterCount effecterCount,
-                                     uint8_t bootRestartPos)
+                                     uint8_t stateIdPos, uint8_t stateSetValue)
 {
     std::vector<uint8_t> request(
         sizeof(pldm_msg_hdr) + sizeof(effecterId) + sizeof(effecterCount) +
@@ -217,11 +283,10 @@
 
     for (uint8_t effecterPos = 0; effecterPos < effecterCount; effecterPos++)
     {
-        if (effecterPos == bootRestartPos)
+        if (effecterPos == stateIdPos)
         {
-            stateField.emplace_back(set_effecter_state_field{
-                PLDM_REQUEST_SET,
-                PLDM_STATE_SET_BOOT_RESTART_CAUSE_WARM_RESET});
+            stateField.emplace_back(
+                set_effecter_state_field{PLDM_REQUEST_SET, stateSetValue});
         }
         else
         {
@@ -244,34 +309,9 @@
 {
     if (!isPDREffecterCacheValid())
     {
-        PdrList pdrs{};
-
-        auto& bus = open_power::occ::utils::getBus();
-        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 sdbusplus::exception::exception& 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);
+        fetchEffecterInfo(
+            PLDM_ENTITY_PROC_MODULE, PLDM_STATE_SET_BOOT_RESTART_CAUSE,
+            occInstanceToEffecter, OCCEffecterCount, bootRestartPosition);
     }
 
     // Find the matching effecter for the OCC instance
@@ -286,7 +326,65 @@
     }
 
     uint8_t instanceId{};
+    if (!getMctpInstanceId(instanceId))
+    {
+        return;
+    }
 
+    // Prepare the SetStateEffecterStates request to reset the OCC
+    auto request = prepareSetEffecterReq(
+        instanceId, effecterEntry->second, OCCEffecterCount,
+        bootRestartPosition, PLDM_STATE_SET_BOOT_RESTART_CAUSE_WARM_RESET);
+
+    if (request.empty())
+    {
+        log<level::ERR>("pldm: SetStateEffecterStates OCC reset request empty");
+        return;
+    }
+
+    sendPldm(request);
+}
+
+void Interface::sendHRESET(open_power::occ::instanceID sbeInstanceId)
+{
+    if (sbeInstanceToEffecter.empty())
+    {
+        fetchEffecterInfo(PLDM_ENTITY_PROC, PLDM_OEM_IBM_SBE_MAINTENANCE_STATE,
+                          sbeInstanceToEffecter, SBEEffecterCount,
+                          sbeMaintenanceStatePosition);
+    }
+
+    auto effecterEntry = sbeInstanceToEffecter.find(sbeInstanceId);
+    if (effecterEntry == sbeInstanceToEffecter.end())
+    {
+        log<level::ERR>(
+            "pldm: Failed to find a matching effecter for SBE instance",
+            entry("SBE=%d", sbeInstanceId));
+        return;
+    }
+
+    uint8_t instanceId{};
+    if (!getMctpInstanceId(instanceId))
+    {
+        return;
+    }
+
+    // Prepare the SetStateEffecterStates request to HRESET the SBE
+    auto request = prepareSetEffecterReq(
+        instanceId, effecterEntry->second, SBEEffecterCount,
+        sbeMaintenanceStatePosition, SBE_RETRY_REQUIRED);
+
+    if (request.empty())
+    {
+        log<level::ERR>("pldm: SetStateEffecterStates HRESET request empty");
+        return;
+    }
+
+    sendPldm(request);
+}
+
+bool Interface::getMctpInstanceId(uint8_t& instanceId)
+{
     auto& bus = open_power::occ::utils::getBus();
     try
     {
@@ -301,19 +399,14 @@
     {
         log<level::ERR>("pldm: GetInstanceId returned error",
                         entry("ERROR=%s", e.what()));
-        return;
+        return false;
     }
 
-    // Prepare the SetStateEffecterStates request to reset the OCC
-    auto request = prepareSetEffecterReq(instanceId, effecterEntry->second,
-                                         effecterCount, bootRestartPosition);
+    return true;
+}
 
-    if (request.empty())
-    {
-        log<level::ERR>("pldm: SetStateEffecterStates request message empty");
-        return;
-    }
-
+void Interface::sendPldm(const std::vector<uint8_t>& request)
+{
     // Connect to MCTP scoket
     int fd = pldm_open();
     if (fd == -1)
@@ -321,6 +414,7 @@
         log<level::ERR>("pldm: Failed to connect to MCTP socket");
         return;
     }
+
     open_power::occ::FileDescriptor fileFd(fd);
 
     // Send the PLDM request message to HBRT
@@ -332,8 +426,7 @@
                                                                std::free};
     if (rc)
     {
-        log<level::ERR>("pldm: pldm_send_recv failed for OCC reset",
-                        entry("RC=%d", rc));
+        log<level::ERR>("pldm: pldm_send_recv failed", entry("RC=%d", rc));
     }
 
     uint8_t completionCode{};
@@ -347,8 +440,6 @@
             entry("RC=%d", rcDecode),
             entry("COMPLETION_CODE=%d", completionCode));
     }
-
-    return;
 }
 
 } // namespace pldm
diff --git a/pldm.hpp b/pldm.hpp
index 811ed47..3add821 100644
--- a/pldm.hpp
+++ b/pldm.hpp
@@ -17,11 +17,11 @@
 using EntityType = uint16_t;
 using EntityInstance = uint16_t;
 using EventState = uint8_t;
-using OccInstanceToEffecter = std::map<open_power::occ::instanceID, EffecterID>;
+using InstanceToEffecter = 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 SensorToInstance = std::map<SensorID, open_power::occ::instanceID>;
 using TerminusID = uint8_t;
 
 /** @brief Hardcoded TID */
@@ -53,8 +53,10 @@
      *                        changes.
      */
     explicit Interface(
-        std::function<bool(open_power::occ::instanceID, bool)> callBack) :
+        std::function<bool(open_power::occ::instanceID, bool)> callBack,
+        std::function<void(open_power::occ::instanceID, bool)> sbeCallBack) :
         callBack(callBack),
+        sbeCallBack(sbeCallBack),
         pldmEventSignal(
             open_power::occ::utils::getBus(),
             MatchRules::type::signal() +
@@ -71,45 +73,46 @@
                       std::placeholders::_1))
     {}
 
-    /** @brief Fetch the OCC state sensor PDRs and populate the cache with
-     *         sensorId to OCC instance mapping information and the sensor
-     *         offset for Operational Running Status.
+    /** @brief Fetch the state sensor PDRs and populate the cache with
+     *         sensorId to OCC/SBE instance mapping information and the sensor
+     *         offset for the relevent state set.
      *
-     *  @param[in] pdrs - OCC state sensor PDRs
-     *  @param[out] sensorInstanceMap - map of sensorID to OCC instance
+     *  @param[in] stateSetId - the state set ID to look for
+     *  @param[out] sensorInstanceMap - map of sensorID to instance
      *  @param[out] sensorOffset - sensor offset of interested state set ID
      */
-    void fetchOCCSensorInfo(const PdrList& pdrs,
-                            SensorToOCCInstance& sensorInstanceMap,
-                            SensorOffset& sensorOffset);
+    void fetchSensorInfo(uint16_t stateSetId,
+                         SensorToInstance& sensorInstanceMap,
+                         SensorOffset& sensorOffset);
 
-    /** @brief Fetch the OCC state effecter PDRs and populate the cache with
-     *         OCC instance to EffecterID information.
+    /** @brief Fetch the OCC/SBE state effecter PDRs and populate the cache
+     *         with OCC/SBE instance to EffecterID information.
      *
-     *  @param[in] pdrs - OCC state effecter PDRs
-     *  @param[out] instanceToEffecterMap - map of OCC instance to effecterID
+     *  @param[in] entityId - the entity ID to query
+     *  @param[in] stateSetId - the state set ID to look for
+     *  @param[out] instanceToEffecterMap - map of instance to effecterID
      *  @param[out] count - sensor offset of interested state set ID
-     *  @param[out] bootRestartPos - position of Boot/Restart Cause stateSetID
+     *  @param[out] stateIdPos - position of the stateSetID
      */
-    void fetchOCCEffecterInfo(const PdrList& pdrs,
-                              OccInstanceToEffecter& instanceToEffecterMap,
-                              CompositeEffecterCount& count,
-                              uint8_t& bootRestartPos);
+    void fetchEffecterInfo(uint16_t entityId, uint16_t stateSetId,
+                           InstanceToEffecter& instanceToEffecterMap,
+                           CompositeEffecterCount& count, uint8_t& stateIdPos);
 
     /** @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
+     *  @param[in] effecterId - the instance effecter ID
+     *  @param[in] effecterCount - compositeEffecterCount for the effecter PDR
+     *  @param[in] stateIdPos - position of the stateSetID
+     *  @param[in] stateSetValue - the value to set the state set to
      *
-     *  @return PLDM request message to be sent to host for OCC reset, empty
-     *          response in the case of failure.
+     *  @return PLDM request message to be sent to host for OCC reset or SBE
+     *          HRESET, empty response in the case of failure.
      */
     std::vector<uint8_t>
         prepareSetEffecterReq(uint8_t instanceId, EffecterID effecterId,
                               CompositeEffecterCount effecterCount,
-                              uint8_t bootRestartPos);
+                              uint8_t stateIdPos, uint8_t stateSetValue);
 
     /** @brief Send the PLDM message to reset the OCC
      *
@@ -118,12 +121,24 @@
      */
     void resetOCC(open_power::occ::instanceID occInstanceId);
 
+    /** @brief Send the PLDM message to perform the HRESET
+     *
+     *  @param[in] instanceID - SBE instance to HRESET
+     */
+    void sendHRESET(open_power::occ::instanceID sbeInstanceId);
+
   private:
     /** @brief Callback handler to be invoked when the state of the OCC
      *         changes
      */
     std::function<bool(open_power::occ::instanceID, bool)> callBack = nullptr;
 
+    /** @brief Callback handler to be invoked when the maintenance state of the
+     *         SBE changes
+     */
+    std::function<void(open_power::occ::instanceID, bool)> sbeCallBack =
+        nullptr;
+
     /** @brief Used to subscribe to D-Bus PLDM StateSensorEvent signal and
      *         processes if the event corresponds to OCC state change.
      */
@@ -134,25 +149,46 @@
 
     /** @brief PLDM Sensor ID to OCC Instance mapping
      */
-    SensorToOCCInstance sensorToOCCInstance;
+    SensorToInstance sensorToOCCInstance;
 
-    /** @brief Sensor offset of state set ID
+    /** @brief PLDM Sensor ID to SBE Instance mapping
+     */
+    SensorToInstance sensorToSBEInstance;
+
+    /** @brief Sensor offset of OCC state set ID
      * PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS in state sensor PDR.
      */
-    SensorOffset sensorOffset;
+    SensorOffset OCCSensorOffset;
+
+    /** @brief Sensor offset of the SBE state set ID
+     * PLDM_OEM_IBM_SBE_HRESET_STATE in state sensor PDR.
+     */
+    SensorOffset SBESensorOffset;
 
     /** @brief OCC Instance mapping to PLDM Effecter ID
      */
-    OccInstanceToEffecter occInstanceToEffecter;
+    InstanceToEffecter occInstanceToEffecter;
+
+    /** @brief SBE instance mapping to PLDM Effecter ID
+     */
+    InstanceToEffecter sbeInstanceToEffecter;
 
     /** @brief compositeEffecterCount for OCC reset state effecter PDR */
-    CompositeEffecterCount effecterCount = 0;
+    CompositeEffecterCount OCCEffecterCount = 0;
+
+    /** @brief compositeEffecterCount for SBE HRESET state effecter PDR */
+    CompositeEffecterCount SBEEffecterCount = 0;
 
     /** @brief Position of Boot/Restart Cause stateSetID in OCC state
      *         effecter PDR
      */
     uint8_t bootRestartPosition = 0;
 
+    /** @brief Position of the SBE maintenance stateSetID in the state
+     *         effecter PDR
+     */
+    uint8_t sbeMaintenanceStatePosition = 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
@@ -189,6 +225,20 @@
     {
         return (occInstanceToEffecter.empty() ? false : true);
     }
+
+    /** @brief Query PLDM for the MCTP requester instance id
+     *
+     * @param[out] - the instance id
+     *
+     * @return true if the id was found and false if not
+     */
+    bool getMctpInstanceId(uint8_t& instanceId);
+
+    /** @brief Send the PLDM request
+     *
+     * @param[in] - the request data
+     */
+    void sendPldm(const std::vector<uint8_t>& request);
 };
 
 } // namespace pldm