xref: /openbmc/pldm/platform-mc/platform_manager.cpp (revision fb732fd99648eae137a1606a702906b2aa5b4756)
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 
25         /* Get Fru */
26         uint16_t totalTableRecords = 0;
27         if (terminus->doesSupportCommand(PLDM_FRU,
28                                          PLDM_GET_FRU_RECORD_TABLE_METADATA))
29         {
30             auto rc =
31                 co_await getFRURecordTableMetadata(tid, &totalTableRecords);
32             if (rc)
33             {
34                 lg2::error(
35                     "Failed to get FRU Metadata for terminus {TID}, error {ERROR}",
36                     "TID", tid, "ERROR", rc);
37             }
38             if (!totalTableRecords)
39             {
40                 lg2::info("Fru record table meta data has 0 records");
41             }
42         }
43 
44         std::vector<uint8_t> fruData{};
45         if ((totalTableRecords != 0) &&
46             terminus->doesSupportCommand(PLDM_FRU, PLDM_GET_FRU_RECORD_TABLE))
47         {
48             auto rc =
49                 co_await getFRURecordTables(tid, totalTableRecords, fruData);
50             if (rc)
51             {
52                 lg2::error(
53                     "Failed to get Fru Record table for terminus {TID}, error {ERROR}",
54                     "TID", tid, "ERROR", rc);
55             }
56         }
57 
58         if (terminus->doesSupportCommand(PLDM_PLATFORM, PLDM_GET_PDR))
59         {
60             auto rc = co_await getPDRs(terminus);
61             if (rc)
62             {
63                 lg2::error(
64                     "Failed to fetch PDRs for terminus with TID: {TID}, error: {ERROR}",
65                     "TID", tid, "ERROR", rc);
66                 continue; // Continue to next terminus
67             }
68 
69             terminus->parseTerminusPDRs();
70         }
71 
72         /**
73          * Need terminus name from PDRs before updating Inventory object with
74          * Fru data
75          */
76         if (fruData.size())
77         {
78             updateInventoryWithFru(tid, fruData.data(), fruData.size());
79         }
80 
81         uint16_t terminusMaxBufferSize = terminus->maxBufferSize;
82         if (!terminus->doesSupportCommand(PLDM_PLATFORM,
83                                           PLDM_EVENT_MESSAGE_BUFFER_SIZE))
84         {
85             terminusMaxBufferSize = PLDM_PLATFORM_DEFAULT_MESSAGE_BUFFER_SIZE;
86         }
87         else
88         {
89             /* Get maxBufferSize use PLDM command eventMessageBufferSize */
90             auto rc = co_await eventMessageBufferSize(
91                 tid, terminus->maxBufferSize, terminusMaxBufferSize);
92             if (rc != PLDM_SUCCESS)
93             {
94                 lg2::error(
95                     "Failed to get message buffer size for terminus with TID: {TID}, error: {ERROR}",
96                     "TID", tid, "ERROR", rc);
97                 terminusMaxBufferSize =
98                     PLDM_PLATFORM_DEFAULT_MESSAGE_BUFFER_SIZE;
99             }
100         }
101         terminus->maxBufferSize =
102             std::min(terminus->maxBufferSize, terminusMaxBufferSize);
103 
104         auto rc = co_await configEventReceiver(tid);
105         if (rc)
106         {
107             lg2::error(
108                 "Failed to config event receiver for terminus with TID: {TID}, error: {ERROR}",
109                 "TID", tid, "ERROR", rc);
110         }
111         terminus->initialized = true;
112     }
113 
114     co_return PLDM_SUCCESS;
115 }
116 
117 exec::task<int> PlatformManager::configEventReceiver(pldm_tid_t tid)
118 {
119     if (!termini.contains(tid))
120     {
121         co_return PLDM_ERROR;
122     }
123 
124     auto& terminus = termini[tid];
125     if (!terminus->doesSupportCommand(PLDM_PLATFORM,
126                                       PLDM_EVENT_MESSAGE_SUPPORTED))
127     {
128         terminus->synchronyConfigurationSupported.byte =
129             1 << PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC_KEEP_ALIVE;
130     }
131     else
132     {
133         /**
134          *  Get synchronyConfigurationSupported use PLDM command
135          *  eventMessageBufferSize
136          */
137         uint8_t synchronyConfiguration = 0;
138         uint8_t numberEventClassReturned = 0;
139         std::vector<uint8_t> eventClass{};
140         auto rc = co_await eventMessageSupported(
141             tid, 1, synchronyConfiguration,
142             terminus->synchronyConfigurationSupported, numberEventClassReturned,
143             eventClass);
144         if (rc != PLDM_SUCCESS)
145         {
146             lg2::error(
147                 "Failed to get event message supported for terminus with TID: {TID}, error: {ERROR}",
148                 "TID", tid, "ERROR", rc);
149             terminus->synchronyConfigurationSupported.byte = 0;
150         }
151     }
152 
153     if (!terminus->doesSupportCommand(PLDM_PLATFORM, PLDM_SET_EVENT_RECEIVER))
154     {
155         lg2::error("Terminus {TID} does not support Event", "TID", tid);
156         co_return PLDM_ERROR;
157     }
158 
159     /**
160      *  Set Event receiver base on synchronyConfigurationSupported data
161      *  use PLDM command SetEventReceiver
162      */
163     pldm_event_message_global_enable eventMessageGlobalEnable =
164         PLDM_EVENT_MESSAGE_GLOBAL_DISABLE;
165     uint16_t heartbeatTimer = 0;
166 
167     /* Use PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC_KEEP_ALIVE when
168      * for eventMessageGlobalEnable when the terminus supports that type
169      */
170     if (terminus->synchronyConfigurationSupported.byte &
171         (1 << PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC_KEEP_ALIVE))
172     {
173         heartbeatTimer = HEARTBEAT_TIMEOUT;
174         eventMessageGlobalEnable =
175             PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC_KEEP_ALIVE;
176     }
177     /* Use PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC when
178      * for eventMessageGlobalEnable when the terminus does not support
179      * PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC_KEEP_ALIVE
180      * and supports PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC type
181      */
182     else if (terminus->synchronyConfigurationSupported.byte &
183              (1 << PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC))
184     {
185         eventMessageGlobalEnable = PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC;
186     }
187     /* Only use PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_POLLING
188      * for eventMessageGlobalEnable when the terminus only supports
189      * this type
190      */
191     else if (terminus->synchronyConfigurationSupported.byte &
192              (1 << PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_POLLING))
193     {
194         eventMessageGlobalEnable = PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_POLLING;
195     }
196 
197     if (eventMessageGlobalEnable != PLDM_EVENT_MESSAGE_GLOBAL_DISABLE)
198     {
199         auto rc = co_await setEventReceiver(tid, eventMessageGlobalEnable,
200                                             PLDM_TRANSPORT_PROTOCOL_TYPE_MCTP,
201                                             heartbeatTimer);
202         if (rc != PLDM_SUCCESS)
203         {
204             lg2::error(
205                 "Failed to set event receiver for terminus with TID: {TID}, error: {ERROR}",
206                 "TID", tid, "ERROR", rc);
207         }
208     }
209 
210     co_return PLDM_SUCCESS;
211 }
212 
213 exec::task<int> PlatformManager::getPDRs(std::shared_ptr<Terminus> terminus)
214 {
215     pldm_tid_t tid = terminus->getTid();
216 
217     /* Setting default values when getPDRRepositoryInfo fails or does not
218      * support */
219     uint8_t repositoryState = PLDM_AVAILABLE;
220     uint32_t recordCount = std::numeric_limits<uint32_t>::max();
221     uint32_t repositorySize = 0;
222     uint32_t largestRecordSize = std::numeric_limits<uint32_t>::max();
223     if (terminus->doesSupportCommand(PLDM_PLATFORM,
224                                      PLDM_GET_PDR_REPOSITORY_INFO))
225     {
226         auto rc =
227             co_await getPDRRepositoryInfo(tid, repositoryState, recordCount,
228                                           repositorySize, largestRecordSize);
229         if (rc)
230         {
231             lg2::error(
232                 "Failed to get PDR Repository Info for terminus with TID: {TID}, error: {ERROR}",
233                 "TID", tid, "ERROR", rc);
234         }
235         else
236         {
237             recordCount =
238                 std::min(recordCount + 1, std::numeric_limits<uint32_t>::max());
239             largestRecordSize = std::min(largestRecordSize + 1,
240                                          std::numeric_limits<uint32_t>::max());
241         }
242     }
243 
244     if (repositoryState != PLDM_AVAILABLE)
245     {
246         co_return PLDM_ERROR_NOT_READY;
247     }
248 
249     uint32_t recordHndl = 0;
250     uint32_t nextRecordHndl = 0;
251     uint32_t nextDataTransferHndl = 0;
252     uint8_t transferFlag = 0;
253     uint16_t responseCnt = 0;
254     constexpr uint16_t recvBufSize = PLDM_PLATFORM_GETPDR_MAX_RECORD_BYTES;
255     std::vector<uint8_t> recvBuf(recvBufSize);
256     uint8_t transferCrc = 0;
257 
258     terminus->pdrs.clear();
259     uint32_t receivedRecordCount = 0;
260 
261     do
262     {
263         auto rc =
264             co_await getPDR(tid, recordHndl, 0, PLDM_GET_FIRSTPART, recvBufSize,
265                             0, nextRecordHndl, nextDataTransferHndl,
266                             transferFlag, responseCnt, recvBuf, transferCrc);
267 
268         if (rc)
269         {
270             lg2::error(
271                 "Failed to get PDRs for terminus {TID}, error: {RC}, first part of record handle {RECORD}",
272                 "TID", tid, "RC", rc, "RECORD", recordHndl);
273             terminus->pdrs.clear();
274             co_return rc;
275         }
276 
277         if (transferFlag == PLDM_PLATFORM_TRANSFER_START_AND_END)
278         {
279             // single-part
280             terminus->pdrs.emplace_back(std::vector<uint8_t>(
281                 recvBuf.begin(), recvBuf.begin() + responseCnt));
282             recordHndl = nextRecordHndl;
283         }
284         else
285         {
286             // multipart transfer
287             uint32_t receivedRecordSize = responseCnt;
288             auto pdrHdr = reinterpret_cast<pldm_pdr_hdr*>(recvBuf.data());
289             uint16_t recordChgNum = le16toh(pdrHdr->record_change_num);
290             std::vector<uint8_t> receivedPdr(recvBuf.begin(),
291                                              recvBuf.begin() + responseCnt);
292             do
293             {
294                 rc = co_await getPDR(
295                     tid, recordHndl, nextDataTransferHndl, PLDM_GET_NEXTPART,
296                     recvBufSize, recordChgNum, nextRecordHndl,
297                     nextDataTransferHndl, transferFlag, responseCnt, recvBuf,
298                     transferCrc);
299                 if (rc)
300                 {
301                     lg2::error(
302                         "Failed to get PDRs for terminus {TID}, error: {RC}, get middle part of record handle {RECORD}",
303                         "TID", tid, "RC", rc, "RECORD", recordHndl);
304                     terminus->pdrs.clear();
305                     co_return rc;
306                 }
307 
308                 receivedPdr.insert(receivedPdr.end(), recvBuf.begin(),
309                                    recvBuf.begin() + responseCnt);
310                 receivedRecordSize += responseCnt;
311 
312                 if (transferFlag == PLDM_PLATFORM_TRANSFER_END)
313                 {
314                     terminus->pdrs.emplace_back(std::move(receivedPdr));
315                     recordHndl = nextRecordHndl;
316                 }
317             } while (nextDataTransferHndl != 0 &&
318                      receivedRecordSize < largestRecordSize);
319         }
320         receivedRecordCount++;
321     } while (nextRecordHndl != 0 && receivedRecordCount < recordCount);
322 
323     co_return PLDM_SUCCESS;
324 }
325 
326 exec::task<int> PlatformManager::getPDR(
327     const pldm_tid_t tid, const uint32_t recordHndl,
328     const uint32_t dataTransferHndl, const uint8_t transferOpFlag,
329     const uint16_t requestCnt, const uint16_t recordChgNum,
330     uint32_t& nextRecordHndl, uint32_t& nextDataTransferHndl,
331     uint8_t& transferFlag, uint16_t& responseCnt,
332     std::vector<uint8_t>& recordData, uint8_t& transferCrc)
333 {
334     Request request(sizeof(pldm_msg_hdr) + PLDM_GET_PDR_REQ_BYTES);
335     auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
336     auto rc = encode_get_pdr_req(0, recordHndl, dataTransferHndl,
337                                  transferOpFlag, requestCnt, recordChgNum,
338                                  requestMsg, PLDM_GET_PDR_REQ_BYTES);
339     if (rc)
340     {
341         lg2::error(
342             "Failed to encode request GetPDR for terminus ID {TID}, error {RC} ",
343             "TID", tid, "RC", rc);
344         co_return rc;
345     }
346 
347     const pldm_msg* responseMsg = nullptr;
348     size_t responseLen = 0;
349     rc = co_await terminusManager.sendRecvPldmMsg(tid, request, &responseMsg,
350                                                   &responseLen);
351     if (rc)
352     {
353         lg2::error(
354             "Failed to send GetPDR message for terminus {TID}, error {RC}",
355             "TID", tid, "RC", rc);
356         co_return rc;
357     }
358 
359     uint8_t completionCode;
360     rc = decode_get_pdr_resp(responseMsg, responseLen, &completionCode,
361                              &nextRecordHndl, &nextDataTransferHndl,
362                              &transferFlag, &responseCnt, recordData.data(),
363                              recordData.size(), &transferCrc);
364     if (rc)
365     {
366         lg2::error(
367             "Failed to decode response GetPDR for terminus ID {TID}, error {RC} ",
368             "TID", tid, "RC", rc);
369         co_return rc;
370     }
371 
372     if (completionCode != PLDM_SUCCESS)
373     {
374         lg2::error("Error : GetPDR for terminus ID {TID}, complete code {CC}.",
375                    "TID", tid, "CC", completionCode);
376         co_return rc;
377     }
378 
379     co_return completionCode;
380 }
381 
382 exec::task<int> PlatformManager::getPDRRepositoryInfo(
383     const pldm_tid_t tid, uint8_t& repositoryState, uint32_t& recordCount,
384     uint32_t& repositorySize, uint32_t& largestRecordSize)
385 {
386     Request request(sizeof(pldm_msg_hdr) + sizeof(uint8_t));
387     auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
388     auto rc = encode_pldm_header_only(PLDM_REQUEST, 0, PLDM_PLATFORM,
389                                       PLDM_GET_PDR_REPOSITORY_INFO, requestMsg);
390     if (rc)
391     {
392         lg2::error(
393             "Failed to encode request GetPDRRepositoryInfo for terminus ID {TID}, error {RC} ",
394             "TID", tid, "RC", rc);
395         co_return rc;
396     }
397 
398     const pldm_msg* responseMsg = nullptr;
399     size_t responseLen = 0;
400     rc = co_await terminusManager.sendRecvPldmMsg(tid, request, &responseMsg,
401                                                   &responseLen);
402     if (rc)
403     {
404         lg2::error(
405             "Failed to send GetPDRRepositoryInfo message for terminus {TID}, error {RC}",
406             "TID", tid, "RC", rc);
407         co_return rc;
408     }
409 
410     uint8_t completionCode = 0;
411     std::array<uint8_t, PLDM_TIMESTAMP104_SIZE> updateTime = {};
412     std::array<uint8_t, PLDM_TIMESTAMP104_SIZE> oemUpdateTime = {};
413     uint8_t dataTransferHandleTimeout = 0;
414 
415     rc = decode_get_pdr_repository_info_resp(
416         responseMsg, responseLen, &completionCode, &repositoryState,
417         updateTime.data(), oemUpdateTime.data(), &recordCount, &repositorySize,
418         &largestRecordSize, &dataTransferHandleTimeout);
419     if (rc)
420     {
421         lg2::error(
422             "Failed to decode response GetPDRRepositoryInfo for terminus ID {TID}, error {RC} ",
423             "TID", tid, "RC", rc);
424         co_return rc;
425     }
426 
427     if (completionCode != PLDM_SUCCESS)
428     {
429         lg2::error(
430             "Error : GetPDRRepositoryInfo for terminus ID {TID}, complete code {CC}.",
431             "TID", tid, "CC", completionCode);
432         co_return rc;
433     }
434 
435     co_return completionCode;
436 }
437 
438 exec::task<int> PlatformManager::eventMessageBufferSize(
439     pldm_tid_t tid, uint16_t receiverMaxBufferSize,
440     uint16_t& terminusBufferSize)
441 {
442     Request request(
443         sizeof(pldm_msg_hdr) + PLDM_EVENT_MESSAGE_BUFFER_SIZE_REQ_BYTES);
444     auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
445     auto rc = encode_event_message_buffer_size_req(0, receiverMaxBufferSize,
446                                                    requestMsg);
447     if (rc)
448     {
449         lg2::error(
450             "Failed to encode request GetPDRRepositoryInfo for terminus ID {TID}, error {RC} ",
451             "TID", tid, "RC", rc);
452         co_return rc;
453     }
454 
455     const pldm_msg* responseMsg = nullptr;
456     size_t responseLen = 0;
457     rc = co_await terminusManager.sendRecvPldmMsg(tid, request, &responseMsg,
458                                                   &responseLen);
459     if (rc)
460     {
461         lg2::error(
462             "Failed to send EventMessageBufferSize message for terminus {TID}, error {RC}",
463             "TID", tid, "RC", rc);
464         co_return rc;
465     }
466 
467     uint8_t completionCode;
468     rc = decode_event_message_buffer_size_resp(
469         responseMsg, responseLen, &completionCode, &terminusBufferSize);
470     if (rc)
471     {
472         lg2::error(
473             "Failed to decode response EventMessageBufferSize for terminus ID {TID}, error {RC} ",
474             "TID", tid, "RC", rc);
475         co_return rc;
476     }
477 
478     if (completionCode != PLDM_SUCCESS)
479     {
480         lg2::error(
481             "Error : EventMessageBufferSize for terminus ID {TID}, complete code {CC}.",
482             "TID", tid, "CC", completionCode);
483         co_return completionCode;
484     }
485 
486     co_return completionCode;
487 }
488 
489 exec::task<int> PlatformManager::setEventReceiver(
490     pldm_tid_t tid, pldm_event_message_global_enable eventMessageGlobalEnable,
491     pldm_transport_protocol_type protocolType, uint16_t heartbeatTimer)
492 {
493     size_t requestBytes = PLDM_SET_EVENT_RECEIVER_REQ_BYTES;
494     /**
495      * Ignore heartbeatTimer bytes when eventMessageGlobalEnable is not
496      * ENABLE_ASYNC_KEEP_ALIVE
497      */
498     if (eventMessageGlobalEnable !=
499         PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC_KEEP_ALIVE)
500     {
501         requestBytes = requestBytes - sizeof(heartbeatTimer);
502     }
503     Request request(sizeof(pldm_msg_hdr) + requestBytes);
504     auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
505     auto rc = encode_set_event_receiver_req(
506         0, eventMessageGlobalEnable, protocolType,
507         terminusManager.getLocalEid(), heartbeatTimer, requestMsg);
508     if (rc)
509     {
510         lg2::error(
511             "Failed to encode request SetEventReceiver for terminus ID {TID}, error {RC} ",
512             "TID", tid, "RC", rc);
513         co_return rc;
514     }
515 
516     const pldm_msg* responseMsg = nullptr;
517     size_t responseLen = 0;
518     rc = co_await terminusManager.sendRecvPldmMsg(tid, request, &responseMsg,
519                                                   &responseLen);
520     if (rc)
521     {
522         lg2::error(
523             "Failed to send SetEventReceiver message for terminus {TID}, error {RC}",
524             "TID", tid, "RC", rc);
525         co_return rc;
526     }
527 
528     uint8_t completionCode;
529     rc = decode_set_event_receiver_resp(responseMsg, responseLen,
530                                         &completionCode);
531     if (rc)
532     {
533         lg2::error(
534             "Failed to decode response SetEventReceiver for terminus ID {TID}, error {RC} ",
535             "TID", tid, "RC", rc);
536         co_return rc;
537     }
538 
539     if (completionCode != PLDM_SUCCESS)
540     {
541         lg2::error(
542             "Error : SetEventReceiver for terminus ID {TID}, complete code {CC}.",
543             "TID", tid, "CC", completionCode);
544         co_return completionCode;
545     }
546 
547     co_return completionCode;
548 }
549 
550 exec::task<int> PlatformManager::eventMessageSupported(
551     pldm_tid_t tid, uint8_t formatVersion, uint8_t& synchronyConfiguration,
552     bitfield8_t& synchronyConfigurationSupported,
553     uint8_t& numberEventClassReturned, std::vector<uint8_t>& eventClass)
554 {
555     Request request(
556         sizeof(pldm_msg_hdr) + PLDM_EVENT_MESSAGE_SUPPORTED_REQ_BYTES);
557     auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
558     auto rc = encode_event_message_supported_req(0, formatVersion, requestMsg);
559     if (rc)
560     {
561         lg2::error(
562             "Failed to encode request EventMessageSupported for terminus ID {TID}, error {RC} ",
563             "TID", tid, "RC", rc);
564         co_return rc;
565     }
566 
567     const pldm_msg* responseMsg = nullptr;
568     size_t responseLen = 0;
569     rc = co_await terminusManager.sendRecvPldmMsg(tid, request, &responseMsg,
570                                                   &responseLen);
571     if (rc)
572     {
573         lg2::error(
574             "Failed to send EventMessageSupported message for terminus {TID}, error {RC}",
575             "TID", tid, "RC", rc);
576         co_return rc;
577     }
578 
579     uint8_t completionCode = 0;
580     uint8_t eventClassCount = static_cast<uint8_t>(responseLen) -
581                               PLDM_EVENT_MESSAGE_SUPPORTED_MIN_RESP_BYTES;
582     eventClass.resize(eventClassCount);
583 
584     rc = decode_event_message_supported_resp(
585         responseMsg, responseLen, &completionCode, &synchronyConfiguration,
586         &synchronyConfigurationSupported, &numberEventClassReturned,
587         eventClass.data(), eventClassCount);
588     if (rc)
589     {
590         lg2::error(
591             "Failed to decode response EventMessageSupported for terminus ID {TID}, error {RC} ",
592             "TID", tid, "RC", rc);
593         co_return rc;
594     }
595 
596     if (completionCode != PLDM_SUCCESS)
597     {
598         lg2::error(
599             "Error : EventMessageSupported for terminus ID {TID}, complete code {CC}.",
600             "TID", tid, "CC", completionCode);
601         co_return completionCode;
602     }
603 
604     co_return completionCode;
605 }
606 
607 exec::task<int>
608     PlatformManager::getFRURecordTableMetadata(pldm_tid_t tid, uint16_t* total)
609 {
610     Request request(
611         sizeof(pldm_msg_hdr) + PLDM_GET_FRU_RECORD_TABLE_METADATA_REQ_BYTES);
612     auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
613 
614     auto rc = encode_get_fru_record_table_metadata_req(
615         0, requestMsg, PLDM_GET_FRU_RECORD_TABLE_METADATA_REQ_BYTES);
616     if (rc)
617     {
618         lg2::error(
619             "Failed to encode request GetFRURecordTableMetadata for terminus ID {TID}, error {RC} ",
620             "TID", tid, "RC", rc);
621         co_return rc;
622     }
623 
624     const pldm_msg* responseMsg = nullptr;
625     size_t responseLen = 0;
626 
627     rc = co_await terminusManager.sendRecvPldmMsg(tid, request, &responseMsg,
628                                                   &responseLen);
629     if (rc)
630     {
631         lg2::error(
632             "Failed to send GetFRURecordTableMetadata message for terminus {TID}, error {RC}",
633             "TID", tid, "RC", rc);
634         co_return rc;
635     }
636 
637     uint8_t completionCode = 0;
638     if (responseMsg == nullptr || !responseLen)
639     {
640         lg2::error(
641             "No response data for GetFRURecordTableMetadata for terminus {TID}",
642             "TID", tid);
643         co_return rc;
644     }
645 
646     uint8_t fru_data_major_version, fru_data_minor_version;
647     uint32_t fru_table_maximum_size, fru_table_length;
648     uint16_t total_record_set_identifiers;
649     uint32_t checksum;
650     rc = decode_get_fru_record_table_metadata_resp(
651         responseMsg, responseLen, &completionCode, &fru_data_major_version,
652         &fru_data_minor_version, &fru_table_maximum_size, &fru_table_length,
653         &total_record_set_identifiers, total, &checksum);
654 
655     if (rc)
656     {
657         lg2::error(
658             "Failed to decode response GetFRURecordTableMetadata for terminus ID {TID}, error {RC} ",
659             "TID", tid, "RC", rc);
660         co_return rc;
661     }
662 
663     if (completionCode != PLDM_SUCCESS)
664     {
665         lg2::error(
666             "Error : GetFRURecordTableMetadata for terminus ID {TID}, complete code {CC}.",
667             "TID", tid, "CC", completionCode);
668         co_return rc;
669     }
670 
671     co_return rc;
672 }
673 
674 exec::task<int> PlatformManager::getFRURecordTable(
675     pldm_tid_t tid, const uint32_t dataTransferHndl,
676     const uint8_t transferOpFlag, uint32_t* nextDataTransferHndl,
677     uint8_t* transferFlag, size_t* responseCnt,
678     std::vector<uint8_t>& recordData)
679 {
680     Request request(sizeof(pldm_msg_hdr) + PLDM_GET_FRU_RECORD_TABLE_REQ_BYTES);
681     auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
682 
683     auto rc = encode_get_fru_record_table_req(
684         0, dataTransferHndl, transferOpFlag, requestMsg,
685         PLDM_GET_FRU_RECORD_TABLE_REQ_BYTES);
686     if (rc != PLDM_SUCCESS)
687     {
688         lg2::error(
689             "Failed to encode request GetFRURecordTable for terminus ID {TID}, error {RC} ",
690             "TID", tid, "RC", rc);
691         co_return rc;
692     }
693 
694     const pldm_msg* responseMsg = nullptr;
695     size_t responseLen = 0;
696 
697     rc = co_await terminusManager.sendRecvPldmMsg(tid, request, &responseMsg,
698                                                   &responseLen);
699     if (rc)
700     {
701         lg2::error(
702             "Failed to send GetFRURecordTable message for terminus {TID}, error {RC}",
703             "TID", tid, "RC", rc);
704         co_return rc;
705     }
706 
707     uint8_t completionCode = 0;
708     if (responseMsg == nullptr || !responseLen)
709     {
710         lg2::error("No response data for GetFRURecordTable for terminus {TID}",
711                    "TID", tid);
712         co_return rc;
713     }
714 
715     auto responsePtr = reinterpret_cast<const struct pldm_msg*>(responseMsg);
716     rc = decode_get_fru_record_table_resp(
717         responsePtr, responseLen - sizeof(pldm_msg_hdr), &completionCode,
718         nextDataTransferHndl, transferFlag, recordData.data(), responseCnt);
719 
720     if (rc)
721     {
722         lg2::error(
723             "Failed to decode response GetFRURecordTable for terminus ID {TID}, error {RC} ",
724             "TID", tid, "RC", rc);
725         co_return rc;
726     }
727 
728     if (completionCode != PLDM_SUCCESS)
729     {
730         lg2::error(
731             "Error : GetFRURecordTable for terminus ID {TID}, complete code {CC}.",
732             "TID", tid, "CC", completionCode);
733         co_return rc;
734     }
735 
736     co_return rc;
737 }
738 
739 void PlatformManager::updateInventoryWithFru(
740     pldm_tid_t tid, const uint8_t* fruData, const size_t fruLen)
741 {
742     if (tid == PLDM_TID_RESERVED || !termini.contains(tid) || !termini[tid])
743     {
744         lg2::error("Invalid terminus {TID}", "TID", tid);
745         return;
746     }
747 
748     termini[tid]->updateInventoryWithFru(fruData, fruLen);
749 }
750 
751 exec::task<int> PlatformManager::getFRURecordTables(
752     pldm_tid_t tid, const uint16_t& totalTableRecords,
753     std::vector<uint8_t>& fruData)
754 {
755     if (!totalTableRecords)
756     {
757         lg2::info("Fru record table has 0 records");
758         co_return PLDM_ERROR;
759     }
760 
761     uint32_t dataTransferHndl = 0;
762     uint32_t nextDataTransferHndl = 0;
763     uint8_t transferFlag = 0;
764     uint8_t transferOpFlag = PLDM_GET_FIRSTPART;
765     size_t responseCnt = 0;
766     std::vector<uint8_t> recvBuf(PLDM_PLATFORM_GETPDR_MAX_RECORD_BYTES);
767 
768     size_t fruLength = 0;
769     std::vector<uint8_t> receivedFru(0);
770     do
771     {
772         auto rc = co_await getFRURecordTable(
773             tid, dataTransferHndl, transferOpFlag, &nextDataTransferHndl,
774             &transferFlag, &responseCnt, recvBuf);
775 
776         if (rc)
777         {
778             lg2::error(
779                 "Failed to get Fru Record Data for terminus {TID}, error: {RC}, first part of data handle {RECORD}",
780                 "TID", tid, "RC", rc, "RECORD", dataTransferHndl);
781             co_return rc;
782         }
783 
784         receivedFru.insert(receivedFru.end(), recvBuf.begin(),
785                            recvBuf.begin() + responseCnt);
786         fruLength += responseCnt;
787         if (transferFlag == PLDM_PLATFORM_TRANSFER_START_AND_END ||
788             transferFlag == PLDM_PLATFORM_TRANSFER_END)
789         {
790             break;
791         }
792 
793         // multipart transfer
794         dataTransferHndl = nextDataTransferHndl;
795         transferOpFlag = PLDM_GET_NEXTPART;
796 
797     } while (nextDataTransferHndl != 0);
798 
799     if (fruLength != receivedFru.size())
800     {
801         lg2::error(
802             "Size of Fru Record Data {SIZE} for terminus {TID} is different the responded size {RSPSIZE}.",
803             "SIZE", receivedFru.size(), "RSPSIZE", fruLength);
804         co_return PLDM_ERROR_INVALID_LENGTH;
805     }
806 
807     fruData = receivedFru;
808 
809     co_return PLDM_SUCCESS;
810 }
811 
812 } // namespace platform_mc
813 } // namespace pldm
814