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 
initTerminus()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 
configEventReceiver(pldm_tid_t tid)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 
getPDRs(std::shared_ptr<Terminus> terminus)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 
getPDR(const pldm_tid_t tid,const uint32_t recordHndl,const uint32_t dataTransferHndl,const uint8_t transferOpFlag,const uint16_t requestCnt,const uint16_t recordChgNum,uint32_t & nextRecordHndl,uint32_t & nextDataTransferHndl,uint8_t & transferFlag,uint16_t & responseCnt,std::vector<uint8_t> & recordData,uint8_t & transferCrc)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 
getPDRRepositoryInfo(const pldm_tid_t tid,uint8_t & repositoryState,uint32_t & recordCount,uint32_t & repositorySize,uint32_t & largestRecordSize)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 
eventMessageBufferSize(pldm_tid_t tid,uint16_t receiverMaxBufferSize,uint16_t & terminusBufferSize)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 
setEventReceiver(pldm_tid_t tid,pldm_event_message_global_enable eventMessageGlobalEnable,pldm_transport_protocol_type protocolType,uint16_t heartbeatTimer)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                           PLDM_SET_EVENT_RECEIVER_REQ_HEARTBEAT_BYTES;
453     /**
454      * Ignore heartbeatTimer bytes when eventMessageGlobalEnable is not
455      * ENABLE_ASYNC_KEEP_ALIVE
456      */
457     if (eventMessageGlobalEnable !=
458         PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC_KEEP_ALIVE)
459     {
460         requestBytes = requestBytes - sizeof(heartbeatTimer);
461     }
462     Request request(sizeof(pldm_msg_hdr) + requestBytes);
463     auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
464     auto rc = encode_set_event_receiver_req(
465         0, eventMessageGlobalEnable, protocolType,
466         terminusManager.getLocalEid(), heartbeatTimer, requestMsg);
467     if (rc)
468     {
469         lg2::error(
470             "Failed to encode request SetEventReceiver for terminus ID {TID}, error {RC} ",
471             "TID", tid, "RC", rc);
472         co_return rc;
473     }
474 
475     const pldm_msg* responseMsg = nullptr;
476     size_t responseLen = 0;
477     rc = co_await terminusManager.sendRecvPldmMsg(tid, request, &responseMsg,
478                                                   &responseLen);
479     if (rc)
480     {
481         lg2::error(
482             "Failed to send SetEventReceiver message for terminus {TID}, error {RC}",
483             "TID", tid, "RC", rc);
484         co_return rc;
485     }
486 
487     uint8_t completionCode;
488     rc = decode_set_event_receiver_resp(responseMsg, responseLen,
489                                         &completionCode);
490     if (rc)
491     {
492         lg2::error(
493             "Failed to decode response SetEventReceiver for terminus ID {TID}, error {RC} ",
494             "TID", tid, "RC", rc);
495         co_return rc;
496     }
497 
498     if (completionCode != PLDM_SUCCESS)
499     {
500         lg2::error(
501             "Error : SetEventReceiver for terminus ID {TID}, complete code {CC}.",
502             "TID", tid, "CC", completionCode);
503         co_return completionCode;
504     }
505 
506     co_return completionCode;
507 }
508 
eventMessageSupported(pldm_tid_t tid,uint8_t formatVersion,uint8_t & synchronyConfiguration,bitfield8_t & synchronyConfigurationSupported,uint8_t & numberEventClassReturned,std::vector<uint8_t> & eventClass)509 exec::task<int> PlatformManager::eventMessageSupported(
510     pldm_tid_t tid, uint8_t formatVersion, uint8_t& synchronyConfiguration,
511     bitfield8_t& synchronyConfigurationSupported,
512     uint8_t& numberEventClassReturned, std::vector<uint8_t>& eventClass)
513 {
514     Request request(
515         sizeof(pldm_msg_hdr) + PLDM_EVENT_MESSAGE_SUPPORTED_REQ_BYTES);
516     auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
517     auto rc = encode_event_message_supported_req(0, formatVersion, requestMsg);
518     if (rc)
519     {
520         lg2::error(
521             "Failed to encode request EventMessageSupported for terminus ID {TID}, error {RC} ",
522             "TID", tid, "RC", rc);
523         co_return rc;
524     }
525 
526     const pldm_msg* responseMsg = nullptr;
527     size_t responseLen = 0;
528     rc = co_await terminusManager.sendRecvPldmMsg(tid, request, &responseMsg,
529                                                   &responseLen);
530     if (rc)
531     {
532         lg2::error(
533             "Failed to send EventMessageSupported message for terminus {TID}, error {RC}",
534             "TID", tid, "RC", rc);
535         co_return rc;
536     }
537 
538     uint8_t completionCode = 0;
539     uint8_t eventClassCount = static_cast<uint8_t>(responseLen) -
540                               PLDM_EVENT_MESSAGE_SUPPORTED_MIN_RESP_BYTES;
541     eventClass.resize(eventClassCount);
542 
543     rc = decode_event_message_supported_resp(
544         responseMsg, responseLen, &completionCode, &synchronyConfiguration,
545         &synchronyConfigurationSupported, &numberEventClassReturned,
546         eventClass.data(), eventClassCount);
547     if (rc)
548     {
549         lg2::error(
550             "Failed to decode response EventMessageSupported for terminus ID {TID}, error {RC} ",
551             "TID", tid, "RC", rc);
552         co_return rc;
553     }
554 
555     if (completionCode != PLDM_SUCCESS)
556     {
557         lg2::error(
558             "Error : EventMessageSupported for terminus ID {TID}, complete code {CC}.",
559             "TID", tid, "CC", completionCode);
560         co_return completionCode;
561     }
562 
563     co_return completionCode;
564 }
565 } // namespace platform_mc
566 } // namespace pldm
567