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