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