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