xref: /openbmc/pldm/host-bmc/host_pdr_handler.cpp (revision 70262ed7bf854b25d4b65628bc3c892ddfe9380f)
1 #include "host_pdr_handler.hpp"
2 
3 #include <libpldm/fru.h>
4 #ifdef OEM_IBM
5 #include <libpldm/oem/ibm/fru.h>
6 #endif
7 #include "dbus/custom_dbus.hpp"
8 
9 #include <nlohmann/json.hpp>
10 #include <phosphor-logging/lg2.hpp>
11 #include <sdeventplus/clock.hpp>
12 #include <sdeventplus/exception.hpp>
13 #include <sdeventplus/source/io.hpp>
14 #include <sdeventplus/source/time.hpp>
15 
16 #include <cassert>
17 #include <fstream>
18 #include <type_traits>
19 
20 PHOSPHOR_LOG2_USING;
21 
22 namespace pldm
23 {
24 using namespace pldm::responder::events;
25 using namespace pldm::utils;
26 using namespace sdbusplus::bus::match::rules;
27 using namespace pldm::responder::pdr_utils;
28 using namespace pldm::hostbmc::utils;
29 using Json = nlohmann::json;
30 namespace fs = std::filesystem;
31 using namespace pldm::dbus;
32 const Json emptyJson{};
33 
34 template <typename T>
extractTerminusHandle(std::vector<uint8_t> & pdr)35 uint16_t extractTerminusHandle(std::vector<uint8_t>& pdr)
36 {
37     T* var = nullptr;
38     if (std::is_same<T, pldm_pdr_fru_record_set>::value)
39     {
40         var = (T*)(pdr.data() + sizeof(pldm_pdr_hdr));
41     }
42     else
43     {
44         var = (T*)(pdr.data());
45     }
46     if (var != nullptr)
47     {
48         return var->terminus_handle;
49     }
50     return TERMINUS_HANDLE;
51 }
52 
53 template <typename T>
updateContainerId(pldm_entity_association_tree * entityTree,std::vector<uint8_t> & pdr)54 void updateContainerId(pldm_entity_association_tree* entityTree,
55                        std::vector<uint8_t>& pdr)
56 {
57     T* t = nullptr;
58     if (entityTree == nullptr)
59     {
60         return;
61     }
62     if (std::is_same<T, pldm_pdr_fru_record_set>::value)
63     {
64         t = (T*)(pdr.data() + sizeof(pldm_pdr_hdr));
65     }
66     else
67     {
68         t = (T*)(pdr.data());
69     }
70     if (t == nullptr)
71     {
72         return;
73     }
74 
75     pldm_entity entity{t->entity_type, t->entity_instance, t->container_id};
76     auto node = pldm_entity_association_tree_find_with_locality(
77         entityTree, &entity, true);
78     if (node)
79     {
80         pldm_entity e = pldm_entity_extract(node);
81         t->container_id = e.entity_container_id;
82     }
83 }
84 
HostPDRHandler(int,uint8_t mctp_eid,sdeventplus::Event & event,pldm_pdr * repo,const std::string & eventsJsonsDir,pldm_entity_association_tree * entityTree,pldm_entity_association_tree * bmcEntityTree,pldm::InstanceIdDb & instanceIdDb,pldm::requester::Handler<pldm::requester::Request> * handler)85 HostPDRHandler::HostPDRHandler(
86     int /* mctp_fd */, uint8_t mctp_eid, sdeventplus::Event& event,
87     pldm_pdr* repo, const std::string& eventsJsonsDir,
88     pldm_entity_association_tree* entityTree,
89     pldm_entity_association_tree* bmcEntityTree,
90     pldm::InstanceIdDb& instanceIdDb,
91     pldm::requester::Handler<pldm::requester::Request>* handler) :
92     mctp_eid(mctp_eid), event(event), repo(repo),
93     stateSensorHandler(eventsJsonsDir), entityTree(entityTree),
94     instanceIdDb(instanceIdDb), handler(handler),
95     entityMaps(parseEntityMap(ENTITY_MAP_JSON)), oemUtilsHandler(nullptr)
96 {
97     mergedHostParents = false;
98     hostOffMatch = std::make_unique<sdbusplus::bus::match_t>(
99         pldm::utils::DBusHandler::getBus(),
100         propertiesChanged("/xyz/openbmc_project/state/host0",
101                           "xyz.openbmc_project.State.Host"),
102         [this, repo, entityTree, bmcEntityTree](sdbusplus::message_t& msg) {
103             DbusChangedProps props{};
104             std::string intf;
105             msg.read(intf, props);
106             const auto itr = props.find("CurrentHostState");
107             if (itr != props.end())
108             {
109                 PropertyValue value = itr->second;
110                 auto propVal = std::get<std::string>(value);
111                 if (propVal == "xyz.openbmc_project.State.Host.HostState.Off")
112                 {
113                     // Delete all the remote terminus information
114                     std::erase_if(tlPDRInfo, [](const auto& item) {
115                         const auto& [key, value] = item;
116                         return key != TERMINUS_HANDLE;
117                     });
118                     // when the host is powered off, set the availability
119                     // state of all the dbus objects to false
120                     this->setPresenceFrus();
121                     pldm_pdr_remove_remote_pdrs(repo);
122                     pldm_entity_association_tree_destroy_root(entityTree);
123                     pldm_entity_association_tree_copy_root(bmcEntityTree,
124                                                            entityTree);
125                     this->sensorMap.clear();
126                     this->responseReceived = false;
127                     this->mergedHostParents = false;
128                 }
129             }
130         });
131 }
132 
setPresenceFrus()133 void HostPDRHandler::setPresenceFrus()
134 {
135     // iterate over all dbus objects
136     for (const auto& [path, entityId] : objPathMap)
137     {
138         CustomDBus::getCustomDBus().setAvailabilityState(path, false);
139     }
140 }
141 
fetchPDR(PDRRecordHandles && recordHandles)142 void HostPDRHandler::fetchPDR(PDRRecordHandles&& recordHandles)
143 {
144     pdrRecordHandles.clear();
145     modifiedPDRRecordHandles.clear();
146 
147     if (isHostPdrModified)
148     {
149         modifiedPDRRecordHandles = std::move(recordHandles);
150     }
151     else
152     {
153         pdrRecordHandles = std::move(recordHandles);
154     }
155 
156     // Defer the actual fetch of PDRs from the host (by queuing the call on the
157     // main event loop). That way, we can respond to the platform event msg from
158     // the host firmware.
159     pdrFetchEvent = std::make_unique<sdeventplus::source::Defer>(
160         event, std::bind(std::mem_fn(&HostPDRHandler::_fetchPDR), this,
161                          std::placeholders::_1));
162 }
163 
_fetchPDR(sdeventplus::source::EventBase &)164 void HostPDRHandler::_fetchPDR(sdeventplus::source::EventBase& /*source*/)
165 {
166     getHostPDR();
167 }
168 
getHostPDR(uint32_t nextRecordHandle)169 void HostPDRHandler::getHostPDR(uint32_t nextRecordHandle)
170 {
171     pdrFetchEvent.reset();
172 
173     std::vector<uint8_t> requestMsg(
174         sizeof(pldm_msg_hdr) + PLDM_GET_PDR_REQ_BYTES);
175     auto request = new (requestMsg.data()) pldm_msg;
176     uint32_t recordHandle{};
177     if (!nextRecordHandle && (!modifiedPDRRecordHandles.empty()) &&
178         isHostPdrModified)
179     {
180         recordHandle = modifiedPDRRecordHandles.front();
181         modifiedPDRRecordHandles.pop_front();
182     }
183     else if (!nextRecordHandle && (!pdrRecordHandles.empty()))
184     {
185         recordHandle = pdrRecordHandles.front();
186         pdrRecordHandles.pop_front();
187     }
188     else
189     {
190         recordHandle = nextRecordHandle;
191     }
192     auto instanceIdResult =
193         pldm::utils::getInstanceId(instanceIdDb.next(mctp_eid));
194     if (!instanceIdResult)
195     {
196         return;
197     }
198     auto instanceId = instanceIdResult.value();
199 
200     auto rc =
201         encode_get_pdr_req(instanceId, recordHandle, 0, PLDM_GET_FIRSTPART,
202                            UINT16_MAX, 0, request, PLDM_GET_PDR_REQ_BYTES);
203     if (rc != PLDM_SUCCESS)
204     {
205         instanceIdDb.free(mctp_eid, instanceId);
206         error("Failed to encode get pdr request, response code '{RC}'", "RC",
207               rc);
208         return;
209     }
210 
211     rc = handler->registerRequest(
212         mctp_eid, instanceId, PLDM_PLATFORM, PLDM_GET_PDR,
213         std::move(requestMsg),
214         [this](mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen) {
215             this->processHostPDRs(eid, response, respMsgLen);
216         });
217     if (rc)
218     {
219         error(
220             "Failed to send the getPDR request to remote terminus, response code '{RC}'",
221             "RC", rc);
222     }
223 }
224 
handleStateSensorEvent(const StateSensorEntry & entry,pdr::EventState state)225 int HostPDRHandler::handleStateSensorEvent(const StateSensorEntry& entry,
226                                            pdr::EventState state)
227 {
228     auto rc = stateSensorHandler.eventAction(entry, state);
229     if (rc != PLDM_SUCCESS)
230     {
231         error("Failed to fetch and update D-bus property, response code '{RC}'",
232               "RC", rc);
233         return rc;
234     }
235     return PLDM_SUCCESS;
236 }
237 
mergeEntityAssociations(const std::vector<uint8_t> & pdr,const uint32_t & size,const uint32_t & record_handle)238 void HostPDRHandler::mergeEntityAssociations(
239     const std::vector<uint8_t>& pdr, [[maybe_unused]] const uint32_t& size,
240     [[maybe_unused]] const uint32_t& record_handle)
241 {
242     size_t numEntities{};
243     pldm_entity* entities = nullptr;
244     bool merged = false;
245     auto entityPdr = new (const_cast<uint8_t*>(pdr.data()) +
246                           sizeof(pldm_pdr_hdr)) pldm_pdr_entity_association;
247 
248     if (oemPlatformHandler &&
249         oemPlatformHandler->checkRecordHandleInRange(record_handle))
250     {
251         // Adding the remote range PDRs to the repo before merging it
252         uint32_t handle = record_handle;
253         pldm_pdr_add(repo, pdr.data(), size, true, 0xFFFF, &handle);
254     }
255 
256     pldm_entity_association_pdr_extract(pdr.data(), pdr.size(), &numEntities,
257                                         &entities);
258     if (numEntities > 0)
259     {
260         pldm_entity_node* pNode = nullptr;
261         if (!mergedHostParents)
262         {
263             pNode = pldm_entity_association_tree_find_with_locality(
264                 entityTree, &entities[0], false);
265         }
266         else
267         {
268             pNode = pldm_entity_association_tree_find_with_locality(
269                 entityTree, &entities[0], true);
270         }
271         if (!pNode)
272         {
273             return;
274         }
275 
276         Entities entityAssoc;
277         entityAssoc.push_back(pNode);
278         for (size_t i = 1; i < numEntities; ++i)
279         {
280             bool isUpdateContainerId = true;
281             if (oemPlatformHandler)
282             {
283                 isUpdateContainerId =
284                     checkIfLogicalBitSet(entities[i].entity_container_id);
285             }
286             auto node = pldm_entity_association_tree_add_entity(
287                 entityTree, &entities[i], entities[i].entity_instance_num,
288                 pNode, entityPdr->association_type, true, isUpdateContainerId,
289                 0xFFFF);
290             if (!node)
291             {
292                 continue;
293             }
294             merged = true;
295             entityAssoc.push_back(node);
296         }
297 
298         mergedHostParents = true;
299         if (merged)
300         {
301             entityAssociations.push_back(entityAssoc);
302         }
303     }
304 
305     if (merged)
306     {
307         // Update our PDR repo with the merged entity association PDRs
308         pldm_entity_node* node = nullptr;
309         pldm_find_entity_ref_in_tree(entityTree, entities[0], &node);
310         if (node == nullptr)
311         {
312             error("Failed to find reference of the entity in the tree");
313         }
314         else
315         {
316             int rc = 0;
317             if (oemPlatformHandler)
318             {
319                 auto record = oemPlatformHandler->fetchLastBMCRecord(repo);
320 
321                 uint32_t record_handle =
322                     pldm_pdr_get_record_handle(repo, record);
323 
324                 rc =
325                     pldm_entity_association_pdr_add_from_node_with_record_handle(
326                         node, repo, &entities, numEntities, true,
327                         TERMINUS_HANDLE, (record_handle + 1));
328             }
329             else
330             {
331                 rc = pldm_entity_association_pdr_add_from_node(
332                     node, repo, &entities, numEntities, true, TERMINUS_HANDLE);
333             }
334 
335             if (rc)
336             {
337                 error(
338                     "Failed to add entity association PDR from node, response code '{RC}'",
339                     "RC", rc);
340             }
341         }
342     }
343     free(entities);
344 }
345 
sendPDRRepositoryChgEvent(std::vector<uint8_t> && pdrTypes,uint8_t eventDataFormat)346 void HostPDRHandler::sendPDRRepositoryChgEvent(std::vector<uint8_t>&& pdrTypes,
347                                                uint8_t eventDataFormat)
348 {
349     assert(eventDataFormat == FORMAT_IS_PDR_HANDLES);
350 
351     // Extract from the PDR repo record handles of PDRs we want the host
352     // to pull up.
353     std::vector<uint8_t> eventDataOps{PLDM_RECORDS_ADDED};
354     std::vector<uint8_t> numsOfChangeEntries(1);
355     std::vector<std::vector<ChangeEntry>> changeEntries(
356         numsOfChangeEntries.size());
357     for (auto pdrType : pdrTypes)
358     {
359         const pldm_pdr_record* record{};
360         do
361         {
362             record = pldm_pdr_find_record_by_type(repo, pdrType, record,
363                                                   nullptr, nullptr);
364             if (record && pldm_pdr_record_is_remote(record))
365             {
366                 changeEntries[0].push_back(
367                     pldm_pdr_get_record_handle(repo, record));
368             }
369         } while (record);
370     }
371     if (changeEntries.empty())
372     {
373         return;
374     }
375     numsOfChangeEntries[0] = changeEntries[0].size();
376 
377     // Encode PLDM platform event msg to indicate a PDR repo change.
378     size_t maxSize = PLDM_PDR_REPOSITORY_CHG_EVENT_MIN_LENGTH +
379                      PLDM_PDR_REPOSITORY_CHANGE_RECORD_MIN_LENGTH +
380                      changeEntries[0].size() * sizeof(uint32_t);
381     std::vector<uint8_t> eventDataVec{};
382     eventDataVec.resize(maxSize);
383     auto eventData = new (eventDataVec.data())
384         pldm_pdr_repository_chg_event_data;
385     size_t actualSize{};
386     auto firstEntry = changeEntries[0].data();
387     auto rc = encode_pldm_pdr_repository_chg_event_data(
388         eventDataFormat, 1, eventDataOps.data(), numsOfChangeEntries.data(),
389         &firstEntry, eventData, &actualSize, maxSize);
390     if (rc != PLDM_SUCCESS)
391     {
392         error(
393             "Failed to encode pldm pdr repository change event data, response code '{RC}'",
394             "RC", rc);
395         return;
396     }
397     auto instanceIdResult =
398         pldm::utils::getInstanceId(instanceIdDb.next(mctp_eid));
399     if (!instanceIdResult)
400     {
401         return;
402     }
403     auto instanceId = instanceIdResult.value();
404     std::vector<uint8_t> requestMsg(
405         sizeof(pldm_msg_hdr) + PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES +
406         actualSize);
407     auto request = new (requestMsg.data()) pldm_msg;
408     rc = encode_platform_event_message_req(
409         instanceId, 1, TERMINUS_ID, PLDM_PDR_REPOSITORY_CHG_EVENT,
410         eventDataVec.data(), actualSize, request,
411         actualSize + PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES);
412     if (rc != PLDM_SUCCESS)
413     {
414         instanceIdDb.free(mctp_eid, instanceId);
415         error(
416             "Failed to encode platform event message request, response code '{RC}'",
417             "RC", rc);
418         return;
419     }
420 
421     auto platformEventMessageResponseHandler = [](mctp_eid_t /*eid*/,
422                                                   const pldm_msg* response,
423                                                   size_t respMsgLen) {
424         if (response == nullptr || !respMsgLen)
425         {
426             error(
427                 "Failed to receive response for the PDR repository changed event");
428             return;
429         }
430 
431         uint8_t completionCode{};
432         uint8_t status{};
433         auto responsePtr = reinterpret_cast<const struct pldm_msg*>(response);
434         auto rc = decode_platform_event_message_resp(responsePtr, respMsgLen,
435                                                      &completionCode, &status);
436         if (rc || completionCode)
437         {
438             error(
439                 "Failed to decode platform event message response, response code '{RC}' and completion code '{CC}'",
440                 "RC", rc, "CC", completionCode);
441         }
442     };
443 
444     rc = handler->registerRequest(
445         mctp_eid, instanceId, PLDM_PLATFORM, PLDM_PLATFORM_EVENT_MESSAGE,
446         std::move(requestMsg), std::move(platformEventMessageResponseHandler));
447     if (rc)
448     {
449         error(
450             "Failed to send the PDR repository changed event request, response code '{RC}'",
451             "RC", rc);
452     }
453 }
454 
parseStateSensorPDRs(const PDRList & stateSensorPDRs)455 void HostPDRHandler::parseStateSensorPDRs(const PDRList& stateSensorPDRs)
456 {
457     for (const auto& pdr : stateSensorPDRs)
458     {
459         SensorEntry sensorEntry{};
460         const auto& [terminusHandle, sensorID, sensorInfo] =
461             responder::pdr_utils::parseStateSensorPDR(pdr);
462         sensorEntry.sensorID = sensorID;
463         try
464         {
465             sensorEntry.terminusID = std::get<0>(tlPDRInfo.at(terminusHandle));
466         }
467         // If there is no mapping for terminusHandle assign the reserved TID
468         // value of 0xFF to indicate that.
469         catch (const std::out_of_range&)
470         {
471             sensorEntry.terminusID = PLDM_TID_RESERVED;
472         }
473         sensorMap.emplace(sensorEntry, std::move(sensorInfo));
474     }
475 }
476 
processHostPDRs(mctp_eid_t,const pldm_msg * response,size_t respMsgLen)477 void HostPDRHandler::processHostPDRs(
478     mctp_eid_t /*eid*/, const pldm_msg* response, size_t respMsgLen)
479 {
480     static bool merged = false;
481     static PDRList stateSensorPDRs{};
482     static PDRList fruRecordSetPDRs{};
483     uint32_t nextRecordHandle{};
484     uint8_t tlEid = 0;
485     bool tlValid = true;
486     uint32_t rh = 0;
487     uint16_t terminusHandle = 0;
488     uint16_t pdrTerminusHandle = 0;
489     uint8_t tid = 0;
490 
491     uint8_t completionCode{};
492     uint32_t nextDataTransferHandle{};
493     uint8_t transferFlag{};
494     uint16_t respCount{};
495     uint8_t transferCRC{};
496     if (response == nullptr || !respMsgLen)
497     {
498         error("Failed to receive response for the GetPDR command");
499         pldm::utils::reportError(
500             "xyz.openbmc_project.PLDM.Error.GetPDR.PDRExchangeFailure");
501         return;
502     }
503 
504     auto rc = decode_get_pdr_resp(
505         response, respMsgLen /*- sizeof(pldm_msg_hdr)*/, &completionCode,
506         &nextRecordHandle, &nextDataTransferHandle, &transferFlag, &respCount,
507         nullptr, 0, &transferCRC);
508     std::vector<uint8_t> responsePDRMsg;
509     responsePDRMsg.resize(respMsgLen + sizeof(pldm_msg_hdr));
510     memcpy(responsePDRMsg.data(), response, respMsgLen + sizeof(pldm_msg_hdr));
511     if (rc != PLDM_SUCCESS)
512     {
513         error(
514             "Failed to decode getPDR response for next record handle '{NEXT_RECORD_HANDLE}', response code '{RC}'",
515             "NEXT_RECORD_HANDLE", nextRecordHandle, "RC", rc);
516         return;
517     }
518     else
519     {
520         std::vector<uint8_t> pdr(respCount, 0);
521         rc = decode_get_pdr_resp(response, respMsgLen, &completionCode,
522                                  &nextRecordHandle, &nextDataTransferHandle,
523                                  &transferFlag, &respCount, pdr.data(),
524                                  respCount, &transferCRC);
525         if (rc != PLDM_SUCCESS || completionCode != PLDM_SUCCESS)
526         {
527             error(
528                 "Failed to decode getPDR response for next record handle '{NEXT_RECORD_HANDLE}', next data transfer handle '{DATA_TRANSFER_HANDLE}' and transfer flag '{FLAG}', response code '{RC}' and completion code '{CC}'",
529                 "NEXT_RECORD_HANDLE", nextRecordHandle, "DATA_TRANSFER_HANDLE",
530                 nextDataTransferHandle, "FLAG", transferFlag, "RC", rc, "CC",
531                 completionCode);
532             return;
533         }
534         else
535         {
536             // when nextRecordHandle is 0, we need the recordHandle of the last
537             // PDR and not 0-1.
538             if (!nextRecordHandle)
539             {
540                 rh = nextRecordHandle;
541             }
542             else
543             {
544                 rh = nextRecordHandle - 1;
545             }
546 
547             auto pdrHdr = new (pdr.data()) pldm_pdr_hdr;
548             if (!rh)
549             {
550                 rh = pdrHdr->record_handle;
551             }
552 
553             if (pdrHdr->type == PLDM_PDR_ENTITY_ASSOCIATION)
554             {
555                 this->mergeEntityAssociations(pdr, respCount, rh);
556                 merged = true;
557             }
558             else
559             {
560                 if (pdrHdr->type == PLDM_TERMINUS_LOCATOR_PDR)
561                 {
562                     pdrTerminusHandle =
563                         extractTerminusHandle<pldm_terminus_locator_pdr>(pdr);
564                     auto tlpdr =
565                         reinterpret_cast<const pldm_terminus_locator_pdr*>(
566                             pdr.data());
567 
568                     terminusHandle = tlpdr->terminus_handle;
569                     tid = tlpdr->tid;
570                     auto terminus_locator_type = tlpdr->terminus_locator_type;
571                     if (terminus_locator_type ==
572                         PLDM_TERMINUS_LOCATOR_TYPE_MCTP_EID)
573                     {
574                         auto locatorValue = reinterpret_cast<
575                             const pldm_terminus_locator_type_mctp_eid*>(
576                             tlpdr->terminus_locator_value);
577                         tlEid = static_cast<uint8_t>(locatorValue->eid);
578                     }
579                     if (tlpdr->validity == 0)
580                     {
581                         tlValid = false;
582                     }
583                     for (const auto& terminusMap : tlPDRInfo)
584                     {
585                         if ((terminusHandle == (terminusMap.first)) &&
586                             (get<1>(terminusMap.second) == tlEid) &&
587                             (get<2>(terminusMap.second) == tlpdr->validity))
588                         {
589                             // TL PDR already present with same validity don't
590                             // add the PDR to the repo just return
591                             return;
592                         }
593                     }
594                     tlPDRInfo.insert_or_assign(
595                         tlpdr->terminus_handle,
596                         std::make_tuple(tlpdr->tid, tlEid, tlpdr->validity));
597                 }
598                 else if (pdrHdr->type == PLDM_STATE_SENSOR_PDR)
599                 {
600                     pdrTerminusHandle =
601                         extractTerminusHandle<pldm_state_sensor_pdr>(pdr);
602                     updateContainerId<pldm_state_sensor_pdr>(entityTree, pdr);
603                     stateSensorPDRs.emplace_back(pdr);
604                 }
605                 else if (pdrHdr->type == PLDM_PDR_FRU_RECORD_SET)
606                 {
607                     pdrTerminusHandle =
608                         extractTerminusHandle<pldm_pdr_fru_record_set>(pdr);
609                     updateContainerId<pldm_pdr_fru_record_set>(entityTree, pdr);
610                     fruRecordSetPDRs.emplace_back(pdr);
611                 }
612                 else if (pdrHdr->type == PLDM_STATE_EFFECTER_PDR)
613                 {
614                     pdrTerminusHandle =
615                         extractTerminusHandle<pldm_state_effecter_pdr>(pdr);
616                     updateContainerId<pldm_state_effecter_pdr>(entityTree, pdr);
617                 }
618                 else if (pdrHdr->type == PLDM_NUMERIC_EFFECTER_PDR)
619                 {
620                     pdrTerminusHandle =
621                         extractTerminusHandle<pldm_numeric_effecter_value_pdr>(
622                             pdr);
623                     updateContainerId<pldm_numeric_effecter_value_pdr>(
624                         entityTree, pdr);
625                 }
626                 // if the TLPDR is invalid update the repo accordingly
627                 if (!tlValid)
628                 {
629                     pldm_pdr_update_TL_pdr(repo, terminusHandle, tid, tlEid,
630                                            tlValid);
631 
632                     if (!isHostUp())
633                     {
634                         // The terminus PDR becomes invalid when the terminus
635                         // itself is down. We don't need to do PDR exchange in
636                         // that case, so setting the next record handle to 0.
637                         nextRecordHandle = 0;
638                     }
639                 }
640                 else
641                 {
642                     rc = pldm_pdr_add(repo, pdr.data(), respCount, true,
643                                       pdrTerminusHandle, &rh);
644                     if (rc)
645                     {
646                         // pldm_pdr_add() assert()ed on failure to add a PDR.
647                         throw std::runtime_error("Failed to add PDR");
648                     }
649                 }
650             }
651         }
652     }
653     if (!nextRecordHandle)
654     {
655         updateEntityAssociation(entityAssociations, entityTree, objPathMap,
656                                 entityMaps, oemPlatformHandler);
657         if (oemUtilsHandler)
658         {
659             oemUtilsHandler->setCoreCount(entityAssociations, entityMaps);
660         }
661         /*received last record*/
662         this->parseStateSensorPDRs(stateSensorPDRs);
663         this->createDbusObjects(fruRecordSetPDRs);
664         if (isHostUp())
665         {
666             this->setHostSensorState(stateSensorPDRs);
667         }
668         stateSensorPDRs.clear();
669         fruRecordSetPDRs.clear();
670         entityAssociations.clear();
671 
672         if (merged)
673         {
674             merged = false;
675             deferredPDRRepoChgEvent =
676                 std::make_unique<sdeventplus::source::Defer>(
677                     event,
678                     std::bind(
679                         std::mem_fn((&HostPDRHandler::_processPDRRepoChgEvent)),
680                         this, std::placeholders::_1));
681         }
682     }
683     else
684     {
685         if (modifiedPDRRecordHandles.empty() && isHostPdrModified)
686         {
687             isHostPdrModified = false;
688         }
689         else
690         {
691             deferredFetchPDREvent =
692                 std::make_unique<sdeventplus::source::Defer>(
693                     event,
694                     std::bind(
695                         std::mem_fn((&HostPDRHandler::_processFetchPDREvent)),
696                         this, nextRecordHandle, std::placeholders::_1));
697         }
698     }
699 }
700 
_processPDRRepoChgEvent(sdeventplus::source::EventBase &)701 void HostPDRHandler::_processPDRRepoChgEvent(
702     sdeventplus::source::EventBase& /*source */)
703 {
704     deferredPDRRepoChgEvent.reset();
705     this->sendPDRRepositoryChgEvent(
706         std::vector<uint8_t>(1, PLDM_PDR_ENTITY_ASSOCIATION),
707         FORMAT_IS_PDR_HANDLES);
708 }
709 
_processFetchPDREvent(uint32_t nextRecordHandle,sdeventplus::source::EventBase &)710 void HostPDRHandler::_processFetchPDREvent(
711     uint32_t nextRecordHandle, sdeventplus::source::EventBase& /*source */)
712 {
713     deferredFetchPDREvent.reset();
714     if (!this->pdrRecordHandles.empty())
715     {
716         nextRecordHandle = this->pdrRecordHandles.front();
717         this->pdrRecordHandles.pop_front();
718     }
719     if (isHostPdrModified && (!this->modifiedPDRRecordHandles.empty()))
720     {
721         nextRecordHandle = this->modifiedPDRRecordHandles.front();
722         this->modifiedPDRRecordHandles.pop_front();
723     }
724     this->getHostPDR(nextRecordHandle);
725 }
726 
setHostFirmwareCondition()727 void HostPDRHandler::setHostFirmwareCondition()
728 {
729     responseReceived = false;
730     auto instanceIdResult =
731         pldm::utils::getInstanceId(instanceIdDb.next(mctp_eid));
732     if (!instanceIdResult)
733     {
734         return;
735     }
736     auto instanceId = instanceIdResult.value();
737     std::vector<uint8_t> requestMsg(
738         sizeof(pldm_msg_hdr) + PLDM_GET_VERSION_REQ_BYTES);
739     auto request = new (requestMsg.data()) pldm_msg;
740     auto rc = encode_get_version_req(instanceId, 0, PLDM_GET_FIRSTPART,
741                                      PLDM_BASE, request);
742     if (rc != PLDM_SUCCESS)
743     {
744         error("Failed to encode GetPLDMVersion, response code {RC}", "RC",
745               lg2::hex, rc);
746         instanceIdDb.free(mctp_eid, instanceId);
747         return;
748     }
749 
750     auto getPLDMVersionHandler = [this](mctp_eid_t /*eid*/,
751                                         const pldm_msg* response,
752                                         size_t respMsgLen) {
753         if (response == nullptr || !respMsgLen)
754         {
755             error(
756                 "Failed to receive response for getPLDMVersion command, Host seems to be off");
757             return;
758         }
759         info("Getting the response code '{RC}'", "RC", lg2::hex,
760              response->payload[0]);
761         this->responseReceived = true;
762     };
763     rc = handler->registerRequest(mctp_eid, instanceId, PLDM_BASE,
764                                   PLDM_GET_PLDM_VERSION, std::move(requestMsg),
765                                   std::move(getPLDMVersionHandler));
766     if (rc)
767     {
768         error(
769             "Failed to discover remote terminus state. Assuming remote terminus as off");
770     }
771 }
772 
isHostUp()773 bool HostPDRHandler::isHostUp()
774 {
775     return responseReceived;
776 }
777 
setHostSensorState(const PDRList & stateSensorPDRs)778 void HostPDRHandler::setHostSensorState(const PDRList& stateSensorPDRs)
779 {
780     for (const auto& stateSensorPDR : stateSensorPDRs)
781     {
782         auto pdr = reinterpret_cast<const pldm_state_sensor_pdr*>(
783             stateSensorPDR.data());
784 
785         if (!pdr)
786         {
787             error("Failed to get state sensor PDR");
788             pldm::utils::reportError(
789                 "xyz.openbmc_project.bmc.pldm.InternalFailure");
790             return;
791         }
792 
793         uint16_t sensorId = pdr->sensor_id;
794 
795         for (const auto& [terminusHandle, terminusInfo] : tlPDRInfo)
796         {
797             if (terminusHandle == pdr->terminus_handle)
798             {
799                 if (std::get<2>(terminusInfo) == PLDM_TL_PDR_VALID)
800                 {
801                     mctp_eid = std::get<1>(terminusInfo);
802                 }
803 
804                 bitfield8_t sensorRearm;
805                 sensorRearm.byte = 0;
806                 uint8_t tid = std::get<0>(terminusInfo);
807 
808                 auto instanceIdResult =
809                     pldm::utils::getInstanceId(instanceIdDb.next(mctp_eid));
810                 if (!instanceIdResult)
811                 {
812                     return;
813                 }
814                 auto instanceId = instanceIdResult.value();
815                 std::vector<uint8_t> requestMsg(
816                     sizeof(pldm_msg_hdr) +
817                     PLDM_GET_STATE_SENSOR_READINGS_REQ_BYTES);
818                 auto request = new (requestMsg.data()) pldm_msg;
819                 auto rc = encode_get_state_sensor_readings_req(
820                     instanceId, sensorId, sensorRearm, 0, request);
821 
822                 if (rc != PLDM_SUCCESS)
823                 {
824                     instanceIdDb.free(mctp_eid, instanceId);
825                     error(
826                         "Failed to encode get state sensor readings request for sensorID '{SENSOR_ID}' and  instanceID '{INSTANCE}', response code '{RC}'",
827                         "SENSOR_ID", sensorId, "INSTANCE", instanceId, "RC",
828                         rc);
829                     pldm::utils::reportError(
830                         "xyz.openbmc_project.bmc.pldm.InternalFailure");
831                     return;
832                 }
833 
834                 auto getStateSensorReadingRespHandler = [=, this](
835                                                             mctp_eid_t /*eid*/,
836                                                             const pldm_msg*
837                                                                 response,
838                                                             size_t respMsgLen) {
839                     if (response == nullptr || !respMsgLen)
840                     {
841                         error(
842                             "Failed to receive response for get state sensor reading command for sensorID '{SENSOR_ID}' and  instanceID '{INSTANCE}'",
843                             "SENSOR_ID", sensorId, "INSTANCE", instanceId);
844                         return;
845                     }
846                     std::array<get_sensor_state_field, 8> stateField{};
847                     uint8_t completionCode = 0;
848                     uint8_t comp_sensor_count = 0;
849 
850                     auto rc = decode_get_state_sensor_readings_resp(
851                         response, respMsgLen, &completionCode,
852                         &comp_sensor_count, stateField.data());
853 
854                     if (rc != PLDM_SUCCESS || completionCode != PLDM_SUCCESS)
855                     {
856                         error(
857                             "Failed to decode get state sensor readings response for sensorID '{SENSOR_ID}' and  instanceID '{INSTANCE}', response code'{RC}' and completion code '{CC}'",
858                             "SENSOR_ID", sensorId, "INSTANCE", instanceId, "RC",
859                             rc, "CC", completionCode);
860                         pldm::utils::reportError(
861                             "xyz.openbmc_project.bmc.pldm.InternalFailure");
862                     }
863 
864                     uint8_t eventState;
865                     uint8_t previousEventState;
866 
867                     for (uint8_t sensorOffset = 0;
868                          sensorOffset < comp_sensor_count; sensorOffset++)
869                     {
870                         eventState = stateField[sensorOffset].present_state;
871                         previousEventState =
872                             stateField[sensorOffset].previous_state;
873 
874                         emitStateSensorEventSignal(tid, sensorId, sensorOffset,
875                                                    eventState,
876                                                    previousEventState);
877 
878                         SensorEntry sensorEntry{tid, sensorId};
879 
880                         pldm::pdr::EntityInfo entityInfo{};
881                         pldm::pdr::CompositeSensorStates
882                             compositeSensorStates{};
883                         std::vector<pldm::pdr::StateSetId> stateSetIds{};
884 
885                         try
886                         {
887                             std::tie(entityInfo, compositeSensorStates,
888                                      stateSetIds) =
889                                 lookupSensorInfo(sensorEntry);
890                         }
891                         catch (const std::out_of_range&)
892                         {
893                             try
894                             {
895                                 sensorEntry.terminusID = PLDM_TID_RESERVED;
896                                 std::tie(entityInfo, compositeSensorStates,
897                                          stateSetIds) =
898                                     lookupSensorInfo(sensorEntry);
899                             }
900                             catch (const std::out_of_range&)
901                             {
902                                 error("No mapping for the events");
903                             }
904                         }
905 
906                         if ((compositeSensorStates.size() > 1) &&
907                             (sensorOffset > (compositeSensorStates.size() - 1)))
908                         {
909                             error(
910                                 "Error Invalid data, Invalid sensor offset '{SENSOR_OFFSET}'",
911                                 "SENSOR_OFFSET", sensorOffset);
912                             return;
913                         }
914 
915                         const auto& possibleStates =
916                             compositeSensorStates[sensorOffset];
917                         if (possibleStates.find(eventState) ==
918                             possibleStates.end())
919                         {
920                             error(
921                                 "Error invalid_data, Invalid event state '{STATE}'",
922                                 "STATE", eventState);
923                             return;
924                         }
925                         const auto& [containerId, entityType, entityInstance] =
926                             entityInfo;
927                         auto stateSetId = stateSetIds[sensorOffset];
928                         pldm::responder::events::StateSensorEntry
929                             stateSensorEntry{containerId,    entityType,
930                                              entityInstance, sensorOffset,
931                                              stateSetId,     false};
932                         handleStateSensorEvent(stateSensorEntry, eventState);
933                     }
934                 };
935 
936                 rc = handler->registerRequest(
937                     mctp_eid, instanceId, PLDM_PLATFORM,
938                     PLDM_GET_STATE_SENSOR_READINGS, std::move(requestMsg),
939                     std::move(getStateSensorReadingRespHandler));
940 
941                 if (rc != PLDM_SUCCESS)
942                 {
943                     error(
944                         "Failed to send request to get state sensor reading on remote terminus for sensorID '{SENSOR_ID}' and  instanceID '{INSTANCE}', response code '{RC}'",
945                         "SENSOR_ID", sensorId, "INSTANCE", instanceId, "RC",
946                         rc);
947                 }
948             }
949         }
950     }
951 }
952 
getFRURecordTableMetadataByRemote(const PDRList & fruRecordSetPDRs)953 void HostPDRHandler::getFRURecordTableMetadataByRemote(
954     const PDRList& fruRecordSetPDRs)
955 {
956     auto instanceIdResult =
957         pldm::utils::getInstanceId(instanceIdDb.next(mctp_eid));
958     if (!instanceIdResult)
959     {
960         return;
961     }
962     auto instanceId = instanceIdResult.value();
963     std::vector<uint8_t> requestMsg(
964         sizeof(pldm_msg_hdr) + PLDM_GET_FRU_RECORD_TABLE_METADATA_REQ_BYTES);
965 
966     // GetFruRecordTableMetadata
967     auto request = new (requestMsg.data()) pldm_msg;
968     auto rc = encode_get_fru_record_table_metadata_req(
969         instanceId, request, requestMsg.size() - sizeof(pldm_msg_hdr));
970     if (rc != PLDM_SUCCESS)
971     {
972         instanceIdDb.free(mctp_eid, instanceId);
973         error(
974             "Failed to encode get fru record table metadata request, response code '{RC}'",
975             "RC", lg2::hex, rc);
976         return;
977     }
978 
979     auto getFruRecordTableMetadataResponseHandler = [this, fruRecordSetPDRs](
980                                                         mctp_eid_t /*eid*/,
981                                                         const pldm_msg*
982                                                             response,
983                                                         size_t respMsgLen) {
984         if (response == nullptr || !respMsgLen)
985         {
986             error(
987                 "Failed to receive response for the get fru record table metadata");
988             return;
989         }
990 
991         uint8_t cc = 0;
992         uint8_t fru_data_major_version, fru_data_minor_version;
993         uint32_t fru_table_maximum_size, fru_table_length;
994         uint16_t total_record_set_identifiers;
995         uint16_t total;
996         uint32_t checksum;
997 
998         auto rc = decode_get_fru_record_table_metadata_resp(
999             response, respMsgLen, &cc, &fru_data_major_version,
1000             &fru_data_minor_version, &fru_table_maximum_size, &fru_table_length,
1001             &total_record_set_identifiers, &total, &checksum);
1002 
1003         if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
1004         {
1005             error(
1006                 "Failed to decode get fru record table metadata response, response code '{RC}' and completion code '{CC}'",
1007                 "RC", lg2::hex, rc, "CC", cc);
1008             return;
1009         }
1010 
1011         // pass total to getFRURecordTableByRemote
1012         this->getFRURecordTableByRemote(fruRecordSetPDRs, total);
1013     };
1014 
1015     rc = handler->registerRequest(
1016         mctp_eid, instanceId, PLDM_FRU, PLDM_GET_FRU_RECORD_TABLE_METADATA,
1017         std::move(requestMsg),
1018         std::move(getFruRecordTableMetadataResponseHandler));
1019     if (rc != PLDM_SUCCESS)
1020     {
1021         error(
1022             "Failed to send the the set state effecter states request, response code '{RC}'",
1023             "RC", rc);
1024     }
1025 
1026     return;
1027 }
1028 
getFRURecordTableByRemote(const PDRList & fruRecordSetPDRs,uint16_t totalTableRecords)1029 void HostPDRHandler::getFRURecordTableByRemote(const PDRList& fruRecordSetPDRs,
1030                                                uint16_t totalTableRecords)
1031 {
1032     fruRecordData.clear();
1033 
1034     if (!totalTableRecords)
1035     {
1036         error("Failed to get fru record table");
1037         return;
1038     }
1039 
1040     auto instanceIdResult =
1041         pldm::utils::getInstanceId(instanceIdDb.next(mctp_eid));
1042     if (!instanceIdResult)
1043     {
1044         return;
1045     }
1046     auto instanceId = instanceIdResult.value();
1047     std::vector<uint8_t> requestMsg(
1048         sizeof(pldm_msg_hdr) + PLDM_GET_FRU_RECORD_TABLE_REQ_BYTES);
1049 
1050     // send the getFruRecordTable command
1051     auto request = new (requestMsg.data()) pldm_msg;
1052     auto rc = encode_get_fru_record_table_req(
1053         instanceId, 0, PLDM_GET_FIRSTPART, request,
1054         requestMsg.size() - sizeof(pldm_msg_hdr));
1055     if (rc != PLDM_SUCCESS)
1056     {
1057         instanceIdDb.free(mctp_eid, instanceId);
1058         error(
1059             "Failed to encode get fru record table request, response code '{RC}'",
1060             "RC", lg2::hex, rc);
1061         return;
1062     }
1063 
1064     auto getFruRecordTableResponseHandler = [totalTableRecords, this,
1065                                              fruRecordSetPDRs](
1066                                                 mctp_eid_t /*eid*/,
1067                                                 const pldm_msg* response,
1068                                                 size_t respMsgLen) {
1069         if (response == nullptr || !respMsgLen)
1070         {
1071             error("Failed to receive response for the get fru record table");
1072             return;
1073         }
1074 
1075         uint8_t cc = 0;
1076         uint32_t next_data_transfer_handle = 0;
1077         uint8_t transfer_flag = 0;
1078         size_t fru_record_table_length = 0;
1079         std::vector<uint8_t> fru_record_table_data(
1080             respMsgLen - sizeof(pldm_msg_hdr));
1081         auto responsePtr = reinterpret_cast<const struct pldm_msg*>(response);
1082         auto rc = decode_get_fru_record_table_resp(
1083             responsePtr, respMsgLen, &cc, &next_data_transfer_handle,
1084             &transfer_flag, fru_record_table_data.data(),
1085             &fru_record_table_length);
1086 
1087         if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
1088         {
1089             error(
1090                 "Failed to decode get fru record table resp, response code '{RC}' and completion code '{CC}'",
1091                 "RC", lg2::hex, rc, "CC", cc);
1092             return;
1093         }
1094 
1095         fruRecordData = responder::pdr_utils::parseFruRecordTable(
1096             fru_record_table_data.data(), fru_record_table_length);
1097 
1098         if (totalTableRecords != fruRecordData.size())
1099         {
1100             fruRecordData.clear();
1101 
1102             error("Failed to parse fru record data format.");
1103             return;
1104         }
1105 
1106         this->setFRUDataOnDBus(fruRecordSetPDRs, fruRecordData);
1107     };
1108 
1109     rc = handler->registerRequest(
1110         mctp_eid, instanceId, PLDM_FRU, PLDM_GET_FRU_RECORD_TABLE,
1111         std::move(requestMsg), std::move(getFruRecordTableResponseHandler));
1112     if (rc != PLDM_SUCCESS)
1113     {
1114         error("Failed to send the the set state effecter states request");
1115     }
1116 }
1117 
getRSI(const PDRList & fruRecordSetPDRs,const pldm_entity & entity)1118 std::optional<uint16_t> HostPDRHandler::getRSI(const PDRList& fruRecordSetPDRs,
1119                                                const pldm_entity& entity)
1120 {
1121     for (const auto& pdr : fruRecordSetPDRs)
1122     {
1123         auto fruPdr = reinterpret_cast<const pldm_pdr_fru_record_set*>(
1124             const_cast<uint8_t*>(pdr.data()) + sizeof(pldm_pdr_hdr));
1125 
1126         if (fruPdr->entity_type == entity.entity_type &&
1127             fruPdr->entity_instance == entity.entity_instance_num &&
1128             fruPdr->container_id == entity.entity_container_id)
1129         {
1130             return fruPdr->fru_rsi;
1131         }
1132     }
1133 
1134     return std::nullopt;
1135 }
1136 
setFRUDataOnDBus(const PDRList & fruRecordSetPDRs,const std::vector<responder::pdr_utils::FruRecordDataFormat> & fruRecordData)1137 void HostPDRHandler::setFRUDataOnDBus(
1138     [[maybe_unused]] const PDRList& fruRecordSetPDRs,
1139     [[maybe_unused]] const std::vector<
1140         responder::pdr_utils::FruRecordDataFormat>& fruRecordData)
1141 {
1142 #ifdef OEM_IBM
1143     for (const auto& entity : objPathMap)
1144     {
1145         pldm_entity node = pldm_entity_extract(entity.second);
1146         auto fruRSI = getRSI(fruRecordSetPDRs, node);
1147 
1148         for (const auto& data : fruRecordData)
1149         {
1150             if (!fruRSI || *fruRSI != data.fruRSI)
1151             {
1152                 continue;
1153             }
1154 
1155             if (data.fruRecType == PLDM_FRU_RECORD_TYPE_OEM)
1156             {
1157                 for (const auto& tlv : data.fruTLV)
1158                 {
1159                     if (tlv.fruFieldType ==
1160                         PLDM_OEM_FRU_FIELD_TYPE_LOCATION_CODE)
1161                     {
1162                         CustomDBus::getCustomDBus().setLocationCode(
1163                             entity.first,
1164                             std::string(reinterpret_cast<const char*>(
1165                                             tlv.fruFieldValue.data()),
1166                                         tlv.fruFieldLen));
1167                     }
1168                 }
1169             }
1170         }
1171     }
1172 #endif
1173 }
1174 
setPresentPropertyStatus(const std::string & path)1175 void HostPDRHandler::setPresentPropertyStatus(const std::string& path)
1176 {
1177     CustomDBus::getCustomDBus().updateItemPresentStatus(path, true);
1178 }
1179 
setAvailabilityState(const std::string & path)1180 void HostPDRHandler::setAvailabilityState(const std::string& path)
1181 {
1182     CustomDBus::getCustomDBus().setAvailabilityState(path, true);
1183 }
1184 
createDbusObjects(const PDRList & fruRecordSetPDRs)1185 void HostPDRHandler::createDbusObjects(const PDRList& fruRecordSetPDRs)
1186 {
1187     // Creating and Refreshing dbus hosted by remote PLDM entity Fru PDRs
1188 
1189     for (const auto& entity : objPathMap)
1190     {
1191         // update the Present Property
1192         setPresentPropertyStatus(entity.first);
1193         // Implement & update the Availability to true
1194         setAvailabilityState(entity.first);
1195 
1196         pldm_entity node = pldm_entity_extract(entity.second);
1197         switch (node.entity_type)
1198         {
1199             case PLDM_ENTITY_PROC | 0x8000:
1200                 CustomDBus::getCustomDBus().implementCpuCoreInterface(
1201                     entity.first);
1202                 break;
1203             case PLDM_ENTITY_SYSTEM_CHASSIS:
1204                 CustomDBus::getCustomDBus().implementChassisInterface(
1205                     entity.first);
1206                 break;
1207             case PLDM_ENTITY_POWER_SUPPLY:
1208                 CustomDBus::getCustomDBus().implementPowerSupplyInterface(
1209                     entity.first);
1210                 break;
1211             case PLDM_ENTITY_CHASSIS_FRONT_PANEL_BOARD:
1212                 CustomDBus::getCustomDBus().implementPanelInterface(
1213                     entity.first);
1214                 break;
1215             case PLDM_ENTITY_POWER_CONVERTER:
1216                 CustomDBus::getCustomDBus().implementVRMInterface(entity.first);
1217                 break;
1218             case PLDM_ENTITY_SLOT:
1219                 CustomDBus::getCustomDBus().implementPCIeSlotInterface(
1220                     entity.first);
1221                 break;
1222             case PLDM_ENTITY_CONNECTOR:
1223                 CustomDBus::getCustomDBus().implementConnecterInterface(
1224                     entity.first);
1225                 break;
1226             case PLDM_ENTITY_BOARD:
1227                 CustomDBus::getCustomDBus().implementBoard(entity.first);
1228                 break;
1229             case PLDM_ENTITY_CARD:
1230                 CustomDBus::getCustomDBus().implementPCIeDeviceInterface(
1231                     entity.first);
1232                 break;
1233             case PLDM_ENTITY_SYS_BOARD:
1234                 CustomDBus::getCustomDBus().implementMotherboardInterface(
1235                     entity.first);
1236                 break;
1237             case PLDM_ENTITY_FAN:
1238                 CustomDBus::getCustomDBus().implementFanInterface(entity.first);
1239                 break;
1240             case PLDM_ENTITY_IO_MODULE:
1241                 CustomDBus::getCustomDBus().implementFabricAdapter(
1242                     entity.first);
1243                 break;
1244             default:
1245                 break;
1246         }
1247     }
1248     getFRURecordTableMetadataByRemote(fruRecordSetPDRs);
1249 }
1250 
1251 } // namespace pldm
1252