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