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