1 #include "terminus_manager.hpp" 2 3 #include "manager.hpp" 4 5 #include <phosphor-logging/lg2.hpp> 6 7 PHOSPHOR_LOG2_USING; 8 9 namespace pldm 10 { 11 namespace platform_mc 12 { 13 14 std::optional<MctpInfo> TerminusManager::toMctpInfo(const pldm_tid_t& tid) 15 { 16 if (tid == PLDM_TID_UNASSIGNED || tid == PLDM_TID_RESERVED) 17 { 18 return std::nullopt; 19 } 20 21 if ((!this->transportLayerTable.contains(tid)) || 22 (this->transportLayerTable[tid] != SupportedTransportLayer::MCTP)) 23 { 24 return std::nullopt; 25 } 26 27 auto mctpInfoIt = mctpInfoTable.find(tid); 28 if (mctpInfoIt == mctpInfoTable.end()) 29 { 30 return std::nullopt; 31 } 32 33 return mctpInfoIt->second; 34 } 35 36 std::optional<pldm_tid_t> TerminusManager::toTid(const MctpInfo& mctpInfo) const 37 { 38 if (!pldm::utils::isValidEID(std::get<0>(mctpInfo))) 39 { 40 return std::nullopt; 41 } 42 43 auto mctpInfoTableIt = std::find_if( 44 mctpInfoTable.begin(), mctpInfoTable.end(), [&mctpInfo](auto& v) { 45 return (std::get<0>(v.second) == std::get<0>(mctpInfo)) && 46 (std::get<3>(v.second) == std::get<3>(mctpInfo)); 47 }); 48 if (mctpInfoTableIt == mctpInfoTable.end()) 49 { 50 return std::nullopt; 51 } 52 return mctpInfoTableIt->first; 53 } 54 55 std::optional<pldm_tid_t> 56 TerminusManager::storeTerminusInfo(const MctpInfo& mctpInfo, pldm_tid_t tid) 57 { 58 if (tid == PLDM_TID_UNASSIGNED || tid == PLDM_TID_RESERVED) 59 { 60 return std::nullopt; 61 } 62 63 if (!pldm::utils::isValidEID(std::get<0>(mctpInfo))) 64 { 65 return std::nullopt; 66 } 67 68 if (tidPool[tid]) 69 { 70 return std::nullopt; 71 } 72 73 tidPool[tid] = true; 74 transportLayerTable[tid] = SupportedTransportLayer::MCTP; 75 mctpInfoTable[tid] = mctpInfo; 76 77 return tid; 78 } 79 80 std::optional<pldm_tid_t> TerminusManager::mapTid(const MctpInfo& mctpInfo) 81 { 82 if (!pldm::utils::isValidEID(std::get<0>(mctpInfo))) 83 { 84 return std::nullopt; 85 } 86 87 auto mctpInfoTableIt = std::find_if( 88 mctpInfoTable.begin(), mctpInfoTable.end(), [&mctpInfo](auto& v) { 89 return (std::get<0>(v.second) == std::get<0>(mctpInfo)) && 90 (std::get<3>(v.second) == std::get<3>(mctpInfo)); 91 }); 92 if (mctpInfoTableIt != mctpInfoTable.end()) 93 { 94 return mctpInfoTableIt->first; 95 } 96 97 auto tidPoolIt = std::find(tidPool.begin(), tidPool.end(), false); 98 if (tidPoolIt == tidPool.end()) 99 { 100 return std::nullopt; 101 } 102 103 pldm_tid_t tid = std::distance(tidPool.begin(), tidPoolIt); 104 return storeTerminusInfo(mctpInfo, tid); 105 } 106 107 bool TerminusManager::unmapTid(const pldm_tid_t& tid) 108 { 109 if (tid == PLDM_TID_UNASSIGNED || tid == PLDM_TID_RESERVED) 110 { 111 return false; 112 } 113 tidPool[tid] = false; 114 115 if (transportLayerTable.contains(tid)) 116 { 117 transportLayerTable.erase(tid); 118 } 119 120 if (mctpInfoTable.contains(tid)) 121 { 122 mctpInfoTable.erase(tid); 123 } 124 125 return true; 126 } 127 128 void TerminusManager::discoverMctpTerminus(const MctpInfos& mctpInfos) 129 { 130 queuedMctpInfos.emplace(mctpInfos); 131 if (discoverMctpTerminusTaskHandle.has_value()) 132 { 133 auto& [scope, rcOpt] = *discoverMctpTerminusTaskHandle; 134 if (!rcOpt.has_value()) 135 { 136 return; 137 } 138 stdexec::sync_wait(scope.on_empty()); 139 discoverMctpTerminusTaskHandle.reset(); 140 } 141 auto& [scope, rcOpt] = discoverMctpTerminusTaskHandle.emplace(); 142 scope.spawn(discoverMctpTerminusTask() | 143 stdexec::then([&](int rc) { rcOpt.emplace(rc); }), 144 exec::default_task_context<void>(exec::inline_scheduler{})); 145 } 146 147 TerminiMapper::iterator 148 TerminusManager::findTerminusPtr(const MctpInfo& mctpInfo) 149 { 150 auto foundIter = std::find_if( 151 termini.begin(), termini.end(), [&](const auto& terminusPair) { 152 auto terminusMctpInfo = toMctpInfo(terminusPair.first); 153 return (terminusMctpInfo && 154 (std::get<0>(terminusMctpInfo.value()) == 155 std::get<0>(mctpInfo)) && 156 (std::get<3>(terminusMctpInfo.value()) == 157 std::get<3>(mctpInfo))); 158 }); 159 160 return foundIter; 161 } 162 163 exec::task<int> TerminusManager::discoverMctpTerminusTask() 164 { 165 while (!queuedMctpInfos.empty()) 166 { 167 if (manager) 168 { 169 co_await manager->beforeDiscoverTerminus(); 170 } 171 172 const MctpInfos& mctpInfos = queuedMctpInfos.front(); 173 for (const auto& mctpInfo : mctpInfos) 174 { 175 auto it = findTerminusPtr(mctpInfo); 176 if (it == termini.end()) 177 { 178 co_await initMctpTerminus(mctpInfo); 179 } 180 } 181 182 if (manager) 183 { 184 co_await manager->afterDiscoverTerminus(); 185 } 186 187 queuedMctpInfos.pop(); 188 } 189 190 co_return PLDM_SUCCESS; 191 } 192 193 void TerminusManager::removeMctpTerminus(const MctpInfos& mctpInfos) 194 { 195 // remove terminus 196 for (const auto& mctpInfo : mctpInfos) 197 { 198 auto it = findTerminusPtr(mctpInfo); 199 if (it == termini.end()) 200 { 201 continue; 202 } 203 204 unmapTid(it->first); 205 termini.erase(it); 206 } 207 } 208 209 exec::task<int> TerminusManager::initMctpTerminus(const MctpInfo& mctpInfo) 210 { 211 mctp_eid_t eid = std::get<0>(mctpInfo); 212 pldm_tid_t tid = 0; 213 bool isMapped = false; 214 auto rc = co_await getTidOverMctp(eid, &tid); 215 if (rc != PLDM_SUCCESS) 216 { 217 lg2::error("Failed to Get Terminus ID, error {ERROR}.", "ERROR", rc); 218 co_return PLDM_ERROR; 219 } 220 221 if (tid == PLDM_TID_RESERVED) 222 { 223 lg2::error("Terminus responses the reserved {TID}.", "TID", tid); 224 co_return PLDM_ERROR; 225 } 226 227 /* Terminus already has TID */ 228 if (tid != PLDM_TID_UNASSIGNED) 229 { 230 /* TID is used by one discovered terminus */ 231 auto it = termini.find(tid); 232 if (it != termini.end()) 233 { 234 auto terminusMctpInfo = toMctpInfo(it->first); 235 /* The discovered terminus has the same MCTP Info */ 236 if (terminusMctpInfo && 237 (std::get<0>(terminusMctpInfo.value()) == 238 std::get<0>(mctpInfo)) && 239 (std::get<3>(terminusMctpInfo.value()) == 240 std::get<3>(mctpInfo))) 241 { 242 co_return PLDM_SUCCESS; 243 } 244 else 245 { 246 /* ToDo: 247 * Maybe the terminus supports multiple medium interfaces 248 * Or the TID is used by other terminus. 249 * Check the UUID to confirm. 250 */ 251 isMapped = false; 252 } 253 } 254 /* Use the terminus TID for mapping */ 255 else 256 { 257 auto mappedTid = storeTerminusInfo(mctpInfo, tid); 258 if (!mappedTid) 259 { 260 lg2::error("Failed to store Terminus Info for terminus {TID}.", 261 "TID", tid); 262 co_return PLDM_ERROR; 263 } 264 isMapped = true; 265 } 266 } 267 268 if (!isMapped) 269 { 270 // Assigning a tid. If it has been mapped, mapTid() 271 // returns the tid assigned before. 272 auto mappedTid = mapTid(mctpInfo); 273 if (!mappedTid) 274 { 275 lg2::error("Failed to store Terminus Info for terminus {TID}.", 276 "TID", tid); 277 co_return PLDM_ERROR; 278 } 279 280 tid = mappedTid.value(); 281 rc = co_await setTidOverMctp(eid, tid); 282 if (rc != PLDM_SUCCESS) 283 { 284 lg2::error("Failed to Set terminus TID, error{ERROR}.", "ERROR", 285 rc); 286 unmapTid(tid); 287 co_return rc; 288 } 289 290 if (rc != PLDM_SUCCESS && rc != PLDM_ERROR_UNSUPPORTED_PLDM_CMD) 291 { 292 lg2::error("Terminus {TID} does not support SetTID command.", "TID", 293 tid); 294 unmapTid(tid); 295 co_return rc; 296 } 297 298 if (termini.contains(tid)) 299 { 300 // the terminus has been discovered before 301 co_return PLDM_SUCCESS; 302 } 303 } 304 /* Discovery the mapped terminus */ 305 uint64_t supportedTypes = 0; 306 rc = co_await getPLDMTypes(tid, supportedTypes); 307 if (rc) 308 { 309 lg2::error("Failed to Get PLDM Types for terminus {TID}, error {ERROR}", 310 "TID", tid, "ERROR", rc); 311 co_return PLDM_ERROR; 312 } 313 314 try 315 { 316 termini[tid] = std::make_shared<Terminus>(tid, supportedTypes); 317 } 318 catch (const sdbusplus::exception_t& e) 319 { 320 lg2::error("Failed to create terminus manager for terminus {TID}", 321 "TID", tid); 322 co_return PLDM_ERROR; 323 } 324 325 uint8_t type = PLDM_BASE; 326 auto size = PLDM_MAX_TYPES * (PLDM_MAX_CMDS_PER_TYPE / 8); 327 std::vector<uint8_t> pldmCmds(size); 328 while ((type < PLDM_MAX_TYPES)) 329 { 330 if (!termini[tid]->doesSupportType(type)) 331 { 332 type++; 333 continue; 334 } 335 std::vector<bitfield8_t> cmds(PLDM_MAX_CMDS_PER_TYPE / 8); 336 auto rc = co_await getPLDMCommands(tid, type, cmds.data()); 337 if (rc) 338 { 339 lg2::error( 340 "Failed to Get PLDM Commands for terminus {TID}, error {ERROR}", 341 "TID", tid, "ERROR", rc); 342 } 343 344 for (size_t i = 0; i < cmds.size(); i++) 345 { 346 auto idx = type * (PLDM_MAX_CMDS_PER_TYPE / 8) + i; 347 if (idx >= pldmCmds.size()) 348 { 349 lg2::error( 350 "Calculated index {IDX} out of bounds for pldmCmds, type {TYPE}, command index {CMD_IDX}", 351 "IDX", idx, "TYPE", type, "CMD_IDX", i); 352 continue; 353 } 354 pldmCmds[idx] = cmds[i].byte; 355 } 356 type++; 357 } 358 termini[tid]->setSupportedCommands(pldmCmds); 359 360 co_return PLDM_SUCCESS; 361 } 362 363 exec::task<int> TerminusManager::sendRecvPldmMsgOverMctp( 364 mctp_eid_t eid, Request& request, const pldm_msg** responseMsg, 365 size_t* responseLen) 366 { 367 int rc = 0; 368 try 369 { 370 std::tie(rc, *responseMsg, *responseLen) = 371 co_await handler.sendRecvMsg(eid, std::move(request)); 372 } 373 catch (const sdbusplus::exception_t& e) 374 { 375 lg2::error( 376 "Send and Receive PLDM message over MCTP throw error - {ERROR}.", 377 "ERROR", e); 378 co_return PLDM_ERROR; 379 } 380 catch (const int& e) 381 { 382 lg2::error( 383 "Send and Receive PLDM message over MCTP throw int error - {ERROR}.", 384 "ERROR", e); 385 co_return PLDM_ERROR; 386 } 387 388 co_return rc; 389 } 390 391 exec::task<int> TerminusManager::getTidOverMctp(mctp_eid_t eid, pldm_tid_t* tid) 392 { 393 auto instanceId = instanceIdDb.next(eid); 394 Request request(sizeof(pldm_msg_hdr)); 395 auto requestMsg = reinterpret_cast<pldm_msg*>(request.data()); 396 auto rc = encode_get_tid_req(instanceId, requestMsg); 397 if (rc) 398 { 399 instanceIdDb.free(eid, instanceId); 400 lg2::error( 401 "Failed to encode request GetTID for endpoint ID {EID}, error {RC} ", 402 "EID", eid, "RC", rc); 403 co_return rc; 404 } 405 406 const pldm_msg* responseMsg = nullptr; 407 size_t responseLen = 0; 408 rc = co_await sendRecvPldmMsgOverMctp(eid, request, &responseMsg, 409 &responseLen); 410 if (rc) 411 { 412 lg2::error("Failed to send GetTID for Endpoint {EID}, error {RC}", 413 "EID", eid, "RC", rc); 414 co_return rc; 415 } 416 417 uint8_t completionCode = 0; 418 rc = decode_get_tid_resp(responseMsg, responseLen, &completionCode, tid); 419 if (rc) 420 { 421 lg2::error( 422 "Failed to decode response GetTID for Endpoint ID {EID}, error {RC} ", 423 "EID", eid, "RC", rc); 424 co_return rc; 425 } 426 427 if (completionCode != PLDM_SUCCESS) 428 { 429 lg2::error("Error : GetTID for Endpoint ID {EID}, complete code {CC}.", 430 "EID", eid, "CC", completionCode); 431 co_return rc; 432 } 433 434 co_return completionCode; 435 } 436 437 exec::task<int> TerminusManager::setTidOverMctp(mctp_eid_t eid, pldm_tid_t tid) 438 { 439 auto instanceId = instanceIdDb.next(eid); 440 Request request(sizeof(pldm_msg_hdr) + sizeof(pldm_set_tid_req)); 441 auto requestMsg = reinterpret_cast<pldm_msg*>(request.data()); 442 auto rc = encode_set_tid_req(instanceId, tid, requestMsg); 443 if (rc) 444 { 445 instanceIdDb.free(eid, instanceId); 446 lg2::error( 447 "Failed to encode request SetTID for endpoint ID {EID}, error {RC} ", 448 "EID", eid, "RC", rc); 449 co_return rc; 450 } 451 452 const pldm_msg* responseMsg = nullptr; 453 size_t responseLen = 0; 454 rc = co_await sendRecvPldmMsgOverMctp(eid, request, &responseMsg, 455 &responseLen); 456 if (rc) 457 { 458 lg2::error("Failed to send SetTID for Endpoint {EID}, error {RC}", 459 "EID", eid, "RC", rc); 460 co_return rc; 461 } 462 463 if (responseMsg == NULL || responseLen != PLDM_SET_TID_RESP_BYTES) 464 { 465 lg2::error( 466 "Failed to decode response SetTID for Endpoint ID {EID}, error {RC} ", 467 "EID", eid, "RC", rc); 468 co_return PLDM_ERROR_INVALID_LENGTH; 469 } 470 471 co_return responseMsg->payload[0]; 472 } 473 474 exec::task<int> 475 TerminusManager::getPLDMTypes(pldm_tid_t tid, uint64_t& supportedTypes) 476 { 477 Request request(sizeof(pldm_msg_hdr)); 478 auto requestMsg = reinterpret_cast<pldm_msg*>(request.data()); 479 auto rc = encode_get_types_req(0, requestMsg); 480 if (rc) 481 { 482 lg2::error( 483 "Failed to encode request getPLDMTypes for terminus ID {TID}, error {RC} ", 484 "TID", tid, "RC", rc); 485 co_return rc; 486 } 487 488 const pldm_msg* responseMsg = nullptr; 489 size_t responseLen = 0; 490 491 rc = co_await sendRecvPldmMsg(tid, request, &responseMsg, &responseLen); 492 if (rc) 493 { 494 lg2::error("Failed to send GetPLDMTypes for terminus {TID}, error {RC}", 495 "TID", tid, "RC", rc); 496 co_return rc; 497 } 498 499 uint8_t completionCode = 0; 500 bitfield8_t* types = reinterpret_cast<bitfield8_t*>(&supportedTypes); 501 rc = 502 decode_get_types_resp(responseMsg, responseLen, &completionCode, types); 503 if (rc) 504 { 505 lg2::error( 506 "Failed to decode response GetPLDMTypes for terminus ID {TID}, error {RC} ", 507 "TID", tid, "RC", rc); 508 co_return rc; 509 } 510 511 if (completionCode != PLDM_SUCCESS) 512 { 513 lg2::error( 514 "Error : GetPLDMTypes for terminus ID {TID}, complete code {CC}.", 515 "TID", tid, "CC", completionCode); 516 co_return rc; 517 } 518 co_return completionCode; 519 } 520 521 exec::task<int> TerminusManager::getPLDMCommands(pldm_tid_t tid, uint8_t type, 522 bitfield8_t* supportedCmds) 523 { 524 Request request(sizeof(pldm_msg_hdr) + PLDM_GET_COMMANDS_REQ_BYTES); 525 auto requestMsg = reinterpret_cast<pldm_msg*>(request.data()); 526 ver32_t version{0xFF, 0xFF, 0xFF, 0xFF}; 527 528 auto rc = encode_get_commands_req(0, type, version, requestMsg); 529 if (rc) 530 { 531 lg2::error( 532 "Failed to encode request GetPLDMCommands for terminus ID {TID}, error {RC} ", 533 "TID", tid, "RC", rc); 534 co_return rc; 535 } 536 537 const pldm_msg* responseMsg = nullptr; 538 size_t responseLen = 0; 539 540 rc = co_await sendRecvPldmMsg(tid, request, &responseMsg, &responseLen); 541 if (rc) 542 { 543 lg2::error( 544 "Failed to send GetPLDMCommands message for terminus {TID}, error {RC}", 545 "TID", tid, "RC", rc); 546 co_return rc; 547 } 548 549 /* Process response */ 550 uint8_t completionCode = 0; 551 rc = decode_get_commands_resp(responseMsg, responseLen, &completionCode, 552 supportedCmds); 553 if (rc) 554 { 555 lg2::error( 556 "Failed to decode response GetPLDMCommands for terminus ID {TID}, error {RC} ", 557 "TID", tid, "RC", rc); 558 co_return rc; 559 } 560 561 if (completionCode != PLDM_SUCCESS) 562 { 563 lg2::error( 564 "Error : GetPLDMCommands for terminus ID {TID}, complete code {CC}.", 565 "TID", tid, "CC", completionCode); 566 co_return rc; 567 } 568 569 co_return completionCode; 570 } 571 572 exec::task<int> TerminusManager::sendRecvPldmMsg( 573 pldm_tid_t tid, Request& request, const pldm_msg** responseMsg, 574 size_t* responseLen) 575 { 576 /** 577 * Size of tidPool is `std::numeric_limits<pldm_tid_t>::max() + 1` 578 * tidPool[i] always exist 579 */ 580 if (!tidPool[tid]) 581 { 582 co_return PLDM_ERROR_NOT_READY; 583 } 584 585 if (!transportLayerTable.contains(tid)) 586 { 587 co_return PLDM_ERROR_NOT_READY; 588 } 589 590 if (transportLayerTable[tid] != SupportedTransportLayer::MCTP) 591 { 592 co_return PLDM_ERROR_NOT_READY; 593 } 594 595 auto mctpInfo = toMctpInfo(tid); 596 if (!mctpInfo.has_value()) 597 { 598 co_return PLDM_ERROR_NOT_READY; 599 } 600 601 auto eid = std::get<0>(mctpInfo.value()); 602 auto requestMsg = reinterpret_cast<pldm_msg*>(request.data()); 603 requestMsg->hdr.instance_id = instanceIdDb.next(eid); 604 auto rc = co_await sendRecvPldmMsgOverMctp(eid, request, responseMsg, 605 responseLen); 606 607 co_return rc; 608 } 609 610 } // namespace platform_mc 611 } // namespace pldm 612