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 
110     uint32_t nextRecordHandle{};
111     uint32_t recordHandle{};
112     bool isFormatRecHandles = false;
113     if (!pdrRecordHandles.empty())
114     {
115         recordHandle = pdrRecordHandles.front();
116         pdrRecordHandles.pop_front();
117         isFormatRecHandles = true;
118     }
119 
120     do
121     {
122         auto instanceId = requester.getInstanceId(mctp_eid);
123 
124         auto rc =
125             encode_get_pdr_req(instanceId, recordHandle, 0, PLDM_GET_FIRSTPART,
126                                UINT16_MAX, 0, request, PLDM_GET_PDR_REQ_BYTES);
127         if (rc != PLDM_SUCCESS)
128         {
129             requester.markFree(mctp_eid, instanceId);
130             std::cerr << "Failed to encode_get_pdr_req, rc = " << rc
131                       << std::endl;
132             return;
133         }
134 
135         uint8_t* responseMsg = nullptr;
136         size_t responseMsgSize{};
137         auto requesterRc =
138             pldm_send_recv(mctp_eid, mctp_fd, requestMsg.data(),
139                            requestMsg.size(), &responseMsg, &responseMsgSize);
140         std::unique_ptr<uint8_t, decltype(std::free)*> responseMsgPtr{
141             responseMsg, std::free};
142         requester.markFree(mctp_eid, instanceId);
143         if (requesterRc != PLDM_REQUESTER_SUCCESS)
144         {
145             std::cerr << "Failed to send msg to fetch pdrs, rc = "
146                       << requesterRc << std::endl;
147             return;
148         }
149 
150         uint8_t completionCode{};
151         uint32_t nextDataTransferHandle{};
152         uint8_t transferFlag{};
153         uint16_t respCount{};
154         uint8_t transferCRC{};
155         auto responsePtr =
156             reinterpret_cast<struct pldm_msg*>(responseMsgPtr.get());
157         rc = decode_get_pdr_resp(
158             responsePtr, responseMsgSize - sizeof(pldm_msg_hdr),
159             &completionCode, &nextRecordHandle, &nextDataTransferHandle,
160             &transferFlag, &respCount, nullptr, 0, &transferCRC);
161         if (rc != PLDM_SUCCESS)
162         {
163             std::cerr << "Failed to decode_get_pdr_resp, rc = " << rc
164                       << std::endl;
165         }
166         else
167         {
168             std::vector<uint8_t> pdr(respCount, 0);
169             rc = decode_get_pdr_resp(
170                 responsePtr, responseMsgSize - sizeof(pldm_msg_hdr),
171                 &completionCode, &nextRecordHandle, &nextDataTransferHandle,
172                 &transferFlag, &respCount, pdr.data(), respCount, &transferCRC);
173             if (rc != PLDM_SUCCESS || completionCode != PLDM_SUCCESS)
174             {
175                 std::cerr << "Failed to decode_get_pdr_resp: "
176                           << "rc=" << rc
177                           << ", cc=" << static_cast<unsigned>(completionCode)
178                           << std::endl;
179             }
180             else
181             {
182                 // Process the PDR host firmware sent us. The most common action
183                 // is to add the PDR to the the BMC's PDR repo.
184                 auto pdrHdr = reinterpret_cast<pldm_pdr_hdr*>(pdr.data());
185                 if (pdrHdr->type == PLDM_PDR_ENTITY_ASSOCIATION)
186                 {
187                     mergeEntityAssociations(pdr);
188                     merged = true;
189                 }
190                 else
191                 {
192                     if (pdrHdr->type == PLDM_STATE_SENSOR_PDR)
193                     {
194                         const auto& [terminusHandle, sensorID, sensorInfo] =
195                             responder::pdr_utils::parseStateSensorPDR(pdr);
196                         SensorEntry sensorEntry{};
197                         // TODO: Lookup Terminus ID from the Terminus Locator
198                         // PDR with terminusHandle
199                         sensorEntry.terminusID = 0;
200                         sensorEntry.sensorID = sensorID;
201                         sensorMap.emplace(sensorEntry, std::move(sensorInfo));
202                     }
203                     pldm_pdr_add(repo, pdr.data(), respCount, 0, true);
204                 }
205             }
206 
207             recordHandle = nextRecordHandle;
208             if (!pdrRecordHandles.empty())
209             {
210                 recordHandle = pdrRecordHandles.front();
211                 pdrRecordHandles.pop_front();
212             }
213             else if (isFormatRecHandles)
214             {
215                 break;
216             }
217         }
218     } while (recordHandle);
219 
220     if (merged)
221     {
222         // We have merged host's entity association PDRs with our own. Send an
223         // event to the host firmware to indicate the same.
224         sendPDRRepositoryChgEvent(
225             std::move(std::vector<uint8_t>(1, PLDM_PDR_ENTITY_ASSOCIATION)),
226             FORMAT_IS_PDR_HANDLES);
227     }
228 }
229 
230 bool HostPDRHandler::getParent(EntityType type, pldm_entity& parent)
231 {
232     auto found = parents.find(type);
233     if (found != parents.end())
234     {
235         parent.entity_type = found->second.entity_type;
236         parent.entity_instance_num = found->second.entity_instance_num;
237         return true;
238     }
239 
240     return false;
241 }
242 
243 void HostPDRHandler::mergeEntityAssociations(const std::vector<uint8_t>& pdr)
244 {
245     size_t numEntities{};
246     pldm_entity* entities = nullptr;
247     bool merged = false;
248     auto entityPdr = reinterpret_cast<pldm_pdr_entity_association*>(
249         const_cast<uint8_t*>(pdr.data()) + sizeof(pldm_pdr_hdr));
250 
251     pldm_entity_association_pdr_extract(pdr.data(), pdr.size(), &numEntities,
252                                         &entities);
253     for (size_t i = 0; i < numEntities; ++i)
254     {
255         pldm_entity parent{};
256         if (getParent(entities[i].entity_type, parent))
257         {
258             auto node = pldm_entity_association_tree_find(entityTree, &parent);
259             if (node)
260             {
261                 pldm_entity_association_tree_add(entityTree, &entities[i], node,
262                                                  entityPdr->association_type);
263                 merged = true;
264             }
265         }
266     }
267     free(entities);
268 
269     if (merged)
270     {
271         // Update our PDR repo with the merged entity association PDRs
272         pldm_entity_association_pdr_add(entityTree, repo, true);
273     }
274 }
275 
276 void HostPDRHandler::sendPDRRepositoryChgEvent(std::vector<uint8_t>&& pdrTypes,
277                                                uint8_t eventDataFormat)
278 {
279     assert(eventDataFormat == FORMAT_IS_PDR_HANDLES);
280 
281     // Extract from the PDR repo record handles of PDRs we want the host
282     // to pull up.
283     std::vector<uint8_t> eventDataOps{PLDM_RECORDS_ADDED};
284     std::vector<uint8_t> numsOfChangeEntries(1);
285     std::vector<std::vector<ChangeEntry>> changeEntries(
286         numsOfChangeEntries.size());
287     for (auto pdrType : pdrTypes)
288     {
289         const pldm_pdr_record* record{};
290         do
291         {
292             record = pldm_pdr_find_record_by_type(repo, pdrType, record,
293                                                   nullptr, nullptr);
294             if (record && pldm_pdr_record_is_remote(record))
295             {
296                 changeEntries[0].push_back(
297                     pldm_pdr_get_record_handle(repo, record));
298             }
299         } while (record);
300     }
301     if (changeEntries.empty())
302     {
303         return;
304     }
305     numsOfChangeEntries[0] = changeEntries[0].size();
306 
307     // Encode PLDM platform event msg to indicate a PDR repo change.
308     size_t maxSize = PLDM_PDR_REPOSITORY_CHG_EVENT_MIN_LENGTH +
309                      PLDM_PDR_REPOSITORY_CHANGE_RECORD_MIN_LENGTH +
310                      changeEntries[0].size() * sizeof(uint32_t);
311     std::vector<uint8_t> eventDataVec{};
312     eventDataVec.resize(maxSize);
313     auto eventData =
314         reinterpret_cast<struct pldm_pdr_repository_chg_event_data*>(
315             eventDataVec.data());
316     size_t actualSize{};
317     auto firstEntry = changeEntries[0].data();
318     auto rc = encode_pldm_pdr_repository_chg_event_data(
319         eventDataFormat, 1, eventDataOps.data(), numsOfChangeEntries.data(),
320         &firstEntry, eventData, &actualSize, maxSize);
321     if (rc != PLDM_SUCCESS)
322     {
323         std::cerr
324             << "Failed to encode_pldm_pdr_repository_chg_event_data, rc = "
325             << rc << std::endl;
326         return;
327     }
328     auto instanceId = requester.getInstanceId(mctp_eid);
329     std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
330                                     PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES +
331                                     actualSize);
332     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
333     rc = encode_platform_event_message_req(
334         instanceId, 1, 0, PLDM_PDR_REPOSITORY_CHG_EVENT, eventDataVec.data(),
335         actualSize, request,
336         actualSize + PLDM_PLATFORM_EVENT_MESSAGE_MIN_REQ_BYTES);
337     if (rc != PLDM_SUCCESS)
338     {
339         requester.markFree(mctp_eid, instanceId);
340         std::cerr << "Failed to encode_platform_event_message_req, rc = " << rc
341                   << std::endl;
342         return;
343     }
344 
345     // Send up the event to host.
346     uint8_t* responseMsg = nullptr;
347     size_t responseMsgSize{};
348     auto requesterRc =
349         pldm_send_recv(mctp_eid, mctp_fd, requestMsg.data(), requestMsg.size(),
350                        &responseMsg, &responseMsgSize);
351     requester.markFree(mctp_eid, instanceId);
352     if (requesterRc != PLDM_REQUESTER_SUCCESS)
353     {
354         std::cerr << "Failed to send msg to report pdrs, rc = " << requesterRc
355                   << std::endl;
356         return;
357     }
358     uint8_t completionCode{};
359     uint8_t status{};
360     auto responsePtr = reinterpret_cast<struct pldm_msg*>(responseMsg);
361     rc = decode_platform_event_message_resp(
362         responsePtr, responseMsgSize - sizeof(pldm_msg_hdr), &completionCode,
363         &status);
364     free(responseMsg);
365     if (rc != PLDM_SUCCESS || completionCode != PLDM_SUCCESS)
366     {
367         std::cerr << "Failed to decode_platform_event_message_resp: "
368                   << "rc=" << rc
369                   << ", cc=" << static_cast<unsigned>(completionCode)
370                   << std::endl;
371     }
372 }
373 
374 } // namespace pldm
375