1 #include "config.h"
2 
3 #include "host_pdr_handler.hpp"
4 
5 #include "libpldm/requester/pldm.h"
6 
7 #include <assert.h>
8 
9 #include <nlohmann/json.hpp>
10 
11 #include <fstream>
12 
13 namespace pldm
14 {
15 
16 using namespace pldm::utils;
17 using namespace sdbusplus::bus::match::rules;
18 using Json = nlohmann::json;
19 namespace fs = std::filesystem;
20 constexpr auto fruJson = "host_frus.json";
21 const Json emptyJson{};
22 const std::vector<Json> emptyJsonList{};
23 
24 HostPDRHandler::HostPDRHandler(
25     int mctp_fd, uint8_t mctp_eid, sdeventplus::Event& event, pldm_pdr* repo,
26     const std::string& eventsJsonsDir, pldm_entity_association_tree* entityTree,
27     pldm_entity_association_tree* bmcEntityTree, Requester& requester,
28     pldm::requester::Handler<pldm::requester::Request>* handler, bool verbose) :
29     mctp_fd(mctp_fd),
30     mctp_eid(mctp_eid), event(event), repo(repo),
31     stateSensorHandler(eventsJsonsDir), entityTree(entityTree),
32     bmcEntityTree(bmcEntityTree), requester(requester), handler(handler),
33     verbose(verbose)
34 {
35     fs::path hostFruJson(fs::path(HOST_JSONS_DIR) / fruJson);
36     if (fs::exists(hostFruJson))
37     {
38         // Note parent entities for entities sent down by the host firmware.
39         // This will enable a merge of entity associations.
40         try
41         {
42             std::ifstream jsonFile(hostFruJson);
43             auto data = Json::parse(jsonFile, nullptr, false);
44             if (data.is_discarded())
45             {
46                 std::cerr << "Parsing Host FRU json file failed" << std::endl;
47             }
48             else
49             {
50                 auto entities = data.value("entities", emptyJsonList);
51                 for (auto& entity : entities)
52                 {
53                     EntityType entityType = entity.value("entity_type", 0);
54                     auto parent = entity.value("parent", emptyJson);
55                     pldm_entity p{};
56                     p.entity_type = parent.value("entity_type", 0);
57                     p.entity_instance_num = parent.value("entity_instance", 0);
58                     parents.emplace(entityType, std::move(p));
59                 }
60             }
61         }
62         catch (const std::exception& e)
63         {
64             std::cerr << "Parsing Host FRU json file failed, exception = "
65                       << e.what() << std::endl;
66         }
67     }
68 
69     hostOffMatch = std::make_unique<sdbusplus::bus::match::match>(
70         pldm::utils::DBusHandler::getBus(),
71         propertiesChanged("/xyz/openbmc_project/state/host0",
72                           "xyz.openbmc_project.State.Host"),
73         [this, repo, entityTree,
74          bmcEntityTree](sdbusplus::message::message& msg) {
75             DbusChangedProps props{};
76             std::string intf;
77             msg.read(intf, props);
78             const auto itr = props.find("CurrentHostState");
79             if (itr != props.end())
80             {
81                 PropertyValue value = itr->second;
82                 auto propVal = std::get<std::string>(value);
83                 if (propVal == "xyz.openbmc_project.State.Host.HostState.Off")
84                 {
85                     pldm_pdr_remove_remote_pdrs(repo);
86                     pldm_entity_association_tree_destroy_root(entityTree);
87                     pldm_entity_association_tree_copy_root(bmcEntityTree,
88                                                            entityTree);
89                     this->sensorMap.clear();
90                 }
91             }
92         });
93 }
94 
95 void HostPDRHandler::fetchPDR(PDRRecordHandles&& recordHandles)
96 {
97     pdrRecordHandles.clear();
98     pdrRecordHandles = std::move(recordHandles);
99 
100     // Defer the actual fetch of PDRs from the host (by queuing the call on the
101     // main event loop). That way, we can respond to the platform event msg from
102     // the host firmware.
103     pdrFetchEvent = std::make_unique<sdeventplus::source::Defer>(
104         event, std::bind(std::mem_fn(&HostPDRHandler::_fetchPDR), this,
105                          std::placeholders::_1));
106 }
107 
108 void HostPDRHandler::_fetchPDR(sdeventplus::source::EventBase& /*source*/)
109 {
110     getHostPDR();
111 }
112 
113 void HostPDRHandler::getHostPDR(uint32_t nextRecordHandle)
114 {
115     pdrFetchEvent.reset();
116 
117     std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
118                                     PLDM_GET_PDR_REQ_BYTES);
119     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
120     uint32_t recordHandle{};
121     if (!nextRecordHandle)
122     {
123         if (!pdrRecordHandles.empty())
124         {
125             recordHandle = pdrRecordHandles.front();
126             pdrRecordHandles.pop_front();
127         }
128     }
129     else
130     {
131         recordHandle = nextRecordHandle;
132     }
133     auto instanceId = requester.getInstanceId(mctp_eid);
134 
135     auto rc =
136         encode_get_pdr_req(instanceId, recordHandle, 0, PLDM_GET_FIRSTPART,
137                            UINT16_MAX, 0, request, PLDM_GET_PDR_REQ_BYTES);
138     if (rc != PLDM_SUCCESS)
139     {
140         requester.markFree(mctp_eid, instanceId);
141         std::cerr << "Failed to encode_get_pdr_req, rc = " << rc << std::endl;
142         return;
143     }
144     if (verbose)
145     {
146         std::cout << "Sending Msg:" << std::endl;
147         printBuffer(requestMsg, verbose);
148     }
149 
150     rc = handler->registerRequest(
151         mctp_eid, instanceId, PLDM_PLATFORM, PLDM_GET_PDR,
152         std::move(requestMsg),
153         std::move(std::bind_front(&HostPDRHandler::processHostPDRs, this)));
154     if (rc)
155     {
156         std::cerr << "Failed to send the GetPDR request to Host \n";
157     }
158 }
159 
160 int HostPDRHandler::handleStateSensorEvent(const StateSensorEntry& entry,
161                                            pdr::EventState state)
162 {
163     auto rc = stateSensorHandler.eventAction(entry, state);
164     if (rc != PLDM_SUCCESS)
165     {
166         std::cerr << "Failed to fetch and update D-bus property, rc = " << rc
167                   << std::endl;
168         return rc;
169     }
170     return PLDM_SUCCESS;
171 }
172 bool HostPDRHandler::getParent(EntityType type, pldm_entity& parent)
173 {
174     auto found = parents.find(type);
175     if (found != parents.end())
176     {
177         parent.entity_type = found->second.entity_type;
178         parent.entity_instance_num = found->second.entity_instance_num;
179         return true;
180     }
181 
182     return false;
183 }
184 
185 void HostPDRHandler::mergeEntityAssociations(const std::vector<uint8_t>& pdr)
186 {
187     size_t numEntities{};
188     pldm_entity* entities = nullptr;
189     bool merged = false;
190     auto entityPdr = reinterpret_cast<pldm_pdr_entity_association*>(
191         const_cast<uint8_t*>(pdr.data()) + sizeof(pldm_pdr_hdr));
192 
193     pldm_entity_association_pdr_extract(pdr.data(), pdr.size(), &numEntities,
194                                         &entities);
195     for (size_t i = 0; i < numEntities; ++i)
196     {
197         pldm_entity parent{};
198         if (getParent(entities[i].entity_type, parent))
199         {
200             auto node = pldm_entity_association_tree_find(entityTree, &parent);
201             if (node)
202             {
203                 pldm_entity_association_tree_add(entityTree, &entities[i],
204                                                  0xFFFF, node,
205                                                  entityPdr->association_type);
206                 merged = true;
207             }
208         }
209     }
210     free(entities);
211 
212     if (merged)
213     {
214         // Update our PDR repo with the merged entity association PDRs
215         pldm_entity_association_pdr_add(entityTree, repo, true);
216     }
217 }
218 
219 void HostPDRHandler::sendPDRRepositoryChgEvent(std::vector<uint8_t>&& pdrTypes,
220                                                uint8_t eventDataFormat)
221 {
222     assert(eventDataFormat == FORMAT_IS_PDR_HANDLES);
223 
224     // Extract from the PDR repo record handles of PDRs we want the host
225     // to pull up.
226     std::vector<uint8_t> eventDataOps{PLDM_RECORDS_ADDED};
227     std::vector<uint8_t> numsOfChangeEntries(1);
228     std::vector<std::vector<ChangeEntry>> changeEntries(
229         numsOfChangeEntries.size());
230     for (auto pdrType : pdrTypes)
231     {
232         const pldm_pdr_record* record{};
233         do
234         {
235             record = pldm_pdr_find_record_by_type(repo, pdrType, record,
236                                                   nullptr, nullptr);
237             if (record && pldm_pdr_record_is_remote(record))
238             {
239                 changeEntries[0].push_back(
240                     pldm_pdr_get_record_handle(repo, record));
241             }
242         } while (record);
243     }
244     if (changeEntries.empty())
245     {
246         return;
247     }
248     numsOfChangeEntries[0] = changeEntries[0].size();
249 
250     // Encode PLDM platform event msg to indicate a PDR repo change.
251     size_t maxSize = PLDM_PDR_REPOSITORY_CHG_EVENT_MIN_LENGTH +
252                      PLDM_PDR_REPOSITORY_CHANGE_RECORD_MIN_LENGTH +
253                      changeEntries[0].size() * sizeof(uint32_t);
254     std::vector<uint8_t> eventDataVec{};
255     eventDataVec.resize(maxSize);
256     auto eventData =
257         reinterpret_cast<struct pldm_pdr_repository_chg_event_data*>(
258             eventDataVec.data());
259     size_t actualSize{};
260     auto firstEntry = changeEntries[0].data();
261     auto rc = encode_pldm_pdr_repository_chg_event_data(
262         eventDataFormat, 1, eventDataOps.data(), numsOfChangeEntries.data(),
263         &firstEntry, eventData, &actualSize, maxSize);
264     if (rc != PLDM_SUCCESS)
265     {
266         std::cerr
267             << "Failed to encode_pldm_pdr_repository_chg_event_data, rc = "
268             << rc << std::endl;
269         return;
270     }
271     auto instanceId = requester.getInstanceId(mctp_eid);
272     std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
273                                     PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES +
274                                     actualSize);
275     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
276     rc = encode_platform_event_message_req(
277         instanceId, 1, 0, PLDM_PDR_REPOSITORY_CHG_EVENT, eventDataVec.data(),
278         actualSize, request,
279         actualSize + PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES);
280     if (rc != PLDM_SUCCESS)
281     {
282         requester.markFree(mctp_eid, instanceId);
283         std::cerr << "Failed to encode_platform_event_message_req, rc = " << rc
284                   << std::endl;
285         return;
286     }
287 
288     auto platformEventMessageResponseHandler = [](mctp_eid_t /*eid*/,
289                                                   const pldm_msg* response,
290                                                   size_t respMsgLen) {
291         if (response == nullptr || !respMsgLen)
292         {
293             std::cerr << "Failed to receive response for the PDR repository "
294                          "changed event"
295                       << "\n";
296             return;
297         }
298 
299         uint8_t completionCode{};
300         uint8_t status{};
301         auto responsePtr = reinterpret_cast<const struct pldm_msg*>(response);
302         auto rc = decode_platform_event_message_resp(
303             responsePtr, respMsgLen - sizeof(pldm_msg_hdr), &completionCode,
304             &status);
305         if (rc || completionCode)
306         {
307             std::cerr << "Failed to decode_platform_event_message_resp: "
308                       << "rc=" << rc
309                       << ", cc=" << static_cast<unsigned>(completionCode)
310                       << std::endl;
311         }
312     };
313 
314     rc = handler->registerRequest(
315         mctp_eid, instanceId, PLDM_PLATFORM, PLDM_PDR_REPOSITORY_CHG_EVENT,
316         std::move(requestMsg), std::move(platformEventMessageResponseHandler));
317     if (rc)
318     {
319         std::cerr << "Failed to send the PDR repository changed event request"
320                   << "\n";
321     }
322 }
323 
324 void HostPDRHandler::parseStateSensorPDRs(const PDRList& stateSensorPDRs,
325                                           const TLPDRMap& tlpdrInfo)
326 {
327     for (const auto& pdr : stateSensorPDRs)
328     {
329         SensorEntry sensorEntry{};
330         const auto& [terminusHandle, sensorID, sensorInfo] =
331             responder::pdr_utils::parseStateSensorPDR(pdr);
332         sensorEntry.sensorID = sensorID;
333         try
334         {
335             sensorEntry.terminusID = tlpdrInfo.at(terminusHandle);
336         }
337         // If there is no mapping for terminusHandle assign the reserved TID
338         // value of 0xFF to indicate that.
339         catch (const std::out_of_range& e)
340         {
341             sensorEntry.terminusID = PLDM_TID_RESERVED;
342         }
343         sensorMap.emplace(sensorEntry, std::move(sensorInfo));
344     }
345 }
346 
347 void HostPDRHandler::processHostPDRs(mctp_eid_t /*eid*/,
348                                      const pldm_msg* response,
349                                      size_t respMsgLen)
350 {
351     static bool merged = false;
352     static PDRList stateSensorPDRs{};
353     static TLPDRMap tlpdrInfo{};
354     uint32_t nextRecordHandle{};
355     uint8_t completionCode{};
356     uint32_t nextDataTransferHandle{};
357     uint8_t transferFlag{};
358     uint16_t respCount{};
359     uint8_t transferCRC{};
360     if (response == nullptr || !respMsgLen)
361     {
362         std::cerr << "Failed to receive response for the GetPDR"
363                      " command \n";
364         return;
365     }
366 
367     auto rc = decode_get_pdr_resp(
368         response, respMsgLen /*- sizeof(pldm_msg_hdr)*/, &completionCode,
369         &nextRecordHandle, &nextDataTransferHandle, &transferFlag, &respCount,
370         nullptr, 0, &transferCRC);
371     std::vector<uint8_t> responsePDRMsg;
372     responsePDRMsg.resize(respMsgLen + sizeof(pldm_msg_hdr));
373     memcpy(responsePDRMsg.data(), response, respMsgLen + sizeof(pldm_msg_hdr));
374     if (verbose)
375     {
376         std::cout << "Receiving Msg:" << std::endl;
377         printBuffer(responsePDRMsg, verbose);
378     }
379     if (rc != PLDM_SUCCESS)
380     {
381         std::cerr << "Failed to decode_get_pdr_resp, rc = " << rc << std::endl;
382         return;
383     }
384     else
385     {
386         std::vector<uint8_t> pdr(respCount, 0);
387         rc = decode_get_pdr_resp(response, respMsgLen, &completionCode,
388                                  &nextRecordHandle, &nextDataTransferHandle,
389                                  &transferFlag, &respCount, pdr.data(),
390                                  respCount, &transferCRC);
391         if (rc != PLDM_SUCCESS || completionCode != PLDM_SUCCESS)
392         {
393             std::cerr << "Failed to decode_get_pdr_resp: "
394                       << "rc=" << rc
395                       << ", cc=" << static_cast<unsigned>(completionCode)
396                       << std::endl;
397             return;
398         }
399         else
400         {
401             auto pdrHdr = reinterpret_cast<pldm_pdr_hdr*>(pdr.data());
402             if (pdrHdr->type == PLDM_PDR_ENTITY_ASSOCIATION)
403             {
404                 this->mergeEntityAssociations(pdr);
405                 merged = true;
406             }
407             else
408             {
409                 if (pdrHdr->type == PLDM_TERMINUS_LOCATOR_PDR)
410                 {
411                     auto tlpdr =
412                         reinterpret_cast<const pldm_terminus_locator_pdr*>(
413                             pdr.data());
414                     tlpdrInfo.emplace(
415                         static_cast<pldm::pdr::TerminusHandle>(
416                             tlpdr->terminus_handle),
417                         static_cast<pldm::pdr::TerminusID>(tlpdr->tid));
418                 }
419                 else if (pdrHdr->type == PLDM_STATE_SENSOR_PDR)
420                 {
421                     stateSensorPDRs.emplace_back(pdr);
422                 }
423                 pldm_pdr_add(repo, pdr.data(), respCount, 0, true);
424             }
425         }
426     }
427     if (!nextRecordHandle)
428     {
429         /*received last record*/
430         this->parseStateSensorPDRs(stateSensorPDRs, tlpdrInfo);
431         stateSensorPDRs.clear();
432         tlpdrInfo.clear();
433         if (merged)
434         {
435             merged = false;
436             deferredPDRRepoChgEvent =
437                 std::make_unique<sdeventplus::source::Defer>(
438                     event,
439                     std::bind(
440                         std::mem_fn((&HostPDRHandler::_processPDRRepoChgEvent)),
441                         this, std::placeholders::_1));
442         }
443     }
444     else
445     {
446         deferredFetchPDREvent = std::make_unique<sdeventplus::source::Defer>(
447             event,
448             std::bind(std::mem_fn((&HostPDRHandler::_processFetchPDREvent)),
449                       this, nextRecordHandle, std::placeholders::_1));
450     }
451 }
452 
453 void HostPDRHandler::_processPDRRepoChgEvent(
454     sdeventplus::source::EventBase& /*source */)
455 {
456     deferredPDRRepoChgEvent.reset();
457     this->sendPDRRepositoryChgEvent(
458         std::move(std::vector<uint8_t>(1, PLDM_PDR_ENTITY_ASSOCIATION)),
459         FORMAT_IS_PDR_HANDLES);
460 }
461 
462 void HostPDRHandler::_processFetchPDREvent(
463     uint32_t nextRecordHandle, sdeventplus::source::EventBase& /*source */)
464 {
465     deferredFetchPDREvent.reset();
466     if (!this->pdrRecordHandles.empty())
467     {
468         nextRecordHandle = this->pdrRecordHandles.front();
469         this->pdrRecordHandles.pop_front();
470     }
471     this->getHostPDR(nextRecordHandle);
472 }
473 
474 } // namespace pldm
475