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