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