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