1 #include "platform_manager.hpp"
2 
3 #include "terminus_manager.hpp"
4 
5 #include <phosphor-logging/lg2.hpp>
6 
7 #include <ranges>
8 
9 PHOSPHOR_LOG2_USING;
10 
11 namespace pldm
12 {
13 namespace platform_mc
14 {
15 
16 exec::task<int> PlatformManager::initTerminus()
17 {
18     for (auto& [tid, terminus] : termini)
19     {
20         if (terminus->initialized)
21         {
22             continue;
23         }
24         terminus->initialized = true;
25 
26         if (terminus->doesSupportCommand(PLDM_PLATFORM, PLDM_GET_PDR))
27         {
28             auto rc = co_await getPDRs(terminus);
29             if (rc)
30             {
31                 lg2::error(
32                     "Failed to fetch PDRs for terminus with TID: {TID}, error: {ERROR}",
33                     "TID", tid, "ERROR", rc);
34                 continue; // Continue to next terminus
35             }
36 
37             terminus->parseTerminusPDRs();
38         }
39 
40         uint16_t terminusMaxBufferSize = terminus->maxBufferSize;
41         if (!terminus->doesSupportCommand(PLDM_PLATFORM,
42                                           PLDM_EVENT_MESSAGE_BUFFER_SIZE))
43         {
44             terminusMaxBufferSize = PLDM_PLATFORM_DEFAULT_MESSAGE_BUFFER_SIZE;
45         }
46         else
47         {
48             /* Get maxBufferSize use PLDM command eventMessageBufferSize */
49             auto rc = co_await eventMessageBufferSize(
50                 tid, terminus->maxBufferSize, terminusMaxBufferSize);
51             if (rc != PLDM_SUCCESS)
52             {
53                 lg2::error(
54                     "Failed to get message buffer size for terminus with TID: {TID}, error: {ERROR}",
55                     "TID", tid, "ERROR", rc);
56                 terminusMaxBufferSize =
57                     PLDM_PLATFORM_DEFAULT_MESSAGE_BUFFER_SIZE;
58             }
59         }
60         terminus->maxBufferSize =
61             std::min(terminus->maxBufferSize, terminusMaxBufferSize);
62 
63         auto rc = co_await configEventReceiver(tid);
64         if (rc)
65         {
66             lg2::error(
67                 "Failed to config event receiver for terminus with TID: {TID}, error: {ERROR}",
68                 "TID", tid, "ERROR", rc);
69         }
70     }
71 
72     co_return PLDM_SUCCESS;
73 }
74 
75 exec::task<int> PlatformManager::configEventReceiver(pldm_tid_t tid)
76 {
77     if (!termini.contains(tid))
78     {
79         co_return PLDM_ERROR;
80     }
81 
82     auto& terminus = termini[tid];
83     if (!terminus->doesSupportCommand(PLDM_PLATFORM,
84                                       PLDM_EVENT_MESSAGE_SUPPORTED))
85     {
86         terminus->synchronyConfigurationSupported.byte = 0;
87     }
88     else
89     {
90         /**
91          *  Get synchronyConfigurationSupported use PLDM command
92          *  eventMessageBufferSize
93          */
94         uint8_t synchronyConfiguration = 0;
95         uint8_t numberEventClassReturned = 0;
96         std::vector<uint8_t> eventClass{};
97         auto rc = co_await eventMessageSupported(
98             tid, 1, synchronyConfiguration,
99             terminus->synchronyConfigurationSupported, numberEventClassReturned,
100             eventClass);
101         if (rc != PLDM_SUCCESS)
102         {
103             lg2::error(
104                 "Failed to get event message supported for terminus with TID: {TID}, error: {ERROR}",
105                 "TID", tid, "ERROR", rc);
106             terminus->synchronyConfigurationSupported.byte = 0;
107         }
108     }
109 
110     if (!terminus->doesSupportCommand(PLDM_PLATFORM, PLDM_SET_EVENT_RECEIVER))
111     {
112         lg2::error("Terminus {TID} does not support Event", "TID", tid);
113         co_return PLDM_ERROR;
114     }
115 
116     /**
117      *  Set Event receiver base on synchronyConfigurationSupported data
118      *  use PLDM command SetEventReceiver
119      */
120     pldm_event_message_global_enable eventMessageGlobalEnable =
121         PLDM_EVENT_MESSAGE_GLOBAL_DISABLE;
122     uint16_t heartbeatTimer = 0;
123 
124     /* Use PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC_KEEP_ALIVE when
125      * for eventMessageGlobalEnable when the terminus supports that type
126      */
127     if (terminus->synchronyConfigurationSupported.byte &
128         (1 << PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC_KEEP_ALIVE))
129     {
130         heartbeatTimer = HEARTBEAT_TIMEOUT;
131         eventMessageGlobalEnable =
132             PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC_KEEP_ALIVE;
133     }
134     /* Use PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC when
135      * for eventMessageGlobalEnable when the terminus does not support
136      * PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC_KEEP_ALIVE
137      * and supports PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC type
138      */
139     else if (terminus->synchronyConfigurationSupported.byte &
140              (1 << PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC))
141     {
142         eventMessageGlobalEnable = PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC;
143     }
144     /* Only use PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_POLLING
145      * for eventMessageGlobalEnable when the terminus only supports
146      * this type
147      */
148     else if (terminus->synchronyConfigurationSupported.byte &
149              (1 << PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_POLLING))
150     {
151         eventMessageGlobalEnable = PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_POLLING;
152     }
153 
154     if (eventMessageGlobalEnable != PLDM_EVENT_MESSAGE_GLOBAL_DISABLE)
155     {
156         auto rc = co_await setEventReceiver(tid, eventMessageGlobalEnable,
157                                             PLDM_TRANSPORT_PROTOCOL_TYPE_MCTP,
158                                             heartbeatTimer);
159         if (rc != PLDM_SUCCESS)
160         {
161             lg2::error(
162                 "Failed to set event receiver for terminus with TID: {TID}, error: {ERROR}",
163                 "TID", tid, "ERROR", rc);
164         }
165     }
166 
167     co_return PLDM_SUCCESS;
168 }
169 
170 exec::task<int> PlatformManager::getPDRs(std::shared_ptr<Terminus> terminus)
171 {
172     pldm_tid_t tid = terminus->getTid();
173 
174     /* Setting default values when getPDRRepositoryInfo fails or does not
175      * support */
176     uint8_t repositoryState = PLDM_AVAILABLE;
177     uint32_t recordCount = std::numeric_limits<uint32_t>::max();
178     uint32_t repositorySize = 0;
179     uint32_t largestRecordSize = std::numeric_limits<uint32_t>::max();
180     if (terminus->doesSupportCommand(PLDM_PLATFORM,
181                                      PLDM_GET_PDR_REPOSITORY_INFO))
182     {
183         auto rc =
184             co_await getPDRRepositoryInfo(tid, repositoryState, recordCount,
185                                           repositorySize, largestRecordSize);
186         if (rc)
187         {
188             lg2::error(
189                 "Failed to get PDR Repository Info for terminus with TID: {TID}, error: {ERROR}",
190                 "TID", tid, "ERROR", rc);
191         }
192         else
193         {
194             recordCount =
195                 std::min(recordCount + 1, std::numeric_limits<uint32_t>::max());
196             largestRecordSize = std::min(largestRecordSize + 1,
197                                          std::numeric_limits<uint32_t>::max());
198         }
199     }
200 
201     if (repositoryState != PLDM_AVAILABLE)
202     {
203         co_return PLDM_ERROR_NOT_READY;
204     }
205 
206     uint32_t recordHndl = 0;
207     uint32_t nextRecordHndl = 0;
208     uint32_t nextDataTransferHndl = 0;
209     uint8_t transferFlag = 0;
210     uint16_t responseCnt = 0;
211     constexpr uint16_t recvBufSize = PLDM_PLATFORM_GETPDR_MAX_RECORD_BYTES;
212     std::vector<uint8_t> recvBuf(recvBufSize);
213     uint8_t transferCrc = 0;
214 
215     terminus->pdrs.clear();
216     uint32_t receivedRecordCount = 0;
217 
218     do
219     {
220         auto rc =
221             co_await getPDR(tid, recordHndl, 0, PLDM_GET_FIRSTPART, recvBufSize,
222                             0, nextRecordHndl, nextDataTransferHndl,
223                             transferFlag, responseCnt, recvBuf, transferCrc);
224 
225         if (rc)
226         {
227             lg2::error(
228                 "Failed to get PDRs for terminus {TID}, error: {RC}, first part of record handle {RECORD}",
229                 "TID", tid, "RC", rc, "RECORD", recordHndl);
230             terminus->pdrs.clear();
231             co_return rc;
232         }
233 
234         if (transferFlag == PLDM_PLATFORM_TRANSFER_START_AND_END)
235         {
236             // single-part
237             terminus->pdrs.emplace_back(std::vector<uint8_t>(
238                 recvBuf.begin(), recvBuf.begin() + responseCnt));
239             recordHndl = nextRecordHndl;
240         }
241         else
242         {
243             // multipart transfer
244             uint32_t receivedRecordSize = responseCnt;
245             auto pdrHdr = reinterpret_cast<pldm_pdr_hdr*>(recvBuf.data());
246             uint16_t recordChgNum = le16toh(pdrHdr->record_change_num);
247             std::vector<uint8_t> receivedPdr(recvBuf.begin(),
248                                              recvBuf.begin() + responseCnt);
249             do
250             {
251                 rc = co_await getPDR(
252                     tid, recordHndl, nextDataTransferHndl, PLDM_GET_NEXTPART,
253                     recvBufSize, recordChgNum, nextRecordHndl,
254                     nextDataTransferHndl, transferFlag, responseCnt, recvBuf,
255                     transferCrc);
256                 if (rc)
257                 {
258                     lg2::error(
259                         "Failed to get PDRs for terminus {TID}, error: {RC}, get middle part of record handle {RECORD}",
260                         "TID", tid, "RC", rc, "RECORD", recordHndl);
261                     terminus->pdrs.clear();
262                     co_return rc;
263                 }
264 
265                 receivedPdr.insert(receivedPdr.end(), recvBuf.begin(),
266                                    recvBuf.begin() + responseCnt);
267                 receivedRecordSize += responseCnt;
268 
269                 if (transferFlag == PLDM_PLATFORM_TRANSFER_END)
270                 {
271                     terminus->pdrs.emplace_back(std::move(receivedPdr));
272                     recordHndl = nextRecordHndl;
273                 }
274             } while (nextDataTransferHndl != 0 &&
275                      receivedRecordSize < largestRecordSize);
276         }
277         receivedRecordCount++;
278     } while (nextRecordHndl != 0 && receivedRecordCount < recordCount);
279 
280     co_return PLDM_SUCCESS;
281 }
282 
283 exec::task<int> PlatformManager::getPDR(
284     const pldm_tid_t tid, const uint32_t recordHndl,
285     const uint32_t dataTransferHndl, const uint8_t transferOpFlag,
286     const uint16_t requestCnt, const uint16_t recordChgNum,
287     uint32_t& nextRecordHndl, uint32_t& nextDataTransferHndl,
288     uint8_t& transferFlag, uint16_t& responseCnt,
289     std::vector<uint8_t>& recordData, uint8_t& transferCrc)
290 {
291     Request request(sizeof(pldm_msg_hdr) + PLDM_GET_PDR_REQ_BYTES);
292     auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
293     auto rc = encode_get_pdr_req(0, recordHndl, dataTransferHndl,
294                                  transferOpFlag, requestCnt, recordChgNum,
295                                  requestMsg, PLDM_GET_PDR_REQ_BYTES);
296     if (rc)
297     {
298         lg2::error(
299             "Failed to encode request GetPDR for terminus ID {TID}, error {RC} ",
300             "TID", tid, "RC", rc);
301         co_return rc;
302     }
303 
304     const pldm_msg* responseMsg = nullptr;
305     size_t responseLen = 0;
306     rc = co_await terminusManager.sendRecvPldmMsg(tid, request, &responseMsg,
307                                                   &responseLen);
308     if (rc)
309     {
310         lg2::error(
311             "Failed to send GetPDR message for terminus {TID}, error {RC}",
312             "TID", tid, "RC", rc);
313         co_return rc;
314     }
315 
316     uint8_t completionCode;
317     rc = decode_get_pdr_resp(responseMsg, responseLen, &completionCode,
318                              &nextRecordHndl, &nextDataTransferHndl,
319                              &transferFlag, &responseCnt, recordData.data(),
320                              recordData.size(), &transferCrc);
321     if (rc)
322     {
323         lg2::error(
324             "Failed to decode response GetPDR for terminus ID {TID}, error {RC} ",
325             "TID", tid, "RC", rc);
326         co_return rc;
327     }
328 
329     if (completionCode != PLDM_SUCCESS)
330     {
331         lg2::error("Error : GetPDR for terminus ID {TID}, complete code {CC}.",
332                    "TID", tid, "CC", completionCode);
333         co_return rc;
334     }
335 
336     co_return completionCode;
337 }
338 
339 exec::task<int> PlatformManager::getPDRRepositoryInfo(
340     const pldm_tid_t tid, uint8_t& repositoryState, uint32_t& recordCount,
341     uint32_t& repositorySize, uint32_t& largestRecordSize)
342 {
343     Request request(sizeof(pldm_msg_hdr) + sizeof(uint8_t));
344     auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
345     auto rc = encode_pldm_header_only(PLDM_REQUEST, 0, PLDM_PLATFORM,
346                                       PLDM_GET_PDR_REPOSITORY_INFO, requestMsg);
347     if (rc)
348     {
349         lg2::error(
350             "Failed to encode request GetPDRRepositoryInfo for terminus ID {TID}, error {RC} ",
351             "TID", tid, "RC", rc);
352         co_return rc;
353     }
354 
355     const pldm_msg* responseMsg = nullptr;
356     size_t responseLen = 0;
357     rc = co_await terminusManager.sendRecvPldmMsg(tid, request, &responseMsg,
358                                                   &responseLen);
359     if (rc)
360     {
361         lg2::error(
362             "Failed to send GetPDRRepositoryInfo message for terminus {TID}, error {RC}",
363             "TID", tid, "RC", rc);
364         co_return rc;
365     }
366 
367     uint8_t completionCode = 0;
368     std::array<uint8_t, PLDM_TIMESTAMP104_SIZE> updateTime = {};
369     std::array<uint8_t, PLDM_TIMESTAMP104_SIZE> oemUpdateTime = {};
370     uint8_t dataTransferHandleTimeout = 0;
371 
372     rc = decode_get_pdr_repository_info_resp(
373         responseMsg, responseLen, &completionCode, &repositoryState,
374         updateTime.data(), oemUpdateTime.data(), &recordCount, &repositorySize,
375         &largestRecordSize, &dataTransferHandleTimeout);
376     if (rc)
377     {
378         lg2::error(
379             "Failed to decode response GetPDRRepositoryInfo for terminus ID {TID}, error {RC} ",
380             "TID", tid, "RC", rc);
381         co_return rc;
382     }
383 
384     if (completionCode != PLDM_SUCCESS)
385     {
386         lg2::error(
387             "Error : GetPDRRepositoryInfo for terminus ID {TID}, complete code {CC}.",
388             "TID", tid, "CC", completionCode);
389         co_return rc;
390     }
391 
392     co_return completionCode;
393 }
394 
395 exec::task<int> PlatformManager::eventMessageBufferSize(
396     pldm_tid_t tid, uint16_t receiverMaxBufferSize,
397     uint16_t& terminusBufferSize)
398 {
399     Request request(
400         sizeof(pldm_msg_hdr) + PLDM_EVENT_MESSAGE_BUFFER_SIZE_REQ_BYTES);
401     auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
402     auto rc = encode_event_message_buffer_size_req(0, receiverMaxBufferSize,
403                                                    requestMsg);
404     if (rc)
405     {
406         lg2::error(
407             "Failed to encode request GetPDRRepositoryInfo for terminus ID {TID}, error {RC} ",
408             "TID", tid, "RC", rc);
409         co_return rc;
410     }
411 
412     const pldm_msg* responseMsg = nullptr;
413     size_t responseLen = 0;
414     rc = co_await terminusManager.sendRecvPldmMsg(tid, request, &responseMsg,
415                                                   &responseLen);
416     if (rc)
417     {
418         lg2::error(
419             "Failed to send EventMessageBufferSize message for terminus {TID}, error {RC}",
420             "TID", tid, "RC", rc);
421         co_return rc;
422     }
423 
424     uint8_t completionCode;
425     rc = decode_event_message_buffer_size_resp(
426         responseMsg, responseLen, &completionCode, &terminusBufferSize);
427     if (rc)
428     {
429         lg2::error(
430             "Failed to decode response EventMessageBufferSize for terminus ID {TID}, error {RC} ",
431             "TID", tid, "RC", rc);
432         co_return rc;
433     }
434 
435     if (completionCode != PLDM_SUCCESS)
436     {
437         lg2::error(
438             "Error : EventMessageBufferSize for terminus ID {TID}, complete code {CC}.",
439             "TID", tid, "CC", completionCode);
440         co_return completionCode;
441     }
442 
443     co_return completionCode;
444 }
445 
446 exec::task<int> PlatformManager::setEventReceiver(
447     pldm_tid_t tid, pldm_event_message_global_enable eventMessageGlobalEnable,
448     pldm_transport_protocol_type protocolType, uint16_t heartbeatTimer)
449 {
450     size_t requestBytes = PLDM_SET_EVENT_RECEIVER_REQ_BYTES;
451     /**
452      * Ignore heartbeatTimer bytes when eventMessageGlobalEnable is not
453      * ENABLE_ASYNC_KEEP_ALIVE
454      */
455     if (eventMessageGlobalEnable !=
456         PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC_KEEP_ALIVE)
457     {
458         requestBytes = requestBytes - sizeof(heartbeatTimer);
459     }
460     Request request(sizeof(pldm_msg_hdr) + requestBytes);
461     auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
462     auto rc = encode_set_event_receiver_req(
463         0, eventMessageGlobalEnable, protocolType,
464         terminusManager.getLocalEid(), heartbeatTimer, requestMsg);
465     if (rc)
466     {
467         lg2::error(
468             "Failed to encode request SetEventReceiver for terminus ID {TID}, error {RC} ",
469             "TID", tid, "RC", rc);
470         co_return rc;
471     }
472 
473     const pldm_msg* responseMsg = nullptr;
474     size_t responseLen = 0;
475     rc = co_await terminusManager.sendRecvPldmMsg(tid, request, &responseMsg,
476                                                   &responseLen);
477     if (rc)
478     {
479         lg2::error(
480             "Failed to send SetEventReceiver message for terminus {TID}, error {RC}",
481             "TID", tid, "RC", rc);
482         co_return rc;
483     }
484 
485     uint8_t completionCode;
486     rc = decode_set_event_receiver_resp(responseMsg, responseLen,
487                                         &completionCode);
488     if (rc)
489     {
490         lg2::error(
491             "Failed to decode response SetEventReceiver for terminus ID {TID}, error {RC} ",
492             "TID", tid, "RC", rc);
493         co_return rc;
494     }
495 
496     if (completionCode != PLDM_SUCCESS)
497     {
498         lg2::error(
499             "Error : SetEventReceiver for terminus ID {TID}, complete code {CC}.",
500             "TID", tid, "CC", completionCode);
501         co_return completionCode;
502     }
503 
504     co_return completionCode;
505 }
506 
507 exec::task<int> PlatformManager::eventMessageSupported(
508     pldm_tid_t tid, uint8_t formatVersion, uint8_t& synchronyConfiguration,
509     bitfield8_t& synchronyConfigurationSupported,
510     uint8_t& numberEventClassReturned, std::vector<uint8_t>& eventClass)
511 {
512     Request request(
513         sizeof(pldm_msg_hdr) + PLDM_EVENT_MESSAGE_SUPPORTED_REQ_BYTES);
514     auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
515     auto rc = encode_event_message_supported_req(0, formatVersion, requestMsg);
516     if (rc)
517     {
518         lg2::error(
519             "Failed to encode request EventMessageSupported for terminus ID {TID}, error {RC} ",
520             "TID", tid, "RC", rc);
521         co_return rc;
522     }
523 
524     const pldm_msg* responseMsg = nullptr;
525     size_t responseLen = 0;
526     rc = co_await terminusManager.sendRecvPldmMsg(tid, request, &responseMsg,
527                                                   &responseLen);
528     if (rc)
529     {
530         lg2::error(
531             "Failed to send EventMessageSupported message for terminus {TID}, error {RC}",
532             "TID", tid, "RC", rc);
533         co_return rc;
534     }
535 
536     uint8_t completionCode = 0;
537     uint8_t eventClassCount = static_cast<uint8_t>(responseLen) -
538                               PLDM_EVENT_MESSAGE_SUPPORTED_MIN_RESP_BYTES;
539     eventClass.resize(eventClassCount);
540 
541     rc = decode_event_message_supported_resp(
542         responseMsg, responseLen, &completionCode, &synchronyConfiguration,
543         &synchronyConfigurationSupported, &numberEventClassReturned,
544         eventClass.data(), eventClassCount);
545     if (rc)
546     {
547         lg2::error(
548             "Failed to decode response EventMessageSupported for terminus ID {TID}, error {RC} ",
549             "TID", tid, "RC", rc);
550         co_return rc;
551     }
552 
553     if (completionCode != PLDM_SUCCESS)
554     {
555         lg2::error(
556             "Error : EventMessageSupported for terminus ID {TID}, complete code {CC}.",
557             "TID", tid, "CC", completionCode);
558         co_return completionCode;
559     }
560 
561     co_return completionCode;
562 }
563 } // namespace platform_mc
564 } // namespace pldm
565