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 "pldmd/handler.hpp"
17 
18 #include <stdint.h>
19 
20 #include <map>
21 
22 namespace pldm
23 {
24 namespace responder
25 {
26 namespace platform
27 {
28 
29 using namespace pldm::utils;
30 using namespace pldm::responder::pdr_utils;
31 using namespace pldm::state_sensor;
32 
33 using generatePDR =
34     std::function<void(const pldm::utils::DBusHandler& dBusIntf,
35                        const Json& json, pdr_utils::RepoInterface& repo)>;
36 
37 using EffecterId = uint16_t;
38 using DbusObjMaps =
39     std::map<EffecterId,
40              std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps>>;
41 using DbusPath = std::string;
42 using EffecterObjs = std::vector<DbusPath>;
43 using EventType = uint8_t;
44 using EventHandler = std::function<int(
45     const pldm_msg* request, size_t payloadLength, uint8_t formatVersion,
46     uint8_t tid, size_t eventDataOffset)>;
47 using EventHandlers = std::vector<EventHandler>;
48 using EventMap = std::map<EventType, EventHandlers>;
49 using AssociatedEntityMap = std::map<DbusPath, pldm_entity>;
50 
51 class Handler : public CmdHandler
52 {
53   public:
54     Handler(const pldm::utils::DBusHandler* dBusIntf,
55             const std::string& pdrJsonsDir, const std::string& eventsJsonsDir,
56             pldm_pdr* repo, HostPDRHandler* hostPDRHandler,
57             DbusToPLDMEvent* dbusToPLDMEventHandler, fru::Handler* fruHandler,
58             bool buildPDRLazily = false,
59             const std::optional<EventMap>& addOnHandlersMap = std::nullopt) :
60         pdrRepo(repo),
61         hostPDRHandler(hostPDRHandler), stateSensorHandler(eventsJsonsDir),
62         dbusToPLDMEventHandler(dbusToPLDMEventHandler), fruHandler(fruHandler),
63         dBusIntf(dBusIntf), 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(
79                                  request, payloadLength);
80                          });
81         handlers.emplace(PLDM_SET_STATE_EFFECTER_STATES,
82                          [this](const pldm_msg* request, size_t payloadLength) {
83                              return this->setStateEffecterStates(request,
84                                                                  payloadLength);
85                          });
86         handlers.emplace(PLDM_PLATFORM_EVENT_MESSAGE,
87                          [this](const pldm_msg* request, size_t payloadLength) {
88                              return this->platformEventMessage(request,
89                                                                payloadLength);
90                          });
91         handlers.emplace(PLDM_GET_STATE_SENSOR_READINGS,
92                          [this](const pldm_msg* request, size_t payloadLength) {
93                              return this->getStateSensorReadings(request,
94                                                                  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,
102                                          tid, 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(request, payloadLength,
108                                                        formatVersion, tid,
109                                                        eventDataOffset);
110             });
111 
112         // Additional OEM event handlers for PLDM events, append it to the
113         // standard handlers
114         if (addOnHandlersMap)
115         {
116             auto addOnHandlers = addOnHandlersMap.value();
117             for (EventMap::iterator iter = addOnHandlers.begin();
118                  iter != addOnHandlers.end(); ++iter)
119             {
120                 auto search = eventHandlers.find(iter->first);
121                 if (search != eventHandlers.end())
122                 {
123                     search->second.insert(std::end(search->second),
124                                           std::begin(iter->second),
125                                           std::end(iter->second));
126                 }
127                 else
128                 {
129                     eventHandlers.emplace(iter->first, iter->second);
130                 }
131             }
132         }
133     }
134 
135     pdr_utils::Repo& getRepo()
136     {
137         return this->pdrRepo;
138     }
139 
140     /** @brief Add D-Bus mapping and value mapping(stateId to D-Bus) for the
141      *         Id. If the same id is added, the previous dbusObjs will
142      *         be "over-written".
143      *
144      *  @param[in] Id - effecter/sensor id
145      *  @param[in] dbusObj - list of D-Bus object structure and list of D-Bus
146      *                       property value to attribute value
147      *  @param[in] typeId - the type id of enum
148      */
149     void addDbusObjMaps(
150         uint16_t id,
151         std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps> dbusObj,
152         TypeId typeId = 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(uint16_t id,
165                        TypeId typeId = 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, Repo& repo);
185 
186     /** @brief Parse PDR JSONs and build state effecter PDR repository
187      *
188      *  @param[in] json - platform specific PDR JSON files
189      *  @param[in] repo - instance of state effecter implementation of Repo
190      */
191     void generateStateEffecterRepo(const Json& json, Repo& repo);
192 
193     /** @brief map of PLDM event type to EventHandlers
194      *
195      */
196     EventMap eventHandlers;
197 
198     /** @brief Handler for GetPDR
199      *
200      *  @param[in] request - Request message payload
201      *  @param[in] payloadLength - Request payload length
202      *  @param[out] Response - Response message written here
203      */
204     Response getPDR(const pldm_msg* request, size_t payloadLength);
205 
206     /** @brief Handler for setNumericEffecterValue
207      *
208      *  @param[in] request - Request message
209      *  @param[in] payloadLength - Request payload length
210      *  @return Response - PLDM Response message
211      */
212     Response setNumericEffecterValue(const pldm_msg* request,
213                                      size_t payloadLength);
214 
215     /** @brief Handler for getStateSensorReadings
216      *
217      *  @param[in] request - Request message
218      *  @param[in] payloadLength - Request payload length
219      *  @return Response - PLDM Response message
220      */
221     Response getStateSensorReadings(const pldm_msg* request,
222                                     size_t payloadLength);
223 
224     /** @brief Handler for setStateEffecterStates
225      *
226      *  @param[in] request - Request message
227      *  @param[in] payloadLength - Request payload length
228      *  @return Response - PLDM Response message
229      */
230     Response setStateEffecterStates(const pldm_msg* request,
231                                     size_t payloadLength);
232 
233     /** @brief Handler for PlatformEventMessage
234      *
235      *  @param[in] request - Request message
236      *  @param[in] payloadLength - Request payload length
237      *  @return Response - PLDM Response message
238      */
239     Response platformEventMessage(const pldm_msg* request,
240                                   size_t payloadLength);
241 
242     /** @brief Handler for event class Sensor event
243      *
244      *  @param[in] request - Request message
245      *  @param[in] payloadLength - Request payload length
246      *  @param[in] formatVersion - Version of the event format
247      *  @param[in] tid - Terminus ID of the event's originator
248      *  @param[in] eventDataOffset - Offset of the event data in the request
249      *                               message
250      *  @return PLDM completion code
251      */
252     int sensorEvent(const pldm_msg* request, size_t payloadLength,
253                     uint8_t formatVersion, uint8_t tid, size_t eventDataOffset);
254 
255     /** @brief Handler for pldmPDRRepositoryChgEvent
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 pldmPDRRepositoryChgEvent(const pldm_msg* request, size_t payloadLength,
266                                   uint8_t formatVersion, uint8_t tid,
267                                   size_t eventDataOffset);
268 
269     /** @brief Handler for extracting the PDR handles from changeEntries
270      *
271      *  @param[in] changeEntryData - ChangeEntry data from changeRecord
272      *  @param[in] changeEntryDataSize - total size of changeEntryData
273      *  @param[in] numberOfChangeEntries - total number of changeEntries to
274      *                                     extract
275      *  @param[out] pdrRecordHandles - std::vector where the extracted PDR
276      *                                 handles are placed
277      *  @return PLDM completion code
278      */
279     int getPDRRecordHandles(const ChangeEntry* changeEntryData,
280                             size_t changeEntryDataSize,
281                             size_t numberOfChangeEntries,
282                             PDRRecordHandles& pdrRecordHandles);
283 
284     /** @brief Function to set the effecter requested by pldm requester
285      *  @param[in] dBusIntf - The interface object
286      *  @param[in] effecterId - Effecter ID sent by the requester to act on
287      *  @param[in] stateField - The state field data for each of the states,
288      * equal to composite effecter count in number
289      *  @return - Success or failure in setting the states. Returns failure in
290      * terms of PLDM completion codes if atleast one state fails to be set
291      */
292     template <class DBusInterface>
293     int setStateEffecterStatesHandler(
294         const DBusInterface& dBusIntf, uint16_t effecterId,
295         const std::vector<set_effecter_state_field>& stateField)
296     {
297         using namespace pldm::responder::pdr;
298         using namespace pldm::utils;
299         using StateSetNum = uint8_t;
300 
301         state_effecter_possible_states* states = nullptr;
302         pldm_state_effecter_pdr* pdr = nullptr;
303         uint8_t compEffecterCnt = stateField.size();
304 
305         std::unique_ptr<pldm_pdr, decltype(&pldm_pdr_destroy)>
306             stateEffecterPdrRepo(pldm_pdr_init(), pldm_pdr_destroy);
307         Repo stateEffecterPDRs(stateEffecterPdrRepo.get());
308         getRepoByType(pdrRepo, stateEffecterPDRs, PLDM_STATE_EFFECTER_PDR);
309         if (stateEffecterPDRs.empty())
310         {
311             std::cerr << "Failed to get record by PDR type\n";
312             return PLDM_PLATFORM_INVALID_EFFECTER_ID;
313         }
314 
315         PdrEntry pdrEntry{};
316         auto pdrRecord = stateEffecterPDRs.getFirstRecord(pdrEntry);
317         while (pdrRecord)
318         {
319             pdr = reinterpret_cast<pldm_state_effecter_pdr*>(pdrEntry.data);
320             if (pdr->effecter_id != effecterId)
321             {
322                 pdr = nullptr;
323                 pdrRecord =
324                     stateEffecterPDRs.getNextRecord(pdrRecord, pdrEntry);
325                 continue;
326             }
327 
328             states = reinterpret_cast<state_effecter_possible_states*>(
329                 pdr->possible_states);
330             if (compEffecterCnt > pdr->composite_effecter_count)
331             {
332                 std::cerr << "The requester sent wrong composite effecter"
333                           << " count for the effecter, EFFECTER_ID="
334                           << effecterId << "COMP_EFF_CNT=" << compEffecterCnt
335                           << "\n";
336                 return PLDM_ERROR_INVALID_DATA;
337             }
338             break;
339         }
340 
341         if (!pdr)
342         {
343             return PLDM_PLATFORM_INVALID_EFFECTER_ID;
344         }
345 
346         int rc = PLDM_SUCCESS;
347         try
348         {
349             const auto& [dbusMappings, dbusValMaps] =
350                 effecterDbusObjMaps.at(effecterId);
351             for (uint8_t currState = 0; currState < compEffecterCnt;
352                  ++currState)
353             {
354                 std::vector<StateSetNum> allowed{};
355                 // computation is based on table 79 from DSP0248 v1.1.1
356                 uint8_t bitfieldIndex =
357                     stateField[currState].effecter_state / 8;
358                 uint8_t bit =
359                     stateField[currState].effecter_state - (8 * bitfieldIndex);
360                 if (states->possible_states_size < bitfieldIndex ||
361                     !(states->states[bitfieldIndex].byte & (1 << bit)))
362                 {
363                     std::cerr
364                         << "Invalid state set value, EFFECTER_ID=" << effecterId
365                         << " VALUE=" << stateField[currState].effecter_state
366                         << " COMPOSITE_EFFECTER_ID=" << currState
367                         << " DBUS_PATH=" << dbusMappings[currState].objectPath
368                         << "\n";
369                     rc = PLDM_PLATFORM_SET_EFFECTER_UNSUPPORTED_SENSORSTATE;
370                     break;
371                 }
372                 const DBusMapping& dbusMapping = dbusMappings[currState];
373                 const StatestoDbusVal& dbusValToMap = dbusValMaps[currState];
374 
375                 if (stateField[currState].set_request == PLDM_REQUEST_SET)
376                 {
377                     try
378                     {
379                         dBusIntf.setDbusProperty(
380                             dbusMapping,
381                             dbusValToMap.at(
382                                 stateField[currState].effecter_state));
383                     }
384                     catch (const std::exception& e)
385                     {
386                         std::cerr
387                             << "Error setting property, ERROR=" << e.what()
388                             << " PROPERTY=" << dbusMapping.propertyName
389                             << " INTERFACE="
390                             << dbusMapping.interface << " PATH="
391                             << dbusMapping.objectPath << "\n";
392                         return PLDM_ERROR;
393                     }
394                 }
395                 uint8_t* nextState =
396                     reinterpret_cast<uint8_t*>(states) +
397                     sizeof(state_effecter_possible_states) -
398                     sizeof(states->states) +
399                     (states->possible_states_size * sizeof(states->states));
400                 states = reinterpret_cast<state_effecter_possible_states*>(
401                     nextState);
402             }
403         }
404         catch (const std::out_of_range& e)
405         {
406             std::cerr << "the effecterId does not exist. effecter id: "
407                       << effecterId << e.what() << '\n';
408         }
409 
410         return rc;
411     }
412 
413     /** @brief Build BMC Terminus Locator PDR
414      *
415      *  @param[in] repo - instance of concrete implementation of Repo
416      */
417     void generateTerminusLocatorPDR(Repo& repo);
418 
419     /** @brief Get std::map associated with the entity
420      *         key: object path
421      *         value: pldm_entity
422      *
423      *  @return std::map<ObjectPath, pldm_entity>
424      */
425     inline const AssociatedEntityMap& getAssociateEntityMap() const
426     {
427         if (fruHandler == nullptr)
428         {
429             throw InternalFailure();
430         }
431         return fruHandler->getAssociateEntityMap();
432     }
433 
434   private:
435     pdr_utils::Repo pdrRepo;
436     uint16_t nextEffecterId{};
437     uint16_t nextSensorId{};
438     DbusObjMaps effecterDbusObjMaps{};
439     DbusObjMaps sensorDbusObjMaps{};
440     HostPDRHandler* hostPDRHandler;
441     events::StateSensorHandler stateSensorHandler;
442     DbusToPLDMEvent* dbusToPLDMEventHandler;
443     fru::Handler* fruHandler;
444     const pldm::utils::DBusHandler* dBusIntf;
445     std::string pdrJsonsDir;
446     bool pdrCreated;
447 };
448 
449 } // namespace platform
450 } // namespace responder
451 } // namespace pldm
452