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