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