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