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