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, bool verbose) :
29     mctp_fd(mctp_fd),
30     mctp_eid(mctp_eid), event(event), repo(repo),
31     stateSensorHandler(eventsJsonsDir), entityTree(entityTree),
32     requester(requester), verbose(verbose)
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         if (verbose)
139         {
140             std::cout << "Sending Msg:" << std::endl;
141             printBuffer(requestMsg, verbose);
142         }
143 
144         uint8_t* responseMsg = nullptr;
145         size_t responseMsgSize{};
146         auto requesterRc =
147             pldm_send_recv(mctp_eid, mctp_fd, requestMsg.data(),
148                            requestMsg.size(), &responseMsg, &responseMsgSize);
149         std::unique_ptr<uint8_t, decltype(std::free)*> responseMsgPtr{
150             responseMsg, std::free};
151         requester.markFree(mctp_eid, instanceId);
152         if (requesterRc != PLDM_REQUESTER_SUCCESS)
153         {
154             std::cerr << "Failed to send msg to fetch pdrs, rc = "
155                       << requesterRc << std::endl;
156             return;
157         }
158 
159         uint8_t completionCode{};
160         uint32_t nextDataTransferHandle{};
161         uint8_t transferFlag{};
162         uint16_t respCount{};
163         uint8_t transferCRC{};
164         auto responsePtr =
165             reinterpret_cast<struct pldm_msg*>(responseMsgPtr.get());
166         rc = decode_get_pdr_resp(
167             responsePtr, responseMsgSize - sizeof(pldm_msg_hdr),
168             &completionCode, &nextRecordHandle, &nextDataTransferHandle,
169             &transferFlag, &respCount, nullptr, 0, &transferCRC);
170 
171         std::vector<uint8_t> responsePDRMsg;
172         responsePDRMsg.resize(responseMsgSize);
173         memcpy(responsePDRMsg.data(), responsePtr, responsePDRMsg.size());
174         if (verbose)
175         {
176             std::cout << "Receiving Msg:" << std::endl;
177             printBuffer(responsePDRMsg, verbose);
178         }
179         if (rc != PLDM_SUCCESS)
180         {
181             std::cerr << "Failed to decode_get_pdr_resp, rc = " << rc
182                       << std::endl;
183         }
184         else
185         {
186             std::vector<uint8_t> pdr(respCount, 0);
187             rc = decode_get_pdr_resp(
188                 responsePtr, responseMsgSize - sizeof(pldm_msg_hdr),
189                 &completionCode, &nextRecordHandle, &nextDataTransferHandle,
190                 &transferFlag, &respCount, pdr.data(), respCount, &transferCRC);
191             if (rc != PLDM_SUCCESS || completionCode != PLDM_SUCCESS)
192             {
193                 std::cerr << "Failed to decode_get_pdr_resp: "
194                           << "rc=" << rc
195                           << ", cc=" << static_cast<unsigned>(completionCode)
196                           << std::endl;
197             }
198             else
199             {
200                 // Process the PDR host firmware sent us. The most common action
201                 // is to add the PDR to the the BMC's PDR repo.
202                 auto pdrHdr = reinterpret_cast<pldm_pdr_hdr*>(pdr.data());
203                 if (pdrHdr->type == PLDM_PDR_ENTITY_ASSOCIATION)
204                 {
205                     mergeEntityAssociations(pdr);
206                     merged = true;
207                 }
208                 else
209                 {
210                     if (pdrHdr->type == PLDM_TERMINUS_LOCATOR_PDR)
211                     {
212                         auto tlpdr =
213                             reinterpret_cast<const pldm_terminus_locator_pdr*>(
214                                 pdr.data());
215                         tlpdrInfo.emplace(
216                             static_cast<pdr::TerminusHandle>(
217                                 tlpdr->terminus_handle),
218                             static_cast<pdr::TerminusID>(tlpdr->tid));
219                     }
220                     else if (pdrHdr->type == PLDM_STATE_SENSOR_PDR)
221                     {
222                         stateSensorPDRs.emplace_back(pdr);
223                     }
224                     pldm_pdr_add(repo, pdr.data(), respCount, 0, true);
225                 }
226             }
227 
228             recordHandle = nextRecordHandle;
229             if (!pdrRecordHandles.empty())
230             {
231                 recordHandle = pdrRecordHandles.front();
232                 pdrRecordHandles.pop_front();
233             }
234             else if (isFormatRecHandles)
235             {
236                 break;
237             }
238         }
239     } while (recordHandle);
240 
241     parseStateSensorPDRs(stateSensorPDRs, tlpdrInfo);
242 
243     if (merged)
244     {
245         // We have merged host's entity association PDRs with our own. Send an
246         // event to the host firmware to indicate the same.
247         sendPDRRepositoryChgEvent(
248             std::move(std::vector<uint8_t>(1, PLDM_PDR_ENTITY_ASSOCIATION)),
249             FORMAT_IS_PDR_HANDLES);
250     }
251 }
252 
253 int HostPDRHandler::handleStateSensorEvent(const StateSensorEntry& entry,
254                                            pdr::EventState state)
255 {
256     auto rc = stateSensorHandler.eventAction(entry, state);
257     if (rc != PLDM_SUCCESS)
258     {
259         std::cerr << "Failed to fetch and update D-bus property, rc = " << rc
260                   << std::endl;
261         return rc;
262     }
263     return PLDM_SUCCESS;
264 }
265 bool HostPDRHandler::getParent(EntityType type, pldm_entity& parent)
266 {
267     auto found = parents.find(type);
268     if (found != parents.end())
269     {
270         parent.entity_type = found->second.entity_type;
271         parent.entity_instance_num = found->second.entity_instance_num;
272         return true;
273     }
274 
275     return false;
276 }
277 
278 void HostPDRHandler::mergeEntityAssociations(const std::vector<uint8_t>& pdr)
279 {
280     size_t numEntities{};
281     pldm_entity* entities = nullptr;
282     bool merged = false;
283     auto entityPdr = reinterpret_cast<pldm_pdr_entity_association*>(
284         const_cast<uint8_t*>(pdr.data()) + sizeof(pldm_pdr_hdr));
285 
286     pldm_entity_association_pdr_extract(pdr.data(), pdr.size(), &numEntities,
287                                         &entities);
288     for (size_t i = 0; i < numEntities; ++i)
289     {
290         pldm_entity parent{};
291         if (getParent(entities[i].entity_type, parent))
292         {
293             auto node = pldm_entity_association_tree_find(entityTree, &parent);
294             if (node)
295             {
296                 pldm_entity_association_tree_add(entityTree, &entities[i], node,
297                                                  entityPdr->association_type);
298                 merged = true;
299             }
300         }
301     }
302     free(entities);
303 
304     if (merged)
305     {
306         // Update our PDR repo with the merged entity association PDRs
307         pldm_entity_association_pdr_add(entityTree, repo, true);
308     }
309 }
310 
311 void HostPDRHandler::sendPDRRepositoryChgEvent(std::vector<uint8_t>&& pdrTypes,
312                                                uint8_t eventDataFormat)
313 {
314     assert(eventDataFormat == FORMAT_IS_PDR_HANDLES);
315 
316     // Extract from the PDR repo record handles of PDRs we want the host
317     // to pull up.
318     std::vector<uint8_t> eventDataOps{PLDM_RECORDS_ADDED};
319     std::vector<uint8_t> numsOfChangeEntries(1);
320     std::vector<std::vector<ChangeEntry>> changeEntries(
321         numsOfChangeEntries.size());
322     for (auto pdrType : pdrTypes)
323     {
324         const pldm_pdr_record* record{};
325         do
326         {
327             record = pldm_pdr_find_record_by_type(repo, pdrType, record,
328                                                   nullptr, nullptr);
329             if (record && pldm_pdr_record_is_remote(record))
330             {
331                 changeEntries[0].push_back(
332                     pldm_pdr_get_record_handle(repo, record));
333             }
334         } while (record);
335     }
336     if (changeEntries.empty())
337     {
338         return;
339     }
340     numsOfChangeEntries[0] = changeEntries[0].size();
341 
342     // Encode PLDM platform event msg to indicate a PDR repo change.
343     size_t maxSize = PLDM_PDR_REPOSITORY_CHG_EVENT_MIN_LENGTH +
344                      PLDM_PDR_REPOSITORY_CHANGE_RECORD_MIN_LENGTH +
345                      changeEntries[0].size() * sizeof(uint32_t);
346     std::vector<uint8_t> eventDataVec{};
347     eventDataVec.resize(maxSize);
348     auto eventData =
349         reinterpret_cast<struct pldm_pdr_repository_chg_event_data*>(
350             eventDataVec.data());
351     size_t actualSize{};
352     auto firstEntry = changeEntries[0].data();
353     auto rc = encode_pldm_pdr_repository_chg_event_data(
354         eventDataFormat, 1, eventDataOps.data(), numsOfChangeEntries.data(),
355         &firstEntry, eventData, &actualSize, maxSize);
356     if (rc != PLDM_SUCCESS)
357     {
358         std::cerr
359             << "Failed to encode_pldm_pdr_repository_chg_event_data, rc = "
360             << rc << std::endl;
361         return;
362     }
363     auto instanceId = requester.getInstanceId(mctp_eid);
364     std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
365                                     PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES +
366                                     actualSize);
367     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
368     rc = encode_platform_event_message_req(
369         instanceId, 1, 0, PLDM_PDR_REPOSITORY_CHG_EVENT, eventDataVec.data(),
370         actualSize, request,
371         actualSize + PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES);
372     if (rc != PLDM_SUCCESS)
373     {
374         requester.markFree(mctp_eid, instanceId);
375         std::cerr << "Failed to encode_platform_event_message_req, rc = " << rc
376                   << std::endl;
377         return;
378     }
379 
380     // Send up the event to host.
381     uint8_t* responseMsg = nullptr;
382     size_t responseMsgSize{};
383     auto requesterRc =
384         pldm_send_recv(mctp_eid, mctp_fd, requestMsg.data(), requestMsg.size(),
385                        &responseMsg, &responseMsgSize);
386     requester.markFree(mctp_eid, instanceId);
387     if (requesterRc != PLDM_REQUESTER_SUCCESS)
388     {
389         std::cerr << "Failed to send msg to report pdrs, rc = " << requesterRc
390                   << std::endl;
391         return;
392     }
393     uint8_t completionCode{};
394     uint8_t status{};
395     auto responsePtr = reinterpret_cast<struct pldm_msg*>(responseMsg);
396     rc = decode_platform_event_message_resp(
397         responsePtr, responseMsgSize - sizeof(pldm_msg_hdr), &completionCode,
398         &status);
399     free(responseMsg);
400     if (rc != PLDM_SUCCESS || completionCode != PLDM_SUCCESS)
401     {
402         std::cerr << "Failed to decode_platform_event_message_resp: "
403                   << "rc=" << rc
404                   << ", cc=" << static_cast<unsigned>(completionCode)
405                   << std::endl;
406     }
407 }
408 
409 void HostPDRHandler::parseStateSensorPDRs(const PDRList& stateSensorPDRs,
410                                           const TLPDRMap& tlpdrInfo)
411 {
412     for (const auto& pdr : stateSensorPDRs)
413     {
414         SensorEntry sensorEntry{};
415         const auto& [terminusHandle, sensorID, sensorInfo] =
416             responder::pdr_utils::parseStateSensorPDR(pdr);
417         sensorEntry.sensorID = sensorID;
418         try
419         {
420             sensorEntry.terminusID = tlpdrInfo.at(terminusHandle);
421         }
422         // If there is no mapping for terminusHandle assign the reserved TID
423         // value of 0xFF to indicate that.
424         catch (const std::out_of_range& e)
425         {
426             sensorEntry.terminusID = PLDM_TID_RESERVED;
427         }
428         sensorMap.emplace(sensorEntry, std::move(sensorInfo));
429     }
430 }
431 
432 } // namespace pldm
433