1 
2 #include "platform.hpp"
3 
4 #include "common/utils.hpp"
5 #include "event_parser.hpp"
6 #include "pdr.hpp"
7 #include "pdr_numeric_effecter.hpp"
8 #include "pdr_state_effecter.hpp"
9 #include "platform_numeric_effecter.hpp"
10 #include "platform_state_effecter.hpp"
11 
12 namespace pldm
13 {
14 namespace responder
15 {
16 namespace platform
17 {
18 
19 using InternalFailure =
20     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
21 
22 static const Json empty{};
23 
24 using EventEntryMap = std::map<EventEntry, DBusInfo>;
25 
26 const EventEntryMap eventEntryMap = {
27     {
28         0x01010007, // SensorID for VMI Port 0 ipv4 = 7, SensorOffset for the
29                     // State Set ID 15 = 1 & PLDM State Set Enumeration List = 1
30                     // (Valid Configuration)
31         {"/xyz/openbmc_project/network/hypervisor/eth0/ipv4/addr0",
32          "xyz.openbmc_project.Object.Enable", "Enabled", "bool", true},
33     },
34     {
35         0x02010007, // SensorID for VMI Port 0 ipv4 = 7, SensorOffset for the
36                     // State Set ID 15 = 1 & PLDM State Set Enumeration List = 2
37                     // (Invalid Configuration)
38         {"/xyz/openbmc_project/network/hypervisor/eth0/ipv4/addr0",
39          "xyz.openbmc_project.Object.Enable", "Enabled", "bool", false},
40     },
41     {
42         0x01010008, // SensorID for VMI Port 1 ipv4 = 8, SensorOffset for the
43                     // State Set ID 15 = 1 & PLDM State Set Enumeration List = 1
44                     // (Valid Configuration)
45         {"/xyz/openbmc_project/network/hypervisor/eth1/ipv4/addr0",
46          "xyz.openbmc_project.Object.Enable", "Enabled", "bool", true},
47     },
48     {
49         0x02010008, // SensorID for VMI Port 1 ipv4 = 8, SensorOffset for the
50                     // State Set ID 15 = 1 & PLDM State Set Enumeration List = 2
51                     // (Invalid Configuration)
52         {"/xyz/openbmc_project/network/hypervisor/eth1/ipv4/addr0",
53          "xyz.openbmc_project.Object.Enable", "Enabled", "bool", false},
54     }};
55 
56 void Handler::addDbusObjMaps(
57     uint16_t effecterId,
58     std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps> dbusObj)
59 {
60     dbusObjMaps.emplace(effecterId, dbusObj);
61 }
62 
63 const std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps>&
64     Handler::getDbusObjMaps(uint16_t effecterId) const
65 {
66     return dbusObjMaps.at(effecterId);
67 }
68 
69 void Handler::generate(const pldm::utils::DBusHandler& dBusIntf,
70                        const std::string& dir, Repo& repo)
71 {
72     if (!fs::exists(dir))
73     {
74         return;
75     }
76 
77     // A map of PDR type to a lambda that handles creation of that PDR type.
78     // The lambda essentially would parse the platform specific PDR JSONs to
79     // generate the PDR structures. This function iterates through the map to
80     // invoke all lambdas, so that all PDR types can be created.
81 
82     const std::map<Type, generatePDR> generateHandlers = {
83         {PLDM_STATE_EFFECTER_PDR,
84          [this](const DBusHandler& dBusIntf, const auto& json,
85                 RepoInterface& repo) {
86              pdr_state_effecter::generateStateEffecterPDR<
87                  pldm::utils::DBusHandler, Handler>(dBusIntf, json, *this,
88                                                     repo);
89          }},
90         {PLDM_NUMERIC_EFFECTER_PDR,
91          [this](const DBusHandler& dBusIntf, const auto& json,
92                 RepoInterface& repo) {
93              pdr_numeric_effecter::generateNumericEffecterPDR<
94                  pldm::utils::DBusHandler, Handler>(dBusIntf, json, *this,
95                                                     repo);
96          }}};
97 
98     Type pdrType{};
99     for (const auto& dirEntry : fs::directory_iterator(dir))
100     {
101         try
102         {
103             auto json = readJson(dirEntry.path().string());
104             if (!json.empty())
105             {
106                 auto effecterPDRs = json.value("effecterPDRs", empty);
107                 for (const auto& effecter : effecterPDRs)
108                 {
109                     pdrType = effecter.value("pdrType", 0);
110                     generateHandlers.at(pdrType)(dBusIntf, effecter, repo);
111                 }
112             }
113         }
114         catch (const InternalFailure& e)
115         {
116             std::cerr << "PDR config directory does not exist or empty, TYPE= "
117                       << pdrType << "PATH= " << dirEntry
118                       << " ERROR=" << e.what() << "\n";
119         }
120         catch (const Json::exception& e)
121         {
122             std::cerr << "Failed parsing PDR JSON file, TYPE= " << pdrType
123                       << " ERROR=" << e.what() << "\n";
124             pldm::utils::reportError(
125                 "xyz.openbmc_project.bmc.pldm.InternalFailure");
126         }
127         catch (const std::exception& e)
128         {
129             std::cerr << "Failed parsing PDR JSON file, TYPE= " << pdrType
130                       << " ERROR=" << e.what() << "\n";
131             pldm::utils::reportError(
132                 "xyz.openbmc_project.bmc.pldm.InternalFailure");
133         }
134     }
135 }
136 
137 Response Handler::getPDR(const pldm_msg* request, size_t payloadLength)
138 {
139     if (!pdrCreated)
140     {
141         generateTerminusLocatorPDR(pdrRepo);
142         generate(*dBusIntf, pdrJsonsDir, pdrRepo);
143         pdrCreated = true;
144     }
145 
146     // Build FRU table if not built, since entity association PDR's are built
147     // when the FRU table is constructed.
148     if (fruHandler)
149     {
150         fruHandler->buildFRUTable();
151     }
152 
153     Response response(sizeof(pldm_msg_hdr) + PLDM_GET_PDR_MIN_RESP_BYTES, 0);
154     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
155 
156     if (payloadLength != PLDM_GET_PDR_REQ_BYTES)
157     {
158         return CmdHandler::ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
159     }
160 
161     uint32_t recordHandle{};
162     uint32_t dataTransferHandle{};
163     uint8_t transferOpFlag{};
164     uint16_t reqSizeBytes{};
165     uint16_t recordChangeNum{};
166 
167     auto rc = decode_get_pdr_req(request, payloadLength, &recordHandle,
168                                  &dataTransferHandle, &transferOpFlag,
169                                  &reqSizeBytes, &recordChangeNum);
170     if (rc != PLDM_SUCCESS)
171     {
172         return CmdHandler::ccOnlyResponse(request, rc);
173     }
174 
175     uint16_t respSizeBytes{};
176     uint8_t* recordData = nullptr;
177     try
178     {
179         pdr_utils::PdrEntry e;
180         auto record = pdr::getRecordByHandle(pdrRepo, recordHandle, e);
181         if (record == NULL)
182         {
183             return CmdHandler::ccOnlyResponse(
184                 request, PLDM_PLATFORM_INVALID_RECORD_HANDLE);
185         }
186 
187         if (reqSizeBytes)
188         {
189             respSizeBytes = e.size;
190             if (respSizeBytes > reqSizeBytes)
191             {
192                 respSizeBytes = reqSizeBytes;
193             }
194             recordData = e.data;
195         }
196         response.resize(sizeof(pldm_msg_hdr) + PLDM_GET_PDR_MIN_RESP_BYTES +
197                             respSizeBytes,
198                         0);
199         responsePtr = reinterpret_cast<pldm_msg*>(response.data());
200         rc = encode_get_pdr_resp(
201             request->hdr.instance_id, PLDM_SUCCESS, e.handle.nextRecordHandle,
202             0, PLDM_START_AND_END, respSizeBytes, recordData, 0, responsePtr);
203         if (rc != PLDM_SUCCESS)
204         {
205             return ccOnlyResponse(request, rc);
206         }
207     }
208     catch (const std::exception& e)
209     {
210         std::cerr << "Error accessing PDR, HANDLE=" << recordHandle
211                   << " ERROR=" << e.what() << "\n";
212         return CmdHandler::ccOnlyResponse(request, PLDM_ERROR);
213     }
214     return response;
215 }
216 
217 Response Handler::setStateEffecterStates(const pldm_msg* request,
218                                          size_t payloadLength)
219 {
220     Response response(
221         sizeof(pldm_msg_hdr) + PLDM_SET_STATE_EFFECTER_STATES_RESP_BYTES, 0);
222     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
223     uint16_t effecterId;
224     uint8_t compEffecterCnt;
225     constexpr auto maxCompositeEffecterCnt = 8;
226     std::vector<set_effecter_state_field> stateField(maxCompositeEffecterCnt,
227                                                      {0, 0});
228 
229     if ((payloadLength > PLDM_SET_STATE_EFFECTER_STATES_REQ_BYTES) ||
230         (payloadLength < sizeof(effecterId) + sizeof(compEffecterCnt) +
231                              sizeof(set_effecter_state_field)))
232     {
233         return CmdHandler::ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
234     }
235 
236     int rc = decode_set_state_effecter_states_req(request, payloadLength,
237                                                   &effecterId, &compEffecterCnt,
238                                                   stateField.data());
239 
240     if (rc != PLDM_SUCCESS)
241     {
242         return CmdHandler::ccOnlyResponse(request, rc);
243     }
244 
245     stateField.resize(compEffecterCnt);
246     const pldm::utils::DBusHandler dBusIntf;
247     rc = platform_state_effecter::setStateEffecterStatesHandler<
248         pldm::utils::DBusHandler, Handler>(dBusIntf, *this, effecterId,
249                                            stateField);
250     if (rc != PLDM_SUCCESS)
251     {
252         return CmdHandler::ccOnlyResponse(request, rc);
253     }
254 
255     rc = encode_set_state_effecter_states_resp(request->hdr.instance_id, rc,
256                                                responsePtr);
257     if (rc != PLDM_SUCCESS)
258     {
259         return ccOnlyResponse(request, rc);
260     }
261 
262     return response;
263 }
264 
265 Response Handler::platformEventMessage(const pldm_msg* request,
266                                        size_t payloadLength)
267 {
268     uint8_t formatVersion{};
269     uint8_t tid{};
270     uint8_t eventClass{};
271     size_t offset{};
272 
273     auto rc = decode_platform_event_message_req(
274         request, payloadLength, &formatVersion, &tid, &eventClass, &offset);
275     if (rc != PLDM_SUCCESS)
276     {
277         return CmdHandler::ccOnlyResponse(request, rc);
278     }
279 
280     try
281     {
282         const auto& handlers = eventHandlers.at(eventClass);
283         for (const auto& handler : handlers)
284         {
285             auto rc =
286                 handler(request, payloadLength, formatVersion, tid, offset);
287             if (rc != PLDM_SUCCESS)
288             {
289                 return CmdHandler::ccOnlyResponse(request, rc);
290             }
291         }
292     }
293     catch (const std::out_of_range& e)
294     {
295         return CmdHandler::ccOnlyResponse(request, PLDM_ERROR_INVALID_DATA);
296     }
297 
298     Response response(
299         sizeof(pldm_msg_hdr) + PLDM_PLATFORM_EVENT_MESSAGE_RESP_BYTES, 0);
300     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
301 
302     rc = encode_platform_event_message_resp(request->hdr.instance_id, rc,
303                                             PLDM_EVENT_NO_LOGGING, responsePtr);
304     if (rc != PLDM_SUCCESS)
305     {
306         return ccOnlyResponse(request, rc);
307     }
308 
309     return response;
310 }
311 
312 int Handler::sensorEvent(const pldm_msg* request, size_t payloadLength,
313                          uint8_t /*formatVersion*/, uint8_t tid,
314                          size_t eventDataOffset)
315 {
316     uint16_t sensorId{};
317     uint8_t eventClass{};
318     size_t eventClassDataOffset{};
319     auto eventData =
320         reinterpret_cast<const uint8_t*>(request->payload) + eventDataOffset;
321     auto eventDataSize = payloadLength - eventDataOffset;
322 
323     auto rc = decode_sensor_event_data(eventData, eventDataSize, &sensorId,
324                                        &eventClass, &eventClassDataOffset);
325     if (rc != PLDM_SUCCESS)
326     {
327         return rc;
328     }
329 
330     auto eventClassData = reinterpret_cast<const uint8_t*>(request->payload) +
331                           eventDataOffset + eventClassDataOffset;
332     auto eventClassDataSize =
333         payloadLength - eventDataOffset - eventClassDataOffset;
334 
335     if (eventClass == PLDM_STATE_SENSOR_STATE)
336     {
337         uint8_t sensorOffset{};
338         uint8_t eventState{};
339         uint8_t previousEventState{};
340 
341         rc = decode_state_sensor_data(eventClassData, eventClassDataSize,
342                                       &sensorOffset, &eventState,
343                                       &previousEventState);
344         if (rc != PLDM_SUCCESS)
345         {
346             return PLDM_ERROR;
347         }
348 
349         // Emitting state sensor event signal
350         emitStateSensorEventSignal(tid, sensorId, sensorOffset, eventState,
351                                    previousEventState);
352 
353         // Handle PLDM events for which PDR is not available, setSensorEventData
354         // will return PLDM_ERROR_INVALID_DATA if the sensorID is not found in
355         // the hardcoded sensor list.
356         rc = setSensorEventData(sensorId, sensorOffset, eventState);
357         if (rc != PLDM_ERROR_INVALID_DATA)
358         {
359             return rc;
360         }
361 
362         // If there are no HOST PDR's, there is no further action
363         if (hostPDRHandler == NULL)
364         {
365             return PLDM_SUCCESS;
366         }
367 
368         // Handle PLDM events for which PDR is available
369         SensorEntry sensorEntry{tid, sensorId};
370         try
371         {
372             const auto& [entityInfo, compositeSensorStates] =
373                 hostPDRHandler->lookupSensorInfo(sensorEntry);
374             if (sensorOffset >= compositeSensorStates.size())
375             {
376                 return PLDM_ERROR_INVALID_DATA;
377             }
378 
379             const auto& possibleStates = compositeSensorStates[sensorOffset];
380             if (possibleStates.find(eventState) == possibleStates.end())
381             {
382                 return PLDM_ERROR_INVALID_DATA;
383             }
384 
385             const auto& [containerId, entityType, entityInstance] = entityInfo;
386             events::StateSensorEntry stateSensorEntry{
387                 containerId, entityType, entityInstance, sensorOffset};
388             return stateSensorHandler.eventAction(stateSensorEntry, eventState);
389         }
390         // If there is no mapping for events return PLDM_SUCCESS
391         catch (const std::out_of_range& e)
392         {
393             return PLDM_SUCCESS;
394         }
395     }
396     else
397     {
398         return PLDM_ERROR_INVALID_DATA;
399     }
400 
401     return PLDM_SUCCESS;
402 }
403 
404 int Handler::setSensorEventData(uint16_t sensorId, uint8_t sensorOffset,
405                                 uint8_t eventState)
406 {
407     EventEntry eventEntry = ((static_cast<uint32_t>(eventState)) << 24) +
408                             ((static_cast<uint32_t>(sensorOffset)) << 16) +
409                             sensorId;
410     auto iter = eventEntryMap.find(eventEntry);
411     if (iter == eventEntryMap.end())
412     {
413         return PLDM_ERROR_INVALID_DATA;
414     }
415 
416     const auto& dBusInfo = iter->second;
417     try
418     {
419         pldm::utils::DBusMapping dbusMapping{
420             dBusInfo.dBusValues.objectPath, dBusInfo.dBusValues.interface,
421             dBusInfo.dBusValues.propertyName, dBusInfo.dBusValues.propertyType};
422         pldm::utils::DBusHandler().setDbusProperty(dbusMapping,
423                                                    dBusInfo.dBusPropertyValue);
424     }
425     catch (std::exception& e)
426     {
427         std::cerr
428             << "Error Setting dbus property,SensorID=" << eventEntry
429             << "DBusInfo=" << dBusInfo.dBusValues.objectPath
430             << dBusInfo.dBusValues.interface << dBusInfo.dBusValues.propertyName
431             << "ERROR=" << e.what() << "\n";
432         return PLDM_ERROR;
433     }
434     return PLDM_SUCCESS;
435 }
436 
437 int Handler::pldmPDRRepositoryChgEvent(const pldm_msg* request,
438                                        size_t payloadLength,
439                                        uint8_t /*formatVersion*/,
440                                        uint8_t /*tid*/, size_t eventDataOffset)
441 {
442     uint8_t eventDataFormat{};
443     uint8_t numberOfChangeRecords{};
444     size_t dataOffset{};
445 
446     auto eventData =
447         reinterpret_cast<const uint8_t*>(request->payload) + eventDataOffset;
448     auto eventDataSize = payloadLength - eventDataOffset;
449 
450     auto rc = decode_pldm_pdr_repository_chg_event_data(
451         eventData, eventDataSize, &eventDataFormat, &numberOfChangeRecords,
452         &dataOffset);
453     if (rc != PLDM_SUCCESS)
454     {
455         return rc;
456     }
457 
458     PDRRecordHandles pdrRecordHandles;
459 
460     if (eventDataFormat == FORMAT_IS_PDR_TYPES)
461     {
462         return PLDM_ERROR_INVALID_DATA;
463     }
464 
465     if (eventDataFormat == FORMAT_IS_PDR_HANDLES)
466     {
467         uint8_t eventDataOperation{};
468         uint8_t numberOfChangeEntries{};
469 
470         auto changeRecordData = eventData + dataOffset;
471         auto changeRecordDataSize = eventDataSize - dataOffset;
472 
473         while (changeRecordDataSize)
474         {
475             rc = decode_pldm_pdr_repository_change_record_data(
476                 changeRecordData, changeRecordDataSize, &eventDataOperation,
477                 &numberOfChangeEntries, &dataOffset);
478 
479             if (rc != PLDM_SUCCESS)
480             {
481                 return rc;
482             }
483 
484             if (eventDataOperation == PLDM_RECORDS_ADDED)
485             {
486                 rc = getPDRRecordHandles(
487                     reinterpret_cast<const ChangeEntry*>(changeRecordData +
488                                                          dataOffset),
489                     changeRecordDataSize - dataOffset,
490                     static_cast<size_t>(numberOfChangeEntries),
491                     pdrRecordHandles);
492 
493                 if (rc != PLDM_SUCCESS)
494                 {
495                     return rc;
496                 }
497             }
498 
499             changeRecordData +=
500                 dataOffset + (numberOfChangeEntries * sizeof(ChangeEntry));
501             changeRecordDataSize -=
502                 dataOffset + (numberOfChangeEntries * sizeof(ChangeEntry));
503         }
504     }
505     if (hostPDRHandler)
506     {
507         hostPDRHandler->fetchPDR(std::move(pdrRecordHandles));
508     }
509 
510     return PLDM_SUCCESS;
511 }
512 
513 int Handler::getPDRRecordHandles(const ChangeEntry* changeEntryData,
514                                  size_t changeEntryDataSize,
515                                  size_t numberOfChangeEntries,
516                                  PDRRecordHandles& pdrRecordHandles)
517 {
518     if (numberOfChangeEntries > (changeEntryDataSize / sizeof(ChangeEntry)))
519     {
520         return PLDM_ERROR_INVALID_DATA;
521     }
522     for (size_t i = 0; i < numberOfChangeEntries; i++)
523     {
524         pdrRecordHandles.push_back(changeEntryData[i]);
525     }
526     return PLDM_SUCCESS;
527 }
528 
529 Response Handler::setNumericEffecterValue(const pldm_msg* request,
530                                           size_t payloadLength)
531 {
532     Response response(sizeof(pldm_msg_hdr) +
533                       PLDM_SET_NUMERIC_EFFECTER_VALUE_RESP_BYTES);
534     uint16_t effecterId{};
535     uint8_t effecterDataSize{};
536     uint8_t effecterValue[4] = {};
537 
538     if ((payloadLength > sizeof(effecterId) + sizeof(effecterDataSize) +
539                              sizeof(union_effecter_data_size)) ||
540         (payloadLength < sizeof(effecterId) + sizeof(effecterDataSize) + 1))
541     {
542         return ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
543     }
544 
545     int rc = decode_set_numeric_effecter_value_req(
546         request, payloadLength, &effecterId, &effecterDataSize,
547         reinterpret_cast<uint8_t*>(&effecterValue));
548 
549     if (rc == PLDM_SUCCESS)
550     {
551         const pldm::utils::DBusHandler dBusIntf;
552         rc = platform_numeric_effecter::setNumericEffecterValueHandler<
553             pldm::utils::DBusHandler, Handler>(dBusIntf, *this, effecterId,
554                                                effecterDataSize, effecterValue,
555                                                sizeof(effecterValue));
556     }
557 
558     return ccOnlyResponse(request, rc);
559 }
560 
561 void Handler::generateTerminusLocatorPDR(Repo& repo)
562 {
563     std::vector<uint8_t> pdrBuffer(sizeof(pldm_terminus_locator_pdr));
564 
565     auto pdr = reinterpret_cast<pldm_terminus_locator_pdr*>(pdrBuffer.data());
566 
567     pdr->hdr.record_handle = 0;
568     pdr->hdr.version = 1;
569     pdr->hdr.type = PLDM_TERMINUS_LOCATOR_PDR;
570     pdr->hdr.record_change_num = 0;
571     pdr->hdr.length = sizeof(pldm_terminus_locator_pdr) - sizeof(pldm_pdr_hdr);
572     pdr->terminus_handle = BmcPldmTerminusHandle;
573     pdr->validity = PLDM_TL_PDR_VALID;
574     pdr->tid = BmcTerminusId;
575     pdr->container_id = 0x0;
576     pdr->terminus_locator_type = PLDM_TERMINUS_LOCATOR_TYPE_MCTP_EID;
577     pdr->terminus_locator_value_size =
578         sizeof(pldm_terminus_locator_type_mctp_eid);
579     auto locatorValue = reinterpret_cast<pldm_terminus_locator_type_mctp_eid*>(
580         pdr->terminus_locator_value);
581     locatorValue->eid = BmcMctpEid;
582 
583     PdrEntry pdrEntry{};
584     pdrEntry.data = pdrBuffer.data();
585     pdrEntry.size = pdrBuffer.size();
586     repo.addRecord(pdrEntry);
587 }
588 } // namespace platform
589 } // namespace responder
590 } // namespace pldm
591