1 #pragma once
2 
3 #include "config.h"
4 
5 #include "libpldm/platform.h"
6 #include "libpldm/states.h"
7 #include "pdr.h"
8 
9 #include "common/utils.hpp"
10 #include "event_parser.hpp"
11 #include "fru.hpp"
12 #include "host-bmc/dbus_to_event_handler.hpp"
13 #include "host-bmc/host_pdr_handler.hpp"
14 #include "libpldmresponder/pdr.hpp"
15 #include "libpldmresponder/pdr_utils.hpp"
16 #include "pldmd/handler.hpp"
17 
18 #include <stdint.h>
19 
20 #include <map>
21 
22 namespace pldm
23 {
24 namespace responder
25 {
26 namespace platform
27 {
28 
29 using namespace pldm::utils;
30 using namespace pldm::responder::pdr_utils;
31 using namespace pldm::state_sensor;
32 
33 using generatePDR =
34     std::function<void(const pldm::utils::DBusHandler& dBusIntf,
35                        const Json& json, pdr_utils::RepoInterface& repo)>;
36 
37 using EffecterId = uint16_t;
38 using DbusObjMaps =
39     std::map<EffecterId,
40              std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps>>;
41 using DbusPath = std::string;
42 using EffecterObjs = std::vector<DbusPath>;
43 using EventType = uint8_t;
44 using EventHandler = std::function<int(
45     const pldm_msg* request, size_t payloadLength, uint8_t formatVersion,
46     uint8_t tid, size_t eventDataOffset)>;
47 using EventHandlers = std::vector<EventHandler>;
48 using EventMap = std::map<EventType, EventHandlers>;
49 using AssociatedEntityMap = std::map<DbusPath, pldm_entity>;
50 
51 // EventEntry = <uint8_t> - EventState <uint8_t> - SensorOffset <uint16_t> -
52 // SensorID
53 using EventEntry = uint32_t;
54 struct DBusInfo
55 {
56     pldm::utils::DBusMapping dBusValues;
57     pldm::utils::PropertyValue dBusPropertyValue;
58 };
59 
60 class Handler : public CmdHandler
61 {
62   public:
63     Handler(const pldm::utils::DBusHandler* dBusIntf,
64             const std::string& pdrJsonsDir, const std::string& eventsJsonsDir,
65             pldm_pdr* repo, HostPDRHandler* hostPDRHandler,
66             DbusToPLDMEvent* dbusToPLDMEventHandler, fru::Handler* fruHandler,
67             bool buildPDRLazily = false,
68             const std::optional<EventMap>& addOnHandlersMap = std::nullopt) :
69         pdrRepo(repo),
70         hostPDRHandler(hostPDRHandler), stateSensorHandler(eventsJsonsDir),
71         dbusToPLDMEventHandler(dbusToPLDMEventHandler), fruHandler(fruHandler),
72         dBusIntf(dBusIntf), pdrJsonsDir(pdrJsonsDir), pdrCreated(false)
73     {
74         if (!buildPDRLazily)
75         {
76             generateTerminusLocatorPDR(pdrRepo);
77             generate(*dBusIntf, pdrJsonsDir, pdrRepo);
78             pdrCreated = true;
79         }
80 
81         handlers.emplace(PLDM_GET_PDR,
82                          [this](const pldm_msg* request, size_t payloadLength) {
83                              return this->getPDR(request, payloadLength);
84                          });
85         handlers.emplace(PLDM_SET_NUMERIC_EFFECTER_VALUE,
86                          [this](const pldm_msg* request, size_t payloadLength) {
87                              return this->setNumericEffecterValue(
88                                  request, payloadLength);
89                          });
90         handlers.emplace(PLDM_SET_STATE_EFFECTER_STATES,
91                          [this](const pldm_msg* request, size_t payloadLength) {
92                              return this->setStateEffecterStates(request,
93                                                                  payloadLength);
94                          });
95         handlers.emplace(PLDM_PLATFORM_EVENT_MESSAGE,
96                          [this](const pldm_msg* request, size_t payloadLength) {
97                              return this->platformEventMessage(request,
98                                                                payloadLength);
99                          });
100         handlers.emplace(PLDM_GET_STATE_SENSOR_READINGS,
101                          [this](const pldm_msg* request, size_t payloadLength) {
102                              return this->getStateSensorReadings(request,
103                                                                  payloadLength);
104                          });
105 
106         // Default handler for PLDM Events
107         eventHandlers[PLDM_SENSOR_EVENT].emplace_back(
108             [this](const pldm_msg* request, size_t payloadLength,
109                    uint8_t formatVersion, uint8_t tid, size_t eventDataOffset) {
110                 return this->sensorEvent(request, payloadLength, formatVersion,
111                                          tid, eventDataOffset);
112             });
113         eventHandlers[PLDM_PDR_REPOSITORY_CHG_EVENT].emplace_back(
114             [this](const pldm_msg* request, size_t payloadLength,
115                    uint8_t formatVersion, uint8_t tid, size_t eventDataOffset) {
116                 return this->pldmPDRRepositoryChgEvent(request, payloadLength,
117                                                        formatVersion, tid,
118                                                        eventDataOffset);
119             });
120 
121         // Additional OEM event handlers for PLDM events, append it to the
122         // standard handlers
123         if (addOnHandlersMap)
124         {
125             auto addOnHandlers = addOnHandlersMap.value();
126             for (EventMap::iterator iter = addOnHandlers.begin();
127                  iter != addOnHandlers.end(); ++iter)
128             {
129                 auto search = eventHandlers.find(iter->first);
130                 if (search != eventHandlers.end())
131                 {
132                     search->second.insert(std::end(search->second),
133                                           std::begin(iter->second),
134                                           std::end(iter->second));
135                 }
136                 else
137                 {
138                     eventHandlers.emplace(iter->first, iter->second);
139                 }
140             }
141         }
142     }
143 
144     pdr_utils::Repo& getRepo()
145     {
146         return this->pdrRepo;
147     }
148 
149     /** @brief Add D-Bus mapping and value mapping(stateId to D-Bus) for the
150      *         Id. If the same id is added, the previous dbusObjs will
151      *         be "over-written".
152      *
153      *  @param[in] Id - effecter/sensor id
154      *  @param[in] dbusObj - list of D-Bus object structure and list of D-Bus
155      *                       property value to attribute value
156      *  @param[in] typeId - the type id of enum
157      */
158     void addDbusObjMaps(
159         uint16_t id,
160         std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps> dbusObj,
161         TypeId typeId = TypeId::PLDM_EFFECTER_ID);
162 
163     /** @brief Retrieve an id -> D-Bus objects mapping
164      *
165      *  @param[in] Id - id
166      *  @param[in] typeId - the type id of enum
167      *
168      *  @return std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps> -
169      *          list of D-Bus object structure and list of D-Bus property value
170      *          to attribute value
171      */
172     const std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps>&
173         getDbusObjMaps(uint16_t id,
174                        TypeId typeId = TypeId::PLDM_EFFECTER_ID) const;
175 
176     uint16_t getNextEffecterId()
177     {
178         return ++nextEffecterId;
179     }
180 
181     uint16_t getNextSensorId()
182     {
183         return ++nextSensorId;
184     }
185 
186     /** @brief Parse PDR JSONs and build PDR repository
187      *
188      *  @param[in] dBusIntf - The interface object
189      *  @param[in] dir - directory housing platform specific PDR JSON files
190      *  @param[in] repo - instance of concrete implementation of Repo
191      */
192     void generate(const pldm::utils::DBusHandler& dBusIntf,
193                   const std::string& dir, Repo& repo);
194 
195     /** @brief Parse PDR JSONs and build state effecter PDR repository
196      *
197      *  @param[in] json - platform specific PDR JSON files
198      *  @param[in] repo - instance of state effecter implementation of Repo
199      */
200     void generateStateEffecterRepo(const Json& json, Repo& repo);
201 
202     /** @brief map of PLDM event type to EventHandlers
203      *
204      */
205     EventMap eventHandlers;
206 
207     /** @brief Handler for GetPDR
208      *
209      *  @param[in] request - Request message payload
210      *  @param[in] payloadLength - Request payload length
211      *  @param[out] Response - Response message written here
212      */
213     Response getPDR(const pldm_msg* request, size_t payloadLength);
214 
215     /** @brief Handler for setNumericEffecterValue
216      *
217      *  @param[in] request - Request message
218      *  @param[in] payloadLength - Request payload length
219      *  @return Response - PLDM Response message
220      */
221     Response setNumericEffecterValue(const pldm_msg* request,
222                                      size_t payloadLength);
223 
224     /** @brief Handler for getStateSensorReadings
225      *
226      *  @param[in] request - Request message
227      *  @param[in] payloadLength - Request payload length
228      *  @return Response - PLDM Response message
229      */
230     Response getStateSensorReadings(const pldm_msg* request,
231                                     size_t payloadLength);
232 
233     /** @brief Handler for setStateEffecterStates
234      *
235      *  @param[in] request - Request message
236      *  @param[in] payloadLength - Request payload length
237      *  @return Response - PLDM Response message
238      */
239     Response setStateEffecterStates(const pldm_msg* request,
240                                     size_t payloadLength);
241 
242     /** @brief Handler for PlatformEventMessage
243      *
244      *  @param[in] request - Request message
245      *  @param[in] payloadLength - Request payload length
246      *  @return Response - PLDM Response message
247      */
248     Response platformEventMessage(const pldm_msg* request,
249                                   size_t payloadLength);
250 
251     /** @brief Handler for event class Sensor event
252      *
253      *  @param[in] request - Request message
254      *  @param[in] payloadLength - Request payload length
255      *  @param[in] formatVersion - Version of the event format
256      *  @param[in] tid - Terminus ID of the event's originator
257      *  @param[in] eventDataOffset - Offset of the event data in the request
258      *                               message
259      *  @return PLDM completion code
260      */
261     int sensorEvent(const pldm_msg* request, size_t payloadLength,
262                     uint8_t formatVersion, uint8_t tid, size_t eventDataOffset);
263 
264     /** @brief Handler for pldmPDRRepositoryChgEvent
265      *
266      *  @param[in] request - Request message
267      *  @param[in] payloadLength - Request payload length
268      *  @param[in] formatVersion - Version of the event format
269      *  @param[in] tid - Terminus ID of the event's originator
270      *  @param[in] eventDataOffset - Offset of the event data in the request
271      *                               message
272      *  @return PLDM completion code
273      */
274     int pldmPDRRepositoryChgEvent(const pldm_msg* request, size_t payloadLength,
275                                   uint8_t formatVersion, uint8_t tid,
276                                   size_t eventDataOffset);
277 
278     /** @brief Handler for extracting the PDR handles from changeEntries
279      *
280      *  @param[in] changeEntryData - ChangeEntry data from changeRecord
281      *  @param[in] changeEntryDataSize - total size of changeEntryData
282      *  @param[in] numberOfChangeEntries - total number of changeEntries to
283      *                                     extract
284      *  @param[out] pdrRecordHandles - std::vector where the extracted PDR
285      *                                 handles are placed
286      *  @return PLDM completion code
287      */
288     int getPDRRecordHandles(const ChangeEntry* changeEntryData,
289                             size_t changeEntryDataSize,
290                             size_t numberOfChangeEntries,
291                             PDRRecordHandles& pdrRecordHandles);
292 
293     /** @brief Handler for setting Sensor event data
294      *
295      *  @param[in] sensorId - sensorID value of the sensor
296      *  @param[in] sensorOffset - Identifies which state sensor within a
297      * composite state sensor the event is being returned for
298      *  @param[in] eventState - The event state value from the state change that
299      * triggered the event message
300      *  @return PLDM completion code
301      */
302     int setSensorEventData(uint16_t sensorId, uint8_t sensorOffset,
303                            uint8_t eventState);
304 
305     /** @brief Function to set the effecter requested by pldm requester
306      *  @param[in] dBusIntf - The interface object
307      *  @param[in] effecterId - Effecter ID sent by the requester to act on
308      *  @param[in] stateField - The state field data for each of the states,
309      * equal to composite effecter count in number
310      *  @return - Success or failure in setting the states. Returns failure in
311      * terms of PLDM completion codes if atleast one state fails to be set
312      */
313     template <class DBusInterface>
314     int setStateEffecterStatesHandler(
315         const DBusInterface& dBusIntf, uint16_t effecterId,
316         const std::vector<set_effecter_state_field>& stateField)
317     {
318         using namespace pldm::responder::pdr;
319         using namespace pldm::utils;
320         using StateSetNum = uint8_t;
321 
322         state_effecter_possible_states* states = nullptr;
323         pldm_state_effecter_pdr* pdr = nullptr;
324         uint8_t compEffecterCnt = stateField.size();
325 
326         std::unique_ptr<pldm_pdr, decltype(&pldm_pdr_destroy)>
327             stateEffecterPdrRepo(pldm_pdr_init(), pldm_pdr_destroy);
328         Repo stateEffecterPDRs(stateEffecterPdrRepo.get());
329         getRepoByType(pdrRepo, stateEffecterPDRs, PLDM_STATE_EFFECTER_PDR);
330         if (stateEffecterPDRs.empty())
331         {
332             std::cerr << "Failed to get record by PDR type\n";
333             return PLDM_PLATFORM_INVALID_EFFECTER_ID;
334         }
335 
336         PdrEntry pdrEntry{};
337         auto pdrRecord = stateEffecterPDRs.getFirstRecord(pdrEntry);
338         while (pdrRecord)
339         {
340             pdr = reinterpret_cast<pldm_state_effecter_pdr*>(pdrEntry.data);
341             if (pdr->effecter_id != effecterId)
342             {
343                 pdr = nullptr;
344                 pdrRecord =
345                     stateEffecterPDRs.getNextRecord(pdrRecord, pdrEntry);
346                 continue;
347             }
348 
349             states = reinterpret_cast<state_effecter_possible_states*>(
350                 pdr->possible_states);
351             if (compEffecterCnt > pdr->composite_effecter_count)
352             {
353                 std::cerr << "The requester sent wrong composite effecter"
354                           << " count for the effecter, EFFECTER_ID="
355                           << effecterId << "COMP_EFF_CNT=" << compEffecterCnt
356                           << "\n";
357                 return PLDM_ERROR_INVALID_DATA;
358             }
359             break;
360         }
361 
362         if (!pdr)
363         {
364             return PLDM_PLATFORM_INVALID_EFFECTER_ID;
365         }
366 
367         int rc = PLDM_SUCCESS;
368         try
369         {
370             const auto& [dbusMappings, dbusValMaps] =
371                 effecterDbusObjMaps.at(effecterId);
372             for (uint8_t currState = 0; currState < compEffecterCnt;
373                  ++currState)
374             {
375                 std::vector<StateSetNum> allowed{};
376                 // computation is based on table 79 from DSP0248 v1.1.1
377                 uint8_t bitfieldIndex =
378                     stateField[currState].effecter_state / 8;
379                 uint8_t bit =
380                     stateField[currState].effecter_state - (8 * bitfieldIndex);
381                 if (states->possible_states_size < bitfieldIndex ||
382                     !(states->states[bitfieldIndex].byte & (1 << bit)))
383                 {
384                     std::cerr
385                         << "Invalid state set value, EFFECTER_ID=" << effecterId
386                         << " VALUE=" << stateField[currState].effecter_state
387                         << " COMPOSITE_EFFECTER_ID=" << currState
388                         << " DBUS_PATH=" << dbusMappings[currState].objectPath
389                         << "\n";
390                     rc = PLDM_PLATFORM_SET_EFFECTER_UNSUPPORTED_SENSORSTATE;
391                     break;
392                 }
393                 const DBusMapping& dbusMapping = dbusMappings[currState];
394                 const StatestoDbusVal& dbusValToMap = dbusValMaps[currState];
395 
396                 if (stateField[currState].set_request == PLDM_REQUEST_SET)
397                 {
398                     try
399                     {
400                         dBusIntf.setDbusProperty(
401                             dbusMapping,
402                             dbusValToMap.at(
403                                 stateField[currState].effecter_state));
404                     }
405                     catch (const std::exception& e)
406                     {
407                         std::cerr
408                             << "Error setting property, ERROR=" << e.what()
409                             << " PROPERTY=" << dbusMapping.propertyName
410                             << " INTERFACE="
411                             << dbusMapping.interface << " PATH="
412                             << dbusMapping.objectPath << "\n";
413                         return PLDM_ERROR;
414                     }
415                 }
416                 uint8_t* nextState =
417                     reinterpret_cast<uint8_t*>(states) +
418                     sizeof(state_effecter_possible_states) -
419                     sizeof(states->states) +
420                     (states->possible_states_size * sizeof(states->states));
421                 states = reinterpret_cast<state_effecter_possible_states*>(
422                     nextState);
423             }
424         }
425         catch (const std::out_of_range& e)
426         {
427             std::cerr << "the effecterId does not exist. effecter id: "
428                       << effecterId << e.what() << '\n';
429         }
430 
431         return rc;
432     }
433 
434     /** @brief Build BMC Terminus Locator PDR
435      *
436      *  @param[in] repo - instance of concrete implementation of Repo
437      */
438     void generateTerminusLocatorPDR(Repo& repo);
439 
440     /** @brief Get std::map associated with the entity
441      *         key: object path
442      *         value: pldm_entity
443      *
444      *  @return std::map<ObjectPath, pldm_entity>
445      */
446     inline const AssociatedEntityMap& getAssociateEntityMap() const
447     {
448         if (fruHandler == nullptr)
449         {
450             throw InternalFailure();
451         }
452         return fruHandler->getAssociateEntityMap();
453     }
454 
455   private:
456     pdr_utils::Repo pdrRepo;
457     uint16_t nextEffecterId{};
458     uint16_t nextSensorId{};
459     DbusObjMaps effecterDbusObjMaps{};
460     DbusObjMaps sensorDbusObjMaps{};
461     HostPDRHandler* hostPDRHandler;
462     events::StateSensorHandler stateSensorHandler;
463     DbusToPLDMEvent* dbusToPLDMEventHandler;
464     fru::Handler* fruHandler;
465     const pldm::utils::DBusHandler* dBusIntf;
466     std::string pdrJsonsDir;
467     bool pdrCreated;
468 };
469 
470 } // namespace platform
471 } // namespace responder
472 } // namespace pldm
473