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