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