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