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