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