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