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