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