xref: /openbmc/pldm/libpldmresponder/platform.hpp (revision d169dd164c3bc0d0a0b3d394bb75983a8cdf268b)
1 #pragma once
2 
3 #include "config.h"
4 
5 #include "libpldm/platform.h"
6 #include "libpldm/states.h"
7 #include "pdr.h"
8 
9 #include "common/utils.hpp"
10 #include "event_parser.hpp"
11 #include "fru.hpp"
12 #include "host-bmc/dbus_to_event_handler.hpp"
13 #include "host-bmc/host_pdr_handler.hpp"
14 #include "libpldmresponder/pdr.hpp"
15 #include "libpldmresponder/pdr_utils.hpp"
16 #include "oem_handler.hpp"
17 #include "pldmd/handler.hpp"
18 
19 #include <stdint.h>
20 
21 #include <map>
22 
23 namespace pldm
24 {
25 namespace responder
26 {
27 namespace platform
28 {
29 
30 using namespace pldm::utils;
31 using namespace pldm::responder::pdr_utils;
32 using namespace pldm::state_sensor;
33 
34 using generatePDR =
35     std::function<void(const pldm::utils::DBusHandler& dBusIntf,
36                        const Json& json, pdr_utils::RepoInterface& repo)>;
37 
38 using EffecterId = uint16_t;
39 using DbusObjMaps =
40     std::map<EffecterId,
41              std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps>>;
42 using DbusPath = std::string;
43 using EffecterObjs = std::vector<DbusPath>;
44 using EventType = uint8_t;
45 using EventHandler = std::function<int(
46     const pldm_msg* request, size_t payloadLength, uint8_t formatVersion,
47     uint8_t tid, size_t eventDataOffset)>;
48 using EventHandlers = std::vector<EventHandler>;
49 using EventMap = std::map<EventType, EventHandlers>;
50 using AssociatedEntityMap = std::map<DbusPath, pldm_entity>;
51 
52 class Handler : public CmdHandler
53 {
54   public:
55     Handler(const pldm::utils::DBusHandler* dBusIntf,
56             const std::string& pdrJsonsDir, pldm_pdr* repo,
57             HostPDRHandler* hostPDRHandler,
58             DbusToPLDMEvent* dbusToPLDMEventHandler, fru::Handler* fruHandler,
59             pldm::responder::oem_platform::Handler* oemPlatformHandler,
60             sdeventplus::Event& event, bool buildPDRLazily = false,
61             const std::optional<EventMap>& addOnHandlersMap = std::nullopt) :
62         pdrRepo(repo),
63         hostPDRHandler(hostPDRHandler),
64         dbusToPLDMEventHandler(dbusToPLDMEventHandler), fruHandler(fruHandler),
65         dBusIntf(dBusIntf), oemPlatformHandler(oemPlatformHandler),
66         event(event), pdrJsonsDir(pdrJsonsDir), pdrCreated(false)
67     {
68         if (!buildPDRLazily)
69         {
70             generateTerminusLocatorPDR(pdrRepo);
71             generate(*dBusIntf, pdrJsonsDir, pdrRepo);
72             pdrCreated = true;
73         }
74 
75         handlers.emplace(PLDM_GET_PDR,
76                          [this](const pldm_msg* request, size_t payloadLength) {
77                              return this->getPDR(request, payloadLength);
78                          });
79         handlers.emplace(PLDM_SET_NUMERIC_EFFECTER_VALUE,
80                          [this](const pldm_msg* request, size_t payloadLength) {
81                              return this->setNumericEffecterValue(
82                                  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,
87                                                                  payloadLength);
88                          });
89         handlers.emplace(PLDM_PLATFORM_EVENT_MESSAGE,
90                          [this](const pldm_msg* request, size_t payloadLength) {
91                              return this->platformEventMessage(request,
92                                                                payloadLength);
93                          });
94         handlers.emplace(PLDM_GET_STATE_SENSOR_READINGS,
95                          [this](const pldm_msg* request, size_t payloadLength) {
96                              return this->getStateSensorReadings(request,
97                                                                  payloadLength);
98                          });
99 
100         // Default handler for PLDM Events
101         eventHandlers[PLDM_SENSOR_EVENT].emplace_back(
102             [this](const pldm_msg* request, size_t payloadLength,
103                    uint8_t formatVersion, uint8_t tid, size_t eventDataOffset) {
104                 return this->sensorEvent(request, payloadLength, formatVersion,
105                                          tid, eventDataOffset);
106             });
107         eventHandlers[PLDM_PDR_REPOSITORY_CHG_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->pldmPDRRepositoryChgEvent(request, payloadLength,
111                                                        formatVersion, tid,
112                                                        eventDataOffset);
113             });
114 
115         // Additional OEM event handlers for PLDM events, append it to the
116         // standard handlers
117         if (addOnHandlersMap)
118         {
119             auto addOnHandlers = addOnHandlersMap.value();
120             for (EventMap::iterator iter = addOnHandlers.begin();
121                  iter != addOnHandlers.end(); ++iter)
122             {
123                 auto search = eventHandlers.find(iter->first);
124                 if (search != eventHandlers.end())
125                 {
126                     search->second.insert(std::end(search->second),
127                                           std::begin(iter->second),
128                                           std::end(iter->second));
129                 }
130                 else
131                 {
132                     eventHandlers.emplace(iter->first, iter->second);
133                 }
134             }
135         }
136     }
137 
138     pdr_utils::Repo& getRepo()
139     {
140         return this->pdrRepo;
141     }
142 
143     /** @brief Add D-Bus mapping and value mapping(stateId to D-Bus) for the
144      *         Id. If the same id is added, the previous dbusObjs will
145      *         be "over-written".
146      *
147      *  @param[in] Id - effecter/sensor id
148      *  @param[in] dbusObj - list of D-Bus object structure and list of D-Bus
149      *                       property value to attribute value
150      *  @param[in] typeId - the type id of enum
151      */
152     void addDbusObjMaps(
153         uint16_t id,
154         std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps> dbusObj,
155         TypeId typeId = TypeId::PLDM_EFFECTER_ID);
156 
157     /** @brief Retrieve an id -> D-Bus objects mapping
158      *
159      *  @param[in] Id - id
160      *  @param[in] typeId - the type id of enum
161      *
162      *  @return std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps> -
163      *          list of D-Bus object structure and list of D-Bus property value
164      *          to attribute value
165      */
166     const std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps>&
167         getDbusObjMaps(uint16_t id,
168                        TypeId typeId = TypeId::PLDM_EFFECTER_ID) const;
169 
170     uint16_t getNextEffecterId()
171     {
172         return ++nextEffecterId;
173     }
174 
175     uint16_t getNextSensorId()
176     {
177         return ++nextSensorId;
178     }
179 
180     /** @brief Parse PDR JSONs and build PDR repository
181      *
182      *  @param[in] dBusIntf - The interface object
183      *  @param[in] dir - directory housing platform specific PDR JSON files
184      *  @param[in] repo - instance of concrete implementation of Repo
185      */
186     void generate(const pldm::utils::DBusHandler& dBusIntf,
187                   const std::string& dir, 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 Json& json, Repo& repo);
195 
196     /** @brief map of PLDM event type to EventHandlers
197      *
198      */
199     EventMap eventHandlers;
200 
201     /** @brief Handler for GetPDR
202      *
203      *  @param[in] request - Request message payload
204      *  @param[in] payloadLength - Request payload length
205      *  @param[out] Response - Response message written here
206      */
207     Response getPDR(const pldm_msg* request, size_t payloadLength);
208 
209     /** @brief Handler for setNumericEffecterValue
210      *
211      *  @param[in] request - Request message
212      *  @param[in] payloadLength - Request payload length
213      *  @return Response - PLDM Response message
214      */
215     Response setNumericEffecterValue(const pldm_msg* request,
216                                      size_t payloadLength);
217 
218     /** @brief Handler for getStateSensorReadings
219      *
220      *  @param[in] request - Request message
221      *  @param[in] payloadLength - Request payload length
222      *  @return Response - PLDM Response message
223      */
224     Response getStateSensorReadings(const pldm_msg* request,
225                                     size_t payloadLength);
226 
227     /** @brief Handler for setStateEffecterStates
228      *
229      *  @param[in] request - Request message
230      *  @param[in] payloadLength - Request payload length
231      *  @return Response - PLDM Response message
232      */
233     Response setStateEffecterStates(const pldm_msg* request,
234                                     size_t payloadLength);
235 
236     /** @brief Handler for PlatformEventMessage
237      *
238      *  @param[in] request - Request message
239      *  @param[in] payloadLength - Request payload length
240      *  @return Response - PLDM Response message
241      */
242     Response platformEventMessage(const pldm_msg* request,
243                                   size_t payloadLength);
244 
245     /** @brief Handler for event class Sensor event
246      *
247      *  @param[in] request - Request message
248      *  @param[in] payloadLength - Request payload length
249      *  @param[in] formatVersion - Version of the event format
250      *  @param[in] tid - Terminus ID of the event's originator
251      *  @param[in] eventDataOffset - Offset of the event data in the request
252      *                               message
253      *  @return PLDM completion code
254      */
255     int sensorEvent(const pldm_msg* request, size_t payloadLength,
256                     uint8_t formatVersion, uint8_t tid, size_t eventDataOffset);
257 
258     /** @brief Handler for pldmPDRRepositoryChgEvent
259      *
260      *  @param[in] request - Request message
261      *  @param[in] payloadLength - Request payload length
262      *  @param[in] formatVersion - Version of the event format
263      *  @param[in] tid - Terminus ID of the event's originator
264      *  @param[in] eventDataOffset - Offset of the event data in the request
265      *                               message
266      *  @return PLDM completion code
267      */
268     int pldmPDRRepositoryChgEvent(const pldm_msg* request, size_t payloadLength,
269                                   uint8_t formatVersion, uint8_t tid,
270                                   size_t eventDataOffset);
271 
272     /** @brief Handler for extracting the PDR handles from changeEntries
273      *
274      *  @param[in] changeEntryData - ChangeEntry data from changeRecord
275      *  @param[in] changeEntryDataSize - total size of changeEntryData
276      *  @param[in] numberOfChangeEntries - total number of changeEntries to
277      *                                     extract
278      *  @param[out] pdrRecordHandles - std::vector where the extracted PDR
279      *                                 handles are placed
280      *  @return PLDM completion code
281      */
282     int getPDRRecordHandles(const ChangeEntry* changeEntryData,
283                             size_t changeEntryDataSize,
284                             size_t numberOfChangeEntries,
285                             PDRRecordHandles& pdrRecordHandles);
286 
287     /** @brief Function to set the effecter requested by pldm requester
288      *  @param[in] dBusIntf - The interface object
289      *  @param[in] effecterId - Effecter ID sent by the requester to act on
290      *  @param[in] stateField - The state field data for each of the states,
291      * equal to composite effecter count in number
292      *  @return - Success or failure in setting the states. Returns failure in
293      * terms of PLDM completion codes if atleast one state fails to be set
294      */
295     template <class DBusInterface>
296     int setStateEffecterStatesHandler(
297         const DBusInterface& dBusIntf, uint16_t effecterId,
298         const std::vector<set_effecter_state_field>& stateField)
299     {
300         using namespace pldm::responder::pdr;
301         using namespace pldm::utils;
302         using StateSetNum = uint8_t;
303 
304         state_effecter_possible_states* states = nullptr;
305         pldm_state_effecter_pdr* pdr = nullptr;
306         uint8_t compEffecterCnt = stateField.size();
307 
308         std::unique_ptr<pldm_pdr, decltype(&pldm_pdr_destroy)>
309             stateEffecterPdrRepo(pldm_pdr_init(), pldm_pdr_destroy);
310         Repo stateEffecterPDRs(stateEffecterPdrRepo.get());
311         getRepoByType(pdrRepo, stateEffecterPDRs, PLDM_STATE_EFFECTER_PDR);
312         if (stateEffecterPDRs.empty())
313         {
314             std::cerr << "Failed to get record by PDR type\n";
315             return PLDM_PLATFORM_INVALID_EFFECTER_ID;
316         }
317 
318         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 =
327                     stateEffecterPDRs.getNextRecord(pdrRecord, 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                 std::cerr << "The requester sent wrong composite effecter"
336                           << " count for the effecter, EFFECTER_ID="
337                           << (unsigned)effecterId
338                           << "COMP_EFF_CNT=" << (unsigned)compEffecterCnt
339                           << "\n";
340                 return PLDM_ERROR_INVALID_DATA;
341             }
342             break;
343         }
344 
345         if (!pdr)
346         {
347             return PLDM_PLATFORM_INVALID_EFFECTER_ID;
348         }
349 
350         int rc = PLDM_SUCCESS;
351         try
352         {
353             const auto& [dbusMappings, dbusValMaps] =
354                 effecterDbusObjMaps.at(effecterId);
355             for (uint8_t currState = 0; currState < compEffecterCnt;
356                  ++currState)
357             {
358                 std::vector<StateSetNum> allowed{};
359                 // computation is based on table 79 from DSP0248 v1.1.1
360                 uint8_t bitfieldIndex =
361                     stateField[currState].effecter_state / 8;
362                 uint8_t bit =
363                     stateField[currState].effecter_state - (8 * bitfieldIndex);
364                 if (states->possible_states_size < bitfieldIndex ||
365                     !(states->states[bitfieldIndex].byte & (1 << bit)))
366                 {
367                     std::cerr
368                         << "Invalid state set value, EFFECTER_ID="
369                         << (unsigned)effecterId << " VALUE="
370                         << (unsigned)stateField[currState].effecter_state
371                         << " COMPOSITE_EFFECTER_ID=" << (unsigned)currState
372                         << " DBUS_PATH=" << dbusMappings[currState].objectPath
373                         << "\n";
374                     rc = PLDM_PLATFORM_SET_EFFECTER_UNSUPPORTED_SENSORSTATE;
375                     break;
376                 }
377                 const DBusMapping& dbusMapping = dbusMappings[currState];
378                 const StatestoDbusVal& dbusValToMap = dbusValMaps[currState];
379 
380                 if (stateField[currState].set_request == PLDM_REQUEST_SET)
381                 {
382                     try
383                     {
384                         dBusIntf.setDbusProperty(
385                             dbusMapping,
386                             dbusValToMap.at(
387                                 stateField[currState].effecter_state));
388                     }
389                     catch (const std::exception& e)
390                     {
391                         std::cerr
392                             << "Error setting property, ERROR=" << e.what()
393                             << " PROPERTY=" << dbusMapping.propertyName
394                             << " INTERFACE="
395                             << dbusMapping.interface << " PATH="
396                             << dbusMapping.objectPath << "\n";
397                         return PLDM_ERROR;
398                     }
399                 }
400                 uint8_t* nextState =
401                     reinterpret_cast<uint8_t*>(states) +
402                     sizeof(state_effecter_possible_states) -
403                     sizeof(states->states) +
404                     (states->possible_states_size * sizeof(states->states));
405                 states = reinterpret_cast<state_effecter_possible_states*>(
406                     nextState);
407             }
408         }
409         catch (const std::out_of_range& e)
410         {
411             std::cerr << "the effecterId does not exist. effecter id: "
412                       << (unsigned)effecterId << e.what() << '\n';
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(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     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