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