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