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