pldmd: implement a new handler registration scheme
Implement a PLDM request handler registration scheme that requires
handlers to be C++ objects rather than plain functions. This was needed
for a couple of reasons:
- Perform specific actions at the PLDM daemon startup (that's when the
handlers are loaded).
- Share data across PLDM request messages (for eg FRU/BIOS tables),
without having to resort to globals and statics.
Tested:
- existing unit tests still pass
- added tests for the new registration scheme
Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
Change-Id: I1cf1c0a6fccd15da54f08120e61a5f256df6bc36
diff --git a/libpldmresponder/platform.hpp b/libpldmresponder/platform.hpp
index ec168f4..be48950 100644
--- a/libpldmresponder/platform.hpp
+++ b/libpldmresponder/platform.hpp
@@ -2,6 +2,7 @@
#include "config.h"
+#include "handler.hpp"
#include "libpldmresponder/pdr.hpp"
#include "libpldmresponder/utils.hpp"
@@ -14,254 +15,269 @@
namespace pldm
{
-
-using Response = std::vector<uint8_t>;
-
namespace responder
{
-
namespace platform
{
-/** @brief Register handlers for commands from the platform spec
- */
-void registerHandlers();
-
-} // namespace platform
-
-/** @brief Handler for GetPDR
- *
- * @param[in] request - Request message payload
- * @param[in] payloadLength - Request payload length
- * @param[out] Response - Response message written here
- */
-Response getPDR(const pldm_msg* request, size_t payloadLength);
-
-/** @brief Handler for setStateEffecterStates
- *
- * @param[in] request - Request message
- * @param[in] payloadLength - Request payload length
- * @return Response - PLDM Response message
- */
-Response setStateEffecterStates(const pldm_msg* request, size_t payloadLength);
-
-/** @brief Function to set the effecter requested by pldm requester
- * @param[in] dBusIntf - The interface object
- * @param[in] effecterId - Effecter ID sent by the requester to act on
- * @param[in] stateField - The state field data for each of the states, equal
- * to composite effecter count in number
- * @return - Success or failure in setting the states. Returns failure in terms
- * of PLDM completion codes if atleast one state fails to be set
- */
-template <class DBusInterface>
-int setStateEffecterStatesHandler(
- const DBusInterface& dBusIntf, effecter::Id effecterId,
- const std::vector<set_effecter_state_field>& stateField)
+class Handler : public CmdHandler
{
- using namespace std::string_literals;
- using DBusProperty = std::variant<std::string, bool>;
- using StateSetId = uint16_t;
- using StateSetNum = uint8_t;
- using PropertyMap =
- std::map<StateSetId, std::map<StateSetNum, DBusProperty>>;
- static const PropertyMap stateNumToDbusProp = {
- {PLDM_BOOT_PROGRESS_STATE,
- {{PLDM_BOOT_NOT_ACTIVE,
- "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus."
- "Standby"s},
- {PLDM_BOOT_COMPLETED,
- "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus."
- "BootComplete"s}}},
- {PLDM_SYSTEM_POWER_STATE,
- {{PLDM_OFF_SOFT_GRACEFUL,
- "xyz.openbmc_project.State.Chassis.Transition.Off"s}}}};
- using namespace phosphor::logging;
- using namespace pldm::responder::pdr;
- using namespace pldm::responder::effecter::dbus_mapping;
-
- state_effecter_possible_states* states = nullptr;
- pldm_state_effecter_pdr* pdr = nullptr;
- uint8_t compEffecterCnt = stateField.size();
- uint32_t recordHndl{};
- Repo& pdrRepo = get(PDR_JSONS_DIR);
- pdr::Entry pdrEntry{};
-
- while (!pdr)
+ public:
+ Handler()
{
- pdrEntry = pdrRepo.at(recordHndl);
- pldm_pdr_hdr* header = reinterpret_cast<pldm_pdr_hdr*>(pdrEntry.data());
- if (header->type != PLDM_STATE_EFFECTER_PDR)
- {
- recordHndl = pdrRepo.getNextRecordHandle(recordHndl);
- if (recordHndl)
- {
- continue;
- }
- return PLDM_PLATFORM_INVALID_EFFECTER_ID;
- }
- pdr = reinterpret_cast<pldm_state_effecter_pdr*>(pdrEntry.data());
- recordHndl = pdr->hdr.record_handle;
- if (pdr->effecter_id == effecterId)
- {
- states = reinterpret_cast<state_effecter_possible_states*>(
- pdr->possible_states);
- if (compEffecterCnt > pdr->composite_effecter_count)
- {
- log<level::ERR>("The requester sent wrong composite effecter "
- "count for the effecter",
- entry("EFFECTER_ID=%d", effecterId),
- entry("COMP_EFF_CNT=%d", compEffecterCnt));
- return PLDM_ERROR_INVALID_DATA;
- }
- break;
- }
- recordHndl = pdrRepo.getNextRecordHandle(recordHndl);
- if (!recordHndl)
- {
- return PLDM_PLATFORM_INVALID_EFFECTER_ID;
- }
- pdr = nullptr;
+ handlers.emplace(PLDM_GET_PDR,
+ [this](const pldm_msg* request, size_t payloadLength) {
+ return this->getPDR(request, payloadLength);
+ });
+ handlers.emplace(PLDM_SET_STATE_EFFECTER_STATES,
+ [this](const pldm_msg* request, size_t payloadLength) {
+ return this->setStateEffecterStates(request,
+ payloadLength);
+ });
}
- std::map<StateSetId, std::function<int(const std::string& objPath,
- const uint8_t currState)>>
- effecterToDbusEntries = {
- {PLDM_BOOT_PROGRESS_STATE,
- [&](const std::string& objPath, const uint8_t currState) {
- auto stateSet =
- stateNumToDbusProp.find(PLDM_BOOT_PROGRESS_STATE);
- if (stateSet == stateNumToDbusProp.end())
- {
- log<level::ERR>("Couldn't find D-Bus mapping for "
- "PLDM_BOOT_PROGRESS_STATE",
- entry("EFFECTER_ID=%d", effecterId));
- return PLDM_ERROR;
- }
- auto iter = stateSet->second.find(
- stateField[currState].effecter_state);
- if (iter == stateSet->second.end())
- {
- log<level::ERR>(
- "Invalid state field passed or field not "
- "found for PLDM_BOOT_PROGRESS_STATE",
- entry("EFFECTER_ID=%d", effecterId),
- entry("FIELD=%d",
- stateField[currState].effecter_state),
- entry("OBJECT_PATH=%s", objPath.c_str()));
- return PLDM_ERROR_INVALID_DATA;
- }
- auto dbusProp = "OperatingSystemState";
- std::variant<std::string> value{
- std::get<std::string>(iter->second)};
- auto dbusInterface =
- "xyz.openbmc_project.State.OperatingSystem.Status";
- try
- {
- dBusIntf.setDbusProperty(objPath.c_str(), dbusProp,
- dbusInterface, value);
- }
- catch (const std::exception& e)
- {
- log<level::ERR>("Error setting property",
- entry("ERROR=%s", e.what()),
- entry("PROPERTY=%s", dbusProp),
- entry("INTERFACE=%s", dbusInterface),
- entry("PATH=%s", objPath.c_str()));
- return PLDM_ERROR;
- }
- return PLDM_SUCCESS;
- }},
- {PLDM_SYSTEM_POWER_STATE,
- [&](const std::string& objPath, const uint8_t currState) {
- auto stateSet =
- stateNumToDbusProp.find(PLDM_SYSTEM_POWER_STATE);
- if (stateSet == stateNumToDbusProp.end())
- {
- log<level::ERR>("Couldn't find D-Bus mapping for "
- "PLDM_SYSTEM_POWER_STATE",
- entry("EFFECTER_ID=%d", effecterId));
- return PLDM_ERROR;
- }
- auto iter = stateSet->second.find(
- stateField[currState].effecter_state);
- if (iter == stateSet->second.end())
- {
- log<level::ERR>(
- "Invalid state field passed or field not "
- "found for PLDM_SYSTEM_POWER_STATE",
- entry("EFFECTER_ID=%d", effecterId),
- entry("FIELD=%d",
- stateField[currState].effecter_state),
- entry("OBJECT_PATH=%s", objPath.c_str()));
- return PLDM_ERROR_INVALID_DATA;
- }
- auto dbusProp = "RequestedPowerTransition";
- std::variant<std::string> value{
- std::get<std::string>(iter->second)};
- auto dbusInterface = "xyz.openbmc_project.State.Chassis";
- try
- {
- dBusIntf.setDbusProperty(objPath.c_str(), dbusProp,
- dbusInterface, value);
- }
- catch (const std::exception& e)
- {
- log<level::ERR>("Error setting property",
- entry("ERROR=%s", e.what()),
- entry("PROPERTY=%s", dbusProp),
- entry("INTERFACE=%s", dbusInterface),
- entry("PATH=%s", objPath.c_str()));
- return PLDM_ERROR;
- }
- return PLDM_SUCCESS;
- }}};
+ /** @brief Handler for GetPDR
+ *
+ * @param[in] request - Request message payload
+ * @param[in] payloadLength - Request payload length
+ * @param[out] Response - Response message written here
+ */
+ Response getPDR(const pldm_msg* request, size_t payloadLength);
- int rc = PLDM_SUCCESS;
- auto paths = get(effecterId);
- for (uint8_t currState = 0; currState < compEffecterCnt; ++currState)
+ /** @brief Handler for setStateEffecterStates
+ *
+ * @param[in] request - Request message
+ * @param[in] payloadLength - Request payload length
+ * @return Response - PLDM Response message
+ */
+ Response setStateEffecterStates(const pldm_msg* request,
+ size_t payloadLength);
+
+ /** @brief Function to set the effecter requested by pldm requester
+ * @param[in] dBusIntf - The interface object
+ * @param[in] effecterId - Effecter ID sent by the requester to act on
+ * @param[in] stateField - The state field data for each of the states,
+ * equal to composite effecter count in number
+ * @return - Success or failure in setting the states. Returns failure in
+ * terms of PLDM completion codes if atleast one state fails to be set
+ */
+ template <class DBusInterface>
+ int setStateEffecterStatesHandler(
+ const DBusInterface& dBusIntf, effecter::Id effecterId,
+ const std::vector<set_effecter_state_field>& stateField)
{
- std::vector<StateSetNum> allowed{};
- // computation is based on table 79 from DSP0248 v1.1.1
- uint8_t bitfieldIndex = stateField[currState].effecter_state / 8;
- uint8_t bit =
- stateField[currState].effecter_state - (8 * bitfieldIndex);
- if (states->possible_states_size < bitfieldIndex ||
- !(states->states[bitfieldIndex].byte & (1 << bit)))
+ using namespace std::string_literals;
+ using DBusProperty = std::variant<std::string, bool>;
+ using StateSetId = uint16_t;
+ using StateSetNum = uint8_t;
+ using PropertyMap =
+ std::map<StateSetId, std::map<StateSetNum, DBusProperty>>;
+ static const PropertyMap stateNumToDbusProp = {
+ {PLDM_BOOT_PROGRESS_STATE,
+ {{PLDM_BOOT_NOT_ACTIVE,
+ "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus."
+ "Standby"s},
+ {PLDM_BOOT_COMPLETED,
+ "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus."
+ "BootComplete"s}}},
+ {PLDM_SYSTEM_POWER_STATE,
+ {{PLDM_OFF_SOFT_GRACEFUL,
+ "xyz.openbmc_project.State.Chassis.Transition.Off"s}}}};
+ using namespace phosphor::logging;
+ using namespace pldm::responder::pdr;
+ using namespace pldm::responder::effecter::dbus_mapping;
+
+ state_effecter_possible_states* states = nullptr;
+ pldm_state_effecter_pdr* pdr = nullptr;
+ uint8_t compEffecterCnt = stateField.size();
+ uint32_t recordHndl{};
+ Repo& pdrRepo = get(PDR_JSONS_DIR);
+ pdr::Entry pdrEntry{};
+
+ while (!pdr)
{
- log<level::ERR>(
- "Invalid state set value", entry("EFFECTER_ID=%d", effecterId),
- entry("VALUE=%d", stateField[currState].effecter_state),
- entry("COMPOSITE_EFFECTER_ID=%d", currState),
- entry("DBUS_PATH=%c", paths[currState].c_str()));
- rc = PLDM_PLATFORM_SET_EFFECTER_UNSUPPORTED_SENSORSTATE;
- break;
- }
- auto iter = effecterToDbusEntries.find(states->state_set_id);
- if (iter == effecterToDbusEntries.end())
- {
- uint16_t setId = states->state_set_id;
- log<level::ERR>(
- "Did not find the state set for the state effecter pdr ",
- entry("STATE=%d", setId), entry("EFFECTER_ID=%d", effecterId));
- rc = PLDM_PLATFORM_INVALID_STATE_VALUE;
- break;
- }
- if (stateField[currState].set_request == PLDM_REQUEST_SET)
- {
- rc = iter->second(paths[currState], currState);
- if (rc != PLDM_SUCCESS)
+ pdrEntry = pdrRepo.at(recordHndl);
+ pldm_pdr_hdr* header =
+ reinterpret_cast<pldm_pdr_hdr*>(pdrEntry.data());
+ if (header->type != PLDM_STATE_EFFECTER_PDR)
{
+ recordHndl = pdrRepo.getNextRecordHandle(recordHndl);
+ if (recordHndl)
+ {
+ continue;
+ }
+ return PLDM_PLATFORM_INVALID_EFFECTER_ID;
+ }
+ pdr = reinterpret_cast<pldm_state_effecter_pdr*>(pdrEntry.data());
+ recordHndl = pdr->hdr.record_handle;
+ if (pdr->effecter_id == effecterId)
+ {
+ states = reinterpret_cast<state_effecter_possible_states*>(
+ pdr->possible_states);
+ if (compEffecterCnt > pdr->composite_effecter_count)
+ {
+ log<level::ERR>(
+ "The requester sent wrong composite effecter "
+ "count for the effecter",
+ entry("EFFECTER_ID=%d", effecterId),
+ entry("COMP_EFF_CNT=%d", compEffecterCnt));
+ return PLDM_ERROR_INVALID_DATA;
+ }
break;
}
+ recordHndl = pdrRepo.getNextRecordHandle(recordHndl);
+ if (!recordHndl)
+ {
+ return PLDM_PLATFORM_INVALID_EFFECTER_ID;
+ }
+ pdr = nullptr;
}
- uint8_t* nextState =
- reinterpret_cast<uint8_t*>(states) +
- sizeof(state_effecter_possible_states) - sizeof(states->states) +
- (states->possible_states_size * sizeof(states->states));
- states = reinterpret_cast<state_effecter_possible_states*>(nextState);
- }
- return rc;
-}
+ std::map<StateSetId, std::function<int(const std::string& objPath,
+ const uint8_t currState)>>
+ effecterToDbusEntries = {
+ {PLDM_BOOT_PROGRESS_STATE,
+ [&](const std::string& objPath, const uint8_t currState) {
+ auto stateSet =
+ stateNumToDbusProp.find(PLDM_BOOT_PROGRESS_STATE);
+ if (stateSet == stateNumToDbusProp.end())
+ {
+ log<level::ERR>("Couldn't find D-Bus mapping for "
+ "PLDM_BOOT_PROGRESS_STATE",
+ entry("EFFECTER_ID=%d", effecterId));
+ return PLDM_ERROR;
+ }
+ auto iter = stateSet->second.find(
+ stateField[currState].effecter_state);
+ if (iter == stateSet->second.end())
+ {
+ log<level::ERR>(
+ "Invalid state field passed or field not "
+ "found for PLDM_BOOT_PROGRESS_STATE",
+ entry("EFFECTER_ID=%d", effecterId),
+ entry("FIELD=%d",
+ stateField[currState].effecter_state),
+ entry("OBJECT_PATH=%s", objPath.c_str()));
+ return PLDM_ERROR_INVALID_DATA;
+ }
+ auto dbusProp = "OperatingSystemState";
+ std::variant<std::string> value{
+ std::get<std::string>(iter->second)};
+ auto dbusInterface =
+ "xyz.openbmc_project.State.OperatingSystem.Status";
+ try
+ {
+ dBusIntf.setDbusProperty(objPath.c_str(), dbusProp,
+ dbusInterface, value);
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>("Error setting property",
+ entry("ERROR=%s", e.what()),
+ entry("PROPERTY=%s", dbusProp),
+ entry("INTERFACE=%s", dbusInterface),
+ entry("PATH=%s", objPath.c_str()));
+ return PLDM_ERROR;
+ }
+ return PLDM_SUCCESS;
+ }},
+ {PLDM_SYSTEM_POWER_STATE,
+ [&](const std::string& objPath, const uint8_t currState) {
+ auto stateSet =
+ stateNumToDbusProp.find(PLDM_SYSTEM_POWER_STATE);
+ if (stateSet == stateNumToDbusProp.end())
+ {
+ log<level::ERR>("Couldn't find D-Bus mapping for "
+ "PLDM_SYSTEM_POWER_STATE",
+ entry("EFFECTER_ID=%d", effecterId));
+ return PLDM_ERROR;
+ }
+ auto iter = stateSet->second.find(
+ stateField[currState].effecter_state);
+ if (iter == stateSet->second.end())
+ {
+ log<level::ERR>(
+ "Invalid state field passed or field not "
+ "found for PLDM_SYSTEM_POWER_STATE",
+ entry("EFFECTER_ID=%d", effecterId),
+ entry("FIELD=%d",
+ stateField[currState].effecter_state),
+ entry("OBJECT_PATH=%s", objPath.c_str()));
+ return PLDM_ERROR_INVALID_DATA;
+ }
+ auto dbusProp = "RequestedPowerTransition";
+ std::variant<std::string> value{
+ std::get<std::string>(iter->second)};
+ auto dbusInterface = "xyz.openbmc_project.State.Chassis";
+ try
+ {
+ dBusIntf.setDbusProperty(objPath.c_str(), dbusProp,
+ dbusInterface, value);
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>("Error setting property",
+ entry("ERROR=%s", e.what()),
+ entry("PROPERTY=%s", dbusProp),
+ entry("INTERFACE=%s", dbusInterface),
+ entry("PATH=%s", objPath.c_str()));
+ return PLDM_ERROR;
+ }
+ return PLDM_SUCCESS;
+ }}};
+
+ int rc = PLDM_SUCCESS;
+ auto paths = get(effecterId);
+ for (uint8_t currState = 0; currState < compEffecterCnt; ++currState)
+ {
+ std::vector<StateSetNum> allowed{};
+ // computation is based on table 79 from DSP0248 v1.1.1
+ uint8_t bitfieldIndex = stateField[currState].effecter_state / 8;
+ uint8_t bit =
+ stateField[currState].effecter_state - (8 * bitfieldIndex);
+ if (states->possible_states_size < bitfieldIndex ||
+ !(states->states[bitfieldIndex].byte & (1 << bit)))
+ {
+ log<level::ERR>(
+ "Invalid state set value",
+ entry("EFFECTER_ID=%d", effecterId),
+ entry("VALUE=%d", stateField[currState].effecter_state),
+ entry("COMPOSITE_EFFECTER_ID=%d", currState),
+ entry("DBUS_PATH=%c", paths[currState].c_str()));
+ rc = PLDM_PLATFORM_SET_EFFECTER_UNSUPPORTED_SENSORSTATE;
+ break;
+ }
+ auto iter = effecterToDbusEntries.find(states->state_set_id);
+ if (iter == effecterToDbusEntries.end())
+ {
+ uint16_t setId = states->state_set_id;
+ log<level::ERR>(
+ "Did not find the state set for the state effecter pdr ",
+ entry("STATE=%d", setId),
+ entry("EFFECTER_ID=%d", effecterId));
+ rc = PLDM_PLATFORM_INVALID_STATE_VALUE;
+ break;
+ }
+ if (stateField[currState].set_request == PLDM_REQUEST_SET)
+ {
+ rc = iter->second(paths[currState], currState);
+ if (rc != PLDM_SUCCESS)
+ {
+ break;
+ }
+ }
+ uint8_t* nextState =
+ reinterpret_cast<uint8_t*>(states) +
+ sizeof(state_effecter_possible_states) -
+ sizeof(states->states) +
+ (states->possible_states_size * sizeof(states->states));
+ states =
+ reinterpret_cast<state_effecter_possible_states*>(nextState);
+ }
+ return rc;
+ }
+};
+
+} // namespace platform
} // namespace responder
} // namespace pldm