libpldmresponder: implement setStateEffecterStates
This commit implements the handler for setStateEffecterStates response.
Apart from that it actually sets the effecter for PLDM_BOOT_PROGRESS
state. This is used when host sends setStateEffecterStates to mark
any change in hypervisor state. The currently supported states are
"StandBy" and "BootComplete" as per
xyz.openbmc_project.State.OperatingSystem.Status
Change-Id: I205627b2b8a796a0dd4a200ac3d59c1d19d71f01
Signed-off-by: Sampa Misra <sampmisr@in.ibm.com>
diff --git a/libpldm/platform.h b/libpldm/platform.h
index 8e186a0..6fdbf06 100644
--- a/libpldm/platform.h
+++ b/libpldm/platform.h
@@ -46,7 +46,10 @@
/** @brief PLDM Platform M&C completion codes
*/
enum pldm_platform_completion_codes {
+ PLDM_PLATFORM_INVALID_EFFECTER_ID = 0x80,
+ PLDM_PLATFORM_INVALID_STATE_VALUE = 0x81,
PLDM_PLATFORM_INVALID_RECORD_HANDLE = 0x82,
+ PLDM_PLATFORM_SET_EFFECTER_UNSUPPORTED_SENSORSTATE = 0x82,
};
/** @struct pldm_pdr_hdr
diff --git a/libpldmresponder/platform.cpp b/libpldmresponder/platform.cpp
index 5fa14c8..538c49c 100644
--- a/libpldmresponder/platform.cpp
+++ b/libpldmresponder/platform.cpp
@@ -1,11 +1,7 @@
-#include "config.h"
#include "platform.hpp"
-#include "pdr.hpp"
-
-#include <exception>
-#include <phosphor-logging/log.hpp>
+#include "registration.hpp"
namespace pldm
{
@@ -13,7 +9,20 @@
namespace responder
{
+namespace platform
+{
+
+void registerHandlers()
+{
+ registerHandler(PLDM_PLATFORM, PLDM_GET_PDR, std::move(getPDR));
+ registerHandler(PLDM_PLATFORM, PLDM_SET_STATE_EFFECTER_STATES,
+ std::move(setStateEffecterStates));
+}
+
+} // namespace platform
+
using namespace phosphor::logging;
+using namespace pldm::responder::effecter::dbus_mapping;
Response getPDR(const pldm_msg* request, size_t payloadLength)
{
@@ -83,5 +92,42 @@
return response;
}
+Response setStateEffecterStates(const pldm_msg* request, size_t payloadLength)
+{
+ Response response(
+ sizeof(pldm_msg_hdr) + PLDM_SET_STATE_EFFECTER_STATES_RESP_BYTES, 0);
+ auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+ uint16_t effecterId;
+ uint8_t compEffecterCnt;
+ constexpr auto maxCompositeEffecterCnt = 8;
+ std::vector<set_effecter_state_field> stateField(maxCompositeEffecterCnt,
+ {0, 0});
+
+ if ((payloadLength > PLDM_SET_STATE_EFFECTER_STATES_REQ_BYTES) ||
+ (payloadLength < sizeof(effecterId) + sizeof(compEffecterCnt) +
+ sizeof(set_effecter_state_field)))
+ {
+ encode_set_state_effecter_states_resp(
+ request->hdr.instance_id, PLDM_ERROR_INVALID_LENGTH, responsePtr);
+ return response;
+ }
+
+ int rc = decode_set_state_effecter_states_req(request, payloadLength,
+ &effecterId, &compEffecterCnt,
+ stateField.data());
+
+ if (rc == PLDM_SUCCESS)
+ {
+ stateField.resize(compEffecterCnt);
+ const DBusHandler dBusIntf;
+ rc = setStateEffecterStatesHandler<DBusHandler>(dBusIntf, effecterId,
+ stateField);
+ }
+
+ encode_set_state_effecter_states_resp(request->hdr.instance_id, rc,
+ responsePtr);
+ return response;
+}
+
} // namespace responder
} // namespace pldm
diff --git a/libpldmresponder/platform.hpp b/libpldmresponder/platform.hpp
index 253d786..9db93ce 100644
--- a/libpldmresponder/platform.hpp
+++ b/libpldmresponder/platform.hpp
@@ -1,10 +1,16 @@
#pragma once
+#include "config.h"
+
+#include "libpldmresponder/pdr.hpp"
+#include "libpldmresponder/utils.hpp"
+
#include <stdint.h>
-#include <vector>
+#include <map>
#include "libpldm/platform.h"
+#include "libpldm/states.h"
namespace pldm
{
@@ -14,6 +20,15 @@
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
@@ -22,5 +37,185 @@
*/
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)
+{
+ 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}}},
+ };
+ 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)
+ {
+ 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;
+ }
+
+ 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;
+ }}};
+
+ 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 responder
} // namespace pldm
diff --git a/libpldmresponder/utils.hpp b/libpldmresponder/utils.hpp
index 6891d81..5f366be 100644
--- a/libpldmresponder/utils.hpp
+++ b/libpldmresponder/utils.hpp
@@ -4,8 +4,13 @@
#include <systemd/sd-bus.h>
#include <unistd.h>
+#include <exception>
#include <sdbusplus/server.hpp>
#include <string>
+#include <variant>
+#include <vector>
+
+#include "libpldm/base.h"
namespace pldm
{
@@ -88,5 +93,41 @@
return bcd;
}
+constexpr auto dbusProperties = "org.freedesktop.DBus.Properties";
+
+/**
+ * @class DBusHandler
+ *
+ * Wrapper class to handle the D-Bus calls
+ *
+ * This class contains the APIs to handle the D-Bus calls
+ * to cater the request from pldm requester.
+ * A class is created to mock the apis in the test cases
+ */
+class DBusHandler
+{
+ public:
+ /** @brief API to set a D-Bus property
+ *
+ * @param[in] objPath - Object path for the D-Bus object
+ * @param[in] dbusProp - The D-Bus property
+ * @param[in] dbusInterface - The D-Bus interface
+ * @param[in] value - The value to be set
+ * failure
+ */
+ template <typename T>
+ void setDbusProperty(const char* objPath, const char* dbusProp,
+ const char* dbusInterface,
+ const std::variant<T>& value) const
+ {
+ auto bus = sdbusplus::bus::new_default();
+ auto service = getService(bus, objPath, dbusInterface);
+ auto method = bus.new_method_call(service.c_str(), objPath,
+ dbusProperties, "Set");
+ method.append(dbusInterface, dbusProp, value);
+ bus.call_noreply(method);
+ }
+};
+
} // namespace responder
} // namespace pldm
diff --git a/pldmd.cpp b/pldmd.cpp
index 0034bcb..5623975 100644
--- a/pldmd.cpp
+++ b/pldmd.cpp
@@ -1,5 +1,6 @@
#include "libpldmresponder/base.hpp"
#include "libpldmresponder/bios.hpp"
+#include "libpldmresponder/platform.hpp"
#include "libpldmresponder/utils.hpp"
#include "registration.hpp"
@@ -24,6 +25,7 @@
#include "libpldm/base.h"
#include "libpldm/bios.h"
+#include "libpldm/platform.h"
#ifdef OEM_IBM
#include "libpldmresponder/file_io.hpp"
@@ -130,6 +132,7 @@
pldm::responder::base::registerHandlers();
pldm::responder::bios::registerHandlers();
+ pldm::responder::platform::registerHandlers();
// Outgoing message.
struct iovec iov[2]{};
diff --git a/test/Makefile.am b/test/Makefile.am
index fbaa01f..9891a28 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -27,14 +27,18 @@
$(PTHREAD_CFLAGS) \
$(PHOSPHOR_LOGGING_CFLAGS) \
$(SDBUSPLUS_CFLAGS) \
- $(CODE_COVERAGE_CXXFLAGS)
+ $(CODE_COVERAGE_CXXFLAGS) \
+ $(PHOSPHOR_DBUS_INTERFACES_CFLAGS)
test_ldflags = \
-lgtest_main \
-lgtest \
+ -lgmock \
+ -lstdc++fs \
$(PTHREAD_LIBS) \
$(SDBUSPLUS_LIBS) \
$(PHOSPHOR_LOGGING_LIBS) \
+ $(PHOSPHOR_DBUS_INTERFACES_LIBS) \
$(OESDK_TESTCASE_FLAGS)
if OEM_IBM
@@ -162,13 +166,15 @@
libpldmresponder_platform_test_CPPFLAGS = $(test_cppflags)
libpldmresponder_platform_test_CXXFLAGS = $(test_cxxflags)
-libpldmresponder_platform_test_LDFLAGS = $(test_ldflags)
+libpldmresponder_platform_test_LDFLAGS = $(test_ldflags) $(SDBUSPLUS_LIBS)
libpldmresponder_platform_test_LDADD = \
+ $(top_builddir)/pldmd-registration.o \
$(top_builddir)/libpldm/libpldm_la-base.o \
$(top_builddir)/libpldm/libpldm_la-platform.o \
$(top_builddir)/libpldmresponder/libpldmresponder_la-pdr.o \
$(top_builddir)/libpldmresponder/libpldmresponder_la-effecters.o \
$(top_builddir)/libpldmresponder/libpldmresponder_la-platform.o \
+ $(top_builddir)/libpldmresponder/libpldmresponder_la-utils.o
$(PHOSPHOR_LOGGING_LIBS) \
$(PHOSPHOR_DBUS_INTERFACES_LIBS) \
$(SDBUSPLUS_LIBS) \
diff --git a/test/libpldmresponder_pdr_state_effecter_test.cpp b/test/libpldmresponder_pdr_state_effecter_test.cpp
index f763593..c55feb0 100644
--- a/test/libpldmresponder_pdr_state_effecter_test.cpp
+++ b/test/libpldmresponder_pdr_state_effecter_test.cpp
@@ -25,7 +25,7 @@
ASSERT_EQ(pdr->hdr.version, 1);
ASSERT_EQ(pdr->hdr.type, PLDM_STATE_EFFECTER_PDR);
ASSERT_EQ(pdr->hdr.record_change_num, 0);
- ASSERT_EQ(pdr->hdr.length, 19);
+ ASSERT_EQ(pdr->hdr.length, 23);
ASSERT_EQ(pdr->terminus_handle, 0);
ASSERT_EQ(pdr->effecter_id, 1);
@@ -35,7 +35,7 @@
ASSERT_EQ(pdr->effecter_semantic_id, 0);
ASSERT_EQ(pdr->effecter_init, PLDM_NO_INIT);
ASSERT_EQ(pdr->has_description_pdr, false);
- ASSERT_EQ(pdr->composite_effecter_count, 1);
+ ASSERT_EQ(pdr->composite_effecter_count, 2);
state_effecter_possible_states* states =
reinterpret_cast<state_effecter_possible_states*>(pdr->possible_states);
ASSERT_EQ(states->state_set_id, 196);
diff --git a/test/libpldmresponder_platform_test.cpp b/test/libpldmresponder_platform_test.cpp
index dd51e72..6b89b2e 100644
--- a/test/libpldmresponder_platform_test.cpp
+++ b/test/libpldmresponder_platform_test.cpp
@@ -1,11 +1,15 @@
+#include "libpldmresponder/effecters.hpp"
#include "libpldmresponder/pdr.hpp"
#include "libpldmresponder/platform.hpp"
#include <iostream>
+#include <gmock/gmock-matchers.h>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
using namespace pldm::responder;
+using namespace pldm::responder::pdr;
TEST(getPDR, testGoodPath)
{
@@ -171,3 +175,84 @@
}
ASSERT_EQ(found, true);
}
+
+namespace pldm
+{
+
+namespace responder
+{
+
+class MockdBusHandler
+{
+ public:
+ MOCK_CONST_METHOD4(setDbusProperty,
+ int(const std::string&, const std::string&,
+ const std::string&,
+ const std::variant<std::string>&));
+};
+} // namespace responder
+} // namespace pldm
+
+using ::testing::_;
+using ::testing::Return;
+
+TEST(setStateEffecterStatesHandler, testGoodRequest)
+{
+ Repo& pdrRepo = get("./pdr_jsons/state_effecter/good");
+ pdr::Entry e = pdrRepo.at(1);
+ pldm_state_effecter_pdr* pdr =
+ reinterpret_cast<pldm_state_effecter_pdr*>(e.data());
+ EXPECT_EQ(pdr->hdr.type, PLDM_STATE_EFFECTER_PDR);
+
+ std::vector<set_effecter_state_field> stateField;
+ stateField.push_back({PLDM_REQUEST_SET, 1});
+ stateField.push_back({PLDM_REQUEST_SET, 1});
+
+ auto bootProgressInf = "xyz.openbmc_project.State.OperatingSystem.Status";
+ auto bootProgressProp = "OperatingSystemState";
+ std::string objPath = "/foo/bar";
+ std::variant<std::string> value{"xyz.openbmc_project.State.OperatingSystem."
+ "Status.OSStatus.Standby"};
+
+ MockdBusHandler handlerObj;
+ EXPECT_CALL(handlerObj, setDbusProperty(objPath, bootProgressProp,
+ bootProgressInf, value))
+ .Times(2);
+ auto rc = setStateEffecterStatesHandler<MockdBusHandler>(handlerObj, 0x1,
+ stateField);
+ ASSERT_EQ(rc, 0);
+}
+
+TEST(setStateEffecterStatesHandler, testBadRequest)
+{
+ Repo& pdrRepo = get("./pdr_jsons/state_effecter/good");
+ pdr::Entry e = pdrRepo.at(1);
+ pldm_state_effecter_pdr* pdr =
+ reinterpret_cast<pldm_state_effecter_pdr*>(e.data());
+ EXPECT_EQ(pdr->hdr.type, PLDM_STATE_EFFECTER_PDR);
+
+ std::vector<set_effecter_state_field> stateField;
+ stateField.push_back({PLDM_REQUEST_SET, 3});
+ stateField.push_back({PLDM_REQUEST_SET, 4});
+
+ MockdBusHandler handlerObj;
+ auto rc = setStateEffecterStatesHandler<MockdBusHandler>(handlerObj, 0x1,
+ stateField);
+ ASSERT_EQ(rc, PLDM_PLATFORM_SET_EFFECTER_UNSUPPORTED_SENSORSTATE);
+
+ rc = setStateEffecterStatesHandler<MockdBusHandler>(handlerObj, 0x9,
+ stateField);
+ ASSERT_EQ(rc, PLDM_PLATFORM_INVALID_EFFECTER_ID);
+
+ stateField.push_back({PLDM_REQUEST_SET, 4});
+ rc = setStateEffecterStatesHandler<MockdBusHandler>(handlerObj, 0x1,
+ stateField);
+ ASSERT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+
+ std::vector<set_effecter_state_field> newStateField;
+ newStateField.push_back({PLDM_REQUEST_SET, 1});
+
+ rc = setStateEffecterStatesHandler<MockdBusHandler>(handlerObj, 0x2,
+ newStateField);
+ ASSERT_EQ(rc, PLDM_PLATFORM_INVALID_STATE_VALUE);
+}
diff --git a/test/meson.build b/test/meson.build
index 1ddb598..5bfad27 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -38,6 +38,6 @@
implicit_include_directories: false,
link_args: dynamic_linker,
build_rpath: get_option('oe-sdk').enabled() ? rpath : '',
- dependencies: [libpldm, libpldmresponder, gtest, gmock]),
+ dependencies: [libpldm, libpldmresponder, gtest, gmock, dependency('sdbusplus')]),
workdir: meson.current_source_dir())
endforeach
diff --git a/test/pdr_jsons/state_effecter/good/11.json b/test/pdr_jsons/state_effecter/good/11.json
index a037ad4..6a9944b 100644
--- a/test/pdr_jsons/state_effecter/good/11.json
+++ b/test/pdr_jsons/state_effecter/good/11.json
@@ -10,6 +10,14 @@
"states" : [1]
},
"dbus" : "/foo/bar"
+ },
+ {
+ "set" : {
+ "id" : 196,
+ "size" : 1,
+ "states" : [1, 2]
+ },
+ "dbus" : "/foo/bar"
}]
},
{