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