platform-mc: Added Terminus/TerminusManager class
Added requester::sendRecvPldmMsg awaiter type to be able to send and
receive PLDM message by coroutine.
Added TerminusManager to discover terminus from EID list updated by
MCTPDiscovery class. The TerminusManager will initialize TID.
Signed-off-by: Gilbert Chen <gilbert.chen@arm.com>
Signed-off-by: Thu Nguyen <thu@os.amperecomputing.com>
Change-Id: Ifa5bdfff50648f1d7fba8710e160de662e8f9e06
diff --git a/platform-mc/manager.cpp b/platform-mc/manager.cpp
new file mode 100644
index 0000000..7e79a29
--- /dev/null
+++ b/platform-mc/manager.cpp
@@ -0,0 +1,35 @@
+#include "manager.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+
+PHOSPHOR_LOG2_USING;
+
+namespace pldm
+{
+namespace platform_mc
+{
+exec::task<int> Manager::beforeDiscoverTerminus()
+{
+ // Add any setup or checks needed before discovering a terminus
+ // If any setup/check fails, return the appropriate error code
+ // For now, we assume everything is successful
+ co_return PLDM_SUCCESS;
+}
+
+exec::task<int> Manager::afterDiscoverTerminus()
+{
+ auto rc = co_await platformManager.initTerminus();
+ if (rc != PLDM_SUCCESS)
+ {
+ lg2::error("Failed to initialize platform manager, error {RC}", "RC",
+ rc);
+ }
+ else
+ {
+ lg2::info("Successfully initialized platform manager");
+ }
+ co_return rc;
+}
+
+} // namespace platform_mc
+} // namespace pldm
diff --git a/platform-mc/manager.hpp b/platform-mc/manager.hpp
new file mode 100644
index 0000000..7636e94
--- /dev/null
+++ b/platform-mc/manager.hpp
@@ -0,0 +1,82 @@
+#pragma once
+
+#include "libpldm/pldm.h"
+
+#include "common/instance_id.hpp"
+#include "common/types.hpp"
+#include "platform_manager.hpp"
+#include "requester/handler.hpp"
+#include "requester/mctp_endpoint_discovery.hpp"
+#include "terminus_manager.hpp"
+
+namespace pldm
+{
+namespace platform_mc
+{
+
+/**
+ * @brief Manager
+ *
+ * This class handles all the aspect of the PLDM Platform Monitoring and Control
+ * specification for the MCTP devices
+ */
+class Manager : public pldm::MctpDiscoveryHandlerIntf
+{
+ public:
+ Manager() = delete;
+ Manager(const Manager&) = delete;
+ Manager(Manager&&) = delete;
+ Manager& operator=(const Manager&) = delete;
+ Manager& operator=(Manager&&) = delete;
+ ~Manager() = default;
+
+ explicit Manager(sdeventplus::Event& event, RequesterHandler& handler,
+ pldm::InstanceIdDb& instanceIdDb) :
+ terminusManager(event, handler, instanceIdDb, termini, this),
+ platformManager(terminusManager, termini)
+ {}
+
+ /** @brief Helper function to do the actions before discovering terminus
+ *
+ * @return coroutine return_value - PLDM completion code
+ */
+ exec::task<int> beforeDiscoverTerminus();
+
+ /** @brief Helper function to do the actions after discovering terminus
+ *
+ * @return coroutine return_value - PLDM completion code
+ */
+ exec::task<int> afterDiscoverTerminus();
+
+ /** @brief Helper function to invoke registered handlers for
+ * the added MCTP endpoints
+ *
+ * @param[in] mctpInfos - list information of the MCTP endpoints
+ */
+ void handleMctpEndpoints(const MctpInfos& mctpInfos)
+ {
+ terminusManager.discoverMctpTerminus(mctpInfos);
+ }
+
+ /** @brief Helper function to invoke registered handlers for
+ * the removed MCTP endpoints
+ *
+ * @param[in] mctpInfos - list information of the MCTP endpoints
+ */
+ void handleRemovedMctpEndpoints(const MctpInfos& mctpInfos)
+ {
+ terminusManager.removeMctpTerminus(mctpInfos);
+ }
+
+ private:
+ /** @brief List of discovered termini */
+ TerminiMapper termini{};
+
+ /** @brief Terminus interface for calling the hook functions */
+ TerminusManager terminusManager;
+
+ /** @brief Platform interface for calling the hook functions */
+ PlatformManager platformManager;
+};
+} // namespace platform_mc
+} // namespace pldm
diff --git a/platform-mc/platform_manager.cpp b/platform-mc/platform_manager.cpp
new file mode 100644
index 0000000..e1d7f96
--- /dev/null
+++ b/platform-mc/platform_manager.cpp
@@ -0,0 +1,28 @@
+#include "platform_manager.hpp"
+
+#include "terminus_manager.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+
+PHOSPHOR_LOG2_USING;
+
+namespace pldm
+{
+namespace platform_mc
+{
+
+exec::task<int> PlatformManager::initTerminus()
+{
+ for (auto& [tid, terminus] : termini)
+ {
+ if (terminus->initialized)
+ {
+ continue;
+ }
+ terminus->initialized = true;
+ }
+ co_return PLDM_SUCCESS;
+}
+
+} // namespace platform_mc
+} // namespace pldm
diff --git a/platform-mc/platform_manager.hpp b/platform-mc/platform_manager.hpp
new file mode 100644
index 0000000..a7fd412
--- /dev/null
+++ b/platform-mc/platform_manager.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include "libpldm/platform.h"
+#include "libpldm/pldm.h"
+
+#include "terminus.hpp"
+#include "terminus_manager.hpp"
+
+namespace pldm
+{
+
+namespace platform_mc
+{
+
+/**
+ * @brief PlatformManager
+ *
+ * PlatformManager class manages the actions outlined in the platform spec.
+ */
+class PlatformManager
+{
+ public:
+ PlatformManager() = delete;
+ PlatformManager(const PlatformManager&) = delete;
+ PlatformManager(PlatformManager&&) = delete;
+ PlatformManager& operator=(const PlatformManager&) = delete;
+ PlatformManager& operator=(PlatformManager&&) = delete;
+ ~PlatformManager() = default;
+
+ explicit PlatformManager(TerminusManager& terminusManager,
+ TerminiMapper& termini) :
+ terminusManager(terminusManager),
+ termini(termini)
+ {}
+
+ /** @brief Initialize terminus which supports PLDM Type 2
+ *
+ * @return coroutine return_value - PLDM completion code
+ */
+ exec::task<int> initTerminus();
+
+ private:
+ /** reference of TerminusManager for sending PLDM request to terminus*/
+ TerminusManager& terminusManager;
+
+ /** @brief Managed termini list */
+ TerminiMapper& termini;
+};
+} // namespace platform_mc
+} // namespace pldm
diff --git a/platform-mc/terminus.cpp b/platform-mc/terminus.cpp
new file mode 100644
index 0000000..947571b
--- /dev/null
+++ b/platform-mc/terminus.cpp
@@ -0,0 +1,53 @@
+#include "terminus.hpp"
+
+#include "libpldm/platform.h"
+
+#include "terminus_manager.hpp"
+
+namespace pldm
+{
+namespace platform_mc
+{
+
+Terminus::Terminus(pldm_tid_t tid, uint64_t supportedTypes) :
+ initialized(false), tid(tid), supportedTypes(supportedTypes)
+{}
+
+bool Terminus::doesSupportType(uint8_t type)
+{
+ return supportedTypes.test(type);
+}
+
+bool Terminus::doesSupportCommand(uint8_t type, uint8_t command)
+{
+ if (!doesSupportType(type))
+ {
+ return false;
+ }
+
+ try
+ {
+ const size_t idx = type * (PLDM_MAX_CMDS_PER_TYPE / 8) + (command / 8);
+ if (idx >= supportedCmds.size())
+ {
+ return false;
+ }
+
+ if (supportedCmds[idx] & (1 << (command % 8)))
+ {
+ lg2::info(
+ "PLDM type {TYPE} command {CMD} is supported by terminus {TID}",
+ "TYPE", type, "CMD", command, "TID", getTid());
+ return true;
+ }
+ }
+ catch (const std::exception& e)
+ {
+ return false;
+ }
+
+ return false;
+}
+
+} // namespace platform_mc
+} // namespace pldm
diff --git a/platform-mc/terminus.hpp b/platform-mc/terminus.hpp
new file mode 100644
index 0000000..6957fb3
--- /dev/null
+++ b/platform-mc/terminus.hpp
@@ -0,0 +1,101 @@
+#pragma once
+
+#include "libpldm/platform.h"
+
+#include "common/types.hpp"
+#include "requester/handler.hpp"
+
+#include <sdbusplus/server/object.hpp>
+#include <sdeventplus/event.hpp>
+#include <xyz/openbmc_project/Inventory/Item/Board/server.hpp>
+
+#include <algorithm>
+#include <bitset>
+#include <vector>
+
+namespace pldm
+{
+namespace platform_mc
+{
+
+/**
+ * @brief Terminus
+ *
+ * Terminus class holds the TID, supported PLDM Type or PDRs which are needed by
+ * other manager class for sensor monitoring and control.
+ */
+class Terminus
+{
+ public:
+ Terminus(pldm_tid_t tid, uint64_t supportedPLDMTypes);
+
+ /** @brief Check if the terminus supports the PLDM type message
+ *
+ * @param[in] type - PLDM Type
+ * @return support state - True if support, otherwise False
+ */
+ bool doesSupportType(uint8_t type);
+
+ /** @brief Check if the terminus supports the PLDM command message
+ *
+ * @param[in] type - PLDM Type
+ * @param[in] command - PLDM command
+ * @return support state - True if support, otherwise False
+ */
+ bool doesSupportCommand(uint8_t type, uint8_t command);
+
+ /** @brief Set the supported PLDM commands for terminus
+ *
+ * @param[in] cmds - bit mask of the supported PLDM commands
+ * @return success state - True if success, otherwise False
+ */
+ bool setSupportedCommands(const std::vector<uint8_t>& cmds)
+ {
+ const size_t expectedSize = PLDM_MAX_TYPES *
+ (PLDM_MAX_CMDS_PER_TYPE / 8);
+ if (cmds.empty() || cmds.size() != expectedSize)
+ {
+ lg2::error(
+ "setSupportedCommands received invalid bit mask size. Expected: {EXPECTED}, Received: {RECEIVED}",
+ "EXPECTED", expectedSize, "RECEIVED", cmds.size());
+ return false;
+ }
+
+ /* Assign Vector supportedCmds by Vector cmds */
+ supportedCmds.resize(cmds.size());
+ std::copy(cmds.begin(), cmds.begin() + cmds.size(),
+ supportedCmds.begin());
+
+ return true;
+ }
+ /** @brief The getter to return terminus's TID */
+ pldm_tid_t getTid()
+ {
+ return tid;
+ }
+
+ /** @brief A list of PDRs fetched from Terminus */
+ std::vector<std::vector<uint8_t>> pdrs{};
+
+ /** @brief A flag to indicate if terminus has been initialized */
+ bool initialized = false;
+
+ private:
+ /* @brief The terminus's TID */
+ pldm_tid_t tid;
+
+ /* @brief The supported PLDM command types of the terminus */
+ std::bitset<64> supportedTypes;
+
+ /** @brief Store supported PLDM commands of a terminus
+ * Maximum number of PLDM Type is PLDM_MAX_TYPES
+ * Maximum number of PLDM command for each type is
+ * PLDM_MAX_CMDS_PER_TYPE.
+ * Each uint8_t can store the supported state of 8 PLDM commands.
+ * Size of supportedCmds will be
+ * PLDM_MAX_TYPES * (PLDM_MAX_CMDS_PER_TYPE / 8).
+ */
+ std::vector<uint8_t> supportedCmds;
+};
+} // namespace platform_mc
+} // namespace pldm
diff --git a/platform-mc/terminus_manager.cpp b/platform-mc/terminus_manager.cpp
new file mode 100644
index 0000000..54d749d
--- /dev/null
+++ b/platform-mc/terminus_manager.cpp
@@ -0,0 +1,607 @@
+#include "terminus_manager.hpp"
+
+#include "manager.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+
+PHOSPHOR_LOG2_USING;
+
+namespace pldm
+{
+namespace platform_mc
+{
+
+std::optional<MctpInfo> TerminusManager::toMctpInfo(const pldm_tid_t& tid)
+{
+ if (tid == PLDM_TID_UNASSIGNED || tid == PLDM_TID_RESERVED)
+ {
+ return std::nullopt;
+ }
+
+ if ((!this->transportLayerTable.contains(tid)) ||
+ (this->transportLayerTable[tid] != SupportedTransportLayer::MCTP))
+ {
+ return std::nullopt;
+ }
+
+ auto mctpInfoIt = mctpInfoTable.find(tid);
+ if (mctpInfoIt == mctpInfoTable.end())
+ {
+ return std::nullopt;
+ }
+
+ return mctpInfoIt->second;
+}
+
+std::optional<pldm_tid_t> TerminusManager::toTid(const MctpInfo& mctpInfo) const
+{
+ if (!pldm::utils::isValidEID(std::get<0>(mctpInfo)))
+ {
+ return std::nullopt;
+ }
+
+ auto mctpInfoTableIt = std::find_if(
+ mctpInfoTable.begin(), mctpInfoTable.end(), [&mctpInfo](auto& v) {
+ return (std::get<0>(v.second) == std::get<0>(mctpInfo)) &&
+ (std::get<3>(v.second) == std::get<3>(mctpInfo));
+ });
+ if (mctpInfoTableIt == mctpInfoTable.end())
+ {
+ return std::nullopt;
+ }
+ return mctpInfoTableIt->first;
+}
+
+std::optional<pldm_tid_t>
+ TerminusManager::storeTerminusInfo(const MctpInfo& mctpInfo, pldm_tid_t tid)
+{
+ if (tid == PLDM_TID_UNASSIGNED || tid == PLDM_TID_RESERVED)
+ {
+ return std::nullopt;
+ }
+
+ if (!pldm::utils::isValidEID(std::get<0>(mctpInfo)))
+ {
+ return std::nullopt;
+ }
+
+ if (tidPool[tid])
+ {
+ return std::nullopt;
+ }
+
+ tidPool[tid] = true;
+ transportLayerTable[tid] = SupportedTransportLayer::MCTP;
+ mctpInfoTable[tid] = mctpInfo;
+
+ return tid;
+}
+
+std::optional<pldm_tid_t> TerminusManager::mapTid(const MctpInfo& mctpInfo)
+{
+ if (!pldm::utils::isValidEID(std::get<0>(mctpInfo)))
+ {
+ return std::nullopt;
+ }
+
+ auto mctpInfoTableIt = std::find_if(
+ mctpInfoTable.begin(), mctpInfoTable.end(), [&mctpInfo](auto& v) {
+ return (std::get<0>(v.second) == std::get<0>(mctpInfo)) &&
+ (std::get<3>(v.second) == std::get<3>(mctpInfo));
+ });
+ if (mctpInfoTableIt != mctpInfoTable.end())
+ {
+ return mctpInfoTableIt->first;
+ }
+
+ auto tidPoolIt = std::find(tidPool.begin(), tidPool.end(), false);
+ if (tidPoolIt == tidPool.end())
+ {
+ return std::nullopt;
+ }
+
+ pldm_tid_t tid = std::distance(tidPool.begin(), tidPoolIt);
+ return storeTerminusInfo(mctpInfo, tid);
+}
+
+bool TerminusManager::unmapTid(const pldm_tid_t& tid)
+{
+ if (tid == PLDM_TID_UNASSIGNED || tid == PLDM_TID_RESERVED)
+ {
+ return false;
+ }
+ tidPool[tid] = false;
+
+ if (transportLayerTable.contains(tid))
+ {
+ transportLayerTable.erase(tid);
+ }
+
+ if (mctpInfoTable.contains(tid))
+ {
+ mctpInfoTable.erase(tid);
+ }
+
+ return true;
+}
+
+void TerminusManager::discoverMctpTerminus(const MctpInfos& mctpInfos)
+{
+ queuedMctpInfos.emplace(mctpInfos);
+ if (discoverMctpTerminusTaskHandle.has_value())
+ {
+ auto& [scope, rcOpt] = *discoverMctpTerminusTaskHandle;
+ if (!rcOpt.has_value())
+ {
+ return;
+ }
+ stdexec::sync_wait(scope.on_empty());
+ discoverMctpTerminusTaskHandle.reset();
+ }
+ auto& [scope, rcOpt] = discoverMctpTerminusTaskHandle.emplace();
+ scope.spawn(discoverMctpTerminusTask() |
+ stdexec::then([&](int rc) { rcOpt.emplace(rc); }),
+ exec::default_task_context<void>());
+}
+
+auto TerminusManager::findTeminusPtr(const MctpInfo& mctpInfo)
+{
+ auto foundIter = std::find_if(termini.begin(), termini.end(),
+ [&](const auto& terminusPair) {
+ auto terminusMctpInfo = toMctpInfo(terminusPair.first);
+ return (
+ terminusMctpInfo &&
+ (std::get<0>(terminusMctpInfo.value()) == std::get<0>(mctpInfo)) &&
+ (std::get<3>(terminusMctpInfo.value()) == std::get<3>(mctpInfo)));
+ });
+
+ return foundIter;
+}
+
+exec::task<int> TerminusManager::discoverMctpTerminusTask()
+{
+ while (!queuedMctpInfos.empty())
+ {
+ if (manager)
+ {
+ co_await manager->beforeDiscoverTerminus();
+ }
+
+ const MctpInfos& mctpInfos = queuedMctpInfos.front();
+ for (const auto& mctpInfo : mctpInfos)
+ {
+ auto it = findTeminusPtr(mctpInfo);
+ if (it == termini.end())
+ {
+ co_await initMctpTerminus(mctpInfo);
+ }
+ }
+
+ if (manager)
+ {
+ co_await manager->afterDiscoverTerminus();
+ }
+
+ queuedMctpInfos.pop();
+ }
+
+ co_return PLDM_SUCCESS;
+}
+
+void TerminusManager::removeMctpTerminus(const MctpInfos& mctpInfos)
+{
+ // remove terminus
+ for (const auto& mctpInfo : mctpInfos)
+ {
+ auto it = findTeminusPtr(mctpInfo);
+ if (it == termini.end())
+ {
+ continue;
+ }
+
+ unmapTid(it->first);
+ termini.erase(it);
+ }
+}
+
+exec::task<int> TerminusManager::initMctpTerminus(const MctpInfo& mctpInfo)
+{
+ mctp_eid_t eid = std::get<0>(mctpInfo);
+ pldm_tid_t tid = 0;
+ bool isMapped = false;
+ auto rc = co_await getTidOverMctp(eid, &tid);
+ if (rc != PLDM_SUCCESS)
+ {
+ lg2::error("Failed to Get Terminus ID, error {ERROR}.", "ERROR", rc);
+ co_return PLDM_ERROR;
+ }
+
+ if (tid == PLDM_TID_RESERVED)
+ {
+ lg2::error("Terminus responses the reserved {TID}.", "TID", tid);
+ co_return PLDM_ERROR;
+ }
+
+ /* Terminus already has TID */
+ if (tid != PLDM_TID_UNASSIGNED)
+ {
+ /* TID is used by one discovered terminus */
+ auto it = termini.find(tid);
+ if (it != termini.end())
+ {
+ auto terminusMctpInfo = toMctpInfo(it->first);
+ /* The discovered terminus has the same MCTP Info */
+ if (terminusMctpInfo &&
+ (std::get<0>(terminusMctpInfo.value()) ==
+ std::get<0>(mctpInfo)) &&
+ (std::get<3>(terminusMctpInfo.value()) ==
+ std::get<3>(mctpInfo)))
+ {
+ co_return PLDM_SUCCESS;
+ }
+ else
+ {
+ /* ToDo:
+ * Maybe the terminus supports multiple medium interfaces
+ * Or the TID is used by other terminus.
+ * Check the UUID to confirm.
+ */
+ isMapped = false;
+ }
+ }
+ /* Use the terminus TID for mapping */
+ else
+ {
+ auto mappedTid = storeTerminusInfo(mctpInfo, tid);
+ if (!mappedTid)
+ {
+ lg2::error("Failed to store Terminus Info for terminus {TID}.",
+ "TID", tid);
+ co_return PLDM_ERROR;
+ }
+ isMapped = true;
+ }
+ }
+
+ if (!isMapped)
+ {
+ // Assigning a tid. If it has been mapped, mapTid()
+ // returns the tid assigned before.
+ auto mappedTid = mapTid(mctpInfo);
+ if (!mappedTid)
+ {
+ lg2::error("Failed to store Terminus Info for terminus {TID}.",
+ "TID", tid);
+ co_return PLDM_ERROR;
+ }
+
+ tid = mappedTid.value();
+ rc = co_await setTidOverMctp(eid, tid);
+ if (rc != PLDM_SUCCESS)
+ {
+ lg2::error("Failed to Set terminus TID, error{ERROR}.", "ERROR",
+ rc);
+ unmapTid(tid);
+ co_return rc;
+ }
+
+ if (rc != PLDM_SUCCESS && rc != PLDM_ERROR_UNSUPPORTED_PLDM_CMD)
+ {
+ lg2::error("Terminus {TID} does not support SetTID command.", "TID",
+ tid);
+ unmapTid(tid);
+ co_return rc;
+ }
+
+ if (termini.contains(tid))
+ {
+ // the terminus has been discovered before
+ co_return PLDM_SUCCESS;
+ }
+ }
+ /* Discovery the mapped terminus */
+ uint64_t supportedTypes = 0;
+ rc = co_await getPLDMTypes(tid, supportedTypes);
+ if (rc)
+ {
+ lg2::error("Failed to Get PLDM Types for terminus {TID}, error {ERROR}",
+ "TID", tid, "ERROR", rc);
+ co_return PLDM_ERROR;
+ }
+
+ try
+ {
+ termini[tid] = std::make_shared<Terminus>(tid, supportedTypes);
+ }
+ catch (const sdbusplus::exception_t& e)
+ {
+ lg2::error("Failed to create terminus manager for terminus {TID}",
+ "TID", tid);
+ co_return PLDM_ERROR;
+ }
+
+ uint8_t type = PLDM_BASE;
+ auto size = PLDM_MAX_TYPES * (PLDM_MAX_CMDS_PER_TYPE / 8);
+ std::vector<uint8_t> pldmCmds(size);
+ while ((type < PLDM_MAX_TYPES))
+ {
+ if (!termini[tid]->doesSupportType(type))
+ {
+ type++;
+ continue;
+ }
+ std::vector<bitfield8_t> cmds(PLDM_MAX_CMDS_PER_TYPE / 8);
+ auto rc = co_await getPLDMCommands(tid, type, cmds.data());
+ if (rc)
+ {
+ lg2::error(
+ "Failed to Get PLDM Commands for terminus {TID}, error {ERROR}",
+ "TID", tid, "ERROR", rc);
+ }
+
+ for (size_t i = 0; i < cmds.size(); i++)
+ {
+ auto idx = type * (PLDM_MAX_CMDS_PER_TYPE / 8) + i;
+ if (idx >= pldmCmds.size())
+ {
+ lg2::error(
+ "Calculated index {IDX} out of bounds for pldmCmds, type {TYPE}, command index {CMD_IDX}",
+ "IDX", idx, "TYPE", type, "CMD_IDX", i);
+ continue;
+ }
+ pldmCmds[idx] = cmds[i].byte;
+ }
+ type++;
+ }
+ termini[tid]->setSupportedCommands(pldmCmds);
+
+ co_return PLDM_SUCCESS;
+}
+
+exec::task<int>
+ TerminusManager::sendRecvPldmMsgOverMctp(mctp_eid_t eid, Request& request,
+ const pldm_msg** responseMsg,
+ size_t* responseLen)
+{
+ try
+ {
+ std::tie(*responseMsg, *responseLen) =
+ co_await handler.sendRecvMsg(eid, std::move(request));
+ co_return PLDM_SUCCESS;
+ }
+ catch (const sdbusplus::exception_t& e)
+ {
+ lg2::error(
+ "Send and Receive PLDM message over MCTP failed with error - {ERROR}.",
+ "ERROR", e);
+ co_return PLDM_ERROR;
+ }
+}
+
+exec::task<int> TerminusManager::getTidOverMctp(mctp_eid_t eid, pldm_tid_t* tid)
+{
+ auto instanceId = instanceIdDb.next(eid);
+ Request request(sizeof(pldm_msg_hdr));
+ auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
+ auto rc = encode_get_tid_req(instanceId, requestMsg);
+ if (rc)
+ {
+ instanceIdDb.free(eid, instanceId);
+ lg2::error(
+ "Failed to encode request GetTID for endpoint ID {EID}, error {RC} ",
+ "EID", eid, "RC", rc);
+ co_return rc;
+ }
+
+ const pldm_msg* responseMsg = nullptr;
+ size_t responseLen = 0;
+ rc = co_await sendRecvPldmMsgOverMctp(eid, request, &responseMsg,
+ &responseLen);
+ if (rc)
+ {
+ lg2::error("Failed to send GetTID for Endpoint {EID}, error {RC}",
+ "EID", eid, "RC", rc);
+ co_return rc;
+ }
+
+ uint8_t completionCode = 0;
+ rc = decode_get_tid_resp(responseMsg, responseLen, &completionCode, tid);
+ if (rc)
+ {
+ lg2::error(
+ "Failed to decode response GetTID for Endpoint ID {EID}, error {RC} ",
+ "EID", eid, "RC", rc);
+ co_return rc;
+ }
+
+ if (completionCode != PLDM_SUCCESS)
+ {
+ lg2::error("Error : GetTID for Endpoint ID {EID}, complete code {CC}.",
+ "EID", eid, "CC", completionCode);
+ co_return rc;
+ }
+
+ co_return completionCode;
+}
+
+exec::task<int> TerminusManager::setTidOverMctp(mctp_eid_t eid, pldm_tid_t tid)
+{
+ auto instanceId = instanceIdDb.next(eid);
+ Request request(sizeof(pldm_msg_hdr) + sizeof(pldm_set_tid_req));
+ auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
+ auto rc = encode_set_tid_req(instanceId, tid, requestMsg);
+ if (rc)
+ {
+ instanceIdDb.free(eid, instanceId);
+ lg2::error(
+ "Failed to encode request SetTID for endpoint ID {EID}, error {RC} ",
+ "EID", eid, "RC", rc);
+ co_return rc;
+ }
+
+ const pldm_msg* responseMsg = nullptr;
+ size_t responseLen = 0;
+ rc = co_await sendRecvPldmMsgOverMctp(eid, request, &responseMsg,
+ &responseLen);
+ if (rc)
+ {
+ lg2::error("Failed to send SetTID for Endpoint {EID}, error {RC}",
+ "EID", eid, "RC", rc);
+ co_return rc;
+ }
+
+ if (responseMsg == NULL || responseLen != PLDM_SET_TID_RESP_BYTES)
+ {
+ lg2::error(
+ "Failed to decode response SetTID for Endpoint ID {EID}, error {RC} ",
+ "EID", eid, "RC", rc);
+ co_return PLDM_ERROR_INVALID_LENGTH;
+ }
+
+ co_return responseMsg->payload[0];
+}
+
+exec::task<int> TerminusManager::getPLDMTypes(pldm_tid_t tid,
+ uint64_t& supportedTypes)
+{
+ Request request(sizeof(pldm_msg_hdr));
+ auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
+ auto rc = encode_get_types_req(0, requestMsg);
+ if (rc)
+ {
+ lg2::error(
+ "Failed to encode request getPLDMTypes for terminus ID {TID}, error {RC} ",
+ "TID", tid, "RC", rc);
+ co_return rc;
+ }
+
+ const pldm_msg* responseMsg = nullptr;
+ size_t responseLen = 0;
+
+ rc = co_await sendRecvPldmMsg(tid, request, &responseMsg, &responseLen);
+ if (rc)
+ {
+ lg2::error("Failed to send GetPLDMTypes for terminus {TID}, error {RC}",
+ "TID", tid, "RC", rc);
+ co_return rc;
+ }
+
+ uint8_t completionCode = 0;
+ bitfield8_t* types = reinterpret_cast<bitfield8_t*>(&supportedTypes);
+ rc = decode_get_types_resp(responseMsg, responseLen, &completionCode,
+ types);
+ if (rc)
+ {
+ lg2::error(
+ "Failed to decode response GetPLDMTypes for terminus ID {TID}, error {RC} ",
+ "TID", tid, "RC", rc);
+ co_return rc;
+ }
+
+ if (completionCode != PLDM_SUCCESS)
+ {
+ lg2::error(
+ "Error : GetPLDMTypes for terminus ID {TID}, complete code {CC}.",
+ "TID", tid, "CC", completionCode);
+ co_return rc;
+ }
+ co_return completionCode;
+}
+
+exec::task<int> TerminusManager::getPLDMCommands(pldm_tid_t tid, uint8_t type,
+ bitfield8_t* supportedCmds)
+{
+ Request request(sizeof(pldm_msg_hdr) + PLDM_GET_COMMANDS_REQ_BYTES);
+ auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
+ ver32_t version{0xFF, 0xFF, 0xFF, 0xFF};
+
+ auto rc = encode_get_commands_req(0, type, version, requestMsg);
+ if (rc)
+ {
+ lg2::error(
+ "Failed to encode request GetPLDMCommands for terminus ID {TID}, error {RC} ",
+ "TID", tid, "RC", rc);
+ co_return rc;
+ }
+
+ const pldm_msg* responseMsg = nullptr;
+ size_t responseLen = 0;
+
+ rc = co_await sendRecvPldmMsg(tid, request, &responseMsg, &responseLen);
+ if (rc)
+ {
+ lg2::error(
+ "Failed to send GetPLDMCommands message for terminus {TID}, error {RC}",
+ "TID", tid, "RC", rc);
+ co_return rc;
+ }
+
+ /* Process response */
+ uint8_t completionCode = 0;
+ rc = decode_get_commands_resp(responseMsg, responseLen, &completionCode,
+ supportedCmds);
+ if (rc)
+ {
+ lg2::error(
+ "Failed to decode response GetPLDMCommands for terminus ID {TID}, error {RC} ",
+ "TID", tid, "RC", rc);
+ co_return rc;
+ }
+
+ if (completionCode != PLDM_SUCCESS)
+ {
+ lg2::error(
+ "Error : GetPLDMCommands for terminus ID {TID}, complete code {CC}.",
+ "TID", tid, "CC", completionCode);
+ co_return rc;
+ }
+
+ co_return completionCode;
+}
+
+exec::task<int> TerminusManager::sendRecvPldmMsg(pldm_tid_t tid,
+ Request& request,
+ const pldm_msg** responseMsg,
+ size_t* responseLen)
+{
+ /**
+ * Size of tidPool is `std::numeric_limits<pldm_tid_t>::max() + 1`
+ * tidPool[i] always exist
+ */
+ if (!tidPool[tid])
+ {
+ co_return PLDM_ERROR_NOT_READY;
+ }
+
+ if (!transportLayerTable.contains(tid))
+ {
+ co_return PLDM_ERROR_NOT_READY;
+ }
+
+ if (transportLayerTable[tid] != SupportedTransportLayer::MCTP)
+ {
+ co_return PLDM_ERROR_NOT_READY;
+ }
+
+ auto mctpInfo = toMctpInfo(tid);
+ if (!mctpInfo.has_value())
+ {
+ co_return PLDM_ERROR_NOT_READY;
+ }
+
+ auto eid = std::get<0>(mctpInfo.value());
+ auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
+ requestMsg->hdr.instance_id = instanceIdDb.next(eid);
+ auto rc = co_await sendRecvPldmMsgOverMctp(eid, request, responseMsg,
+ responseLen);
+
+ if (responseMsg == nullptr || !responseLen)
+ {
+ co_return PLDM_ERROR_INVALID_DATA;
+ }
+
+ co_return rc;
+}
+
+} // namespace platform_mc
+} // namespace pldm
diff --git a/platform-mc/terminus_manager.hpp b/platform-mc/terminus_manager.hpp
new file mode 100644
index 0000000..fccbb1c
--- /dev/null
+++ b/platform-mc/terminus_manager.hpp
@@ -0,0 +1,244 @@
+#pragma once
+
+#include "config.h"
+
+#include "libpldm/platform.h"
+#include "libpldm/pldm.h"
+
+#include "requester/handler.hpp"
+#include "requester/mctp_endpoint_discovery.hpp"
+#include "terminus.hpp"
+
+#include <limits>
+#include <map>
+#include <memory>
+#include <optional>
+#include <queue>
+#include <utility>
+#include <vector>
+
+namespace pldm
+{
+
+enum class SupportedTransportLayer
+{
+ MCTP
+};
+
+namespace platform_mc
+{
+
+/** @brief Size of TID Pool in pldmd */
+constexpr size_t tidPoolSize = std::numeric_limits<pldm_tid_t>::max() + 1;
+/** @brief Type definition for Requester request handler */
+using RequesterHandler = requester::Handler<requester::Request>;
+/** @brief Type definition for Terminus handler mapper */
+using TerminiMapper = std::map<pldm_tid_t, std::shared_ptr<Terminus>>;
+
+class Manager;
+/**
+ * @brief TerminusManager
+ *
+ * TerminusManager class to discover and initialize PLDM terminus.
+ */
+class TerminusManager
+{
+ public:
+ TerminusManager() = delete;
+ TerminusManager(const TerminusManager&) = delete;
+ TerminusManager(TerminusManager&&) = delete;
+ TerminusManager& operator=(const TerminusManager&) = delete;
+ TerminusManager& operator=(TerminusManager&&) = delete;
+ virtual ~TerminusManager() = default;
+
+ explicit TerminusManager(sdeventplus::Event& event,
+ RequesterHandler& handler,
+ pldm::InstanceIdDb& instanceIdDb,
+ TerminiMapper& termini, Manager* manager) :
+ event(event),
+ handler(handler), instanceIdDb(instanceIdDb), termini(termini),
+ tidPool(tidPoolSize, false), manager(manager)
+ {
+ // DSP0240 v1.1.0 table-8, special value: 0,0xFF = reserved
+ tidPool[0] = true;
+ tidPool[PLDM_TID_RESERVED] = true;
+ }
+
+ /** @brief start a coroutine to discover terminus
+ *
+ * @param[in] mctpInfos - list information of the MCTP endpoints
+ */
+ void discoverMctpTerminus(const MctpInfos& mctpInfos);
+
+ /** @brief remove MCTP endpoints
+ *
+ * @param[in] mctpInfos - list information of the MCTP endpoints
+ */
+ void removeMctpTerminus(const MctpInfos& mctpInfos);
+
+ /** @brief Send request PLDM message to tid. The function will return when
+ * received the response message from terminus. The function will
+ * auto get the instanceID from libmctp and update to request
+ * message.
+ *
+ * @param[in] tid - Destination TID
+ * @param[in] request - request PLDM message
+ * @param[out] responseMsg - response PLDM message
+ * @param[out] responseLen - length of response PLDM message
+ * @return coroutine return_value - PLDM completion code
+ */
+ exec::task<int> sendRecvPldmMsg(pldm_tid_t tid, Request& request,
+ const pldm_msg** responseMsg,
+ size_t* responseLen);
+
+ /** @brief Send request PLDM message to eid. The function will
+ * return when received the response message from terminus.
+ *
+ * @param[in] eid - Destination EID
+ * @param[in] request - request PLDM message
+ * @param[out] responseMsg - response PLDM message
+ * @param[out] responseLen - length of response PLDM message
+ * @return coroutine return_value - PLDM completion code
+ */
+ virtual exec::task<int>
+ sendRecvPldmMsgOverMctp(mctp_eid_t eid, Request& request,
+ const pldm_msg** responseMsg,
+ size_t* responseLen);
+
+ /** @brief member functions to map/unmap tid
+ */
+ std::optional<MctpInfo> toMctpInfo(const pldm_tid_t& tid);
+
+ /** @brief Member functions to response the TID of specific MCTP interface
+ *
+ * @param[in] mctpInfos - list information of the MCTP endpoints
+ *
+ * @return tid - Terminus tid
+ */
+ std::optional<pldm_tid_t> toTid(const MctpInfo& mctpInfo) const;
+
+ /** @brief Member functions to find the TID for MCTP interface. Response the
+ * Terminus TID when mctpInfo is already in the data base. Response
+ * new tid from pool when mctpInfo is new.
+ *
+ * @param[in] mctpInfos - list information of the MCTP endpoints
+ *
+ * @return tid - Terminus tid
+ */
+ std::optional<pldm_tid_t> mapTid(const MctpInfo& mctpInfo);
+
+ /** @brief Member functions to store the mctp info and tid to terminus info
+ * list.
+ *
+ * @param[in] mctpInfos - list information of the MCTP endpoints
+ * @param[in] tid - Destination TID
+ *
+ * @return tid - Terminus tid
+ */
+ std::optional<pldm_tid_t> storeTerminusInfo(const MctpInfo& mctpInfo,
+ pldm_tid_t tid);
+
+ /** @brief Member functions to remove the TID from the transportLayer and
+ * mctpInfo table
+ *
+ * @param[in] tid - Destination TID
+ *
+ * @return true/false - True when tid in the table otherwise return false
+ */
+ bool unmapTid(const pldm_tid_t& tid);
+
+ private:
+ /** @brief Find the terminus object pointer in termini list.
+ *
+ * @param[in] mctpInfos - list information of the MCTP endpoints
+ */
+ auto findTeminusPtr(const MctpInfo& mctpInfo);
+
+ /** @brief The coroutine task execute by discoverMctpTerminus()
+ *
+ * @return coroutine return_value - PLDM completion code
+ */
+ exec::task<int> discoverMctpTerminusTask();
+
+ /** @brief Initialize terminus and then instantiate terminus object to keeps
+ * the data fetched from terminus
+ *
+ * @param[in] mctpInfo - information of the MCTP endpoints
+ * @return coroutine return_value - PLDM completion code
+ */
+ exec::task<int> initMctpTerminus(const MctpInfo& mctpInfo);
+
+ /** @brief Send getTID PLDM command to destination EID and then return the
+ * value of tid in reference parameter.
+ *
+ * @param[in] eid - Destination EID
+ * @param[out] tid - Terminus TID
+ * @return coroutine return_value - PLDM completion code
+ */
+ exec::task<int> getTidOverMctp(mctp_eid_t eid, pldm_tid_t* tid);
+
+ /** @brief Send setTID command to destination EID.
+ *
+ * @param[in] eid - Destination EID
+ * @param[in] tid - Destination TID
+ * @return coroutine return_value - PLDM completion code
+ */
+ exec::task<int> setTidOverMctp(mctp_eid_t eid, pldm_tid_t tid);
+
+ /** @brief Send getPLDMTypes command to destination TID and then return the
+ * value of supportedTypes in reference parameter.
+ *
+ * @param[in] tid - Destination TID
+ * @param[out] supportedTypes - Supported Types returned from terminus
+ * @return coroutine return_value - PLDM completion code
+ */
+ exec::task<int> getPLDMTypes(pldm_tid_t tid, uint64_t& supportedTypes);
+
+ /** @brief Send getPLDMCommands command to destination TID and then return
+ * the value of supportedCommands in reference parameter.
+ *
+ * @param[in] tid - Destination TID
+ * @param[in] type - PLDM Type
+ * @param[in] supportedCmds - Supported commands returned from terminus
+ * for specific type
+ * @return coroutine return_value - PLDM completion code
+ */
+ exec::task<int> getPLDMCommands(pldm_tid_t tid, uint8_t type,
+ bitfield8_t* supportedCmds);
+
+ /** @brief Reference to to PLDM daemon's main event loop.
+ */
+ sdeventplus::Event& event;
+
+ /** @brief Reference to a Handler object that manages the request/response
+ * logic.
+ */
+ RequesterHandler& handler;
+
+ /** @brief Reference to the instanceID data base from libpldm */
+ pldm::InstanceIdDb& instanceIdDb;
+
+ /** @brief Managed termini list */
+ TerminiMapper& termini;
+
+ /** @brief tables for maintaining assigned TID */
+ std::vector<bool> tidPool;
+
+ /** @brief Store the supported transport layers of specific TID */
+ std::map<pldm_tid_t, SupportedTransportLayer> transportLayerTable;
+
+ /** @brief Store the supported MCTP interface info of specific TID */
+ std::map<pldm_tid_t, MctpInfo> mctpInfoTable;
+
+ /** @brief A queue of MctpInfos to be discovered **/
+ std::queue<MctpInfos> queuedMctpInfos{};
+
+ /** @brief coroutine handle of discoverTerminusTask */
+ std::optional<std::pair<exec::async_scope, std::optional<int>>>
+ discoverMctpTerminusTaskHandle{};
+
+ /** @brief A Manager interface for calling the hook functions **/
+ Manager* manager;
+};
+} // namespace platform_mc
+} // namespace pldm
diff --git a/platform-mc/test/meson.build b/platform-mc/test/meson.build
new file mode 100644
index 0000000..f4e4f46
--- /dev/null
+++ b/platform-mc/test/meson.build
@@ -0,0 +1,30 @@
+test_src = declare_dependency(
+ sources: [
+ '../terminus_manager.cpp',
+ '../terminus.cpp',
+ '../platform_manager.cpp',
+ '../manager.cpp',
+ '../../requester/mctp_endpoint_discovery.cpp'],
+ include_directories: ['../../requester', '../../pldmd'])
+
+tests = [
+ 'terminus_manager_test',
+ 'terminus_test',
+]
+
+foreach t : tests
+ test(t, executable(t.underscorify(), t + '.cpp',
+ implicit_include_directories: false,
+ dependencies: [
+ gtest,
+ gmock,
+ libpldm_dep,
+ libpldmutils,
+ nlohmann_json_dep,
+ phosphor_dbus_interfaces,
+ phosphor_logging_dep,
+ sdbusplus,
+ sdeventplus,
+ test_src]),
+ workdir: meson.current_source_dir())
+endforeach
diff --git a/platform-mc/test/mock_terminus_manager.hpp b/platform-mc/test/mock_terminus_manager.hpp
new file mode 100644
index 0000000..eb06734
--- /dev/null
+++ b/platform-mc/test/mock_terminus_manager.hpp
@@ -0,0 +1,74 @@
+#pragma once
+
+#include "platform-mc/terminus_manager.hpp"
+
+#include <queue>
+
+#include <gmock/gmock.h>
+
+namespace pldm
+{
+namespace platform_mc
+{
+
+class MockTerminusManager : public TerminusManager
+{
+ public:
+ MockTerminusManager(sdeventplus::Event& event, RequesterHandler& handler,
+ pldm::InstanceIdDb& instanceIdDb,
+ TerminiMapper& termini, Manager* manager) :
+ TerminusManager(event, handler, instanceIdDb, termini, manager)
+ {}
+
+ exec::task<int> sendRecvPldmMsgOverMctp(mctp_eid_t /*eid*/,
+ Request& /*request*/,
+ const pldm_msg** responseMsg,
+ size_t* responseLen) override
+ {
+ if (responseMsgs.empty() || responseMsg == nullptr ||
+ responseLen == nullptr)
+ {
+ co_return PLDM_ERROR;
+ }
+
+ *responseMsg = responseMsgs.front();
+ *responseLen = responseLens.front() - sizeof(pldm_msg_hdr);
+
+ responseMsgs.pop();
+ responseLens.pop();
+ co_return PLDM_SUCCESS;
+ }
+
+ int enqueueResponse(pldm_msg* responseMsg, size_t responseLen)
+ {
+ if (responseMsg == nullptr)
+ {
+ return PLDM_ERROR_INVALID_DATA;
+ }
+
+ if (responseLen <= sizeof(pldm_msg_hdr))
+ {
+ return PLDM_ERROR_INVALID_LENGTH;
+ }
+
+ responseMsgs.push(responseMsg);
+ responseLens.push(responseLen);
+ return PLDM_SUCCESS;
+ }
+
+ int clearQueuedResponses()
+ {
+ while (!responseMsgs.empty())
+ {
+ responseMsgs.pop();
+ responseLens.pop();
+ }
+ return PLDM_SUCCESS;
+ }
+
+ std::queue<pldm_msg*> responseMsgs;
+ std::queue<size_t> responseLens;
+};
+
+} // namespace platform_mc
+} // namespace pldm
diff --git a/platform-mc/test/terminus_manager_test.cpp b/platform-mc/test/terminus_manager_test.cpp
new file mode 100644
index 0000000..460009d
--- /dev/null
+++ b/platform-mc/test/terminus_manager_test.cpp
@@ -0,0 +1,494 @@
+#include "libpldm/base.h"
+#include "libpldm/bios.h"
+#include "libpldm/fru.h"
+#include "libpldm/platform.h"
+
+#include "common/instance_id.hpp"
+#include "common/types.hpp"
+#include "mock_terminus_manager.hpp"
+#include "platform-mc/terminus_manager.hpp"
+#include "requester/handler.hpp"
+#include "requester/mctp_endpoint_discovery.hpp"
+#include "requester/request.hpp"
+#include "test/test_instance_id.hpp"
+
+#include <sdbusplus/timer.hpp>
+#include <sdeventplus/event.hpp>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using ::testing::AtLeast;
+using ::testing::Between;
+using ::testing::Exactly;
+using ::testing::NiceMock;
+using ::testing::Return;
+
+class TerminusManagerTest : public testing::Test
+{
+ protected:
+ TerminusManagerTest() :
+ bus(pldm::utils::DBusHandler::getBus()),
+ event(sdeventplus::Event::get_default()), instanceIdDb(),
+ reqHandler(pldmTransport, event, instanceIdDb, false,
+ std::chrono::seconds(1), 2, std::chrono::milliseconds(100)),
+ terminusManager(event, reqHandler, instanceIdDb, termini, nullptr),
+ mockTerminusManager(event, reqHandler, instanceIdDb, termini, nullptr)
+ {}
+
+ PldmTransport* pldmTransport = nullptr;
+ sdbusplus::bus_t& bus;
+ sdeventplus::Event event;
+ TestInstanceIdDb instanceIdDb;
+ pldm::requester::Handler<pldm::requester::Request> reqHandler;
+ pldm::platform_mc::TerminusManager terminusManager;
+ pldm::platform_mc::MockTerminusManager mockTerminusManager;
+ std::map<pldm_tid_t, std::shared_ptr<pldm::platform_mc::Terminus>> termini;
+};
+
+TEST_F(TerminusManagerTest, mapTidTest)
+{
+ pldm::MctpInfo mctpInfo1(8, "", "", 0);
+
+ auto mappedTid1 = terminusManager.mapTid(mctpInfo1);
+ EXPECT_NE(mappedTid1, std::nullopt);
+
+ auto tid1 = terminusManager.toTid(mctpInfo1);
+ EXPECT_NE(tid1, std::nullopt);
+
+ auto mctpInfo2 = terminusManager.toMctpInfo(tid1.value());
+ EXPECT_EQ(mctpInfo1, mctpInfo2.value());
+
+ auto ret = terminusManager.unmapTid(tid1.value());
+ EXPECT_EQ(ret, true);
+
+ tid1 = terminusManager.toTid(mctpInfo1);
+ EXPECT_EQ(tid1, std::nullopt);
+}
+
+TEST_F(TerminusManagerTest, negativeMapTidTest)
+{
+ // map null EID(0) to TID
+ pldm::MctpInfo m0(0, "", "", 0);
+ auto mappedTid = terminusManager.mapTid(m0);
+ EXPECT_EQ(mappedTid, std::nullopt);
+
+ // map broadcast EID(0xff) to TID
+ pldm::MctpInfo m1(0xff, "", "", 0);
+ mappedTid = terminusManager.mapTid(m1);
+ EXPECT_EQ(mappedTid, std::nullopt);
+
+ // map EID to tid which has been assigned
+ pldm::MctpInfo m2(9, "", "", 1);
+ pldm::MctpInfo m3(10, "", "", 1);
+ auto mappedTid2 = terminusManager.mapTid(m2);
+ auto mappedTid3 = terminusManager.storeTerminusInfo(m3, mappedTid2.value());
+ EXPECT_NE(mappedTid2, std::nullopt);
+ EXPECT_EQ(mappedTid3, std::nullopt);
+
+ // map two mctpInfo with same EID but different network Id
+ pldm::MctpInfo m4(12, "", "", 1);
+ pldm::MctpInfo m5(12, "", "", 2);
+ auto mappedTid4 = terminusManager.mapTid(m4);
+ auto mappedTid5 = terminusManager.mapTid(m5);
+ EXPECT_NE(mappedTid4.value(), mappedTid5.value());
+
+ // map same mctpInfo twice
+ pldm::MctpInfo m6(12, "", "", 3);
+ auto mappedTid6 = terminusManager.mapTid(m6);
+ auto mappedTid6_1 = terminusManager.mapTid(m6);
+ EXPECT_EQ(mappedTid6.value(), mappedTid6_1.value());
+
+ // look up an unmapped MctpInfo to TID
+ pldm::MctpInfo m7(1, "", "", 0);
+ auto mappedTid7 = terminusManager.toTid(m7);
+ EXPECT_EQ(mappedTid7, std::nullopt);
+
+ // look up reserved TID(0)
+ auto mappedEid = terminusManager.toMctpInfo(0);
+ EXPECT_EQ(mappedEid, std::nullopt);
+
+ // look up reserved TID(0xff)
+ mappedEid = terminusManager.toMctpInfo(0xff);
+ EXPECT_EQ(mappedEid, std::nullopt);
+
+ // look up an unmapped TID
+ terminusManager.unmapTid(1);
+ mappedEid = terminusManager.toMctpInfo(1);
+ EXPECT_EQ(mappedEid, std::nullopt);
+
+ // unmap reserved TID(0)
+ auto ret = terminusManager.unmapTid(0);
+ EXPECT_EQ(ret, false);
+
+ // unmap reserved TID(0)
+ ret = terminusManager.unmapTid(0xff);
+ EXPECT_EQ(ret, false);
+}
+
+TEST_F(TerminusManagerTest, discoverMctpTerminusTest)
+{
+ const size_t getTidRespLen = PLDM_GET_TID_RESP_BYTES;
+ const size_t setTidRespLen = PLDM_SET_TID_RESP_BYTES;
+ const size_t getPldmTypesRespLen = PLDM_GET_TYPES_RESP_BYTES;
+
+ // 0.discover a mctp list
+ auto rc = mockTerminusManager.clearQueuedResponses();
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + getTidRespLen> getTidResp0{
+ 0x00, 0x02, 0x02, 0x00, 0x00};
+ rc = mockTerminusManager.enqueueResponse((pldm_msg*)getTidResp0.data(),
+ sizeof(getTidResp0));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + setTidRespLen> setTidResp0{
+ 0x00, 0x02, 0x01, 0x00};
+ rc = mockTerminusManager.enqueueResponse((pldm_msg*)setTidResp0.data(),
+ sizeof(setTidResp0));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + getPldmTypesRespLen>
+ getPldmTypesResp0{0x00, 0x02, 0x04, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ rc = mockTerminusManager.enqueueResponse(
+ (pldm_msg*)getPldmTypesResp0.data(), sizeof(getPldmTypesResp0));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ pldm::MctpInfos mctpInfos{};
+ mctpInfos.emplace_back(pldm::MctpInfo(12, "", "", 1));
+ mockTerminusManager.discoverMctpTerminus(mctpInfos);
+ EXPECT_EQ(1, termini.size());
+
+ // 1.discover the same mctp list again
+ rc = mockTerminusManager.clearQueuedResponses();
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + getTidRespLen> getTidResp1{
+ 0x00, 0x02, 0x02, 0x00, 0x01};
+ rc = mockTerminusManager.enqueueResponse((pldm_msg*)getTidResp1.data(),
+ sizeof(getTidResp1));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+ rc = mockTerminusManager.enqueueResponse((pldm_msg*)setTidResp0.data(),
+ sizeof(setTidResp0));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+ rc = mockTerminusManager.enqueueResponse(
+ (pldm_msg*)getPldmTypesResp0.data(), sizeof(getPldmTypesResp0));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ mockTerminusManager.discoverMctpTerminus(mctpInfos);
+ EXPECT_EQ(1, termini.size());
+
+ // 2.discover an empty mctp list
+ rc = mockTerminusManager.clearQueuedResponses();
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ mockTerminusManager.removeMctpTerminus(mctpInfos);
+ EXPECT_EQ(0, termini.size());
+}
+
+TEST_F(TerminusManagerTest, negativeDiscoverMctpTerminusTest)
+{
+ const size_t getTidRespLen = PLDM_GET_TID_RESP_BYTES;
+ const size_t setTidRespLen = PLDM_SET_TID_RESP_BYTES;
+ const size_t getPldmTypesRespLen = PLDM_GET_TYPES_RESP_BYTES;
+
+ // 0.terminus returns reserved tid
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + getTidRespLen> getTidResp0{
+ 0x00, 0x02, 0x02, 0x00, PLDM_TID_RESERVED};
+ auto rc = mockTerminusManager.enqueueResponse((pldm_msg*)getTidResp0.data(),
+ sizeof(getTidResp0));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ pldm::MctpInfos mctpInfos{};
+ mctpInfos.emplace_back(pldm::MctpInfo(12, "", "", 1));
+ mockTerminusManager.discoverMctpTerminus(mctpInfos);
+ EXPECT_EQ(0, termini.size());
+
+ // 1.terminus return cc=pldm_error for set tid
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + getTidRespLen> getTidResp1{
+ 0x00, 0x02, 0x02, 0x00, 0x00};
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + setTidRespLen> setTidResp1{
+ 0x00, 0x02, 0x01, PLDM_ERROR};
+
+ rc = mockTerminusManager.enqueueResponse((pldm_msg*)getTidResp1.data(),
+ sizeof(getTidResp1));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+ rc = mockTerminusManager.enqueueResponse((pldm_msg*)setTidResp1.data(),
+ sizeof(setTidResp1));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+ mockTerminusManager.removeMctpTerminus(mctpInfos);
+ EXPECT_EQ(0, termini.size());
+
+ // 2.terminus return cc=unsupported_pldm_cmd for set tid cmd and return
+ // cc=pldm_error for get pldm types cmd
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + getTidRespLen> getTidResp2{
+ 0x00, 0x02, 0x02, 0x00, 0x00};
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + setTidRespLen> setTidResp2{
+ 0x00, 0x02, 0x01, PLDM_ERROR_UNSUPPORTED_PLDM_CMD};
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + getPldmTypesRespLen>
+ getPldmTypesResp2{0x00, 0x02, 0x04, PLDM_ERROR, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ rc = mockTerminusManager.enqueueResponse((pldm_msg*)getTidResp2.data(),
+ sizeof(getTidResp2));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+ rc = mockTerminusManager.enqueueResponse((pldm_msg*)setTidResp2.data(),
+ sizeof(setTidResp2));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ rc = mockTerminusManager.enqueueResponse(
+ (pldm_msg*)getPldmTypesResp2.data(), sizeof(getPldmTypesResp2));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+ mockTerminusManager.removeMctpTerminus(mctpInfos);
+ EXPECT_EQ(0, termini.size());
+}
+
+TEST_F(TerminusManagerTest, doesSupportTypeTest)
+{
+ const size_t getTidRespLen = PLDM_GET_TID_RESP_BYTES;
+ const size_t setTidRespLen = PLDM_SET_TID_RESP_BYTES;
+ const size_t getPldmTypesRespLen = PLDM_GET_TYPES_RESP_BYTES;
+
+ // 0.discover a mctp list
+ auto rc = mockTerminusManager.clearQueuedResponses();
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + getTidRespLen> getTidResp0{
+ 0x00, 0x02, 0x02, 0x00, 0x00};
+ rc = mockTerminusManager.enqueueResponse((pldm_msg*)getTidResp0.data(),
+ sizeof(getTidResp0));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + setTidRespLen> setTidResp0{
+ 0x00, 0x02, 0x01, 0x00};
+ rc = mockTerminusManager.enqueueResponse((pldm_msg*)setTidResp0.data(),
+ sizeof(setTidResp0));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ uint8_t supportedType1Byte = (1 << (PLDM_BASE % 8)) +
+ (1 << (PLDM_PLATFORM % 8)) +
+ (1 << (PLDM_BIOS % 8)) + (1 << (PLDM_FRU % 8));
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + getPldmTypesRespLen>
+ getPldmTypesResp0{0x00, 0x02, 0x04, 0x00, supportedType1Byte,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00};
+ rc = mockTerminusManager.enqueueResponse(
+ (pldm_msg*)getPldmTypesResp0.data(), sizeof(getPldmTypesResp0));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ pldm::MctpInfos mctpInfos{};
+ mctpInfos.emplace_back(pldm::MctpInfo(12, "", "", 1));
+ mockTerminusManager.discoverMctpTerminus(mctpInfos);
+ EXPECT_EQ(1, termini.size());
+
+ EXPECT_EQ(true, termini.contains(1));
+ EXPECT_EQ(false, termini.contains(0));
+ EXPECT_EQ(false, termini.contains(2));
+
+ EXPECT_EQ(true, termini[1]->doesSupportType(PLDM_BASE));
+ EXPECT_EQ(true, termini[1]->doesSupportType(PLDM_PLATFORM));
+ EXPECT_EQ(true, termini[1]->doesSupportType(PLDM_BIOS));
+ EXPECT_EQ(true, termini[1]->doesSupportType(PLDM_FRU));
+ EXPECT_EQ(false, termini[1]->doesSupportType(PLDM_FWUP));
+ EXPECT_EQ(false, termini[1]->doesSupportType(PLDM_OEM));
+}
+
+TEST_F(TerminusManagerTest, doesSupportCommandTest)
+{
+ const size_t getTidRespLen = PLDM_GET_TID_RESP_BYTES;
+ const size_t setTidRespLen = PLDM_SET_TID_RESP_BYTES;
+ const size_t getPldmTypesRespLen = PLDM_GET_TYPES_RESP_BYTES;
+ const size_t getPldmCommandRespLen = PLDM_GET_COMMANDS_RESP_BYTES;
+
+ // 0.discover a mctp list
+ auto rc = mockTerminusManager.clearQueuedResponses();
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + getTidRespLen> getTidResp0{
+ 0x00, 0x02, 0x02, 0x00, 0x00};
+ rc = mockTerminusManager.enqueueResponse((pldm_msg*)getTidResp0.data(),
+ sizeof(getTidResp0));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + setTidRespLen> setTidResp0{
+ 0x00, 0x02, 0x01, 0x00};
+ rc = mockTerminusManager.enqueueResponse((pldm_msg*)setTidResp0.data(),
+ sizeof(setTidResp0));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ uint8_t byte0 = (1 << (PLDM_BASE % 8)) + (1 << (PLDM_PLATFORM % 8)) +
+ (1 << (PLDM_BIOS % 8)) + (1 << (PLDM_FRU % 8));
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + getPldmTypesRespLen>
+ getPldmTypesResp0{0x00, 0x02, 0x04, 0x00, byte0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ rc = mockTerminusManager.enqueueResponse(
+ (pldm_msg*)getPldmTypesResp0.data(), sizeof(getPldmTypesResp0));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ /* Response GetPLDMCommand BASE, CC=0,
+ * SetTID/GetTID/GetPLDMTypes/GetPLDMCommands */
+ byte0 = (1 << (PLDM_SET_TID % 8)) + (1 << (PLDM_GET_TID % 8)) +
+ (1 << (PLDM_GET_PLDM_TYPES % 8)) +
+ (1 << (PLDM_GET_PLDM_COMMANDS % 8));
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + getPldmCommandRespLen>
+ getPldmCommandBaseResp0{0x00, 0x02, 0x05, 0x00, byte0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00};
+ rc = mockTerminusManager.enqueueResponse(
+ (pldm_msg*)getPldmCommandBaseResp0.data(),
+ sizeof(getPldmCommandBaseResp0));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ /* Response GetPLDMCommand PLATFORM, CC=0,
+ * SetEventReceiver/PlatformEventMessage/GetSensorReading/SetNumericEffecterValue/GetNumericEffecterValue/GetPDR
+ */
+ /* byte0 command from 0x00 to 0x07 */
+ byte0 = (1 << (PLDM_SET_EVENT_RECEIVER % 8)); // byte0 = 0x10
+ /* byte1 command from 0x08 to 0xf */
+ uint8_t byte1 = (1 << (PLDM_PLATFORM_EVENT_MESSAGE % 8)) +
+ (1 << (PLDM_POLL_FOR_PLATFORM_EVENT_MESSAGE % 8)) +
+ (1 << (PLDM_EVENT_MESSAGE_SUPPORTED % 8)) +
+ (1 << (PLDM_EVENT_MESSAGE_BUFFER_SIZE % 8)); // byte1 = 0x3c
+ /* byte2 command from 0x10 to 0x17 */
+ uint8_t byte2 = (1 << (PLDM_GET_SENSOR_READING % 8)); // byte2 = 0x02
+ /* byte3 command from 0x18 to 0x1f */
+ /* byte4 command from 0x20 to 0x27 */
+ uint8_t byte4 = (1 << (PLDM_GET_STATE_SENSOR_READINGS % 8)); // byte4 = 0x02
+ /* byte5 command from 0x28 to 0x2f */
+ /* byte6 command from 0x30 to 0x37 */
+ uint8_t byte6 =
+ (1 << (PLDM_SET_NUMERIC_EFFECTER_VALUE % 8)) +
+ (1 << (PLDM_GET_NUMERIC_EFFECTER_VALUE % 8)); // byte6 = 0x06
+ /* byte7 command from 0x38 to 0x3f */
+ uint8_t byte7 = (0 << (PLDM_SET_STATE_EFFECTER_STATES % 8)); // byte7 = 0
+ /* byte8 command from 0x40 to 0x47 */
+ /* byte9 command from 0x48 to 0x4f */
+ /* byte10 command from 0x50 to 0x57 */
+ uint8_t byte10 = (1 << (PLDM_GET_PDR_REPOSITORY_INFO % 8)) +
+ (1 << (PLDM_GET_PDR % 8)); // byte10 = 0x03
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + getPldmCommandRespLen>
+ getPldmCommandPlatResp0{
+ 0x00, 0x02, 0x05, 0x00, byte0, byte1, byte2, 0x00, byte4,
+ 0x00, byte6, byte7, 0x00, 0x00, byte10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ rc = mockTerminusManager.enqueueResponse(
+ (pldm_msg*)getPldmCommandPlatResp0.data(),
+ sizeof(getPldmCommandPlatResp0));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ /* Response GetPLDMCommand BIOS, CC=0, GetDateTime/SetDateTime */
+ /* byte0 command from 1 to 7 */
+ byte0 = (0 << (PLDM_GET_BIOS_TABLE % 8)) +
+ (0 << (PLDM_SET_BIOS_TABLE % 8)) +
+ (0 << (PLDM_SET_BIOS_ATTRIBUTE_CURRENT_VALUE % 8));
+ /* byte1 command from 8 to 15 */
+ byte1 = (0 << (PLDM_GET_BIOS_ATTRIBUTE_CURRENT_VALUE_BY_HANDLE % 8)) +
+ (1 << (PLDM_GET_DATE_TIME % 8)) + (1 << (PLDM_SET_DATE_TIME % 8));
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + getPldmCommandRespLen>
+ getPldmCommandBiosResp0{0x00, 0x02, 0x05, 0x00, byte0, byte1, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ rc = mockTerminusManager.enqueueResponse(
+ (pldm_msg*)getPldmCommandBiosResp0.data(),
+ sizeof(getPldmCommandBiosResp0));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ /* Response GetPLDMCommand FRU, CC=0,
+ * GetFRURecordTableMetadata/GetFRURecordTable */
+ /* byte0 command from 1 to 7 */
+ byte0 = (1 << (PLDM_GET_FRU_RECORD_TABLE_METADATA % 8)) +
+ (1 << (PLDM_GET_FRU_RECORD_TABLE % 8)) +
+ (0 << (PLDM_SET_FRU_RECORD_TABLE % 8)) +
+ (0 << (PLDM_GET_FRU_RECORD_BY_OPTION % 8));
+ /* byte0 command from 8 to 15 */
+ byte1 = 0;
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + getPldmCommandRespLen>
+ getPldmCommandFruResp0{0x00, 0x02, 0x05, 0x00, byte0, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ rc = mockTerminusManager.enqueueResponse(
+ (pldm_msg*)getPldmCommandFruResp0.data(),
+ sizeof(getPldmCommandFruResp0));
+ EXPECT_EQ(rc, PLDM_SUCCESS);
+
+ pldm::MctpInfos mctpInfos{};
+ mctpInfos.emplace_back(pldm::MctpInfo(12, "", "", 1));
+ mockTerminusManager.discoverMctpTerminus(mctpInfos);
+ EXPECT_EQ(1, termini.size());
+ EXPECT_EQ(true, termini.contains(1));
+ EXPECT_EQ(false, termini.contains(0));
+ EXPECT_EQ(false, termini.contains(2));
+
+ EXPECT_EQ(true, termini[1]->doesSupportType(PLDM_BASE));
+ EXPECT_EQ(true, termini[1]->doesSupportType(PLDM_PLATFORM));
+ EXPECT_EQ(true, termini[1]->doesSupportType(PLDM_BIOS));
+ EXPECT_EQ(true, termini[1]->doesSupportType(PLDM_FRU));
+ /* Check PLDM Base commands */
+ EXPECT_EQ(true, termini[1]->doesSupportCommand(PLDM_BASE, PLDM_SET_TID));
+ EXPECT_EQ(true, termini[1]->doesSupportCommand(PLDM_BASE, PLDM_GET_TID));
+ EXPECT_EQ(false,
+ termini[1]->doesSupportCommand(PLDM_BASE, PLDM_GET_PLDM_VERSION));
+ EXPECT_EQ(true,
+ termini[1]->doesSupportCommand(PLDM_BASE, PLDM_GET_PLDM_TYPES));
+
+ EXPECT_EQ(true, termini[1]->doesSupportCommand(PLDM_BASE,
+ PLDM_GET_PLDM_COMMANDS));
+ EXPECT_EQ(false, termini[1]->doesSupportCommand(PLDM_BASE,
+ PLDM_MULTIPART_RECEIVE));
+
+ /* Check PLDM Platform commands */
+ EXPECT_EQ(true, termini[1]->doesSupportCommand(PLDM_PLATFORM,
+ PLDM_SET_EVENT_RECEIVER));
+ EXPECT_EQ(true, termini[1]->doesSupportCommand(
+ PLDM_PLATFORM, PLDM_PLATFORM_EVENT_MESSAGE));
+ EXPECT_EQ(true, termini[1]->doesSupportCommand(
+ PLDM_PLATFORM, PLDM_POLL_FOR_PLATFORM_EVENT_MESSAGE));
+ EXPECT_EQ(true, termini[1]->doesSupportCommand(
+ PLDM_PLATFORM, PLDM_EVENT_MESSAGE_SUPPORTED));
+ EXPECT_EQ(true, termini[1]->doesSupportCommand(
+ PLDM_PLATFORM, PLDM_EVENT_MESSAGE_BUFFER_SIZE));
+ EXPECT_EQ(true, termini[1]->doesSupportCommand(PLDM_PLATFORM,
+ PLDM_GET_SENSOR_READING));
+ EXPECT_EQ(true, termini[1]->doesSupportCommand(
+ PLDM_PLATFORM, PLDM_GET_STATE_SENSOR_READINGS));
+ EXPECT_EQ(true, termini[1]->doesSupportCommand(
+ PLDM_PLATFORM, PLDM_SET_NUMERIC_EFFECTER_VALUE));
+ EXPECT_EQ(true, termini[1]->doesSupportCommand(
+ PLDM_PLATFORM, PLDM_GET_NUMERIC_EFFECTER_VALUE));
+ EXPECT_EQ(false, termini[1]->doesSupportCommand(
+ PLDM_PLATFORM, PLDM_SET_STATE_EFFECTER_STATES));
+ EXPECT_EQ(true, termini[1]->doesSupportCommand(
+ PLDM_PLATFORM, PLDM_GET_PDR_REPOSITORY_INFO));
+ EXPECT_EQ(true,
+ termini[1]->doesSupportCommand(PLDM_PLATFORM, PLDM_GET_PDR));
+
+ /* Check PLDM Bios commands */
+ EXPECT_EQ(false,
+ termini[1]->doesSupportCommand(PLDM_BIOS, PLDM_GET_BIOS_TABLE));
+ EXPECT_EQ(false,
+ termini[1]->doesSupportCommand(PLDM_BIOS, PLDM_SET_BIOS_TABLE));
+ EXPECT_EQ(false, termini[1]->doesSupportCommand(
+ PLDM_BIOS, PLDM_SET_BIOS_ATTRIBUTE_CURRENT_VALUE));
+ EXPECT_EQ(false,
+ termini[1]->doesSupportCommand(
+ PLDM_BIOS, PLDM_GET_BIOS_ATTRIBUTE_CURRENT_VALUE_BY_HANDLE));
+ EXPECT_EQ(true,
+ termini[1]->doesSupportCommand(PLDM_BIOS, PLDM_GET_DATE_TIME));
+ EXPECT_EQ(true,
+ termini[1]->doesSupportCommand(PLDM_BIOS, PLDM_SET_DATE_TIME));
+
+ /* Check PLDM Fru commands */
+ EXPECT_EQ(true, termini[1]->doesSupportCommand(
+ PLDM_FRU, PLDM_GET_FRU_RECORD_TABLE_METADATA));
+ EXPECT_EQ(true, termini[1]->doesSupportCommand(PLDM_FRU,
+ PLDM_GET_FRU_RECORD_TABLE));
+ EXPECT_EQ(false, termini[1]->doesSupportCommand(PLDM_FRU,
+ PLDM_SET_FRU_RECORD_TABLE));
+ EXPECT_EQ(false, termini[1]->doesSupportCommand(
+ PLDM_FRU, PLDM_GET_FRU_RECORD_BY_OPTION));
+}
diff --git a/platform-mc/test/terminus_test.cpp b/platform-mc/test/terminus_test.cpp
new file mode 100644
index 0000000..32fd7b7
--- /dev/null
+++ b/platform-mc/test/terminus_test.cpp
@@ -0,0 +1,23 @@
+#include "platform-mc/terminus.hpp"
+
+#include <gtest/gtest.h>
+
+TEST(TerminusTest, supportedTypeTest)
+{
+ auto t1 = pldm::platform_mc::Terminus(1, 1 << PLDM_BASE);
+ auto t2 = pldm::platform_mc::Terminus(2,
+ 1 << PLDM_BASE | 1 << PLDM_PLATFORM);
+
+ EXPECT_EQ(true, t1.doesSupportType(PLDM_BASE));
+ EXPECT_EQ(false, t1.doesSupportType(PLDM_PLATFORM));
+ EXPECT_EQ(true, t2.doesSupportType(PLDM_BASE));
+ EXPECT_EQ(true, t2.doesSupportType(PLDM_PLATFORM));
+}
+
+TEST(TerminusTest, getTidTest)
+{
+ const pldm_tid_t tid = 1;
+ auto t1 = pldm::platform_mc::Terminus(tid, 1 << PLDM_BASE);
+
+ EXPECT_EQ(tid, t1.getTid());
+}