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