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