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