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
55 std::optional<pldm_tid_t>
storeTerminusInfo(const MctpInfo & mctpInfo,pldm_tid_t tid)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
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
147 TerminiMapper::iterator
findTerminusPtr(const MctpInfo & mctpInfo)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
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 == NULL || 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
504 exec::task<int>
getPLDMTypes(pldm_tid_t tid,uint64_t & supportedTypes)505 TerminusManager::getPLDMTypes(pldm_tid_t tid, 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