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 "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 constexpr auto fruJson = "host_frus.json";
34 const Json emptyJson{};
35 const std::vector<Json> emptyJsonList{};
36 
37 template <typename T>
38 uint16_t extractTerminusHandle(std::vector<uint8_t>& pdr)
39 {
40     T* var = nullptr;
41     if (std::is_same<T, pldm_pdr_fru_record_set>::value)
42     {
43         var = (T*)(pdr.data() + sizeof(pldm_pdr_hdr));
44     }
45     else
46     {
47         var = (T*)(pdr.data());
48     }
49     if (var != nullptr)
50     {
51         return var->terminus_handle;
52     }
53     return TERMINUS_HANDLE;
54 }
55 
56 template <typename T>
57 void updateContainerId(pldm_entity_association_tree* entityTree,
58                        std::vector<uint8_t>& pdr)
59 {
60     T* t = nullptr;
61     if (entityTree == nullptr)
62     {
63         return;
64     }
65     if (std::is_same<T, pldm_pdr_fru_record_set>::value)
66     {
67         t = (T*)(pdr.data() + sizeof(pldm_pdr_hdr));
68     }
69     else
70     {
71         t = (T*)(pdr.data());
72     }
73     if (t == nullptr)
74     {
75         return;
76     }
77 
78     pldm_entity entity{t->entity_type, t->entity_instance, t->container_id};
79     auto node = pldm_entity_association_tree_find_with_locality(entityTree,
80                                                                 &entity, true);
81     if (node)
82     {
83         pldm_entity e = pldm_entity_extract(node);
84         t->container_id = e.entity_container_id;
85     }
86 }
87 
88 HostPDRHandler::HostPDRHandler(
89     int mctp_fd, uint8_t mctp_eid, sdeventplus::Event& event, pldm_pdr* repo,
90     const std::string& eventsJsonsDir, pldm_entity_association_tree* entityTree,
91     pldm_entity_association_tree* bmcEntityTree,
92     pldm::InstanceIdDb& instanceIdDb,
93     pldm::requester::Handler<pldm::requester::Request>* handler,
94     pldm::responder::oem_platform::Handler* oemPlatformHandler) :
95     mctp_fd(mctp_fd),
96     mctp_eid(mctp_eid), event(event), repo(repo),
97     stateSensorHandler(eventsJsonsDir), entityTree(entityTree),
98     bmcEntityTree(bmcEntityTree), instanceIdDb(instanceIdDb), handler(handler),
99     oemPlatformHandler(oemPlatformHandler),
100     entityMaps(parseEntityMap(ENTITY_MAP_JSON))
101 {
102     mergedHostParents = false;
103     hostOffMatch = std::make_unique<sdbusplus::bus::match_t>(
104         pldm::utils::DBusHandler::getBus(),
105         propertiesChanged("/xyz/openbmc_project/state/host0",
106                           "xyz.openbmc_project.State.Host"),
107         [this, repo, entityTree, bmcEntityTree](sdbusplus::message_t& msg) {
108         DbusChangedProps props{};
109         std::string intf;
110         msg.read(intf, props);
111         const auto itr = props.find("CurrentHostState");
112         if (itr != props.end())
113         {
114             PropertyValue value = itr->second;
115             auto propVal = std::get<std::string>(value);
116             if (propVal == "xyz.openbmc_project.State.Host.HostState.Off")
117             {
118                 // Delete all the remote terminus information
119                 std::erase_if(tlPDRInfo, [](const auto& item) {
120                     auto const& [key, value] = item;
121                     return key != TERMINUS_HANDLE;
122                 });
123                 pldm_pdr_remove_remote_pdrs(repo);
124                 pldm_entity_association_tree_destroy_root(entityTree);
125                 pldm_entity_association_tree_copy_root(bmcEntityTree,
126                                                        entityTree);
127                 this->sensorMap.clear();
128                 this->responseReceived = false;
129                 this->mergedHostParents = false;
130             }
131         }
132     });
133 }
134 
135 void HostPDRHandler::fetchPDR(PDRRecordHandles&& recordHandles)
136 {
137     pdrRecordHandles.clear();
138     modifiedPDRRecordHandles.clear();
139 
140     if (isHostPdrModified)
141     {
142         modifiedPDRRecordHandles = std::move(recordHandles);
143     }
144     else
145     {
146         pdrRecordHandles = std::move(recordHandles);
147     }
148 
149     // Defer the actual fetch of PDRs from the host (by queuing the call on the
150     // main event loop). That way, we can respond to the platform event msg from
151     // the host firmware.
152     pdrFetchEvent = std::make_unique<sdeventplus::source::Defer>(
153         event, std::bind(std::mem_fn(&HostPDRHandler::_fetchPDR), this,
154                          std::placeholders::_1));
155 }
156 
157 void HostPDRHandler::_fetchPDR(sdeventplus::source::EventBase& /*source*/)
158 {
159     getHostPDR();
160 }
161 
162 void HostPDRHandler::getHostPDR(uint32_t nextRecordHandle)
163 {
164     pdrFetchEvent.reset();
165 
166     std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
167                                     PLDM_GET_PDR_REQ_BYTES);
168     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
169     uint32_t recordHandle{};
170     if (!nextRecordHandle && (!modifiedPDRRecordHandles.empty()) &&
171         isHostPdrModified)
172     {
173         recordHandle = modifiedPDRRecordHandles.front();
174         modifiedPDRRecordHandles.pop_front();
175     }
176     else if (!nextRecordHandle && (!pdrRecordHandles.empty()))
177     {
178         recordHandle = pdrRecordHandles.front();
179         pdrRecordHandles.pop_front();
180     }
181     else
182     {
183         recordHandle = nextRecordHandle;
184     }
185     auto instanceId = instanceIdDb.next(mctp_eid);
186 
187     auto rc = encode_get_pdr_req(instanceId, recordHandle, 0,
188                                  PLDM_GET_FIRSTPART, UINT16_MAX, 0, request,
189                                  PLDM_GET_PDR_REQ_BYTES);
190     if (rc != PLDM_SUCCESS)
191     {
192         instanceIdDb.free(mctp_eid, instanceId);
193         error("Failed to encode get pdr request, response code '{RC}'", "RC",
194               rc);
195         return;
196     }
197 
198     rc = handler->registerRequest(
199         mctp_eid, instanceId, PLDM_PLATFORM, PLDM_GET_PDR,
200         std::move(requestMsg),
201         std::move(std::bind_front(&HostPDRHandler::processHostPDRs, this)));
202     if (rc)
203     {
204         error(
205             "Failed to send the getPDR request to remote terminus, response code '{RC}'",
206             "RC", rc);
207     }
208 }
209 
210 int HostPDRHandler::handleStateSensorEvent(const StateSensorEntry& entry,
211                                            pdr::EventState state)
212 {
213     auto rc = stateSensorHandler.eventAction(entry, state);
214     if (rc != PLDM_SUCCESS)
215     {
216         error("Failed to fetch and update D-bus property, response code '{RC}'",
217               "RC", rc);
218         return rc;
219     }
220     return PLDM_SUCCESS;
221 }
222 
223 void HostPDRHandler::mergeEntityAssociations(
224     const std::vector<uint8_t>& pdr, [[maybe_unused]] const uint32_t& size,
225     [[maybe_unused]] const uint32_t& record_handle)
226 {
227     size_t numEntities{};
228     pldm_entity* entities = nullptr;
229     bool merged = false;
230     auto entityPdr = reinterpret_cast<pldm_pdr_entity_association*>(
231         const_cast<uint8_t*>(pdr.data()) + sizeof(pldm_pdr_hdr));
232 
233     if (oemPlatformHandler &&
234         oemPlatformHandler->checkRecordHandleInRange(record_handle))
235     {
236         // Adding the remote range PDRs to the repo before merging it
237         uint32_t handle = record_handle;
238         pldm_pdr_add_check(repo, pdr.data(), size, true, 0xFFFF, &handle);
239     }
240 
241     pldm_entity_association_pdr_extract(pdr.data(), pdr.size(), &numEntities,
242                                         &entities);
243     if (numEntities > 0)
244     {
245         pldm_entity_node* pNode = nullptr;
246         if (!mergedHostParents)
247         {
248             pNode = pldm_entity_association_tree_find_with_locality(
249                 entityTree, &entities[0], false);
250         }
251         else
252         {
253             pNode = pldm_entity_association_tree_find_with_locality(
254                 entityTree, &entities[0], true);
255         }
256         if (!pNode)
257         {
258             return;
259         }
260 
261         Entities entityAssoc;
262         entityAssoc.push_back(pNode);
263         for (size_t i = 1; i < numEntities; ++i)
264         {
265             bool isUpdateContainerId = true;
266             if (oemPlatformHandler)
267             {
268                 isUpdateContainerId =
269                     checkIfLogicalBitSet(entities[i].entity_container_id);
270             }
271             auto node = pldm_entity_association_tree_add_entity(
272                 entityTree, &entities[i], entities[i].entity_instance_num,
273                 pNode, entityPdr->association_type, true, isUpdateContainerId,
274                 0xFFFF);
275             if (!node)
276             {
277                 continue;
278             }
279             merged = true;
280             entityAssoc.push_back(node);
281         }
282 
283         mergedHostParents = true;
284         if (merged)
285         {
286             entityAssociations.push_back(entityAssoc);
287         }
288     }
289 
290     if (merged)
291     {
292         // Update our PDR repo with the merged entity association PDRs
293         pldm_entity_node* node = nullptr;
294         pldm_find_entity_ref_in_tree(entityTree, entities[0], &node);
295         if (node == nullptr)
296         {
297             error("Failed to find referrence of the entity in the tree");
298         }
299         else
300         {
301             int rc = 0;
302             if (oemPlatformHandler)
303             {
304                 auto record = oemPlatformHandler->fetchLastBMCRecord(repo);
305 
306                 uint32_t record_handle = pldm_pdr_get_record_handle(repo,
307                                                                     record);
308 
309                 rc =
310                     pldm_entity_association_pdr_add_from_node_with_record_handle(
311                         node, repo, &entities, numEntities, true,
312                         TERMINUS_HANDLE, (record_handle + 1));
313             }
314             else
315             {
316                 rc = pldm_entity_association_pdr_add_from_node_check(
317                     node, repo, &entities, numEntities, true, TERMINUS_HANDLE);
318             }
319 
320             if (rc)
321             {
322                 error(
323                     "Failed to add entity association PDR from node, response code '{RC}'",
324                     "RC", rc);
325             }
326         }
327     }
328     free(entities);
329 }
330 
331 void HostPDRHandler::sendPDRRepositoryChgEvent(std::vector<uint8_t>&& pdrTypes,
332                                                uint8_t eventDataFormat)
333 {
334     assert(eventDataFormat == FORMAT_IS_PDR_HANDLES);
335 
336     // Extract from the PDR repo record handles of PDRs we want the host
337     // to pull up.
338     std::vector<uint8_t> eventDataOps{PLDM_RECORDS_ADDED};
339     std::vector<uint8_t> numsOfChangeEntries(1);
340     std::vector<std::vector<ChangeEntry>> changeEntries(
341         numsOfChangeEntries.size());
342     for (auto pdrType : pdrTypes)
343     {
344         const pldm_pdr_record* record{};
345         do
346         {
347             record = pldm_pdr_find_record_by_type(repo, pdrType, record,
348                                                   nullptr, nullptr);
349             if (record && pldm_pdr_record_is_remote(record))
350             {
351                 changeEntries[0].push_back(
352                     pldm_pdr_get_record_handle(repo, record));
353             }
354         } while (record);
355     }
356     if (changeEntries.empty())
357     {
358         return;
359     }
360     numsOfChangeEntries[0] = changeEntries[0].size();
361 
362     // Encode PLDM platform event msg to indicate a PDR repo change.
363     size_t maxSize = PLDM_PDR_REPOSITORY_CHG_EVENT_MIN_LENGTH +
364                      PLDM_PDR_REPOSITORY_CHANGE_RECORD_MIN_LENGTH +
365                      changeEntries[0].size() * sizeof(uint32_t);
366     std::vector<uint8_t> eventDataVec{};
367     eventDataVec.resize(maxSize);
368     auto eventData =
369         reinterpret_cast<struct pldm_pdr_repository_chg_event_data*>(
370             eventDataVec.data());
371     size_t actualSize{};
372     auto firstEntry = changeEntries[0].data();
373     auto rc = encode_pldm_pdr_repository_chg_event_data(
374         eventDataFormat, 1, eventDataOps.data(), numsOfChangeEntries.data(),
375         &firstEntry, eventData, &actualSize, maxSize);
376     if (rc != PLDM_SUCCESS)
377     {
378         error(
379             "Failed to encode pldm pdr repository change event data, response code '{RC}'",
380             "RC", rc);
381         return;
382     }
383     auto instanceId = instanceIdDb.next(mctp_eid);
384     std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
385                                     PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES +
386                                     actualSize);
387     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
388     rc = encode_platform_event_message_req(
389         instanceId, 1, TERMINUS_ID, PLDM_PDR_REPOSITORY_CHG_EVENT,
390         eventDataVec.data(), actualSize, request,
391         actualSize + PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES);
392     if (rc != PLDM_SUCCESS)
393     {
394         instanceIdDb.free(mctp_eid, instanceId);
395         error(
396             "Failed to encode platform event message request, response code '{RC}'",
397             "RC", rc);
398         return;
399     }
400 
401     auto platformEventMessageResponseHandler =
402         [](mctp_eid_t /*eid*/, const pldm_msg* response, size_t respMsgLen) {
403         if (response == nullptr || !respMsgLen)
404         {
405             error(
406                 "Failed to receive response for the PDR repository changed event");
407             return;
408         }
409 
410         uint8_t completionCode{};
411         uint8_t status{};
412         auto responsePtr = reinterpret_cast<const struct pldm_msg*>(response);
413         auto rc = decode_platform_event_message_resp(responsePtr, respMsgLen,
414                                                      &completionCode, &status);
415         if (rc || completionCode)
416         {
417             error(
418                 "Failed to decode platform event message response, response code '{RC}' and completion code '{CC}'",
419                 "RC", rc, "CC", completionCode);
420         }
421     };
422 
423     rc = handler->registerRequest(
424         mctp_eid, instanceId, PLDM_PLATFORM, PLDM_PLATFORM_EVENT_MESSAGE,
425         std::move(requestMsg), std::move(platformEventMessageResponseHandler));
426     if (rc)
427     {
428         error(
429             "Failed to send the PDR repository changed event request, response code '{RC}'",
430             "RC", rc);
431     }
432 }
433 
434 void HostPDRHandler::parseStateSensorPDRs(const PDRList& stateSensorPDRs)
435 {
436     for (const auto& pdr : stateSensorPDRs)
437     {
438         SensorEntry sensorEntry{};
439         const auto& [terminusHandle, sensorID, sensorInfo] =
440             responder::pdr_utils::parseStateSensorPDR(pdr);
441         sensorEntry.sensorID = sensorID;
442         try
443         {
444             sensorEntry.terminusID = std::get<0>(tlPDRInfo.at(terminusHandle));
445         }
446         // If there is no mapping for terminusHandle assign the reserved TID
447         // value of 0xFF to indicate that.
448         catch (const std::out_of_range&)
449         {
450             sensorEntry.terminusID = PLDM_TID_RESERVED;
451         }
452         sensorMap.emplace(sensorEntry, std::move(sensorInfo));
453     }
454 }
455 
456 void HostPDRHandler::processHostPDRs(mctp_eid_t /*eid*/,
457                                      const pldm_msg* response,
458                                      size_t respMsgLen)
459 {
460     static bool merged = false;
461     static PDRList stateSensorPDRs{};
462     static PDRList fruRecordSetPDRs{};
463     uint32_t nextRecordHandle{};
464     uint8_t tlEid = 0;
465     bool tlValid = true;
466     uint32_t rh = 0;
467     uint16_t terminusHandle = 0;
468     uint16_t pdrTerminusHandle = 0;
469     uint8_t tid = 0;
470 
471     uint8_t completionCode{};
472     uint32_t nextDataTransferHandle{};
473     uint8_t transferFlag{};
474     uint16_t respCount{};
475     uint8_t transferCRC{};
476     if (response == nullptr || !respMsgLen)
477     {
478         error("Failed to receive response for the GetPDR command");
479         return;
480     }
481 
482     auto rc = decode_get_pdr_resp(
483         response, respMsgLen /*- sizeof(pldm_msg_hdr)*/, &completionCode,
484         &nextRecordHandle, &nextDataTransferHandle, &transferFlag, &respCount,
485         nullptr, 0, &transferCRC);
486     std::vector<uint8_t> responsePDRMsg;
487     responsePDRMsg.resize(respMsgLen + sizeof(pldm_msg_hdr));
488     memcpy(responsePDRMsg.data(), response, respMsgLen + sizeof(pldm_msg_hdr));
489     if (rc != PLDM_SUCCESS)
490     {
491         error(
492             "Failed to decode getPDR response for next record handle '{NEXT_RECORD_HANDLE}', response code '{RC}'",
493             "NEXT_RECORD_HANDLE", nextRecordHandle, "RC", rc);
494         return;
495     }
496     else
497     {
498         std::vector<uint8_t> pdr(respCount, 0);
499         rc = decode_get_pdr_resp(response, respMsgLen, &completionCode,
500                                  &nextRecordHandle, &nextDataTransferHandle,
501                                  &transferFlag, &respCount, pdr.data(),
502                                  respCount, &transferCRC);
503         if (rc != PLDM_SUCCESS || completionCode != PLDM_SUCCESS)
504         {
505             error(
506                 "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}'",
507                 "NEXT_RECORD_HANDLE", nextRecordHandle, "DATA_TRANSFER_HANDLE",
508                 nextDataTransferHandle, "FLAG", transferFlag, "RC", rc, "CC",
509                 completionCode);
510             return;
511         }
512         else
513         {
514             // when nextRecordHandle is 0, we need the recordHandle of the last
515             // PDR and not 0-1.
516             if (!nextRecordHandle)
517             {
518                 rh = nextRecordHandle;
519             }
520             else
521             {
522                 rh = nextRecordHandle - 1;
523             }
524 
525             auto pdrHdr = reinterpret_cast<pldm_pdr_hdr*>(pdr.data());
526             if (!rh)
527             {
528                 rh = pdrHdr->record_handle;
529             }
530 
531             if (pdrHdr->type == PLDM_PDR_ENTITY_ASSOCIATION)
532             {
533                 this->mergeEntityAssociations(pdr, respCount, rh);
534                 merged = true;
535             }
536             else
537             {
538                 if (pdrHdr->type == PLDM_TERMINUS_LOCATOR_PDR)
539                 {
540                     pdrTerminusHandle =
541                         extractTerminusHandle<pldm_terminus_locator_pdr>(pdr);
542                     auto tlpdr =
543                         reinterpret_cast<const pldm_terminus_locator_pdr*>(
544                             pdr.data());
545 
546                     terminusHandle = tlpdr->terminus_handle;
547                     tid = tlpdr->tid;
548                     auto terminus_locator_type = tlpdr->terminus_locator_type;
549                     if (terminus_locator_type ==
550                         PLDM_TERMINUS_LOCATOR_TYPE_MCTP_EID)
551                     {
552                         auto locatorValue = reinterpret_cast<
553                             const pldm_terminus_locator_type_mctp_eid*>(
554                             tlpdr->terminus_locator_value);
555                         tlEid = static_cast<uint8_t>(locatorValue->eid);
556                     }
557                     if (tlpdr->validity == 0)
558                     {
559                         tlValid = false;
560                     }
561                     for (const auto& terminusMap : tlPDRInfo)
562                     {
563                         if ((terminusHandle == (terminusMap.first)) &&
564                             (get<1>(terminusMap.second) == tlEid) &&
565                             (get<2>(terminusMap.second) == tlpdr->validity))
566                         {
567                             // TL PDR already present with same validity don't
568                             // add the PDR to the repo just return
569                             return;
570                         }
571                     }
572                     tlPDRInfo.insert_or_assign(
573                         tlpdr->terminus_handle,
574                         std::make_tuple(tlpdr->tid, tlEid, tlpdr->validity));
575                 }
576                 else if (pdrHdr->type == PLDM_STATE_SENSOR_PDR)
577                 {
578                     pdrTerminusHandle =
579                         extractTerminusHandle<pldm_state_sensor_pdr>(pdr);
580                     updateContainerId<pldm_state_sensor_pdr>(entityTree, pdr);
581                     stateSensorPDRs.emplace_back(pdr);
582                 }
583                 else if (pdrHdr->type == PLDM_PDR_FRU_RECORD_SET)
584                 {
585                     pdrTerminusHandle =
586                         extractTerminusHandle<pldm_pdr_fru_record_set>(pdr);
587                     updateContainerId<pldm_pdr_fru_record_set>(entityTree, pdr);
588                     fruRecordSetPDRs.emplace_back(pdr);
589                 }
590                 else if (pdrHdr->type == PLDM_STATE_EFFECTER_PDR)
591                 {
592                     pdrTerminusHandle =
593                         extractTerminusHandle<pldm_state_effecter_pdr>(pdr);
594                     updateContainerId<pldm_state_effecter_pdr>(entityTree, pdr);
595                 }
596                 else if (pdrHdr->type == PLDM_NUMERIC_EFFECTER_PDR)
597                 {
598                     pdrTerminusHandle =
599                         extractTerminusHandle<pldm_numeric_effecter_value_pdr>(
600                             pdr);
601                     updateContainerId<pldm_numeric_effecter_value_pdr>(
602                         entityTree, pdr);
603                 }
604                 // if the TLPDR is invalid update the repo accordingly
605                 if (!tlValid)
606                 {
607                     pldm_pdr_update_TL_pdr(repo, terminusHandle, tid, tlEid,
608                                            tlValid);
609 
610                     if (!isHostUp())
611                     {
612                         // The terminus PDR becomes invalid when the terminus
613                         // itself is down. We don't need to do PDR exchange in
614                         // that case, so setting the next record handle to 0.
615                         nextRecordHandle = 0;
616                     }
617                 }
618                 else
619                 {
620                     rc = pldm_pdr_add_check(repo, pdr.data(), respCount, true,
621                                             pdrTerminusHandle, &rh);
622                     if (rc)
623                     {
624                         // pldm_pdr_add() assert()ed on failure to add a PDR.
625                         throw std::runtime_error("Failed to add PDR");
626                     }
627                 }
628             }
629         }
630     }
631     if (!nextRecordHandle)
632     {
633         updateEntityAssociation(entityAssociations, entityTree, objPathMap,
634                                 entityMaps, oemPlatformHandler);
635 
636         /*received last record*/
637         this->parseStateSensorPDRs(stateSensorPDRs);
638         this->createDbusObjects(fruRecordSetPDRs);
639         if (isHostUp())
640         {
641             this->setHostSensorState(stateSensorPDRs);
642         }
643         stateSensorPDRs.clear();
644         fruRecordSetPDRs.clear();
645         entityAssociations.clear();
646 
647         if (merged)
648         {
649             merged = false;
650             deferredPDRRepoChgEvent =
651                 std::make_unique<sdeventplus::source::Defer>(
652                     event,
653                     std::bind(
654                         std::mem_fn((&HostPDRHandler::_processPDRRepoChgEvent)),
655                         this, std::placeholders::_1));
656         }
657     }
658     else
659     {
660         if (modifiedPDRRecordHandles.empty() && isHostPdrModified)
661         {
662             isHostPdrModified = false;
663         }
664         else
665         {
666             deferredFetchPDREvent =
667                 std::make_unique<sdeventplus::source::Defer>(
668                     event,
669                     std::bind(
670                         std::mem_fn((&HostPDRHandler::_processFetchPDREvent)),
671                         this, nextRecordHandle, std::placeholders::_1));
672         }
673     }
674 }
675 
676 void HostPDRHandler::_processPDRRepoChgEvent(
677     sdeventplus::source::EventBase& /*source */)
678 {
679     deferredPDRRepoChgEvent.reset();
680     this->sendPDRRepositoryChgEvent(
681         std::move(std::vector<uint8_t>(1, PLDM_PDR_ENTITY_ASSOCIATION)),
682         FORMAT_IS_PDR_HANDLES);
683 }
684 
685 void HostPDRHandler::_processFetchPDREvent(
686     uint32_t nextRecordHandle, sdeventplus::source::EventBase& /*source */)
687 {
688     deferredFetchPDREvent.reset();
689     if (!this->pdrRecordHandles.empty())
690     {
691         nextRecordHandle = this->pdrRecordHandles.front();
692         this->pdrRecordHandles.pop_front();
693     }
694     if (isHostPdrModified && (!this->modifiedPDRRecordHandles.empty()))
695     {
696         nextRecordHandle = this->modifiedPDRRecordHandles.front();
697         this->modifiedPDRRecordHandles.pop_front();
698     }
699     this->getHostPDR(nextRecordHandle);
700 }
701 
702 void HostPDRHandler::setHostFirmwareCondition()
703 {
704     responseReceived = false;
705     auto instanceId = instanceIdDb.next(mctp_eid);
706     std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
707                                     PLDM_GET_VERSION_REQ_BYTES);
708     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
709     auto rc = encode_get_version_req(instanceId, 0, PLDM_GET_FIRSTPART,
710                                      PLDM_BASE, request);
711     if (rc != PLDM_SUCCESS)
712     {
713         error("Failed to encode GetPLDMVersion, response code {RC}", "RC",
714               lg2::hex, rc);
715         instanceIdDb.free(mctp_eid, instanceId);
716         return;
717     }
718 
719     auto getPLDMVersionHandler = [this](mctp_eid_t /*eid*/,
720                                         const pldm_msg* response,
721                                         size_t respMsgLen) {
722         if (response == nullptr || !respMsgLen)
723         {
724             error(
725                 "Failed to receive response for getPLDMVersion command, Host seems to be off");
726             return;
727         }
728         info("Getting the response code '{RC}'", "RC", lg2::hex,
729              static_cast<uint16_t>(response->payload[0]));
730         this->responseReceived = true;
731         getHostPDR();
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};
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 recrod 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 
1124     getFRURecordTableMetadataByRemote(fruRecordSetPDRs);
1125 }
1126 
1127 } // namespace pldm
1128