xref: /openbmc/pldm/host-bmc/host_pdr_handler.cpp (revision d432b48b6b5c908248d9bc6cce316514a05f3ed0)
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 = reinterpret_cast<pldm_msg*>(requestMsg.data());
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 = reinterpret_cast<pldm_pdr_entity_association*>(
238         const_cast<uint8_t*>(pdr.data()) + sizeof(pldm_pdr_hdr));
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 =
376         reinterpret_cast<struct pldm_pdr_repository_chg_event_data*>(
377             eventDataVec.data());
378     size_t actualSize{};
379     auto firstEntry = changeEntries[0].data();
380     auto rc = encode_pldm_pdr_repository_chg_event_data(
381         eventDataFormat, 1, eventDataOps.data(), numsOfChangeEntries.data(),
382         &firstEntry, eventData, &actualSize, maxSize);
383     if (rc != PLDM_SUCCESS)
384     {
385         error(
386             "Failed to encode pldm pdr repository change event data, response code '{RC}'",
387             "RC", rc);
388         return;
389     }
390     auto instanceId = instanceIdDb.next(mctp_eid);
391     std::vector<uint8_t> requestMsg(
392         sizeof(pldm_msg_hdr) + PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES +
393         actualSize);
394     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
395     rc = encode_platform_event_message_req(
396         instanceId, 1, TERMINUS_ID, PLDM_PDR_REPOSITORY_CHG_EVENT,
397         eventDataVec.data(), actualSize, request,
398         actualSize + PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES);
399     if (rc != PLDM_SUCCESS)
400     {
401         instanceIdDb.free(mctp_eid, instanceId);
402         error(
403             "Failed to encode platform event message request, response code '{RC}'",
404             "RC", rc);
405         return;
406     }
407 
408     auto platformEventMessageResponseHandler = [](mctp_eid_t /*eid*/,
409                                                   const pldm_msg* response,
410                                                   size_t respMsgLen) {
411         if (response == nullptr || !respMsgLen)
412         {
413             error(
414                 "Failed to receive response for the PDR repository changed event");
415             return;
416         }
417 
418         uint8_t completionCode{};
419         uint8_t status{};
420         auto responsePtr = reinterpret_cast<const struct pldm_msg*>(response);
421         auto rc = decode_platform_event_message_resp(responsePtr, respMsgLen,
422                                                      &completionCode, &status);
423         if (rc || completionCode)
424         {
425             error(
426                 "Failed to decode platform event message response, response code '{RC}' and completion code '{CC}'",
427                 "RC", rc, "CC", completionCode);
428         }
429     };
430 
431     rc = handler->registerRequest(
432         mctp_eid, instanceId, PLDM_PLATFORM, PLDM_PLATFORM_EVENT_MESSAGE,
433         std::move(requestMsg), std::move(platformEventMessageResponseHandler));
434     if (rc)
435     {
436         error(
437             "Failed to send the PDR repository changed event request, response code '{RC}'",
438             "RC", rc);
439     }
440 }
441 
parseStateSensorPDRs(const PDRList & stateSensorPDRs)442 void HostPDRHandler::parseStateSensorPDRs(const PDRList& stateSensorPDRs)
443 {
444     for (const auto& pdr : stateSensorPDRs)
445     {
446         SensorEntry sensorEntry{};
447         const auto& [terminusHandle, sensorID, sensorInfo] =
448             responder::pdr_utils::parseStateSensorPDR(pdr);
449         sensorEntry.sensorID = sensorID;
450         try
451         {
452             sensorEntry.terminusID = std::get<0>(tlPDRInfo.at(terminusHandle));
453         }
454         // If there is no mapping for terminusHandle assign the reserved TID
455         // value of 0xFF to indicate that.
456         catch (const std::out_of_range&)
457         {
458             sensorEntry.terminusID = PLDM_TID_RESERVED;
459         }
460         sensorMap.emplace(sensorEntry, std::move(sensorInfo));
461     }
462 }
463 
processHostPDRs(mctp_eid_t,const pldm_msg * response,size_t respMsgLen)464 void HostPDRHandler::processHostPDRs(
465     mctp_eid_t /*eid*/, const pldm_msg* response, size_t respMsgLen)
466 {
467     static bool merged = false;
468     static PDRList stateSensorPDRs{};
469     static PDRList fruRecordSetPDRs{};
470     uint32_t nextRecordHandle{};
471     uint8_t tlEid = 0;
472     bool tlValid = true;
473     uint32_t rh = 0;
474     uint16_t terminusHandle = 0;
475     uint16_t pdrTerminusHandle = 0;
476     uint8_t tid = 0;
477 
478     uint8_t completionCode{};
479     uint32_t nextDataTransferHandle{};
480     uint8_t transferFlag{};
481     uint16_t respCount{};
482     uint8_t transferCRC{};
483     if (response == nullptr || !respMsgLen)
484     {
485         error("Failed to receive response for the GetPDR command");
486         pldm::utils::reportError(
487             "xyz.openbmc_project.PLDM.Error.GetPDR.PDRExchangeFailure");
488         return;
489     }
490 
491     auto rc = decode_get_pdr_resp(
492         response, respMsgLen /*- sizeof(pldm_msg_hdr)*/, &completionCode,
493         &nextRecordHandle, &nextDataTransferHandle, &transferFlag, &respCount,
494         nullptr, 0, &transferCRC);
495     std::vector<uint8_t> responsePDRMsg;
496     responsePDRMsg.resize(respMsgLen + sizeof(pldm_msg_hdr));
497     memcpy(responsePDRMsg.data(), response, respMsgLen + sizeof(pldm_msg_hdr));
498     if (rc != PLDM_SUCCESS)
499     {
500         error(
501             "Failed to decode getPDR response for next record handle '{NEXT_RECORD_HANDLE}', response code '{RC}'",
502             "NEXT_RECORD_HANDLE", nextRecordHandle, "RC", rc);
503         return;
504     }
505     else
506     {
507         std::vector<uint8_t> pdr(respCount, 0);
508         rc = decode_get_pdr_resp(response, respMsgLen, &completionCode,
509                                  &nextRecordHandle, &nextDataTransferHandle,
510                                  &transferFlag, &respCount, pdr.data(),
511                                  respCount, &transferCRC);
512         if (rc != PLDM_SUCCESS || completionCode != PLDM_SUCCESS)
513         {
514             error(
515                 "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}'",
516                 "NEXT_RECORD_HANDLE", nextRecordHandle, "DATA_TRANSFER_HANDLE",
517                 nextDataTransferHandle, "FLAG", transferFlag, "RC", rc, "CC",
518                 completionCode);
519             return;
520         }
521         else
522         {
523             // when nextRecordHandle is 0, we need the recordHandle of the last
524             // PDR and not 0-1.
525             if (!nextRecordHandle)
526             {
527                 rh = nextRecordHandle;
528             }
529             else
530             {
531                 rh = nextRecordHandle - 1;
532             }
533 
534             auto pdrHdr = reinterpret_cast<pldm_pdr_hdr*>(pdr.data());
535             if (!rh)
536             {
537                 rh = pdrHdr->record_handle;
538             }
539 
540             if (pdrHdr->type == PLDM_PDR_ENTITY_ASSOCIATION)
541             {
542                 this->mergeEntityAssociations(pdr, respCount, rh);
543                 merged = true;
544             }
545             else
546             {
547                 if (pdrHdr->type == PLDM_TERMINUS_LOCATOR_PDR)
548                 {
549                     pdrTerminusHandle =
550                         extractTerminusHandle<pldm_terminus_locator_pdr>(pdr);
551                     auto tlpdr =
552                         reinterpret_cast<const pldm_terminus_locator_pdr*>(
553                             pdr.data());
554 
555                     terminusHandle = tlpdr->terminus_handle;
556                     tid = tlpdr->tid;
557                     auto terminus_locator_type = tlpdr->terminus_locator_type;
558                     if (terminus_locator_type ==
559                         PLDM_TERMINUS_LOCATOR_TYPE_MCTP_EID)
560                     {
561                         auto locatorValue = reinterpret_cast<
562                             const pldm_terminus_locator_type_mctp_eid*>(
563                             tlpdr->terminus_locator_value);
564                         tlEid = static_cast<uint8_t>(locatorValue->eid);
565                     }
566                     if (tlpdr->validity == 0)
567                     {
568                         tlValid = false;
569                     }
570                     for (const auto& terminusMap : tlPDRInfo)
571                     {
572                         if ((terminusHandle == (terminusMap.first)) &&
573                             (get<1>(terminusMap.second) == tlEid) &&
574                             (get<2>(terminusMap.second) == tlpdr->validity))
575                         {
576                             // TL PDR already present with same validity don't
577                             // add the PDR to the repo just return
578                             return;
579                         }
580                     }
581                     tlPDRInfo.insert_or_assign(
582                         tlpdr->terminus_handle,
583                         std::make_tuple(tlpdr->tid, tlEid, tlpdr->validity));
584                 }
585                 else if (pdrHdr->type == PLDM_STATE_SENSOR_PDR)
586                 {
587                     pdrTerminusHandle =
588                         extractTerminusHandle<pldm_state_sensor_pdr>(pdr);
589                     updateContainerId<pldm_state_sensor_pdr>(entityTree, pdr);
590                     stateSensorPDRs.emplace_back(pdr);
591                 }
592                 else if (pdrHdr->type == PLDM_PDR_FRU_RECORD_SET)
593                 {
594                     pdrTerminusHandle =
595                         extractTerminusHandle<pldm_pdr_fru_record_set>(pdr);
596                     updateContainerId<pldm_pdr_fru_record_set>(entityTree, pdr);
597                     fruRecordSetPDRs.emplace_back(pdr);
598                 }
599                 else if (pdrHdr->type == PLDM_STATE_EFFECTER_PDR)
600                 {
601                     pdrTerminusHandle =
602                         extractTerminusHandle<pldm_state_effecter_pdr>(pdr);
603                     updateContainerId<pldm_state_effecter_pdr>(entityTree, pdr);
604                 }
605                 else if (pdrHdr->type == PLDM_NUMERIC_EFFECTER_PDR)
606                 {
607                     pdrTerminusHandle =
608                         extractTerminusHandle<pldm_numeric_effecter_value_pdr>(
609                             pdr);
610                     updateContainerId<pldm_numeric_effecter_value_pdr>(
611                         entityTree, pdr);
612                 }
613                 // if the TLPDR is invalid update the repo accordingly
614                 if (!tlValid)
615                 {
616                     pldm_pdr_update_TL_pdr(repo, terminusHandle, tid, tlEid,
617                                            tlValid);
618 
619                     if (!isHostUp())
620                     {
621                         // The terminus PDR becomes invalid when the terminus
622                         // itself is down. We don't need to do PDR exchange in
623                         // that case, so setting the next record handle to 0.
624                         nextRecordHandle = 0;
625                     }
626                 }
627                 else
628                 {
629                     rc = pldm_pdr_add(repo, pdr.data(), respCount, true,
630                                       pdrTerminusHandle, &rh);
631                     if (rc)
632                     {
633                         // pldm_pdr_add() assert()ed on failure to add a PDR.
634                         throw std::runtime_error("Failed to add PDR");
635                     }
636                 }
637             }
638         }
639     }
640     if (!nextRecordHandle)
641     {
642         updateEntityAssociation(entityAssociations, entityTree, objPathMap,
643                                 entityMaps, oemPlatformHandler);
644         if (oemUtilsHandler)
645         {
646             oemUtilsHandler->setCoreCount(entityAssociations, entityMaps);
647         }
648         /*received last record*/
649         this->parseStateSensorPDRs(stateSensorPDRs);
650         this->createDbusObjects(fruRecordSetPDRs);
651         if (isHostUp())
652         {
653             this->setHostSensorState(stateSensorPDRs);
654         }
655         stateSensorPDRs.clear();
656         fruRecordSetPDRs.clear();
657         entityAssociations.clear();
658 
659         if (merged)
660         {
661             merged = false;
662             deferredPDRRepoChgEvent =
663                 std::make_unique<sdeventplus::source::Defer>(
664                     event,
665                     std::bind(
666                         std::mem_fn((&HostPDRHandler::_processPDRRepoChgEvent)),
667                         this, std::placeholders::_1));
668         }
669     }
670     else
671     {
672         if (modifiedPDRRecordHandles.empty() && isHostPdrModified)
673         {
674             isHostPdrModified = false;
675         }
676         else
677         {
678             deferredFetchPDREvent =
679                 std::make_unique<sdeventplus::source::Defer>(
680                     event,
681                     std::bind(
682                         std::mem_fn((&HostPDRHandler::_processFetchPDREvent)),
683                         this, nextRecordHandle, std::placeholders::_1));
684         }
685     }
686 }
687 
_processPDRRepoChgEvent(sdeventplus::source::EventBase &)688 void HostPDRHandler::_processPDRRepoChgEvent(
689     sdeventplus::source::EventBase& /*source */)
690 {
691     deferredPDRRepoChgEvent.reset();
692     this->sendPDRRepositoryChgEvent(
693         std::vector<uint8_t>(1, PLDM_PDR_ENTITY_ASSOCIATION),
694         FORMAT_IS_PDR_HANDLES);
695 }
696 
_processFetchPDREvent(uint32_t nextRecordHandle,sdeventplus::source::EventBase &)697 void HostPDRHandler::_processFetchPDREvent(
698     uint32_t nextRecordHandle, sdeventplus::source::EventBase& /*source */)
699 {
700     deferredFetchPDREvent.reset();
701     if (!this->pdrRecordHandles.empty())
702     {
703         nextRecordHandle = this->pdrRecordHandles.front();
704         this->pdrRecordHandles.pop_front();
705     }
706     if (isHostPdrModified && (!this->modifiedPDRRecordHandles.empty()))
707     {
708         nextRecordHandle = this->modifiedPDRRecordHandles.front();
709         this->modifiedPDRRecordHandles.pop_front();
710     }
711     this->getHostPDR(nextRecordHandle);
712 }
713 
setHostFirmwareCondition()714 void HostPDRHandler::setHostFirmwareCondition()
715 {
716     responseReceived = false;
717     auto instanceId = instanceIdDb.next(mctp_eid);
718     std::vector<uint8_t> requestMsg(
719         sizeof(pldm_msg_hdr) + PLDM_GET_VERSION_REQ_BYTES);
720     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
721     auto rc = encode_get_version_req(instanceId, 0, PLDM_GET_FIRSTPART,
722                                      PLDM_BASE, request);
723     if (rc != PLDM_SUCCESS)
724     {
725         error("Failed to encode GetPLDMVersion, response code {RC}", "RC",
726               lg2::hex, rc);
727         instanceIdDb.free(mctp_eid, instanceId);
728         return;
729     }
730 
731     auto getPLDMVersionHandler = [this](mctp_eid_t /*eid*/,
732                                         const pldm_msg* response,
733                                         size_t respMsgLen) {
734         if (response == nullptr || !respMsgLen)
735         {
736             error(
737                 "Failed to receive response for getPLDMVersion command, Host seems to be off");
738             return;
739         }
740         info("Getting the response code '{RC}'", "RC", lg2::hex,
741              response->payload[0]);
742         this->responseReceived = true;
743     };
744     rc = handler->registerRequest(mctp_eid, instanceId, PLDM_BASE,
745                                   PLDM_GET_PLDM_VERSION, std::move(requestMsg),
746                                   std::move(getPLDMVersionHandler));
747     if (rc)
748     {
749         error(
750             "Failed to discover remote terminus state. Assuming remote terminus as off");
751     }
752 }
753 
isHostUp()754 bool HostPDRHandler::isHostUp()
755 {
756     return responseReceived;
757 }
758 
setHostSensorState(const PDRList & stateSensorPDRs)759 void HostPDRHandler::setHostSensorState(const PDRList& stateSensorPDRs)
760 {
761     for (const auto& stateSensorPDR : stateSensorPDRs)
762     {
763         auto pdr = reinterpret_cast<const pldm_state_sensor_pdr*>(
764             stateSensorPDR.data());
765 
766         if (!pdr)
767         {
768             error("Failed to get state sensor PDR");
769             pldm::utils::reportError(
770                 "xyz.openbmc_project.bmc.pldm.InternalFailure");
771             return;
772         }
773 
774         uint16_t sensorId = pdr->sensor_id;
775 
776         for (const auto& [terminusHandle, terminusInfo] : tlPDRInfo)
777         {
778             if (terminusHandle == pdr->terminus_handle)
779             {
780                 if (std::get<2>(terminusInfo) == PLDM_TL_PDR_VALID)
781                 {
782                     mctp_eid = std::get<1>(terminusInfo);
783                 }
784 
785                 bitfield8_t sensorRearm;
786                 sensorRearm.byte = 0;
787                 uint8_t tid = std::get<0>(terminusInfo);
788 
789                 auto instanceId = instanceIdDb.next(mctp_eid);
790                 std::vector<uint8_t> requestMsg(
791                     sizeof(pldm_msg_hdr) +
792                     PLDM_GET_STATE_SENSOR_READINGS_REQ_BYTES);
793                 auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
794                 auto rc = encode_get_state_sensor_readings_req(
795                     instanceId, sensorId, sensorRearm, 0, request);
796 
797                 if (rc != PLDM_SUCCESS)
798                 {
799                     instanceIdDb.free(mctp_eid, instanceId);
800                     error(
801                         "Failed to encode get state sensor readings request for sensorID '{SENSOR_ID}' and  instanceID '{INSTANCE}', response code '{RC}'",
802                         "SENSOR_ID", sensorId, "INSTANCE", instanceId, "RC",
803                         rc);
804                     pldm::utils::reportError(
805                         "xyz.openbmc_project.bmc.pldm.InternalFailure");
806                     return;
807                 }
808 
809                 auto getStateSensorReadingRespHandler = [=, this](
810                                                             mctp_eid_t /*eid*/,
811                                                             const pldm_msg*
812                                                                 response,
813                                                             size_t respMsgLen) {
814                     if (response == nullptr || !respMsgLen)
815                     {
816                         error(
817                             "Failed to receive response for get state sensor reading command for sensorID '{SENSOR_ID}' and  instanceID '{INSTANCE}'",
818                             "SENSOR_ID", sensorId, "INSTANCE", instanceId);
819                         return;
820                     }
821                     std::array<get_sensor_state_field, 8> stateField{};
822                     uint8_t completionCode = 0;
823                     uint8_t comp_sensor_count = 0;
824 
825                     auto rc = decode_get_state_sensor_readings_resp(
826                         response, respMsgLen, &completionCode,
827                         &comp_sensor_count, stateField.data());
828 
829                     if (rc != PLDM_SUCCESS || completionCode != PLDM_SUCCESS)
830                     {
831                         error(
832                             "Failed to decode get state sensor readings response for sensorID '{SENSOR_ID}' and  instanceID '{INSTANCE}', response code'{RC}' and completion code '{CC}'",
833                             "SENSOR_ID", sensorId, "INSTANCE", instanceId, "RC",
834                             rc, "CC", completionCode);
835                         pldm::utils::reportError(
836                             "xyz.openbmc_project.bmc.pldm.InternalFailure");
837                     }
838 
839                     uint8_t eventState;
840                     uint8_t previousEventState;
841 
842                     for (uint8_t sensorOffset = 0;
843                          sensorOffset < comp_sensor_count; sensorOffset++)
844                     {
845                         eventState = stateField[sensorOffset].present_state;
846                         previousEventState =
847                             stateField[sensorOffset].previous_state;
848 
849                         emitStateSensorEventSignal(tid, sensorId, sensorOffset,
850                                                    eventState,
851                                                    previousEventState);
852 
853                         SensorEntry sensorEntry{tid, sensorId};
854 
855                         pldm::pdr::EntityInfo entityInfo{};
856                         pldm::pdr::CompositeSensorStates
857                             compositeSensorStates{};
858                         std::vector<pldm::pdr::StateSetId> stateSetIds{};
859 
860                         try
861                         {
862                             std::tie(entityInfo, compositeSensorStates,
863                                      stateSetIds) =
864                                 lookupSensorInfo(sensorEntry);
865                         }
866                         catch (const std::out_of_range&)
867                         {
868                             try
869                             {
870                                 sensorEntry.terminusID = PLDM_TID_RESERVED;
871                                 std::tie(entityInfo, compositeSensorStates,
872                                          stateSetIds) =
873                                     lookupSensorInfo(sensorEntry);
874                             }
875                             catch (const std::out_of_range&)
876                             {
877                                 error("No mapping for the events");
878                             }
879                         }
880 
881                         if ((compositeSensorStates.size() > 1) &&
882                             (sensorOffset > (compositeSensorStates.size() - 1)))
883                         {
884                             error(
885                                 "Error Invalid data, Invalid sensor offset '{SENSOR_OFFSET}'",
886                                 "SENSOR_OFFSET", sensorOffset);
887                             return;
888                         }
889 
890                         const auto& possibleStates =
891                             compositeSensorStates[sensorOffset];
892                         if (possibleStates.find(eventState) ==
893                             possibleStates.end())
894                         {
895                             error(
896                                 "Error invalid_data, Invalid event state '{STATE}'",
897                                 "STATE", eventState);
898                             return;
899                         }
900                         const auto& [containerId, entityType, entityInstance] =
901                             entityInfo;
902                         auto stateSetId = stateSetIds[sensorOffset];
903                         pldm::responder::events::StateSensorEntry
904                             stateSensorEntry{containerId,    entityType,
905                                              entityInstance, sensorOffset,
906                                              stateSetId,     false};
907                         handleStateSensorEvent(stateSensorEntry, eventState);
908                     }
909                 };
910 
911                 rc = handler->registerRequest(
912                     mctp_eid, instanceId, PLDM_PLATFORM,
913                     PLDM_GET_STATE_SENSOR_READINGS, std::move(requestMsg),
914                     std::move(getStateSensorReadingRespHandler));
915 
916                 if (rc != PLDM_SUCCESS)
917                 {
918                     error(
919                         "Failed to send request to get state sensor reading on remote terminus for sensorID '{SENSOR_ID}' and  instanceID '{INSTANCE}', response code '{RC}'",
920                         "SENSOR_ID", sensorId, "INSTANCE", instanceId, "RC",
921                         rc);
922                 }
923             }
924         }
925     }
926 }
927 
getFRURecordTableMetadataByRemote(const PDRList & fruRecordSetPDRs)928 void HostPDRHandler::getFRURecordTableMetadataByRemote(
929     const PDRList& fruRecordSetPDRs)
930 {
931     auto instanceId = instanceIdDb.next(mctp_eid);
932     std::vector<uint8_t> requestMsg(
933         sizeof(pldm_msg_hdr) + PLDM_GET_FRU_RECORD_TABLE_METADATA_REQ_BYTES);
934 
935     // GetFruRecordTableMetadata
936     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
937     auto rc = encode_get_fru_record_table_metadata_req(
938         instanceId, request, requestMsg.size() - sizeof(pldm_msg_hdr));
939     if (rc != PLDM_SUCCESS)
940     {
941         instanceIdDb.free(mctp_eid, instanceId);
942         error(
943             "Failed to encode get fru record table metadata request, response code '{RC}'",
944             "RC", lg2::hex, rc);
945         return;
946     }
947 
948     auto getFruRecordTableMetadataResponseHandler = [this, fruRecordSetPDRs](
949                                                         mctp_eid_t /*eid*/,
950                                                         const pldm_msg*
951                                                             response,
952                                                         size_t respMsgLen) {
953         if (response == nullptr || !respMsgLen)
954         {
955             error(
956                 "Failed to receive response for the get fru record table metadata");
957             return;
958         }
959 
960         uint8_t cc = 0;
961         uint8_t fru_data_major_version, fru_data_minor_version;
962         uint32_t fru_table_maximum_size, fru_table_length;
963         uint16_t total_record_set_identifiers;
964         uint16_t total;
965         uint32_t checksum;
966 
967         auto rc = decode_get_fru_record_table_metadata_resp(
968             response, respMsgLen, &cc, &fru_data_major_version,
969             &fru_data_minor_version, &fru_table_maximum_size, &fru_table_length,
970             &total_record_set_identifiers, &total, &checksum);
971 
972         if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
973         {
974             error(
975                 "Failed to decode get fru record table metadata response, response code '{RC}' and completion code '{CC}'",
976                 "RC", lg2::hex, rc, "CC", cc);
977             return;
978         }
979 
980         // pass total to getFRURecordTableByRemote
981         this->getFRURecordTableByRemote(fruRecordSetPDRs, total);
982     };
983 
984     rc = handler->registerRequest(
985         mctp_eid, instanceId, PLDM_FRU, PLDM_GET_FRU_RECORD_TABLE_METADATA,
986         std::move(requestMsg),
987         std::move(getFruRecordTableMetadataResponseHandler));
988     if (rc != PLDM_SUCCESS)
989     {
990         error(
991             "Failed to send the the set state effecter states request, response code '{RC}'",
992             "RC", rc);
993     }
994 
995     return;
996 }
997 
getFRURecordTableByRemote(const PDRList & fruRecordSetPDRs,uint16_t totalTableRecords)998 void HostPDRHandler::getFRURecordTableByRemote(const PDRList& fruRecordSetPDRs,
999                                                uint16_t totalTableRecords)
1000 {
1001     fruRecordData.clear();
1002 
1003     if (!totalTableRecords)
1004     {
1005         error("Failed to get fru record table");
1006         return;
1007     }
1008 
1009     auto instanceId = instanceIdDb.next(mctp_eid);
1010     std::vector<uint8_t> requestMsg(
1011         sizeof(pldm_msg_hdr) + PLDM_GET_FRU_RECORD_TABLE_REQ_BYTES);
1012 
1013     // send the getFruRecordTable command
1014     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
1015     auto rc = encode_get_fru_record_table_req(
1016         instanceId, 0, PLDM_GET_FIRSTPART, request,
1017         requestMsg.size() - sizeof(pldm_msg_hdr));
1018     if (rc != PLDM_SUCCESS)
1019     {
1020         instanceIdDb.free(mctp_eid, instanceId);
1021         error(
1022             "Failed to encode get fru record table request, response code '{RC}'",
1023             "RC", lg2::hex, rc);
1024         return;
1025     }
1026 
1027     auto getFruRecordTableResponseHandler = [totalTableRecords, this,
1028                                              fruRecordSetPDRs](
1029                                                 mctp_eid_t /*eid*/,
1030                                                 const pldm_msg* response,
1031                                                 size_t respMsgLen) {
1032         if (response == nullptr || !respMsgLen)
1033         {
1034             error("Failed to receive response for the get fru record table");
1035             return;
1036         }
1037 
1038         uint8_t cc = 0;
1039         uint32_t next_data_transfer_handle = 0;
1040         uint8_t transfer_flag = 0;
1041         size_t fru_record_table_length = 0;
1042         std::vector<uint8_t> fru_record_table_data(
1043             respMsgLen - sizeof(pldm_msg_hdr));
1044         auto responsePtr = reinterpret_cast<const struct pldm_msg*>(response);
1045         auto rc = decode_get_fru_record_table_resp(
1046             responsePtr, respMsgLen, &cc, &next_data_transfer_handle,
1047             &transfer_flag, fru_record_table_data.data(),
1048             &fru_record_table_length);
1049 
1050         if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
1051         {
1052             error(
1053                 "Failed to decode get fru record table resp, response code '{RC}' and completion code '{CC}'",
1054                 "RC", lg2::hex, rc, "CC", cc);
1055             return;
1056         }
1057 
1058         fruRecordData = responder::pdr_utils::parseFruRecordTable(
1059             fru_record_table_data.data(), fru_record_table_length);
1060 
1061         if (totalTableRecords != fruRecordData.size())
1062         {
1063             fruRecordData.clear();
1064 
1065             error("Failed to parse fru record data format.");
1066             return;
1067         }
1068 
1069         this->setFRUDataOnDBus(fruRecordSetPDRs, fruRecordData);
1070     };
1071 
1072     rc = handler->registerRequest(
1073         mctp_eid, instanceId, PLDM_FRU, PLDM_GET_FRU_RECORD_TABLE,
1074         std::move(requestMsg), std::move(getFruRecordTableResponseHandler));
1075     if (rc != PLDM_SUCCESS)
1076     {
1077         error("Failed to send the the set state effecter states request");
1078     }
1079 }
1080 
getRSI(const PDRList & fruRecordSetPDRs,const pldm_entity & entity)1081 std::optional<uint16_t> HostPDRHandler::getRSI(const PDRList& fruRecordSetPDRs,
1082                                                const pldm_entity& entity)
1083 {
1084     for (const auto& pdr : fruRecordSetPDRs)
1085     {
1086         auto fruPdr = reinterpret_cast<const pldm_pdr_fru_record_set*>(
1087             const_cast<uint8_t*>(pdr.data()) + sizeof(pldm_pdr_hdr));
1088 
1089         if (fruPdr->entity_type == entity.entity_type &&
1090             fruPdr->entity_instance == entity.entity_instance_num &&
1091             fruPdr->container_id == entity.entity_container_id)
1092         {
1093             return fruPdr->fru_rsi;
1094         }
1095     }
1096 
1097     return std::nullopt;
1098 }
1099 
setFRUDataOnDBus(const PDRList & fruRecordSetPDRs,const std::vector<responder::pdr_utils::FruRecordDataFormat> & fruRecordData)1100 void HostPDRHandler::setFRUDataOnDBus(
1101     [[maybe_unused]] const PDRList& fruRecordSetPDRs,
1102     [[maybe_unused]] const std::vector<
1103         responder::pdr_utils::FruRecordDataFormat>& fruRecordData)
1104 {
1105 #ifdef OEM_IBM
1106     for (const auto& entity : objPathMap)
1107     {
1108         pldm_entity node = pldm_entity_extract(entity.second);
1109         auto fruRSI = getRSI(fruRecordSetPDRs, node);
1110 
1111         for (const auto& data : fruRecordData)
1112         {
1113             if (!fruRSI || *fruRSI != data.fruRSI)
1114             {
1115                 continue;
1116             }
1117 
1118             if (data.fruRecType == PLDM_FRU_RECORD_TYPE_OEM)
1119             {
1120                 for (const auto& tlv : data.fruTLV)
1121                 {
1122                     if (tlv.fruFieldType ==
1123                         PLDM_OEM_FRU_FIELD_TYPE_LOCATION_CODE)
1124                     {
1125                         CustomDBus::getCustomDBus().setLocationCode(
1126                             entity.first,
1127                             std::string(reinterpret_cast<const char*>(
1128                                             tlv.fruFieldValue.data()),
1129                                         tlv.fruFieldLen));
1130                     }
1131                 }
1132             }
1133         }
1134     }
1135 #endif
1136 }
1137 
setPresentPropertyStatus(const std::string & path)1138 void HostPDRHandler::setPresentPropertyStatus(const std::string& path)
1139 {
1140     CustomDBus::getCustomDBus().updateItemPresentStatus(path, true);
1141 }
1142 
setAvailabilityState(const std::string & path)1143 void HostPDRHandler::setAvailabilityState(const std::string& path)
1144 {
1145     CustomDBus::getCustomDBus().setAvailabilityState(path, true);
1146 }
1147 
createDbusObjects(const PDRList & fruRecordSetPDRs)1148 void HostPDRHandler::createDbusObjects(const PDRList& fruRecordSetPDRs)
1149 {
1150     // Creating and Refreshing dbus hosted by remote PLDM entity Fru PDRs
1151 
1152     for (const auto& entity : objPathMap)
1153     {
1154         // update the Present Property
1155         setPresentPropertyStatus(entity.first);
1156         // Implement & update the Availability to true
1157         setAvailabilityState(entity.first);
1158 
1159         pldm_entity node = pldm_entity_extract(entity.second);
1160         switch (node.entity_type)
1161         {
1162             case PLDM_ENTITY_PROC | 0x8000:
1163                 CustomDBus::getCustomDBus().implementCpuCoreInterface(
1164                     entity.first);
1165                 break;
1166             case PLDM_ENTITY_SYSTEM_CHASSIS:
1167                 CustomDBus::getCustomDBus().implementChassisInterface(
1168                     entity.first);
1169                 break;
1170             case PLDM_ENTITY_POWER_SUPPLY:
1171                 CustomDBus::getCustomDBus().implementPowerSupplyInterface(
1172                     entity.first);
1173                 break;
1174             case PLDM_ENTITY_CHASSIS_FRONT_PANEL_BOARD:
1175                 CustomDBus::getCustomDBus().implementPanelInterface(
1176                     entity.first);
1177                 break;
1178             case PLDM_ENTITY_POWER_CONVERTER:
1179                 CustomDBus::getCustomDBus().implementVRMInterface(entity.first);
1180                 break;
1181             case PLDM_ENTITY_SLOT:
1182                 CustomDBus::getCustomDBus().implementPCIeSlotInterface(
1183                     entity.first);
1184                 break;
1185             case PLDM_ENTITY_CONNECTOR:
1186                 CustomDBus::getCustomDBus().implementConnecterInterface(
1187                     entity.first);
1188                 break;
1189             case PLDM_ENTITY_BOARD:
1190                 CustomDBus::getCustomDBus().implementBoard(entity.first);
1191                 break;
1192             case PLDM_ENTITY_CARD:
1193                 CustomDBus::getCustomDBus().implementPCIeDeviceInterface(
1194                     entity.first);
1195                 break;
1196             case PLDM_ENTITY_SYS_BOARD:
1197                 CustomDBus::getCustomDBus().implementMotherboardInterface(
1198                     entity.first);
1199                 break;
1200             case PLDM_ENTITY_FAN:
1201                 CustomDBus::getCustomDBus().implementFanInterface(entity.first);
1202                 break;
1203             case PLDM_ENTITY_IO_MODULE:
1204                 CustomDBus::getCustomDBus().implementFabricAdapter(
1205                     entity.first);
1206                 break;
1207             default:
1208                 break;
1209         }
1210     }
1211     getFRURecordTableMetadataByRemote(fruRecordSetPDRs);
1212 }
1213 
1214 } // namespace pldm
1215