| #include "terminus_manager.hpp" | |
| #include "manager.hpp" | |
| #include <phosphor-logging/lg2.hpp> | |
| #ifndef PLDM_TID_UNASSIGNED | |
| #define PLDM_TID_UNASSIGNED 0x00 | |
| #endif | |
| 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; | |
| } | |
| // Prioritize using the endpoint's EID as its TID to align with the legacy | |
| // pldmd-google behavior. Many devices retain their assigned TID across | |
| // daemon restarts (sticky state). By forcing TID = EID, we prevent | |
| // conflicts where a device responds with its previously assigned EID-based | |
| // TID, which might otherwise collide with a sequentially assigned TID. | |
| pldm_tid_t eid = std::get<0>(mctpInfo); | |
| if (!tidPool[eid]) | |
| { | |
| return storeTerminusInfo(mctpInfo, eid); | |
| } | |
| 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::updateMctpEndpointAvailability(const MctpInfo& mctpInfo, | |
| Availability availability) | |
| { | |
| mctpInfoAvailTable.insert_or_assign(mctpInfo, availability); | |
| if (manager) | |
| { | |
| auto tid = toTid(mctpInfo); | |
| if (tid) | |
| { | |
| manager->updateAvailableState(tid.value(), availability); | |
| } | |
| } | |
| } | |
| std::string TerminusManager::constructEndpointObjPath(const MctpInfo& mctpInfo) | |
| { | |
| std::string eidStr = std::to_string(std::get<0>(mctpInfo)); | |
| std::string networkIDStr = std::to_string(std::get<3>(mctpInfo)); | |
| return std::format("{}/networks/{}/endpoints/{}", MCTPPath, networkIDStr, | |
| eidStr); | |
| } | |
| void TerminusManager::discoverMctpTerminus(const TerminusInfos& 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); })); | |
| } | |
| TerminiMapper::iterator TerminusManager::findTerminusPtr( | |
| 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() | |
| { | |
| std::vector<pldm_tid_t> addedTids; | |
| while (!queuedMctpInfos.empty()) | |
| { | |
| bool terminusInitFailed = false; | |
| if (manager) | |
| { | |
| co_await manager->beforeDiscoverTerminus(); | |
| } | |
| const TerminusInfos& mctpInfos = queuedMctpInfos.front(); | |
| for (const auto& [tid, mctpInfo] : mctpInfos) | |
| { | |
| auto it = findTerminusPtr(mctpInfo); | |
| if (it == termini.end()) | |
| { | |
| mctpInfoAvailTable[mctpInfo] = true; | |
| lg2::info("Discovered new MCTP terminus on EID {EID} " | |
| "(Network ID: {NETWORK}). Starting PLDM discovery...", | |
| "EID", std::get<0>(mctpInfo), "NETWORK", | |
| std::get<3>(mctpInfo)); | |
| auto rc = co_await initMctpTerminus(mctpInfo, tid); | |
| if (rc != PLDM_SUCCESS) | |
| { | |
| lg2::error( | |
| "Failed to initialize terminus with EID {EID}, networkId {NETWORK}, response code {RC}.", | |
| "EID", std::get<0>(mctpInfo), "NETWORK", | |
| std::get<3>(mctpInfo), "RC", rc); | |
| mctpInfoAvailTable.erase(mctpInfo); | |
| terminusInitFailed = true; | |
| continue; | |
| } | |
| } | |
| if (!tid) | |
| { | |
| lg2::error( | |
| "Failed to get TID for terminus with EID {EID}, networkId {NETWORK}.", | |
| "EID", std::get<0>(mctpInfo), "NETWORK", | |
| std::get<3>(mctpInfo)); | |
| mctpInfoAvailTable.erase(mctpInfo); | |
| terminusInitFailed = true; | |
| continue; | |
| } | |
| addedTids.push_back(tid); | |
| } | |
| if (manager) | |
| { | |
| co_await manager->afterDiscoverTerminus(); | |
| } | |
| if (terminusInitFailed) | |
| { | |
| co_return PLDM_ERROR; | |
| } | |
| queuedMctpInfos.pop(); | |
| } | |
| co_return PLDM_SUCCESS; | |
| } | |
| void TerminusManager::removeMctpTerminus(const TerminusInfos& mctpInfos) | |
| { | |
| // remove terminus | |
| for (const auto& [tid, mctpInfo] : mctpInfos) | |
| { | |
| auto it = findTerminusPtr(mctpInfo); | |
| if (it == termini.end()) | |
| { | |
| continue; | |
| } | |
| if (manager) | |
| { | |
| manager->stopSensorPolling(it->second->getTid()); | |
| } | |
| unmapTid(it->first); | |
| termini.erase(it); | |
| mctpInfoAvailTable.erase(mctpInfo); | |
| } | |
| } | |
| exec::task<int> TerminusManager::initMctpTerminus(const MctpInfo& mctpInfo, | |
| const pldm_tid_t tid) | |
| { | |
| pldm_tid_t responseTid = 0; | |
| bool isMapped = false; | |
| lg2::info("Querying Terminus ID (TID) for EID {EID}...", "EID", | |
| std::get<0>(mctpInfo)); | |
| auto rc = co_await getTidOverMctp(mctpInfo, tid, &responseTid); | |
| if (rc != PLDM_SUCCESS) | |
| { | |
| lg2::error("Failed to Get Terminus ID, error {ERROR}.", "ERROR", rc); | |
| co_return PLDM_ERROR; | |
| } | |
| lg2::info("Terminus on EID {EID} currently has TID {TID}", "EID", | |
| std::get<0>(mctpInfo), "TID", responseTid); | |
| if (responseTid == PLDM_TID_RESERVED) | |
| { | |
| lg2::error("Terminus responses the reserved {TID}.", "TID", | |
| responseTid); | |
| co_return PLDM_ERROR; | |
| } | |
| /* Terminus already has TID */ | |
| if (responseTid != PLDM_TID_UNASSIGNED && responseTid != tid) | |
| { | |
| /* TID is used by one discovered terminus */ | |
| auto it = termini.find(responseTid); | |
| 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, responseTid); | |
| if (!mappedTid) | |
| { | |
| lg2::error("Failed to store Terminus Info for terminus {TID}.", | |
| "TID", responseTid); | |
| 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; | |
| } | |
| lg2::info("Assigning TID {TID} to terminus on EID {EID}...", "TID", tid, | |
| "EID", std::get<0>(mctpInfo)); | |
| rc = co_await setTidOverMctp(mctpInfo, tid); | |
| if (rc != PLDM_SUCCESS) | |
| { | |
| if (rc == PLDM_ERROR_UNSUPPORTED_PLDM_CMD) | |
| { | |
| lg2::error("Terminus {TID} does not support SetTID command.", | |
| "TID", tid); | |
| } | |
| else | |
| { | |
| lg2::error( | |
| "Failed to Set terminus TID for terminus {TID}, error {ERROR}.", | |
| "TID", tid, "ERROR", rc); | |
| } | |
| unmapTid(tid); | |
| co_return rc; | |
| } | |
| lg2::info("Successfully assigned TID {TID} to EID {EID}", "TID", tid, | |
| "EID", std::get<0>(mctpInfo)); | |
| if (termini.contains(tid)) | |
| { | |
| // the terminus has been discovered before | |
| co_return PLDM_SUCCESS; | |
| } | |
| } | |
| /* Discovery the mapped terminus */ | |
| lg2::info("Querying supported PLDM Types for EID {EID}...", "EID", | |
| std::get<0>(mctpInfo)); | |
| 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); | |
| unmapTid(tid); | |
| co_return PLDM_ERROR; | |
| } | |
| try | |
| { | |
| termini[tid] = std::make_shared<Terminus>(tid, supportedTypes, event); | |
| } | |
| catch (const sdbusplus::exception_t& e) | |
| { | |
| lg2::error("Failed to create terminus manager for terminus {TID}", | |
| "TID", tid); | |
| unmapTid(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; | |
| } | |
| lg2::info("Querying supported PLDM Commands for EID {EID} " | |
| "(PLDM Type {TYPE})...", | |
| "EID", std::get<0>(mctpInfo), "TYPE", type); | |
| ver32_t version{0xFF, 0xFF, 0xFF, 0xFF}; | |
| auto rc = co_await getPLDMVersion(tid, type, &version); | |
| if (rc) | |
| { | |
| lg2::error( | |
| "Failed to Get PLDM Version for terminus {TID}, PLDM Type {TYPE}, error {ERROR}", | |
| "TID", tid, "TYPE", type, "ERROR", rc); | |
| } | |
| termini[tid]->setSupportedTypeVersions(type, version); | |
| std::vector<bitfield8_t> cmds(PLDM_MAX_CMDS_PER_TYPE / 8); | |
| rc = co_await getPLDMCommands(tid, type, version, 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); | |
| /* Use the MCTP target name or Terminus_<NID>_<EID> as the default terminus | |
| * name */ | |
| MctpInfoName mctpInfoName = std::get<4>(mctpInfo); | |
| std::string defaultName = | |
| (mctpInfoName.has_value() && !mctpInfoName.value().empty()) | |
| ? mctpInfoName.value() | |
| : std::format("Terminus_{}_{}", std::get<NetworkId>(mctpInfo), | |
| std::get<pldm::eid>(mctpInfo)); | |
| lg2::info("Terminus {TID} has default Terminus Name {NAME}", "NAME", | |
| defaultName, "TID", tid); | |
| termini[tid]->setTerminusName(defaultName); | |
| co_return PLDM_SUCCESS; | |
| } | |
| exec::task<int> TerminusManager::sendRecvPldmMsgOverMctp( | |
| mctp_eid_t eid, Request& request, const pldm_msg** responseMsg, | |
| size_t* responseLen) | |
| { | |
| int rc = 0; | |
| try | |
| { | |
| std::tie(rc, *responseMsg, *responseLen) = | |
| co_await handler.sendRecvMsg(eid, std::move(request)); | |
| } | |
| catch (const sdbusplus::exception_t& e) | |
| { | |
| lg2::error( | |
| "Send and Receive PLDM message over MCTP throw error - {ERROR}.", | |
| "ERROR", e); | |
| co_return PLDM_ERROR; | |
| } | |
| catch (const int& e) | |
| { | |
| lg2::error( | |
| "Send and Receive PLDM message over MCTP throw int error - {ERROR}.", | |
| "ERROR", e); | |
| co_return PLDM_ERROR; | |
| } | |
| co_return rc; | |
| } | |
| exec::task<int> TerminusManager::getTidOverMctp( | |
| const MctpInfo& mctpInfo, const pldm_tid_t tid, pldm_tid_t* responseTid) | |
| { | |
| auto instanceIdResult = instanceIdDb.next(tid); | |
| if (!instanceIdResult) | |
| { | |
| co_return PLDM_ERROR; | |
| } | |
| auto instanceId = instanceIdResult.value(); | |
| mctp_eid_t eid = std::get<0>(mctpInfo); | |
| NetworkId networkId = std::get<3>(mctpInfo); | |
| Request request(sizeof(pldm_msg_hdr)); | |
| auto requestMsg = new (request.data()) pldm_msg; | |
| auto rc = encode_get_tid_req(instanceId, requestMsg); | |
| if (rc) | |
| { | |
| instanceIdDb.free(tid, instanceId); | |
| lg2::error( | |
| "Failed to encode request GetTID for network ID {NETWORK_ID} endpoint ID {EID}, error {RC} ", | |
| "NETWORK_ID", networkId, "EID", eid, "RC", rc); | |
| co_return rc; | |
| } | |
| const pldm_msg* responseMsg = nullptr; | |
| size_t responseLen = 0; | |
| rc = co_await sendRecvPldmMsgOverMctp(tid, request, &responseMsg, | |
| &responseLen); | |
| if (rc) | |
| { | |
| lg2::error( | |
| "Failed to send GetTID for network ID {NETWORK_ID} Endpoint {EID}, error {RC}", | |
| "NETWORK_ID", networkId, "EID", eid, "RC", rc); | |
| co_return rc; | |
| } | |
| uint8_t completionCode = 0; | |
| rc = decode_get_tid_resp(responseMsg, responseLen, &completionCode, | |
| responseTid); | |
| if (rc) | |
| { | |
| lg2::error( | |
| "Failed to decode response GetTID for network ID {NETWORK_ID} Endpoint ID {EID}, error {RC} ", | |
| "NETWORK_ID", networkId, "EID", eid, "RC", rc); | |
| co_return rc; | |
| } | |
| if (completionCode != PLDM_SUCCESS) | |
| { | |
| lg2::error( | |
| "Error : GetTID for network ID {NETWORK_ID} Endpoint ID {EID}, complete code {CC}.", | |
| "NETWORK_ID", networkId, "EID", eid, "CC", completionCode); | |
| co_return rc; | |
| } | |
| co_return completionCode; | |
| } | |
| exec::task<int> TerminusManager::setTidOverMctp(const MctpInfo& mctpInfo, | |
| pldm_tid_t tid) | |
| { | |
| auto instanceIdResult = instanceIdDb.next(tid); | |
| if (!instanceIdResult) | |
| { | |
| co_return PLDM_ERROR; | |
| } | |
| auto instanceId = instanceIdResult.value(); | |
| mctp_eid_t eid = std::get<0>(mctpInfo); | |
| NetworkId networkId = std::get<3>(mctpInfo); | |
| Request request(sizeof(pldm_msg_hdr) + sizeof(pldm_set_tid_req)); | |
| auto requestMsg = new (request.data()) pldm_msg; | |
| auto rc = encode_set_tid_req(instanceId, tid, requestMsg); | |
| if (rc) | |
| { | |
| instanceIdDb.free(tid, instanceId); | |
| lg2::error( | |
| "Failed to encode request SetTID for network ID {NETWORK_ID} endpoint ID {EID}, error {RC} ", | |
| "NETWORK_ID", networkId, "EID", eid, "RC", rc); | |
| co_return rc; | |
| } | |
| const pldm_msg* responseMsg = nullptr; | |
| size_t responseLen = 0; | |
| rc = co_await sendRecvPldmMsgOverMctp(tid, request, &responseMsg, | |
| &responseLen); | |
| if (rc) | |
| { | |
| lg2::error( | |
| "Failed to send SetTID for network ID {NETWORK_ID} Endpoint {EID}, error {RC}", | |
| "NETWORK_ID", networkId, "EID", eid, "RC", rc); | |
| co_return rc; | |
| } | |
| if (responseMsg == nullptr || responseLen != PLDM_SET_TID_RESP_BYTES) | |
| { | |
| lg2::error( | |
| "Failed to decode response SetTID for network ID {NETWORK_ID} Endpoint ID {EID}, error {RC} ", | |
| "NETWORK_ID", networkId, "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 = new (request.data()) pldm_msg; | |
| 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, ver32_t version, bitfield8_t* supportedCmds) | |
| { | |
| Request request(sizeof(pldm_msg_hdr) + PLDM_GET_COMMANDS_REQ_BYTES); | |
| auto requestMsg = new (request.data()) pldm_msg; | |
| 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; | |
| } | |
| // There's a cost of maintaining another table to hold availability | |
| // status as we can't ensure that it always synchronizes with the | |
| // mctpInfoTable; std::map operator[] will insert a default of boolean | |
| // which is false to the mctpInfoAvailTable if the mctpInfo key doesn't | |
| // exist. Once we miss to initialize the availability of an available | |
| // endpoint, it will drop all the messages to/from it. | |
| if (!mctpInfoAvailTable[mctpInfo.value()]) | |
| { | |
| co_return PLDM_ERROR_NOT_READY; | |
| } | |
| auto requestMsg = new (request.data()) pldm_msg; | |
| auto instanceIdResult = instanceIdDb.next(tid); | |
| if (!instanceIdResult) | |
| { | |
| co_return PLDM_ERROR; | |
| } | |
| requestMsg->hdr.instance_id = instanceIdResult.value(); | |
| auto rc = co_await sendRecvPldmMsgOverMctp(tid, request, responseMsg, | |
| responseLen); | |
| if (rc == PLDM_ERROR_NOT_READY) | |
| { | |
| // Call Recover() to check enpoint's availability | |
| // Set endpoint's availability in mctpInfoTable to false in advance | |
| // to prevent message forwarding through this endpoint while mctpd | |
| // is checking the endpoint. | |
| std::string endpointObjPath = | |
| constructEndpointObjPath(mctpInfo.value()); | |
| pldm::utils::recoverMctpEndpoint(endpointObjPath); | |
| updateMctpEndpointAvailability(mctpInfo.value(), false); | |
| } | |
| co_return rc; | |
| } | |
| exec::task<int> TerminusManager::getPLDMVersion(pldm_tid_t tid, uint8_t type, | |
| ver32_t* version) | |
| { | |
| Request request(sizeof(pldm_msg_hdr) + PLDM_GET_VERSION_REQ_BYTES); | |
| auto requestMsg = new (request.data()) pldm_msg; | |
| auto rc = | |
| encode_get_version_req(0, 0, PLDM_GET_FIRSTPART, type, requestMsg); | |
| if (rc) | |
| { | |
| lg2::error( | |
| "Failed to encode request getPLDMVersion 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 getPLDMVersion message for terminus {TID}, error {RC}", | |
| "TID", tid, "RC", rc); | |
| co_return rc; | |
| } | |
| /* Process response */ | |
| uint8_t completionCode = 0; | |
| uint8_t transferFlag = 0; | |
| uint32_t transferHandle = 0; | |
| rc = decode_get_version_resp(responseMsg, responseLen, &completionCode, | |
| &transferHandle, &transferFlag, version); | |
| if (rc) | |
| { | |
| lg2::error( | |
| "Failed to decode response getPLDMVersion for terminus ID {TID}, error {RC} ", | |
| "TID", tid, "RC", rc); | |
| co_return rc; | |
| } | |
| if (completionCode != PLDM_SUCCESS) | |
| { | |
| lg2::error( | |
| "Error : getPLDMVersion for terminus ID {TID}, complete code {CC}.", | |
| "TID", tid, "CC", completionCode); | |
| co_return completionCode; | |
| } | |
| co_return completionCode; | |
| } | |
| std::optional<mctp_eid_t> TerminusManager::getActiveEidByName( | |
| const std::string& terminusName) | |
| { | |
| if (!termini.size() || terminusName.empty()) | |
| { | |
| return std::nullopt; | |
| } | |
| for (auto& [tid, terminus] : termini) | |
| { | |
| if (!terminus) | |
| { | |
| continue; | |
| } | |
| auto tmp = terminus->getTerminusName(); | |
| if (!tmp || std::empty(*tmp) || *tmp != terminusName) | |
| { | |
| continue; | |
| } | |
| try | |
| { | |
| auto mctpInfo = toMctpInfo(tid); | |
| if (!mctpInfo || !mctpInfoAvailTable[*mctpInfo]) | |
| { | |
| return std::nullopt; | |
| } | |
| return std::get<0>(*mctpInfo); | |
| } | |
| catch (const std::exception& e) | |
| { | |
| return std::nullopt; | |
| } | |
| } | |
| return std::nullopt; | |
| } | |
| } // namespace platform_mc | |
| } // namespace pldm |