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 toMctpInfo(const pldm_tid_t & tid)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 toTid(const MctpInfo & mctpInfo) const36 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 storeTerminusInfo(const MctpInfo & mctpInfo,pldm_tid_t tid)55 std::optional<pldm_tid_t> TerminusManager::storeTerminusInfo( 56 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 mapTid(const MctpInfo & mctpInfo)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 unmapTid(const pldm_tid_t & tid)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 discoverMctpTerminus(const MctpInfos & mctpInfos)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 findTerminusPtr(const MctpInfo & mctpInfo)147 TerminiMapper::iterator TerminusManager::findTerminusPtr( 148 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 discoverMctpTerminusTask()163 exec::task<int> TerminusManager::discoverMctpTerminusTask() 164 { 165 std::vector<pldm_tid_t> addedTids; 166 while (!queuedMctpInfos.empty()) 167 { 168 if (manager) 169 { 170 co_await manager->beforeDiscoverTerminus(); 171 } 172 173 const MctpInfos& mctpInfos = queuedMctpInfos.front(); 174 for (const auto& mctpInfo : mctpInfos) 175 { 176 auto it = findTerminusPtr(mctpInfo); 177 if (it == termini.end()) 178 { 179 co_await initMctpTerminus(mctpInfo); 180 } 181 182 /* Get TID of initialized terminus */ 183 auto tid = toTid(mctpInfo); 184 if (!tid) 185 { 186 co_return PLDM_ERROR; 187 } 188 addedTids.push_back(tid.value()); 189 } 190 191 if (manager) 192 { 193 co_await manager->afterDiscoverTerminus(); 194 for (const auto& tid : addedTids) 195 { 196 manager->startSensorPolling(tid); 197 } 198 } 199 200 queuedMctpInfos.pop(); 201 } 202 203 co_return PLDM_SUCCESS; 204 } 205 removeMctpTerminus(const MctpInfos & mctpInfos)206 void TerminusManager::removeMctpTerminus(const MctpInfos& mctpInfos) 207 { 208 // remove terminus 209 for (const auto& mctpInfo : mctpInfos) 210 { 211 auto it = findTerminusPtr(mctpInfo); 212 if (it == termini.end()) 213 { 214 continue; 215 } 216 217 if (manager) 218 { 219 manager->stopSensorPolling(it->second->getTid()); 220 } 221 222 unmapTid(it->first); 223 termini.erase(it); 224 } 225 } 226 initMctpTerminus(const MctpInfo & mctpInfo)227 exec::task<int> TerminusManager::initMctpTerminus(const MctpInfo& mctpInfo) 228 { 229 mctp_eid_t eid = std::get<0>(mctpInfo); 230 pldm_tid_t tid = 0; 231 bool isMapped = false; 232 auto rc = co_await getTidOverMctp(eid, &tid); 233 if (rc != PLDM_SUCCESS) 234 { 235 lg2::error("Failed to Get Terminus ID, error {ERROR}.", "ERROR", rc); 236 co_return PLDM_ERROR; 237 } 238 239 if (tid == PLDM_TID_RESERVED) 240 { 241 lg2::error("Terminus responses the reserved {TID}.", "TID", tid); 242 co_return PLDM_ERROR; 243 } 244 245 /* Terminus already has TID */ 246 if (tid != PLDM_TID_UNASSIGNED) 247 { 248 /* TID is used by one discovered terminus */ 249 auto it = termini.find(tid); 250 if (it != termini.end()) 251 { 252 auto terminusMctpInfo = toMctpInfo(it->first); 253 /* The discovered terminus has the same MCTP Info */ 254 if (terminusMctpInfo && 255 (std::get<0>(terminusMctpInfo.value()) == 256 std::get<0>(mctpInfo)) && 257 (std::get<3>(terminusMctpInfo.value()) == 258 std::get<3>(mctpInfo))) 259 { 260 co_return PLDM_SUCCESS; 261 } 262 else 263 { 264 /* ToDo: 265 * Maybe the terminus supports multiple medium interfaces 266 * Or the TID is used by other terminus. 267 * Check the UUID to confirm. 268 */ 269 isMapped = false; 270 } 271 } 272 /* Use the terminus TID for mapping */ 273 else 274 { 275 auto mappedTid = storeTerminusInfo(mctpInfo, tid); 276 if (!mappedTid) 277 { 278 lg2::error("Failed to store Terminus Info for terminus {TID}.", 279 "TID", tid); 280 co_return PLDM_ERROR; 281 } 282 isMapped = true; 283 } 284 } 285 286 if (!isMapped) 287 { 288 // Assigning a tid. If it has been mapped, mapTid() 289 // returns the tid assigned before. 290 auto mappedTid = mapTid(mctpInfo); 291 if (!mappedTid) 292 { 293 lg2::error("Failed to store Terminus Info for terminus {TID}.", 294 "TID", tid); 295 co_return PLDM_ERROR; 296 } 297 298 tid = mappedTid.value(); 299 rc = co_await setTidOverMctp(eid, tid); 300 if (rc != PLDM_SUCCESS) 301 { 302 lg2::error("Failed to Set terminus TID, error{ERROR}.", "ERROR", 303 rc); 304 unmapTid(tid); 305 co_return rc; 306 } 307 308 if (rc != PLDM_SUCCESS && rc != PLDM_ERROR_UNSUPPORTED_PLDM_CMD) 309 { 310 lg2::error("Terminus {TID} does not support SetTID command.", "TID", 311 tid); 312 unmapTid(tid); 313 co_return rc; 314 } 315 316 if (termini.contains(tid)) 317 { 318 // the terminus has been discovered before 319 co_return PLDM_SUCCESS; 320 } 321 } 322 /* Discovery the mapped terminus */ 323 uint64_t supportedTypes = 0; 324 rc = co_await getPLDMTypes(tid, supportedTypes); 325 if (rc) 326 { 327 lg2::error("Failed to Get PLDM Types for terminus {TID}, error {ERROR}", 328 "TID", tid, "ERROR", rc); 329 unmapTid(tid); 330 co_return PLDM_ERROR; 331 } 332 333 try 334 { 335 termini[tid] = std::make_shared<Terminus>(tid, supportedTypes); 336 } 337 catch (const sdbusplus::exception_t& e) 338 { 339 lg2::error("Failed to create terminus manager for terminus {TID}", 340 "TID", tid); 341 unmapTid(tid); 342 co_return PLDM_ERROR; 343 } 344 345 uint8_t type = PLDM_BASE; 346 auto size = PLDM_MAX_TYPES * (PLDM_MAX_CMDS_PER_TYPE / 8); 347 std::vector<uint8_t> pldmCmds(size); 348 while ((type < PLDM_MAX_TYPES)) 349 { 350 if (!termini[tid]->doesSupportType(type)) 351 { 352 type++; 353 continue; 354 } 355 356 ver32_t version{0xFF, 0xFF, 0xFF, 0xFF}; 357 auto rc = co_await getPLDMVersion(tid, type, &version); 358 if (rc) 359 { 360 lg2::error( 361 "Failed to Get PLDM Version for terminus {TID}, PLDM Type {TYPE}, error {ERROR}", 362 "TID", tid, "TYPE", type, "ERROR", rc); 363 } 364 termini[tid]->setSupportedTypeVersions(type, version); 365 std::vector<bitfield8_t> cmds(PLDM_MAX_CMDS_PER_TYPE / 8); 366 rc = co_await getPLDMCommands(tid, type, version, cmds.data()); 367 if (rc) 368 { 369 lg2::error( 370 "Failed to Get PLDM Commands for terminus {TID}, error {ERROR}", 371 "TID", tid, "ERROR", rc); 372 } 373 374 for (size_t i = 0; i < cmds.size(); i++) 375 { 376 auto idx = type * (PLDM_MAX_CMDS_PER_TYPE / 8) + i; 377 if (idx >= pldmCmds.size()) 378 { 379 lg2::error( 380 "Calculated index {IDX} out of bounds for pldmCmds, type {TYPE}, command index {CMD_IDX}", 381 "IDX", idx, "TYPE", type, "CMD_IDX", i); 382 continue; 383 } 384 pldmCmds[idx] = cmds[i].byte; 385 } 386 type++; 387 } 388 termini[tid]->setSupportedCommands(pldmCmds); 389 390 co_return PLDM_SUCCESS; 391 } 392 sendRecvPldmMsgOverMctp(mctp_eid_t eid,Request & request,const pldm_msg ** responseMsg,size_t * responseLen)393 exec::task<int> TerminusManager::sendRecvPldmMsgOverMctp( 394 mctp_eid_t eid, Request& request, const pldm_msg** responseMsg, 395 size_t* responseLen) 396 { 397 int rc = 0; 398 try 399 { 400 std::tie(rc, *responseMsg, *responseLen) = 401 co_await handler.sendRecvMsg(eid, std::move(request)); 402 } 403 catch (const sdbusplus::exception_t& e) 404 { 405 lg2::error( 406 "Send and Receive PLDM message over MCTP throw error - {ERROR}.", 407 "ERROR", e); 408 co_return PLDM_ERROR; 409 } 410 catch (const int& e) 411 { 412 lg2::error( 413 "Send and Receive PLDM message over MCTP throw int error - {ERROR}.", 414 "ERROR", e); 415 co_return PLDM_ERROR; 416 } 417 418 co_return rc; 419 } 420 getTidOverMctp(mctp_eid_t eid,pldm_tid_t * tid)421 exec::task<int> TerminusManager::getTidOverMctp(mctp_eid_t eid, pldm_tid_t* tid) 422 { 423 auto instanceId = instanceIdDb.next(eid); 424 Request request(sizeof(pldm_msg_hdr)); 425 auto requestMsg = reinterpret_cast<pldm_msg*>(request.data()); 426 auto rc = encode_get_tid_req(instanceId, requestMsg); 427 if (rc) 428 { 429 instanceIdDb.free(eid, instanceId); 430 lg2::error( 431 "Failed to encode request GetTID for endpoint ID {EID}, error {RC} ", 432 "EID", eid, "RC", rc); 433 co_return rc; 434 } 435 436 const pldm_msg* responseMsg = nullptr; 437 size_t responseLen = 0; 438 rc = co_await sendRecvPldmMsgOverMctp(eid, request, &responseMsg, 439 &responseLen); 440 if (rc) 441 { 442 lg2::error("Failed to send GetTID for Endpoint {EID}, error {RC}", 443 "EID", eid, "RC", rc); 444 co_return rc; 445 } 446 447 uint8_t completionCode = 0; 448 rc = decode_get_tid_resp(responseMsg, responseLen, &completionCode, tid); 449 if (rc) 450 { 451 lg2::error( 452 "Failed to decode response GetTID for Endpoint ID {EID}, error {RC} ", 453 "EID", eid, "RC", rc); 454 co_return rc; 455 } 456 457 if (completionCode != PLDM_SUCCESS) 458 { 459 lg2::error("Error : GetTID for Endpoint ID {EID}, complete code {CC}.", 460 "EID", eid, "CC", completionCode); 461 co_return rc; 462 } 463 464 co_return completionCode; 465 } 466 setTidOverMctp(mctp_eid_t eid,pldm_tid_t tid)467 exec::task<int> TerminusManager::setTidOverMctp(mctp_eid_t eid, pldm_tid_t tid) 468 { 469 auto instanceId = instanceIdDb.next(eid); 470 Request request(sizeof(pldm_msg_hdr) + sizeof(pldm_set_tid_req)); 471 auto requestMsg = reinterpret_cast<pldm_msg*>(request.data()); 472 auto rc = encode_set_tid_req(instanceId, tid, requestMsg); 473 if (rc) 474 { 475 instanceIdDb.free(eid, instanceId); 476 lg2::error( 477 "Failed to encode request SetTID for endpoint ID {EID}, error {RC} ", 478 "EID", eid, "RC", rc); 479 co_return rc; 480 } 481 482 const pldm_msg* responseMsg = nullptr; 483 size_t responseLen = 0; 484 rc = co_await sendRecvPldmMsgOverMctp(eid, request, &responseMsg, 485 &responseLen); 486 if (rc) 487 { 488 lg2::error("Failed to send SetTID for Endpoint {EID}, error {RC}", 489 "EID", eid, "RC", rc); 490 co_return rc; 491 } 492 493 if (responseMsg == nullptr || responseLen != PLDM_SET_TID_RESP_BYTES) 494 { 495 lg2::error( 496 "Failed to decode response SetTID for Endpoint ID {EID}, error {RC} ", 497 "EID", eid, "RC", rc); 498 co_return PLDM_ERROR_INVALID_LENGTH; 499 } 500 501 co_return responseMsg->payload[0]; 502 } 503 getPLDMTypes(pldm_tid_t tid,uint64_t & supportedTypes)504 exec::task<int> TerminusManager::getPLDMTypes(pldm_tid_t tid, 505 uint64_t& supportedTypes) 506 { 507 Request request(sizeof(pldm_msg_hdr)); 508 auto requestMsg = reinterpret_cast<pldm_msg*>(request.data()); 509 auto rc = encode_get_types_req(0, requestMsg); 510 if (rc) 511 { 512 lg2::error( 513 "Failed to encode request getPLDMTypes for terminus ID {TID}, error {RC} ", 514 "TID", tid, "RC", rc); 515 co_return rc; 516 } 517 518 const pldm_msg* responseMsg = nullptr; 519 size_t responseLen = 0; 520 521 rc = co_await sendRecvPldmMsg(tid, request, &responseMsg, &responseLen); 522 if (rc) 523 { 524 lg2::error("Failed to send GetPLDMTypes for terminus {TID}, error {RC}", 525 "TID", tid, "RC", rc); 526 co_return rc; 527 } 528 529 uint8_t completionCode = 0; 530 bitfield8_t* types = reinterpret_cast<bitfield8_t*>(&supportedTypes); 531 rc = 532 decode_get_types_resp(responseMsg, responseLen, &completionCode, types); 533 if (rc) 534 { 535 lg2::error( 536 "Failed to decode response GetPLDMTypes for terminus ID {TID}, error {RC} ", 537 "TID", tid, "RC", rc); 538 co_return rc; 539 } 540 541 if (completionCode != PLDM_SUCCESS) 542 { 543 lg2::error( 544 "Error : GetPLDMTypes for terminus ID {TID}, complete code {CC}.", 545 "TID", tid, "CC", completionCode); 546 co_return rc; 547 } 548 co_return completionCode; 549 } 550 getPLDMCommands(pldm_tid_t tid,uint8_t type,ver32_t version,bitfield8_t * supportedCmds)551 exec::task<int> TerminusManager::getPLDMCommands( 552 pldm_tid_t tid, uint8_t type, ver32_t version, bitfield8_t* supportedCmds) 553 { 554 Request request(sizeof(pldm_msg_hdr) + PLDM_GET_COMMANDS_REQ_BYTES); 555 auto requestMsg = reinterpret_cast<pldm_msg*>(request.data()); 556 557 auto rc = encode_get_commands_req(0, type, version, requestMsg); 558 if (rc) 559 { 560 lg2::error( 561 "Failed to encode request GetPLDMCommands for terminus ID {TID}, error {RC} ", 562 "TID", tid, "RC", rc); 563 co_return rc; 564 } 565 566 const pldm_msg* responseMsg = nullptr; 567 size_t responseLen = 0; 568 569 rc = co_await sendRecvPldmMsg(tid, request, &responseMsg, &responseLen); 570 if (rc) 571 { 572 lg2::error( 573 "Failed to send GetPLDMCommands message for terminus {TID}, error {RC}", 574 "TID", tid, "RC", rc); 575 co_return rc; 576 } 577 578 /* Process response */ 579 uint8_t completionCode = 0; 580 rc = decode_get_commands_resp(responseMsg, responseLen, &completionCode, 581 supportedCmds); 582 if (rc) 583 { 584 lg2::error( 585 "Failed to decode response GetPLDMCommands for terminus ID {TID}, error {RC} ", 586 "TID", tid, "RC", rc); 587 co_return rc; 588 } 589 590 if (completionCode != PLDM_SUCCESS) 591 { 592 lg2::error( 593 "Error : GetPLDMCommands for terminus ID {TID}, complete code {CC}.", 594 "TID", tid, "CC", completionCode); 595 co_return rc; 596 } 597 598 co_return completionCode; 599 } 600 sendRecvPldmMsg(pldm_tid_t tid,Request & request,const pldm_msg ** responseMsg,size_t * responseLen)601 exec::task<int> TerminusManager::sendRecvPldmMsg( 602 pldm_tid_t tid, Request& request, const pldm_msg** responseMsg, 603 size_t* responseLen) 604 { 605 /** 606 * Size of tidPool is `std::numeric_limits<pldm_tid_t>::max() + 1` 607 * tidPool[i] always exist 608 */ 609 if (!tidPool[tid]) 610 { 611 co_return PLDM_ERROR_NOT_READY; 612 } 613 614 if (!transportLayerTable.contains(tid)) 615 { 616 co_return PLDM_ERROR_NOT_READY; 617 } 618 619 if (transportLayerTable[tid] != SupportedTransportLayer::MCTP) 620 { 621 co_return PLDM_ERROR_NOT_READY; 622 } 623 624 auto mctpInfo = toMctpInfo(tid); 625 if (!mctpInfo.has_value()) 626 { 627 co_return PLDM_ERROR_NOT_READY; 628 } 629 630 auto eid = std::get<0>(mctpInfo.value()); 631 auto requestMsg = reinterpret_cast<pldm_msg*>(request.data()); 632 requestMsg->hdr.instance_id = instanceIdDb.next(eid); 633 auto rc = co_await sendRecvPldmMsgOverMctp(eid, request, responseMsg, 634 responseLen); 635 636 co_return rc; 637 } 638 getPLDMVersion(pldm_tid_t tid,uint8_t type,ver32_t * version)639 exec::task<int> TerminusManager::getPLDMVersion(pldm_tid_t tid, uint8_t type, 640 ver32_t* version) 641 { 642 Request request(sizeof(pldm_msg_hdr) + PLDM_GET_VERSION_REQ_BYTES); 643 auto requestMsg = new (request.data()) pldm_msg; 644 645 auto rc = 646 encode_get_version_req(0, 0, PLDM_GET_FIRSTPART, type, requestMsg); 647 if (rc) 648 { 649 lg2::error( 650 "Failed to encode request getPLDMVersion for terminus ID {TID}, error {RC} ", 651 "TID", tid, "RC", rc); 652 co_return rc; 653 } 654 655 const pldm_msg* responseMsg = nullptr; 656 size_t responseLen = 0; 657 658 rc = co_await sendRecvPldmMsg(tid, request, &responseMsg, &responseLen); 659 if (rc) 660 { 661 lg2::error( 662 "Failed to send getPLDMVersion message for terminus {TID}, error {RC}", 663 "TID", tid, "RC", rc); 664 co_return rc; 665 } 666 667 /* Process response */ 668 uint8_t completionCode = 0; 669 uint8_t transferFlag = 0; 670 uint32_t transferHandle = 0; 671 rc = decode_get_version_resp(responseMsg, responseLen, &completionCode, 672 &transferHandle, &transferFlag, version); 673 if (rc) 674 { 675 lg2::error( 676 "Failed to decode response getPLDMVersion for terminus ID {TID}, error {RC} ", 677 "TID", tid, "RC", rc); 678 co_return rc; 679 } 680 681 if (completionCode != PLDM_SUCCESS) 682 { 683 lg2::error( 684 "Error : getPLDMVersion for terminus ID {TID}, complete code {CC}.", 685 "TID", tid, "CC", completionCode); 686 co_return completionCode; 687 } 688 689 co_return completionCode; 690 } 691 692 } // namespace platform_mc 693 } // namespace pldm 694