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