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