Support numeric effecters in dbus-to-host-effecter
Adds support of the numeric effecter PDR (section `28.11 Numeric
Effecter PDR` DSP0248 V1.3.0) type in dbus-to-host-effecter handler.
This handler will be applied for all PLDM termini but not only host.
The setting for one numeric effecter of one device can be:
{
"mctp_eid": 20,
"effecter_info": {
"effecterPdrType": 9,
"effecterID": 2,
"entityType": 32903,
"entityInstance": 2,
"containerID": 2,
"compositeEffecterCount": 1,
"checkHostState": false
},
"effecters": [
{
"dbus_info": {
"object_path": "/xyz/openbmc_project/sensors/power/A",
"interface": "xyz.openbmc_project.Sensor.Value",
"property_name": "Value",
"property_type": "double"
},
"effecterDataSize": 5,
"resolution": 1,
"offset": 0,
"unitModifier": 0
}
]
}
Where:
+ effecterPdrType to difference state/numeric effecter type. Default
is state effecter.
+ effecterID should be effecter ID and should not empty.
+ checkHostState can be set to false to bypass checking host state.
+ effecterDataSize, resolution, offset, unitModifier are from numeric
effecter PDR (section `28.11 Numeric Effecter PDR` DSP0248 V1.3.0)
Signed-off-by: Thu Nguyen <thu@os.amperecomputing.com>
Change-Id: I438d7f204643edd4066e8a6ba28d53a97503fc4b
diff --git a/.linter-ignore b/.linter-ignore
index 7b5e702..5792d57 100644
--- a/.linter-ignore
+++ b/.linter-ignore
@@ -1,4 +1,4 @@
-host-bmc/test/host_effecter_jsons/malformed/dbus_to_host_effecter.json
+host-bmc/test/host_effecter_jsons/malformed/dbus_to_terminus_effecter.json
host-bmc/test/host_effecter_jsons/no_json/dummy.json
libpldmresponder/test/pdr_jsons/state_effecter/malformed/effecter_pdr.json
libpldmresponder/test/pdr_jsons/state_sensor/malformed/sensor_pdr.json
diff --git a/common/test/pldm_utils_test.cpp b/common/test/pldm_utils_test.cpp
index d00bebd..bab64fc 100644
--- a/common/test/pldm_utils_test.cpp
+++ b/common/test/pldm_utils_test.cpp
@@ -1090,3 +1090,69 @@
result = trimNameForDbus(name);
EXPECT_EQ(expectedName, result);
}
+
+TEST(dbusPropValuesToDouble, goodTest)
+{
+ double value = 0;
+ bool ret =
+ dbusPropValuesToDouble("uint8_t", static_cast<uint8_t>(0x12), &value);
+ EXPECT_EQ(true, ret);
+ EXPECT_EQ(0x12, value);
+ ret =
+ dbusPropValuesToDouble("int16_t", static_cast<int16_t>(0x1234), &value);
+ EXPECT_EQ(true, ret);
+ EXPECT_EQ(0x1234, value);
+ ret = dbusPropValuesToDouble("uint16_t", static_cast<uint16_t>(0x8234),
+ &value);
+ EXPECT_EQ(true, ret);
+ EXPECT_EQ(0x8234, value);
+ ret = dbusPropValuesToDouble("int32_t", static_cast<int32_t>(0x12345678),
+ &value);
+ EXPECT_EQ(true, ret);
+ EXPECT_EQ(0x12345678, value);
+ ret = dbusPropValuesToDouble("uint32_t", static_cast<uint32_t>(0x82345678),
+ &value);
+ EXPECT_EQ(true, ret);
+ EXPECT_EQ(0x82345678, value);
+ ret = dbusPropValuesToDouble(
+ "int64_t", static_cast<int64_t>(0x1234567898765432), &value);
+ EXPECT_EQ(true, ret);
+ EXPECT_EQ(0x1234567898765432, value);
+ ret = dbusPropValuesToDouble(
+ "uint64_t", static_cast<uint64_t>(0x8234567898765432), &value);
+ EXPECT_EQ(true, ret);
+ EXPECT_EQ(0x8234567898765432, value);
+ ret = dbusPropValuesToDouble("double", static_cast<double>(1234.5678),
+ &value);
+ EXPECT_EQ(true, ret);
+ EXPECT_EQ(1234.5678, value);
+}
+
+TEST(dbusPropValuesToDouble, badTest)
+{
+ double value = std::numeric_limits<double>::quiet_NaN();
+ /* Type and Data variant are different */
+ bool ret =
+ dbusPropValuesToDouble("uint8_t", static_cast<uint16_t>(0x12), &value);
+ EXPECT_EQ(false, ret);
+ /* Unsupported Types */
+ ret = dbusPropValuesToDouble("string", static_cast<std::string>("hello"),
+ &value);
+ EXPECT_EQ(false, ret);
+ ret = dbusPropValuesToDouble("bool", static_cast<bool>(true), &value);
+ EXPECT_EQ(false, ret);
+ ret = dbusPropValuesToDouble("vector<uint8_t>",
+ static_cast<std::string>("hello"), &value);
+ EXPECT_EQ(false, ret);
+ ret = dbusPropValuesToDouble("vector<string>",
+ static_cast<std::string>("hello"), &value);
+ EXPECT_EQ(false, ret);
+ /* Support Type but Data Type is unsupported */
+ ret = dbusPropValuesToDouble("double", static_cast<std::string>("hello"),
+ &value);
+ EXPECT_EQ(false, ret);
+ /* Null pointer */
+ ret = dbusPropValuesToDouble("double", static_cast<std::string>("hello"),
+ nullptr);
+ EXPECT_EQ(false, ret);
+}
diff --git a/common/utils.cpp b/common/utils.cpp
index e652021..6f39321 100644
--- a/common/utils.cpp
+++ b/common/utils.cpp
@@ -677,5 +677,66 @@
}
return name;
}
+
+bool dbusPropValuesToDouble(const std::string_view& type,
+ const pldm::utils::PropertyValue& value,
+ double* doubleValue)
+{
+ if (!dbusValueNumericTypeNames.contains(type))
+ {
+ return false;
+ }
+
+ if (!doubleValue)
+ {
+ return false;
+ }
+
+ try
+ {
+ if (type == "uint8_t")
+ {
+ *doubleValue = static_cast<double>(std::get<uint8_t>(value));
+ }
+ else if (type == "int16_t")
+ {
+ *doubleValue = static_cast<double>(std::get<int16_t>(value));
+ }
+ else if (type == "uint16_t")
+ {
+ *doubleValue = static_cast<double>(std::get<uint16_t>(value));
+ }
+ else if (type == "int32_t")
+ {
+ *doubleValue = static_cast<double>(std::get<int32_t>(value));
+ }
+ else if (type == "uint32_t")
+ {
+ *doubleValue = static_cast<double>(std::get<uint32_t>(value));
+ }
+ else if (type == "int64_t")
+ {
+ *doubleValue = static_cast<double>(std::get<int64_t>(value));
+ }
+ else if (type == "uint64_t")
+ {
+ *doubleValue = static_cast<double>(std::get<uint64_t>(value));
+ }
+ else if (type == "double")
+ {
+ *doubleValue = static_cast<double>(std::get<double>(value));
+ }
+ else
+ {
+ return false;
+ }
+ }
+ catch (const std::exception& e)
+ {
+ return false;
+ }
+
+ return true;
+}
} // namespace utils
} // namespace pldm
diff --git a/common/utils.hpp b/common/utils.hpp
index 5561399..6c17191 100644
--- a/common/utils.hpp
+++ b/common/utils.hpp
@@ -36,6 +36,15 @@
{
namespace utils
{
+
+const std::set<std::string_view> dbusValueTypeNames = {
+ "bool", "uint8_t", "int16_t", "uint16_t",
+ "int32_t", "uint32_t", "int64_t", "uint64_t",
+ "double", "string", "vector<uint8_t>", "vector<string>"};
+const std::set<std::string_view> dbusValueNumericTypeNames = {
+ "uint8_t", "int16_t", "uint16_t", "int32_t",
+ "uint32_t", "int64_t", "uint64_t", "double"};
+
namespace fs = std::filesystem;
using Json = nlohmann::json;
constexpr bool Tx = true;
@@ -531,5 +540,18 @@
*/
std::string_view trimNameForDbus(std::string& name);
+/** @brief Convert the number type D-Bus Value to the double
+ *
+ * @param[in] type - string type should in dbusValueNumericTypeNames list
+ * @param[in] value - DBus PropertyValue variant
+ * @param[out] doubleValue - response value
+ *
+ * @return true if data type is corrected and converting is successful
+ * otherwise return false.
+ */
+bool dbusPropValuesToDouble(const std::string_view& type,
+ const pldm::utils::PropertyValue& value,
+ double* doubleValue);
+
} // namespace utils
} // namespace pldm
diff --git a/host-bmc/dbus_to_host_effecters.cpp b/host-bmc/dbus_to_host_effecters.cpp
deleted file mode 100644
index 9b1c250..0000000
--- a/host-bmc/dbus_to_host_effecters.cpp
+++ /dev/null
@@ -1,348 +0,0 @@
-#include "dbus_to_host_effecters.hpp"
-
-#include <libpldm/pdr.h>
-#include <libpldm/platform.h>
-
-#include <phosphor-logging/lg2.hpp>
-#include <xyz/openbmc_project/Common/error.hpp>
-#include <xyz/openbmc_project/State/Boot/Progress/client.hpp>
-#include <xyz/openbmc_project/State/OperatingSystem/Status/server.hpp>
-
-#include <fstream>
-
-PHOSPHOR_LOG2_USING;
-
-using namespace pldm::utils;
-
-namespace pldm
-{
-namespace host_effecters
-{
-using InternalFailure =
- sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
-
-constexpr auto hostEffecterJson = "dbus_to_host_effecter.json";
-
-void HostEffecterParser::populatePropVals(
- const Json& dBusValues, std::vector<PropertyValue>& propertyValues,
- const std::string& propertyType)
-
-{
- for (const auto& elem : dBusValues)
- {
- auto value = jsonEntryToDbusVal(propertyType, elem);
- propertyValues.emplace_back(value);
- }
-}
-
-void HostEffecterParser::parseEffecterJson(const std::string& jsonPath)
-{
- fs::path jsonDir(jsonPath);
- if (!fs::exists(jsonDir) || fs::is_empty(jsonDir))
- {
- error("Effecter json file for remote terminus '{PATH}' does not exist.",
- "PATH", jsonPath);
- return;
- }
-
- fs::path jsonFilePath = jsonDir / hostEffecterJson;
- if (!fs::exists(jsonFilePath))
- {
- error("Json at path '{PATH}' does not exist.", "PATH", jsonFilePath);
- throw InternalFailure();
- }
-
- std::ifstream jsonFile(jsonFilePath);
- auto data = Json::parse(jsonFile, nullptr, false);
- if (data.is_discarded())
- {
- error("Failed to parse json file {PATH}", "PATH", jsonFilePath);
- throw InternalFailure();
- }
- const Json empty{};
- const std::vector<Json> emptyList{};
-
- auto entries = data.value("entries", emptyList);
- for (const auto& entry : entries)
- {
- EffecterInfo effecterInfo;
- effecterInfo.mctpEid = entry.value("mctp_eid", 0xFF);
- auto jsonEffecterInfo = entry.value("effecter_info", empty);
- auto effecterId =
- jsonEffecterInfo.value("effecterID", PLDM_INVALID_EFFECTER_ID);
- effecterInfo.containerId = jsonEffecterInfo.value("containerID", 0);
- effecterInfo.entityType = jsonEffecterInfo.value("entityType", 0);
- effecterInfo.entityInstance =
- jsonEffecterInfo.value("entityInstance", 0);
- effecterInfo.compEffecterCnt =
- jsonEffecterInfo.value("compositeEffecterCount", 0);
- auto effecters = entry.value("effecters", emptyList);
- for (const auto& effecter : effecters)
- {
- DBusEffecterMapping dbusInfo{};
- auto jsonDbusInfo = effecter.value("dbus_info", empty);
- dbusInfo.dbusMap.objectPath = jsonDbusInfo.value("object_path", "");
- dbusInfo.dbusMap.interface = jsonDbusInfo.value("interface", "");
- dbusInfo.dbusMap.propertyName =
- jsonDbusInfo.value("property_name", "");
- dbusInfo.dbusMap.propertyType =
- jsonDbusInfo.value("property_type", "");
- Json propertyValues = jsonDbusInfo["property_values"];
-
- populatePropVals(propertyValues, dbusInfo.propertyValues,
- dbusInfo.dbusMap.propertyType);
-
- const std::vector<uint8_t> emptyStates{};
- auto state = effecter.value("state", empty);
- dbusInfo.state.stateSetId = state.value("id", 0);
- auto states = state.value("state_values", emptyStates);
- if (dbusInfo.propertyValues.size() != states.size())
- {
- error(
- "Number of states do not match with number of D-Bus property values in the json. Object path at '{PATH}' and property '{PROPERTY}' will not be monitored",
- "PATH", dbusInfo.dbusMap.objectPath, "PROPERTY",
- dbusInfo.dbusMap.propertyName);
- continue;
- }
- for (const auto& s : states)
- {
- dbusInfo.state.states.emplace_back(s);
- }
-
- auto effecterInfoIndex = hostEffecterInfo.size();
- auto dbusInfoIndex = effecterInfo.dbusInfo.size();
- createHostEffecterMatch(
- dbusInfo.dbusMap.objectPath, dbusInfo.dbusMap.interface,
- effecterInfoIndex, dbusInfoIndex, effecterId);
- effecterInfo.dbusInfo.emplace_back(std::move(dbusInfo));
- }
- hostEffecterInfo.emplace_back(std::move(effecterInfo));
- }
-}
-
-void HostEffecterParser::processHostEffecterChangeNotification(
- const DbusChgHostEffecterProps& chProperties, size_t effecterInfoIndex,
- size_t dbusInfoIndex, uint16_t effecterId)
-{
- using BootProgress =
- sdbusplus::client::xyz::openbmc_project::state::boot::Progress<>;
-
- const auto& propertyName = hostEffecterInfo[effecterInfoIndex]
- .dbusInfo[dbusInfoIndex]
- .dbusMap.propertyName;
-
- const auto& it = chProperties.find(propertyName);
-
- if (it == chProperties.end())
- {
- return;
- }
-
- if (effecterId == PLDM_INVALID_EFFECTER_ID)
- {
- constexpr auto localOrRemote = false;
- effecterId = findStateEffecterId(
- pdrRepo, hostEffecterInfo[effecterInfoIndex].entityType,
- hostEffecterInfo[effecterInfoIndex].entityInstance,
- hostEffecterInfo[effecterInfoIndex].containerId,
- hostEffecterInfo[effecterInfoIndex]
- .dbusInfo[dbusInfoIndex]
- .state.stateSetId,
- localOrRemote);
- if (effecterId == PLDM_INVALID_EFFECTER_ID)
- {
- error(
- "Effecter ID '{EFFECTERID}' of entity type '{TYPE}', entityInstance '{INSTANCE}' and containerID '{CONTAINER_ID}' not found in pdr repo",
- "EFFECTERID", effecterId, "TYPE",
- hostEffecterInfo[effecterInfoIndex].entityType, "INSTANCE",
- hostEffecterInfo[effecterInfoIndex].entityInstance,
- "CONTAINER_ID",
- hostEffecterInfo[effecterInfoIndex].containerId);
- return;
- }
- }
- constexpr auto hostStatePath = "/xyz/openbmc_project/state/host0";
-
- try
- {
- auto propVal = dbusHandler->getDbusPropertyVariant(
- hostStatePath, "BootProgress", BootProgress::interface);
-
- using Stages = BootProgress::ProgressStages;
- auto currHostState = sdbusplus::message::convert_from_string<Stages>(
- std::get<std::string>(propVal))
- .value();
-
- if (currHostState != Stages::SystemInitComplete &&
- currHostState != Stages::OSRunning &&
- currHostState != Stages::SystemSetup)
- {
- info(
- "Remote terminus is not up/active, current remote terminus state is: '{CURRENT_HOST_STATE}'",
- "CURRENT_HOST_STATE", currHostState);
- return;
- }
- }
- catch (const sdbusplus::exception_t& e)
- {
- error(
- "Error in getting current remote terminus state. Will still continue to set the remote terminus effecter, error - {ERROR}",
- "ERROR", e);
- }
- uint8_t newState{};
- try
- {
- newState =
- findNewStateValue(effecterInfoIndex, dbusInfoIndex, it->second);
- }
- catch (const std::out_of_range& e)
- {
- error("Failed to find new state '{NEW_STATE}' in json, error - {ERROR}",
- "ERROR", e, "NEW_STATE", newState);
- return;
- }
-
- std::vector<set_effecter_state_field> stateField;
- for (uint8_t i = 0; i < hostEffecterInfo[effecterInfoIndex].compEffecterCnt;
- i++)
- {
- if (i == dbusInfoIndex)
- {
- stateField.push_back({PLDM_REQUEST_SET, newState});
- }
- else
- {
- stateField.push_back({PLDM_NO_CHANGE, 0});
- }
- }
- int rc{};
- try
- {
- rc = setHostStateEffecter(effecterInfoIndex, stateField, effecterId);
- }
- catch (const std::runtime_error& e)
- {
- error(
- "Failed to set remote terminus state effecter for effecter ID '{EFFECTERID}', error - {ERROR}",
- "ERROR", e, "EFFECTERID", effecterId);
- return;
- }
- if (rc != PLDM_SUCCESS)
- {
- error(
- "Failed to set the remote terminus state effecter for effecter ID '{EFFECTERID}', response code '{RC}'",
- "EFFECTERID", effecterId, "RC", rc);
- }
-}
-
-uint8_t HostEffecterParser::findNewStateValue(
- size_t effecterInfoIndex, size_t dbusInfoIndex,
- const PropertyValue& propertyValue)
-{
- const auto& propValues = hostEffecterInfo[effecterInfoIndex]
- .dbusInfo[dbusInfoIndex]
- .propertyValues;
- auto it = std::find(propValues.begin(), propValues.end(), propertyValue);
- uint8_t newState{};
- if (it != propValues.end())
- {
- auto index = std::distance(propValues.begin(), it);
- newState = hostEffecterInfo[effecterInfoIndex]
- .dbusInfo[dbusInfoIndex]
- .state.states[index];
- }
- else
- {
- throw std::out_of_range("new state not found in json");
- }
- return newState;
-}
-
-int HostEffecterParser::setHostStateEffecter(
- size_t effecterInfoIndex, std::vector<set_effecter_state_field>& stateField,
- uint16_t effecterId)
-{
- uint8_t& mctpEid = hostEffecterInfo[effecterInfoIndex].mctpEid;
- uint8_t& compEffCnt = hostEffecterInfo[effecterInfoIndex].compEffecterCnt;
- auto instanceId = instanceIdDb->next(mctpEid);
-
- std::vector<uint8_t> requestMsg(
- sizeof(pldm_msg_hdr) + sizeof(effecterId) + sizeof(compEffCnt) +
- sizeof(set_effecter_state_field) * compEffCnt,
- 0);
- auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
- auto rc = encode_set_state_effecter_states_req(
- instanceId, effecterId, compEffCnt, stateField.data(), request);
-
- if (rc != PLDM_SUCCESS)
- {
- error(
- "Failed to encode set state effecter states message for effecter ID '{EFFECTERID}' and instanceID '{INSTANCE}' with response code '{RC}'",
- "EFFECTERID", effecterId, "INSTANCE", instanceId, "RC", lg2::hex,
- rc);
- instanceIdDb->free(mctpEid, instanceId);
- return rc;
- }
-
- auto setStateEffecterStatesRespHandler = [](mctp_eid_t /*eid*/,
- const pldm_msg* response,
- size_t respMsgLen) {
- if (response == nullptr || !respMsgLen)
- {
- error(
- "Failed to receive response for setting state effecter states.");
- return;
- }
- uint8_t completionCode{};
- auto rc = decode_set_state_effecter_states_resp(response, respMsgLen,
- &completionCode);
- if (rc)
- {
- error(
- "Failed to decode response of set state effecter states, response code '{RC}'",
- "RC", rc);
- pldm::utils::reportError(
- "xyz.openbmc_project.bmc.pldm.SetHostEffecterFailed");
- }
- if (completionCode)
- {
- error(
- "Failed to set a remote terminus effecter, completion code '{CC}'",
- "CC", completionCode);
- pldm::utils::reportError(
- "xyz.openbmc_project.bmc.pldm.SetHostEffecterFailed");
- }
- };
-
- rc = handler->registerRequest(
- mctpEid, instanceId, PLDM_PLATFORM, PLDM_SET_STATE_EFFECTER_STATES,
- std::move(requestMsg), std::move(setStateEffecterStatesRespHandler));
- if (rc)
- {
- error(
- "Failed to send request to set an effecter on remote terminus for effecter ID '{EFFECTERID}', response code '{RC}'",
- "EFFECTERID", effecterId, "RC", rc);
- }
- return rc;
-}
-
-void HostEffecterParser::createHostEffecterMatch(
- const std::string& objectPath, const std::string& interface,
- size_t effecterInfoIndex, size_t dbusInfoIndex, uint16_t effecterId)
-{
- using namespace sdbusplus::bus::match::rules;
- effecterInfoMatch.emplace_back(std::make_unique<sdbusplus::bus::match_t>(
- pldm::utils::DBusHandler::getBus(),
- propertiesChanged(objectPath, interface),
- [this, effecterInfoIndex, dbusInfoIndex,
- effecterId](sdbusplus::message_t& msg) {
- DbusChgHostEffecterProps props;
- std::string iface;
- msg.read(iface, props);
- processHostEffecterChangeNotification(props, effecterInfoIndex,
- dbusInfoIndex, effecterId);
- }));
-}
-
-} // namespace host_effecters
-} // namespace pldm
diff --git a/host-bmc/dbus_to_terminus_effecters.cpp b/host-bmc/dbus_to_terminus_effecters.cpp
new file mode 100644
index 0000000..f11dc2f
--- /dev/null
+++ b/host-bmc/dbus_to_terminus_effecters.cpp
@@ -0,0 +1,683 @@
+#include "dbus_to_terminus_effecters.hpp"
+
+#include <libpldm/pdr.h>
+#include <libpldm/platform.h>
+
+#include <phosphor-logging/lg2.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+#include <xyz/openbmc_project/State/Boot/Progress/client.hpp>
+#include <xyz/openbmc_project/State/OperatingSystem/Status/server.hpp>
+
+#include <fstream>
+
+PHOSPHOR_LOG2_USING;
+
+using namespace pldm::utils;
+
+namespace pldm
+{
+namespace host_effecters
+{
+using InternalFailure =
+ sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+
+constexpr auto hostEffecterJson = "dbus_to_terminus_effecter.json";
+
+void HostEffecterParser::populatePropVals(
+ const Json& dBusValues, std::vector<PropertyValue>& propertyValues,
+ const std::string& propertyType)
+
+{
+ for (const auto& elem : dBusValues)
+ {
+ auto value = jsonEntryToDbusVal(propertyType, elem);
+ propertyValues.emplace_back(value);
+ }
+}
+
+void HostEffecterParser::parseEffecterJson(const std::string& jsonPath)
+{
+ fs::path jsonDir(jsonPath);
+ if (!fs::exists(jsonDir) || fs::is_empty(jsonDir))
+ {
+ error("Effecter json file for remote terminus '{PATH}' does not exist.",
+ "PATH", jsonPath);
+ return;
+ }
+
+ fs::path jsonFilePath = jsonDir / hostEffecterJson;
+ if (!fs::exists(jsonFilePath))
+ {
+ error("Json at path '{PATH}' does not exist.", "PATH", jsonFilePath);
+ throw InternalFailure();
+ }
+
+ std::ifstream jsonFile(jsonFilePath);
+ auto data = Json::parse(jsonFile, nullptr, false);
+ if (data.is_discarded())
+ {
+ error("Failed to parse json file {PATH}", "PATH", jsonFilePath);
+ throw InternalFailure();
+ }
+ const Json empty{};
+ const std::vector<Json> emptyList{};
+
+ auto entries = data.value("entries", emptyList);
+ for (const auto& entry : entries)
+ {
+ EffecterInfo effecterInfo;
+ effecterInfo.mctpEid = entry.value("mctp_eid", 0xFF);
+ auto jsonEffecterInfo = entry.value("effecter_info", empty);
+ auto effecterId =
+ jsonEffecterInfo.value("effecterID", PLDM_INVALID_EFFECTER_ID);
+ /* default as PLDM_STATE_EFFECTER_PDR */
+ auto effecterPdrType =
+ jsonEffecterInfo.value("effecterPdrType", PLDM_STATE_EFFECTER_PDR);
+ if (effecterPdrType != PLDM_STATE_EFFECTER_PDR &&
+ effecterPdrType != PLDM_NUMERIC_EFFECTER_PDR)
+ {
+ error(
+ "Effecter PDRType not supported {TYPE} of effecterID '{EFFECTERID}'",
+ "TYPE", effecterPdrType, "EFFECTERID", effecterId);
+ continue;
+ }
+ effecterInfo.effecterPdrType = effecterPdrType;
+ effecterInfo.containerId = jsonEffecterInfo.value("containerID", 0);
+ effecterInfo.entityType = jsonEffecterInfo.value("entityType", 0);
+ effecterInfo.entityInstance =
+ jsonEffecterInfo.value("entityInstance", 0);
+ effecterInfo.compEffecterCnt =
+ jsonEffecterInfo.value("compositeEffecterCount", 0);
+ effecterInfo.checkHostState =
+ jsonEffecterInfo.value("checkHostState", true);
+ auto effecters = entry.value("effecters", emptyList);
+
+ if (effecterPdrType == PLDM_NUMERIC_EFFECTER_PDR)
+ {
+ for (const auto& effecter : effecters)
+ {
+ DBusNumericEffecterMapping dbusInfo{};
+ auto jsonDbusInfo = effecter.value("dbus_info", empty);
+ dbusInfo.dataSize = effecter.value("effecterDataSize", 0);
+ dbusInfo.unitModifier = effecter.value("unitModifier", 0);
+ dbusInfo.resolution = effecter.value("resolution", 1);
+ dbusInfo.offset = effecter.value("offset", 0);
+ dbusInfo.dbusMap.objectPath =
+ jsonDbusInfo.value("object_path", "");
+ dbusInfo.dbusMap.interface =
+ jsonDbusInfo.value("interface", "");
+ dbusInfo.dbusMap.propertyName =
+ jsonDbusInfo.value("property_name", "");
+ dbusInfo.dbusMap.propertyType =
+ jsonDbusInfo.value("property_type", "");
+ /**
+ * Only support these property type for Numeric Effecter D-Bus
+ * property:
+ * "uint8_t", "int16_t", "uint16_t", "int32_t", "uint32_t",
+ * "int64_t", "uint64_t", "double"
+ */
+ if (!dbusValueNumericTypeNames.contains(
+ dbusInfo.dbusMap.propertyType))
+ {
+ lg2::error(
+ "Invalid PropertyType {TYPE} of object path {PATH} property name {NAME}",
+ "TYPE", dbusInfo.dbusMap.propertyType, "PATH",
+ dbusInfo.dbusMap.objectPath, "NAME",
+ dbusInfo.dbusMap.propertyName);
+ continue;
+ }
+
+ dbusInfo.propertyValue =
+ std::numeric_limits<double>::quiet_NaN();
+ auto effecterInfoIndex = hostEffecterInfo.size();
+ auto dbusInfoIndex = effecterInfo.dbusInfo.size();
+ createHostEffecterMatch(
+ dbusInfo.dbusMap.objectPath, dbusInfo.dbusMap.interface,
+ effecterInfoIndex, dbusInfoIndex, effecterId);
+ effecterInfo.dbusNumericEffecterInfo.emplace_back(
+ std::move(dbusInfo));
+ }
+ hostEffecterInfo.emplace_back(std::move(effecterInfo));
+ continue;
+ }
+
+ for (const auto& effecter : effecters)
+ {
+ DBusEffecterMapping dbusInfo{};
+ auto jsonDbusInfo = effecter.value("dbus_info", empty);
+ dbusInfo.dbusMap.objectPath = jsonDbusInfo.value("object_path", "");
+ dbusInfo.dbusMap.interface = jsonDbusInfo.value("interface", "");
+ dbusInfo.dbusMap.propertyName =
+ jsonDbusInfo.value("property_name", "");
+ dbusInfo.dbusMap.propertyType =
+ jsonDbusInfo.value("property_type", "");
+ Json propertyValues = jsonDbusInfo["property_values"];
+
+ populatePropVals(propertyValues, dbusInfo.propertyValues,
+ dbusInfo.dbusMap.propertyType);
+
+ const std::vector<uint8_t> emptyStates{};
+ auto state = effecter.value("state", empty);
+ dbusInfo.state.stateSetId = state.value("id", 0);
+ auto states = state.value("state_values", emptyStates);
+ if (dbusInfo.propertyValues.size() != states.size())
+ {
+ error(
+ "Number of states do not match with number of D-Bus property values in the json. Object path at '{PATH}' and property '{PROPERTY}' will not be monitored",
+ "PATH", dbusInfo.dbusMap.objectPath, "PROPERTY",
+ dbusInfo.dbusMap.propertyName);
+ continue;
+ }
+ for (const auto& s : states)
+ {
+ dbusInfo.state.states.emplace_back(s);
+ }
+
+ auto effecterInfoIndex = hostEffecterInfo.size();
+ auto dbusInfoIndex = effecterInfo.dbusInfo.size();
+ createHostEffecterMatch(
+ dbusInfo.dbusMap.objectPath, dbusInfo.dbusMap.interface,
+ effecterInfoIndex, dbusInfoIndex, effecterId);
+ effecterInfo.dbusInfo.emplace_back(std::move(dbusInfo));
+ }
+ hostEffecterInfo.emplace_back(std::move(effecterInfo));
+ }
+}
+
+bool HostEffecterParser::isHostOn(void)
+{
+ using BootProgress =
+ sdbusplus::client::xyz::openbmc_project::state::boot::Progress<>;
+ constexpr auto hostStatePath = "/xyz/openbmc_project/state/host0";
+ try
+ {
+ auto propVal = dbusHandler->getDbusPropertyVariant(
+ hostStatePath, "BootProgress", BootProgress::interface);
+
+ using Stages = BootProgress::ProgressStages;
+ auto currHostState = sdbusplus::message::convert_from_string<Stages>(
+ std::get<std::string>(propVal))
+ .value();
+
+ if (currHostState != Stages::SystemInitComplete &&
+ currHostState != Stages::OSRunning &&
+ currHostState != Stages::SystemSetup &&
+ currHostState != Stages::OEM)
+ {
+ info(
+ "Remote terminus is not up/active, current remote terminus state is: '{CURRENT_HOST_STATE}'",
+ "CURRENT_HOST_STATE", currHostState);
+ return false;
+ }
+ }
+ catch (const sdbusplus::exception_t& e)
+ {
+ error(
+ "Error in getting current remote terminus state. Will still continue to set the remote terminus effecter, error - {ERROR}",
+ "ERROR", e);
+ return false;
+ }
+
+ return true;
+}
+
+void HostEffecterParser::processHostEffecterChangeNotification(
+ const DbusChgHostEffecterProps& chProperties, size_t effecterInfoIndex,
+ size_t dbusInfoIndex, uint16_t effecterId)
+{
+ const auto& pdrType = hostEffecterInfo[effecterInfoIndex].effecterPdrType;
+ if (pdrType == PLDM_NUMERIC_EFFECTER_PDR)
+ {
+ processTerminusNumericEffecterChangeNotification(
+ chProperties, effecterInfoIndex, dbusInfoIndex, effecterId);
+ return;
+ }
+ const auto& propertyName = hostEffecterInfo[effecterInfoIndex]
+ .dbusInfo[dbusInfoIndex]
+ .dbusMap.propertyName;
+
+ const auto& it = chProperties.find(propertyName);
+
+ if (it == chProperties.end())
+ {
+ return;
+ }
+
+ if (effecterId == PLDM_INVALID_EFFECTER_ID)
+ {
+ constexpr auto localOrRemote = false;
+ effecterId = findStateEffecterId(
+ pdrRepo, hostEffecterInfo[effecterInfoIndex].entityType,
+ hostEffecterInfo[effecterInfoIndex].entityInstance,
+ hostEffecterInfo[effecterInfoIndex].containerId,
+ hostEffecterInfo[effecterInfoIndex]
+ .dbusInfo[dbusInfoIndex]
+ .state.stateSetId,
+ localOrRemote);
+ if (effecterId == PLDM_INVALID_EFFECTER_ID)
+ {
+ error(
+ "Effecter ID '{EFFECTERID}' of entity type '{TYPE}', entityInstance '{INSTANCE}' and containerID '{CONTAINER_ID}' not found in pdr repo",
+ "EFFECTERID", effecterId, "TYPE",
+ hostEffecterInfo[effecterInfoIndex].entityType, "INSTANCE",
+ hostEffecterInfo[effecterInfoIndex].entityInstance,
+ "CONTAINER_ID",
+ hostEffecterInfo[effecterInfoIndex].containerId);
+ return;
+ }
+ }
+
+ if (!isHostOn())
+ {
+ return;
+ }
+
+ uint8_t newState{};
+ try
+ {
+ newState =
+ findNewStateValue(effecterInfoIndex, dbusInfoIndex, it->second);
+ }
+ catch (const std::out_of_range& e)
+ {
+ error("Failed to find new state '{NEW_STATE}' in json, error - {ERROR}",
+ "ERROR", e, "NEW_STATE", newState);
+ return;
+ }
+
+ std::vector<set_effecter_state_field> stateField;
+ for (uint8_t i = 0; i < hostEffecterInfo[effecterInfoIndex].compEffecterCnt;
+ i++)
+ {
+ if (i == dbusInfoIndex)
+ {
+ stateField.push_back({PLDM_REQUEST_SET, newState});
+ }
+ else
+ {
+ stateField.push_back({PLDM_NO_CHANGE, 0});
+ }
+ }
+ int rc{};
+ try
+ {
+ rc = setHostStateEffecter(effecterInfoIndex, stateField, effecterId);
+ }
+ catch (const std::runtime_error& e)
+ {
+ error(
+ "Failed to set remote terminus state effecter for effecter ID '{EFFECTERID}', error - {ERROR}",
+ "ERROR", e, "EFFECTERID", effecterId);
+ return;
+ }
+ if (rc != PLDM_SUCCESS)
+ {
+ error(
+ "Failed to set the remote terminus state effecter for effecter ID '{EFFECTERID}', response code '{RC}'",
+ "EFFECTERID", effecterId, "RC", rc);
+ }
+}
+
+double HostEffecterParser::adjustValue(double value, double offset,
+ double resolution, int8_t modify)
+{
+ double unitModifier = std::pow(10, signed(modify));
+ return std::round((value - offset) * resolution / unitModifier);
+}
+
+void HostEffecterParser::processTerminusNumericEffecterChangeNotification(
+ const DbusChgHostEffecterProps& chProperties, size_t effecterInfoIndex,
+ size_t dbusInfoIndex, uint16_t effecterId)
+{
+ const auto& checkHost = hostEffecterInfo[effecterInfoIndex].checkHostState;
+ const auto& propValues = hostEffecterInfo[effecterInfoIndex]
+ .dbusNumericEffecterInfo[dbusInfoIndex];
+ const auto& propertyName = propValues.dbusMap.propertyName;
+ const auto& propertyType = propValues.dbusMap.propertyType;
+
+ if (effecterId == PLDM_INVALID_EFFECTER_ID)
+ {
+ lg2::error(
+ "Dbus to PLDM Numeric Effecter setting requires valid effecter ID. Invalid effecter ID {EFFECTER_ID}",
+ "EFFECTER_ID", effecterId);
+ }
+
+ if (!dbusValueNumericTypeNames.contains(propertyType))
+ {
+ lg2::error(
+ "DBus Value to PLDM Numeric Effecter setting only supports D-Bus Numeric data type. Invalid type {TYPE}",
+ "TYPE", propertyType);
+ return;
+ }
+
+ const auto& it = chProperties.find(propertyName);
+
+ if (it == chProperties.end())
+ {
+ return;
+ }
+
+ double val = std::numeric_limits<double>::quiet_NaN();
+ if (!pldm::utils::dbusPropValuesToDouble(propertyType, it->second, &val))
+ {
+ lg2::error(
+ "DBus Value to PLDM Numeric Effecter setting only supports Numeric D-Bus data type. Invalid type {TYPE}",
+ "TYPE", propertyType);
+ return;
+ }
+
+ /* Update the current value of D-Bus interface*/
+ if (!std::isnan(val) && std::isnan(propValues.propertyValue))
+ {
+ hostEffecterInfo[effecterInfoIndex]
+ .dbusNumericEffecterInfo[dbusInfoIndex]
+ .propertyValue = val;
+ return;
+ }
+
+ /* Bypass the setting when the current value is NA or settting value is NA*/
+ if (std::isnan(propValues.propertyValue) || std::isnan(val))
+ {
+ return;
+ }
+
+ /* Setting value equals the D-Bus value which is real value of effecter */
+ if (val == propValues.propertyValue)
+ {
+ return;
+ }
+
+ double rawValue = adjustValue(val, propValues.offset, propValues.resolution,
+ propValues.unitModifier);
+
+ if (checkHost && !isHostOn())
+ {
+ return;
+ }
+
+ try
+ {
+ auto rc = setTerminusNumericEffecter(effecterInfoIndex, effecterId,
+ propValues.dataSize, rawValue);
+ if (rc)
+ {
+ error(
+ "Could not set the numeric effecter ID '{EFFECTERID}' return code '{RC}'",
+ "EFFECTERID", effecterId, "RC", rc);
+ return;
+ }
+ }
+ catch (const std::runtime_error& e)
+ {
+ error("Could not set numeric effecter ID= '{EFFECTERID}'", "EFFECTERID",
+ effecterId);
+ return;
+ }
+
+ hostEffecterInfo[effecterInfoIndex]
+ .dbusNumericEffecterInfo[dbusInfoIndex]
+ .propertyValue = val;
+
+ return;
+}
+
+uint8_t HostEffecterParser::findNewStateValue(
+ size_t effecterInfoIndex, size_t dbusInfoIndex,
+ const PropertyValue& propertyValue)
+{
+ const auto& propValues = hostEffecterInfo[effecterInfoIndex]
+ .dbusInfo[dbusInfoIndex]
+ .propertyValues;
+ auto it = std::find(propValues.begin(), propValues.end(), propertyValue);
+ uint8_t newState{};
+ if (it != propValues.end())
+ {
+ auto index = std::distance(propValues.begin(), it);
+ newState = hostEffecterInfo[effecterInfoIndex]
+ .dbusInfo[dbusInfoIndex]
+ .state.states[index];
+ }
+ else
+ {
+ throw std::out_of_range("new state not found in json");
+ }
+ return newState;
+}
+
+size_t getEffecterDataSize(uint8_t effecterDataSize)
+{
+ switch (effecterDataSize)
+ {
+ case PLDM_EFFECTER_DATA_SIZE_UINT8:
+ return sizeof(uint8_t);
+ case PLDM_EFFECTER_DATA_SIZE_SINT8:
+ return sizeof(int8_t);
+ case PLDM_EFFECTER_DATA_SIZE_UINT16:
+ return sizeof(uint16_t);
+ case PLDM_EFFECTER_DATA_SIZE_SINT16:
+ return sizeof(int16_t);
+ case PLDM_EFFECTER_DATA_SIZE_UINT32:
+ return sizeof(uint32_t);
+ case PLDM_EFFECTER_DATA_SIZE_SINT32:
+ return sizeof(int32_t);
+ default:
+ return 0;
+ }
+}
+
+int HostEffecterParser::setTerminusNumericEffecter(
+ size_t effecterInfoIndex, uint16_t effecterId, uint8_t dataSize,
+ double rawValue)
+{
+ uint8_t& mctpEid = hostEffecterInfo[effecterInfoIndex].mctpEid;
+ auto instanceId = instanceIdDb->next(mctpEid);
+ int rc = PLDM_ERROR;
+ std::vector<uint8_t> requestMsg;
+
+ /**
+ * PLDM_SET_NUMERIC_EFFECTER_VALUE_MIN_REQ_BYTES = 4. It includes the 1 byte
+ * value for effecterValue as `Table 48 - SetNumericEffecterValue command
+ * format` DSP0248 V1.3.0 So the payload_length of `SetNumericEffecterValue`
+ * request message will be payload_length =
+ * PLDM_SET_NUMERIC_EFFECTER_VALUE_MIN_REQ_BYTES - 1 + sizeof(dataType)
+ */
+ size_t payload_length = PLDM_SET_NUMERIC_EFFECTER_VALUE_MIN_REQ_BYTES - 1 +
+ getEffecterDataSize(dataSize);
+ requestMsg.resize(sizeof(pldm_msg_hdr) + payload_length);
+ auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+ switch (dataSize)
+ {
+ case PLDM_EFFECTER_DATA_SIZE_UINT8:
+ {
+ auto value_uint8 = (uint8_t)rawValue;
+ rc = encode_set_numeric_effecter_value_req(
+ instanceId, effecterId, dataSize,
+ reinterpret_cast<uint8_t*>(&value_uint8), request,
+ payload_length);
+ break;
+ }
+ case PLDM_EFFECTER_DATA_SIZE_SINT8:
+ {
+ auto value_int8 = (int8_t)rawValue;
+ rc = encode_set_numeric_effecter_value_req(
+ instanceId, effecterId, dataSize,
+ reinterpret_cast<uint8_t*>(&value_int8), request,
+ payload_length);
+ break;
+ }
+ case PLDM_EFFECTER_DATA_SIZE_UINT16:
+ {
+ auto value_uint16 = (uint16_t)rawValue;
+ rc = encode_set_numeric_effecter_value_req(
+ instanceId, effecterId, dataSize,
+ reinterpret_cast<uint8_t*>(&value_uint16), request,
+ payload_length);
+ break;
+ }
+ case PLDM_EFFECTER_DATA_SIZE_SINT16:
+ {
+ auto value_int16 = (int16_t)rawValue;
+ rc = encode_set_numeric_effecter_value_req(
+ instanceId, effecterId, dataSize,
+ reinterpret_cast<uint8_t*>(&value_int16), request,
+ payload_length);
+ break;
+ }
+ case PLDM_EFFECTER_DATA_SIZE_UINT32:
+ {
+ auto value_uint32 = (uint32_t)rawValue;
+ rc = encode_set_numeric_effecter_value_req(
+ instanceId, effecterId, dataSize,
+ reinterpret_cast<uint8_t*>(&value_uint32), request,
+ payload_length);
+ break;
+ }
+ case PLDM_EFFECTER_DATA_SIZE_SINT32:
+ {
+ auto value_int32 = (int32_t)rawValue;
+ rc = encode_set_numeric_effecter_value_req(
+ instanceId, effecterId, dataSize,
+ reinterpret_cast<uint8_t*>(&value_int32), request,
+ payload_length);
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (rc)
+ {
+ error(
+ "Failed to encode set numeric effecter request message for effecter ID '{EFFECTERID}' and instanceID '{INSTANCE}' with error code '{RC}'",
+ "EFFECTERID", effecterId, "INSTANCE", instanceId, "RC", lg2::hex,
+ rc);
+
+ instanceIdDb->free(mctpEid, instanceId);
+ return rc;
+ }
+
+ auto setNumericEffecterRespHandler = [effecterId](mctp_eid_t /*eid*/,
+ const pldm_msg* response,
+ size_t respMsgLen) {
+ if (response == nullptr || !respMsgLen)
+ {
+ error(
+ "Failed to receive response for setNumericEffecterValue command");
+ return;
+ }
+ uint8_t completionCode{};
+ auto rc = decode_set_numeric_effecter_value_resp(response, respMsgLen,
+ &completionCode);
+
+ if (rc)
+ {
+ error(
+ "Failed to decode set numeric effecter response message for effecter ID '{EFFECTERID}' with error code '{RC}'",
+ "EFFECTERID", effecterId, "RC", lg2::hex, rc);
+ }
+ if (completionCode)
+ {
+ error(
+ "Failed to set numeric effecter for effecter ID '{EFFECTERID}' with complete code '{CC}'",
+ "EFFECTERID", effecterId, "CC", lg2::hex, completionCode);
+ }
+ };
+
+ rc = handler->registerRequest(
+ mctpEid, instanceId, PLDM_PLATFORM, PLDM_SET_NUMERIC_EFFECTER_VALUE,
+ std::move(requestMsg), std::move(setNumericEffecterRespHandler));
+ if (rc)
+ {
+ error("Failed to send request to set an effecter on Host");
+ }
+ return rc;
+}
+
+int HostEffecterParser::setHostStateEffecter(
+ size_t effecterInfoIndex, std::vector<set_effecter_state_field>& stateField,
+ uint16_t effecterId)
+{
+ uint8_t& mctpEid = hostEffecterInfo[effecterInfoIndex].mctpEid;
+ uint8_t& compEffCnt = hostEffecterInfo[effecterInfoIndex].compEffecterCnt;
+ auto instanceId = instanceIdDb->next(mctpEid);
+
+ std::vector<uint8_t> requestMsg(
+ sizeof(pldm_msg_hdr) + sizeof(effecterId) + sizeof(compEffCnt) +
+ sizeof(set_effecter_state_field) * compEffCnt,
+ 0);
+ auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+ auto rc = encode_set_state_effecter_states_req(
+ instanceId, effecterId, compEffCnt, stateField.data(), request);
+
+ if (rc != PLDM_SUCCESS)
+ {
+ error(
+ "Failed to encode set state effecter states message for effecter ID '{EFFECTERID}' and instanceID '{INSTANCE}' with response code '{RC}'",
+ "EFFECTERID", effecterId, "INSTANCE", instanceId, "RC", lg2::hex,
+ rc);
+ instanceIdDb->free(mctpEid, instanceId);
+ return rc;
+ }
+
+ auto setStateEffecterStatesRespHandler = [](mctp_eid_t /*eid*/,
+ const pldm_msg* response,
+ size_t respMsgLen) {
+ if (response == nullptr || !respMsgLen)
+ {
+ error(
+ "Failed to receive response for setting state effecter states.");
+ return;
+ }
+ uint8_t completionCode{};
+ auto rc = decode_set_state_effecter_states_resp(response, respMsgLen,
+ &completionCode);
+ if (rc)
+ {
+ error(
+ "Failed to decode response of set state effecter states, response code '{RC}'",
+ "RC", rc);
+ pldm::utils::reportError(
+ "xyz.openbmc_project.bmc.pldm.SetHostEffecterFailed");
+ }
+ if (completionCode)
+ {
+ error(
+ "Failed to set a remote terminus effecter, completion code '{CC}'",
+ "CC", completionCode);
+ pldm::utils::reportError(
+ "xyz.openbmc_project.bmc.pldm.SetHostEffecterFailed");
+ }
+ };
+
+ rc = handler->registerRequest(
+ mctpEid, instanceId, PLDM_PLATFORM, PLDM_SET_STATE_EFFECTER_STATES,
+ std::move(requestMsg), std::move(setStateEffecterStatesRespHandler));
+ if (rc)
+ {
+ error(
+ "Failed to send request to set an effecter on remote terminus for effecter ID '{EFFECTERID}', response code '{RC}'",
+ "EFFECTERID", effecterId, "RC", rc);
+ }
+ return rc;
+}
+
+void HostEffecterParser::createHostEffecterMatch(
+ const std::string& objectPath, const std::string& interface,
+ size_t effecterInfoIndex, size_t dbusInfoIndex, uint16_t effecterId)
+{
+ using namespace sdbusplus::bus::match::rules;
+ effecterInfoMatch.emplace_back(std::make_unique<sdbusplus::bus::match_t>(
+ pldm::utils::DBusHandler::getBus(),
+ propertiesChanged(objectPath, interface),
+ [this, effecterInfoIndex, dbusInfoIndex,
+ effecterId](sdbusplus::message_t& msg) {
+ DbusChgHostEffecterProps props;
+ std::string iface;
+ msg.read(iface, props);
+ processHostEffecterChangeNotification(props, effecterInfoIndex,
+ dbusInfoIndex, effecterId);
+ }));
+}
+
+} // namespace host_effecters
+} // namespace pldm
diff --git a/host-bmc/dbus_to_host_effecters.hpp b/host-bmc/dbus_to_terminus_effecters.hpp
similarity index 68%
rename from host-bmc/dbus_to_host_effecters.hpp
rename to host-bmc/dbus_to_terminus_effecters.hpp
index 7591381..8e4357e 100644
--- a/host-bmc/dbus_to_host_effecters.hpp
+++ b/host-bmc/dbus_to_terminus_effecters.hpp
@@ -43,18 +43,35 @@
PossibleState state; //!< Corresponding effecter states
};
+/** @struct DBusEffecterMapping
+ * Contains the D-Bus information for an effecter
+ */
+struct DBusNumericEffecterMapping
+{
+ pldm::utils::DBusMapping dbusMap;
+ uint8_t dataSize; //!< Numeric effecter PDR data size
+ double resolution; //!< Numeric effecter PDR resolution
+ double offset; //!< Numeric effecter PDR offset
+ int8_t unitModifier; //!< Numeric effecter PDR unitModifier
+ double propertyValue; //!< D-Bus property values
+};
+
/** @struct EffecterInfo
* Contains the effecter information as a whole
*/
struct EffecterInfo
{
- uint8_t mctpEid; //!< Host mctp eid
- uint16_t containerId; //!< Container Id for host effecter
- uint16_t entityType; //!< Entity type for the host effecter
- uint16_t entityInstance; //!< Entity instance for the host effecter
- uint8_t compEffecterCnt; //!< Composite effecter count
+ uint8_t mctpEid; //!< Host mctp eid
+ uint8_t effecterPdrType; //!< Effecter PDR type state/numeric
+ uint16_t containerId; //!< Container Id for host effecter
+ uint16_t entityType; //!< Entity type for the host effecter
+ uint16_t entityInstance; //!< Entity instance for the host effecter
+ uint8_t compEffecterCnt; //!< Composite effecter count
+ bool checkHostState; //!< Check host state before setting effecter
std::vector<DBusEffecterMapping>
- dbusInfo; //!< D-Bus information for the effecter id
+ dbusInfo; //!< D-Bus information for the effecter id
+ std::vector<DBusNumericEffecterMapping>
+ dbusNumericEffecterInfo; //!< D-Bus information for the effecter id
};
/** @class HostEffecterParser
@@ -120,6 +137,20 @@
const DbusChgHostEffecterProps& chProperties, size_t effecterInfoIndex,
size_t dbusInfoIndex, uint16_t effecterId);
+ /* @brief Method to take action when the subscribed D-Bus property is
+ * changed
+ * @param[in] chProperties - list of properties which have changed
+ * @param[in] effecterInfoIndex - index of effecterInfo pointer in
+ * hostEffecterInfo
+ * @param[in] dbusInfoIndex - index on dbusInfo pointer in each effecterInfo
+
+ * @param[in] effecterId - terminus numeric effecter id
+ * @return - none
+ */
+ void processTerminusNumericEffecterChangeNotification(
+ const DbusChgHostEffecterProps& chProperties, size_t effecterInfoIndex,
+ size_t dbusInfoIndex, uint16_t effecterId);
+
/* @brief Populate the property values in each dbusInfo from the json
*
* @param[in] dBusValues - json values
@@ -145,6 +176,19 @@
size_t effecterInfoIndex,
std::vector<set_effecter_state_field>& stateField, uint16_t effecterId);
+ /* @brief Set a terminus numeric effecter
+ *
+ * @param[in] effecterInfoIndex - index of effecterInfo pointer in
+ * hostEffecterInfo
+ * @param[in] effecterId - host effecter id
+ * @param[in] dataSize - data size
+ * @param[in] rawValue - raw value
+ * @return - PLDM status code
+ */
+ virtual int setTerminusNumericEffecter(size_t effecterInfoIndex,
+ uint16_t effecterId,
+ uint8_t dataSize, double rawValue);
+
/* @brief Fetches the new state value and the index in stateField set which
* needs to be set with the new value in the setStateEffecter call
* @param[in] effecterInfoIndex - index of effecterInfo in hostEffecterInfo
@@ -156,7 +200,7 @@
const pldm::utils::PropertyValue& propertyValue);
/* @brief Subscribes for D-Bus property change signal on the specified
- * object
+ * object
*
* @param[in] objectPath - D-Bus object path to look for
* @param[in] interface - D-Bus interface
@@ -169,6 +213,26 @@
const std::string& objectPath, const std::string& interface,
size_t effecterInfoIndex, size_t dbusInfoIndex, uint16_t effecterId);
+ /* @brief Adjust the nummeric effecter value base on the effecter
+ * configurations
+ *
+ * @param[in] value - Raw value
+ * @param[in] offset - offset config
+ * @param[in] resolution - resolution config
+ * @param[in] modify - modify config
+ *
+ * @return - Value of effecter
+ */
+ double adjustValue(double value, double offset, double resolution,
+ int8_t modify);
+
+ private:
+ /* @brief Verify host On state before configure the host effecters
+ *
+ * @return - true if host is on and false for others cases
+ */
+ bool isHostOn(void);
+
protected:
pldm::InstanceIdDb* instanceIdDb; //!< Reference to the InstanceIdDb object
//!< to obtain instance id
diff --git a/host-bmc/test/dbus_to_host_effecter_test.cpp b/host-bmc/test/dbus_to_host_effecter_test.cpp
deleted file mode 100644
index 00653ce..0000000
--- a/host-bmc/test/dbus_to_host_effecter_test.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-#include "common/test/mocked_utils.hpp"
-#include "common/utils.hpp"
-#include "host-bmc/dbus_to_host_effecters.hpp"
-
-#include <nlohmann/json.hpp>
-
-#include <gtest/gtest.h>
-
-using namespace pldm::host_effecters;
-using namespace pldm::utils;
-
-class MockHostEffecterParser : public HostEffecterParser
-{
- public:
- MockHostEffecterParser(int fd, const pldm_pdr* repo,
- DBusHandler* const dbusHandler,
- const std::string& jsonPath) :
- HostEffecterParser(nullptr, fd, repo, dbusHandler, jsonPath, nullptr)
- {}
-
- MOCK_METHOD(int, setHostStateEffecter,
- (size_t, std::vector<set_effecter_state_field>&, uint16_t),
- (override));
-
- MOCK_METHOD(void, createHostEffecterMatch,
- (const std::string&, const std::string&, size_t, size_t,
- uint16_t),
- (override));
-
- const std::vector<EffecterInfo>& gethostEffecterInfo()
- {
- return hostEffecterInfo;
- }
-};
-
-TEST(HostEffecterParser, parseEffecterJsonGoodPath)
-{
- MockdBusHandler dbusHandler;
- int sockfd{};
- MockHostEffecterParser hostEffecterParserGood(sockfd, nullptr, &dbusHandler,
- "./host_effecter_jsons/good");
- auto hostEffecterInfo = hostEffecterParserGood.gethostEffecterInfo();
- ASSERT_EQ(hostEffecterInfo.size(), 1);
- ASSERT_EQ(hostEffecterInfo[0].entityInstance, 0);
- ASSERT_EQ(hostEffecterInfo[0].entityType, 33);
- ASSERT_EQ(hostEffecterInfo[0].dbusInfo.size(), 1);
- DBusEffecterMapping dbusInfo{
- {"/xyz/openbmc_project/control/host0/boot",
- "xyz.openbmc_project.Control.Boot.Mode", "BootMode", "string"},
- {"xyz.openbmc_project.Control.Boot.Mode.Modes.Regular"},
- {196, {2}}};
- auto& temp = hostEffecterInfo[0].dbusInfo[0];
- ASSERT_EQ(temp.dbusMap.objectPath == dbusInfo.dbusMap.objectPath, true);
- ASSERT_EQ(temp.dbusMap.interface == dbusInfo.dbusMap.interface, true);
- ASSERT_EQ(temp.dbusMap.propertyName == dbusInfo.dbusMap.propertyName, true);
- ASSERT_EQ(temp.dbusMap.propertyType == dbusInfo.dbusMap.propertyType, true);
-}
-
-TEST(HostEffecterParser, parseEffecterJsonBadPath)
-{
- MockdBusHandler dbusHandler;
- int sockfd{};
- MockHostEffecterParser hostEffecterParser(sockfd, nullptr, &dbusHandler,
- "./host_effecter_jsons/no_json");
- ASSERT_THROW(
- hostEffecterParser.parseEffecterJson("./host_effecter_jsons/no_json"),
- std::exception);
- ASSERT_THROW(
- hostEffecterParser.parseEffecterJson("./host_effecter_jsons/malformed"),
- std::exception);
-}
-
-TEST(HostEffecterParser, findNewStateValue)
-{
- MockdBusHandler dbusHandler;
- int sockfd{};
- MockHostEffecterParser hostEffecterParser(sockfd, nullptr, &dbusHandler,
- "./host_effecter_jsons/good");
-
- PropertyValue val1{std::in_place_type<std::string>,
- "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular"};
- PropertyValue val2{std::in_place_type<std::string>,
- "xyz.openbmc_project.Control.Boot.Mode.Modes.Setup"};
- auto newState = hostEffecterParser.findNewStateValue(0, 0, val1);
- ASSERT_EQ(newState, 2);
-
- ASSERT_THROW(hostEffecterParser.findNewStateValue(0, 0, val2),
- std::exception);
-}
diff --git a/host-bmc/test/dbus_to_terminus_effecter_test.cpp b/host-bmc/test/dbus_to_terminus_effecter_test.cpp
new file mode 100644
index 0000000..f8ff8d6
--- /dev/null
+++ b/host-bmc/test/dbus_to_terminus_effecter_test.cpp
@@ -0,0 +1,143 @@
+#include "common/test/mocked_utils.hpp"
+#include "common/utils.hpp"
+#include "host-bmc/dbus_to_terminus_effecters.hpp"
+
+#include <nlohmann/json.hpp>
+
+#include <gtest/gtest.h>
+
+using namespace pldm::host_effecters;
+using namespace pldm::utils;
+
+class MockHostEffecterParser : public HostEffecterParser
+{
+ public:
+ MockHostEffecterParser(int fd, const pldm_pdr* repo,
+ DBusHandler* const dbusHandler,
+ const std::string& jsonPath) :
+ HostEffecterParser(nullptr, fd, repo, dbusHandler, jsonPath, nullptr)
+ {}
+
+ MOCK_METHOD(int, setHostStateEffecter,
+ (size_t, std::vector<set_effecter_state_field>&, uint16_t),
+ (override));
+
+ MOCK_METHOD(void, createHostEffecterMatch,
+ (const std::string&, const std::string&, size_t, size_t,
+ uint16_t),
+ (override));
+
+ const std::vector<EffecterInfo>& gethostEffecterInfo()
+ {
+ return hostEffecterInfo;
+ }
+};
+
+TEST(HostEffecterParser, parseEffecterJsonGoodPath)
+{
+ MockdBusHandler dbusHandler;
+ int sockfd{};
+ MockHostEffecterParser hostEffecterParserGood(sockfd, nullptr, &dbusHandler,
+ "./host_effecter_jsons/good");
+ auto hostEffecterInfo = hostEffecterParserGood.gethostEffecterInfo();
+ ASSERT_EQ(hostEffecterInfo.size(), 2);
+ ASSERT_EQ(hostEffecterInfo[0].effecterPdrType, PLDM_STATE_EFFECTER_PDR);
+ ASSERT_EQ(hostEffecterInfo[0].entityInstance, 0);
+ ASSERT_EQ(hostEffecterInfo[0].entityType, 33);
+ ASSERT_EQ(hostEffecterInfo[0].dbusInfo.size(), 1);
+ ASSERT_EQ(hostEffecterInfo[0].checkHostState, true);
+ DBusEffecterMapping dbusInfo{
+ {"/xyz/openbmc_project/control/host0/boot",
+ "xyz.openbmc_project.Control.Boot.Mode", "BootMode", "string"},
+ {"xyz.openbmc_project.Control.Boot.Mode.Modes.Regular"},
+ {196, {2}}};
+ auto& temp = hostEffecterInfo[0].dbusInfo[0];
+ ASSERT_EQ(temp.dbusMap.objectPath == dbusInfo.dbusMap.objectPath, true);
+ ASSERT_EQ(temp.dbusMap.interface == dbusInfo.dbusMap.interface, true);
+ ASSERT_EQ(temp.dbusMap.propertyName == dbusInfo.dbusMap.propertyName, true);
+ ASSERT_EQ(temp.dbusMap.propertyType == dbusInfo.dbusMap.propertyType, true);
+
+ /* Check Numeric Effecter in Good Json file */
+ ASSERT_EQ(hostEffecterInfo[1].effecterPdrType, PLDM_NUMERIC_EFFECTER_PDR);
+ ASSERT_EQ(hostEffecterInfo[1].entityType, 32903);
+ ASSERT_EQ(hostEffecterInfo[1].entityInstance, 6);
+ ASSERT_EQ(hostEffecterInfo[1].containerId, 4);
+ ASSERT_EQ(hostEffecterInfo[1].dbusNumericEffecterInfo.size(), 1);
+ ASSERT_EQ(hostEffecterInfo[1].checkHostState, false);
+ DBusNumericEffecterMapping dbusInfoNumeric{
+ {"/xyz/openbmc_project/effecters/power/PLimit",
+ "xyz.openbmc_project.Effecter.Value", "Value", "double"},
+ 5,
+ 1,
+ 0,
+ -3,
+ 100};
+ auto& tempNumeric = hostEffecterInfo[1].dbusNumericEffecterInfo[0];
+ ASSERT_EQ(tempNumeric.dbusMap.objectPath ==
+ dbusInfoNumeric.dbusMap.objectPath,
+ true);
+ ASSERT_EQ(tempNumeric.dbusMap.interface ==
+ dbusInfoNumeric.dbusMap.interface,
+ true);
+ ASSERT_EQ(tempNumeric.dbusMap.propertyName ==
+ dbusInfoNumeric.dbusMap.propertyName,
+ true);
+ ASSERT_EQ(tempNumeric.dbusMap.propertyType ==
+ dbusInfoNumeric.dbusMap.propertyType,
+ true);
+ ASSERT_EQ(tempNumeric.dataSize == dbusInfoNumeric.dataSize, true);
+ ASSERT_EQ(tempNumeric.resolution == dbusInfoNumeric.resolution, true);
+ ASSERT_EQ(tempNumeric.offset == dbusInfoNumeric.offset, true);
+ ASSERT_EQ(tempNumeric.unitModifier == dbusInfoNumeric.unitModifier, true);
+}
+
+TEST(HostEffecterParser, parseEffecterJsonBadPath)
+{
+ MockdBusHandler dbusHandler;
+ int sockfd{};
+ MockHostEffecterParser hostEffecterParser(sockfd, nullptr, &dbusHandler,
+ "./host_effecter_jsons/no_json");
+ ASSERT_THROW(
+ hostEffecterParser.parseEffecterJson("./host_effecter_jsons/no_json"),
+ std::exception);
+ ASSERT_THROW(
+ hostEffecterParser.parseEffecterJson("./host_effecter_jsons/malformed"),
+ std::exception);
+}
+
+TEST(HostEffecterParser, findNewStateValue)
+{
+ MockdBusHandler dbusHandler;
+ int sockfd{};
+ MockHostEffecterParser hostEffecterParser(sockfd, nullptr, &dbusHandler,
+ "./host_effecter_jsons/good");
+
+ PropertyValue val1{std::in_place_type<std::string>,
+ "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular"};
+ PropertyValue val2{std::in_place_type<std::string>,
+ "xyz.openbmc_project.Control.Boot.Mode.Modes.Setup"};
+ auto newState = hostEffecterParser.findNewStateValue(0, 0, val1);
+ ASSERT_EQ(newState, 2);
+
+ ASSERT_THROW(hostEffecterParser.findNewStateValue(0, 0, val2),
+ std::exception);
+}
+
+TEST(HostEffecterParser, adjustValue)
+{
+ MockdBusHandler dbusHandler;
+ int sockfd{};
+ MockHostEffecterParser hostEffecterParser(sockfd, nullptr, &dbusHandler,
+ "./host_effecter_jsons/good");
+
+ auto realVal = hostEffecterParser.adjustValue(200, -50, 0.5, -2);
+ ASSERT_EQ(realVal, 12500);
+ realVal = hostEffecterParser.adjustValue(0, -50, 1, 0);
+ ASSERT_EQ(realVal, 50);
+ realVal = hostEffecterParser.adjustValue(0, 100, 1, -1);
+ ASSERT_EQ(realVal, -1000);
+ realVal = hostEffecterParser.adjustValue(2.34, 0, 1, -1);
+ ASSERT_EQ(realVal, 23);
+ realVal = hostEffecterParser.adjustValue(2.35, 0, 1, -1);
+ ASSERT_EQ(realVal, 24);
+}
diff --git a/host-bmc/test/host_effecter_jsons/good/dbus_to_host_effecter.json b/host-bmc/test/host_effecter_jsons/good/dbus_to_host_effecter.json
deleted file mode 100644
index 5ae694c..0000000
--- a/host-bmc/test/host_effecter_jsons/good/dbus_to_host_effecter.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "entries": [
- {
- "mctp_eid": 9,
- "effecter_info": {
- "effecterID": 4,
- "containerID": 0,
- "entityType": 33,
- "entityInstance": 0,
- "compositeEffecterCount": 1
- },
- "effecters": [
- {
- "dbus_info": {
- "object_path": "/xyz/openbmc_project/control/host0/boot",
- "interface": "xyz.openbmc_project.Control.Boot.Mode",
- "property_name": "BootMode",
- "property_type": "string",
- "property_values": [
- "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular"
- ]
- },
- "state": {
- "id": 196,
- "state_values": [2]
- }
- }
- ]
- }
- ]
-}
diff --git a/host-bmc/test/host_effecter_jsons/good/dbus_to_terminus_effecter.json b/host-bmc/test/host_effecter_jsons/good/dbus_to_terminus_effecter.json
new file mode 100644
index 0000000..2bc789a
--- /dev/null
+++ b/host-bmc/test/host_effecter_jsons/good/dbus_to_terminus_effecter.json
@@ -0,0 +1,57 @@
+{
+ "entries": [
+ {
+ "mctp_eid": 9,
+ "effecter_info": {
+ "effecterID": 4,
+ "containerID": 0,
+ "entityType": 33,
+ "entityInstance": 0,
+ "compositeEffecterCount": 1
+ },
+ "effecters": [
+ {
+ "dbus_info": {
+ "object_path": "/xyz/openbmc_project/control/host0/boot",
+ "interface": "xyz.openbmc_project.Control.Boot.Mode",
+ "property_name": "BootMode",
+ "property_type": "string",
+ "property_values": [
+ "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular"
+ ]
+ },
+ "state": {
+ "id": 196,
+ "state_values": [2]
+ }
+ }
+ ]
+ },
+ {
+ "mctp_eid": 20,
+ "effecter_info": {
+ "effecterPdrType": 9,
+ "effecterID": 155,
+ "entityType": 32903,
+ "entityInstance": 6,
+ "containerID": 4,
+ "compositeEffecterCount": 1,
+ "checkHostState": false
+ },
+ "effecters": [
+ {
+ "dbus_info": {
+ "object_path": "/xyz/openbmc_project/effecters/power/PLimit",
+ "interface": "xyz.openbmc_project.Effecter.Value",
+ "property_name": "Value",
+ "property_type": "double"
+ },
+ "effecterDataSize": 5,
+ "resolution": 1,
+ "offset": 0,
+ "unitModifier": -3
+ }
+ ]
+ }
+ ]
+}
diff --git a/host-bmc/test/host_effecter_jsons/malformed/dbus_to_host_effecter.json b/host-bmc/test/host_effecter_jsons/malformed/dbus_to_terminus_effecter.json
similarity index 100%
rename from host-bmc/test/host_effecter_jsons/malformed/dbus_to_host_effecter.json
rename to host-bmc/test/host_effecter_jsons/malformed/dbus_to_terminus_effecter.json
diff --git a/host-bmc/test/meson.build b/host-bmc/test/meson.build
index 9f762ed..c1d429d 100644
--- a/host-bmc/test/meson.build
+++ b/host-bmc/test/meson.build
@@ -1,5 +1,5 @@
host_bmc_test_src = declare_dependency(
- sources: ['../dbus_to_host_effecters.cpp'],
+ sources: ['../dbus_to_terminus_effecters.cpp'],
include_directories: '../../requester',
)
@@ -13,7 +13,7 @@
'../dbus/pcie_slot.cpp',
]
-tests = ['dbus_to_host_effecter_test', 'utils_test', 'custom_dbus_test']
+tests = ['dbus_to_terminus_effecter_test', 'utils_test', 'custom_dbus_test']
foreach t : tests
test(
diff --git a/libpldmresponder/meson.build b/libpldmresponder/meson.build
index 4a0f697..2e0ed5a 100644
--- a/libpldmresponder/meson.build
+++ b/libpldmresponder/meson.build
@@ -26,7 +26,7 @@
'../host-bmc/host_pdr_handler.cpp',
'../host-bmc/utils.cpp',
'../host-bmc/dbus_to_event_handler.cpp',
- '../host-bmc/dbus_to_host_effecters.cpp',
+ '../host-bmc/dbus_to_terminus_effecters.cpp',
'../host-bmc/host_condition.cpp',
'../host-bmc/dbus/custom_dbus.cpp',
'../host-bmc/dbus/cable.cpp',
diff --git a/pldmd/pldmd.cpp b/pldmd/pldmd.cpp
index 8637a15..8941418 100644
--- a/pldmd/pldmd.cpp
+++ b/pldmd/pldmd.cpp
@@ -48,7 +48,7 @@
#ifdef LIBPLDMRESPONDER
#include "dbus_impl_pdr.hpp"
#include "host-bmc/dbus_to_event_handler.hpp"
-#include "host-bmc/dbus_to_host_effecters.hpp"
+#include "host-bmc/dbus_to_terminus_effecters.hpp"
#include "host-bmc/host_condition.hpp"
#include "host-bmc/host_pdr_handler.hpp"
#include "libpldmresponder/base.hpp"
@@ -209,15 +209,21 @@
requester::Handler<requester::Request> reqHandler(&pldmTransport, event,
instanceIdDb, verbose);
-#ifdef LIBPLDMRESPONDER
- using namespace pldm::state_sensor;
- dbus_api::Host dbusImplHost(bus, "/xyz/openbmc_project/pldm");
std::unique_ptr<pldm_pdr, decltype(&pldm_pdr_destroy)> pdrRepo(
pldm_pdr_init(), pldm_pdr_destroy);
if (!pdrRepo)
{
throw std::runtime_error("Failed to instantiate PDR repository");
}
+ DBusHandler dbusHandler;
+ std::unique_ptr<pldm::host_effecters::HostEffecterParser>
+ hostEffecterParser =
+ std::make_unique<pldm::host_effecters::HostEffecterParser>(
+ &instanceIdDb, pldmTransport.getEventSource(), pdrRepo.get(),
+ &dbusHandler, HOST_JSONS_DIR, &reqHandler);
+#ifdef LIBPLDMRESPONDER
+ using namespace pldm::state_sensor;
+ dbus_api::Host dbusImplHost(bus, "/xyz/openbmc_project/pldm");
std::unique_ptr<pldm_entity_association_tree,
decltype(&pldm_entity_association_tree_destroy)>
entityTree(pldm_entity_association_tree_init(),
@@ -237,10 +243,7 @@
"Failed to instantiate BMC PDR entity association tree");
}
std::shared_ptr<HostPDRHandler> hostPDRHandler;
- std::unique_ptr<pldm::host_effecters::HostEffecterParser>
- hostEffecterParser;
std::unique_ptr<DbusToPLDMEvent> dbusToPLDMEventHandler;
- DBusHandler dbusHandler;
std::unique_ptr<platform_config::Handler> platformConfigHandler{};
platformConfigHandler =
std::make_unique<platform_config::Handler>(PDR_JSONS_DIR);
@@ -256,10 +259,6 @@
// is running
dbusImplHost.setHostPdrObj(hostPDRHandler);
- hostEffecterParser =
- std::make_unique<pldm::host_effecters::HostEffecterParser>(
- &instanceIdDb, pldmTransport.getEventSource(), pdrRepo.get(),
- &dbusHandler, HOST_JSONS_DIR, &reqHandler);
dbusToPLDMEventHandler = std::make_unique<DbusToPLDMEvent>(
pldmTransport.getEventSource(), hostEID, instanceIdDb, &reqHandler);
}