1 #pragma once
2 
3 #include "config.h"
4 
5 #include "libpldm/platform.h"
6 #include "libpldm/states.h"
7 
8 #include "common/utils.hpp"
9 #include "event_parser.hpp"
10 #include "fru.hpp"
11 #include "host-bmc/host_pdr_handler.hpp"
12 #include "libpldmresponder/pdr.hpp"
13 #include "libpldmresponder/pdr_utils.hpp"
14 #include "pldmd/handler.hpp"
15 
16 #include <stdint.h>
17 
18 #include <map>
19 
20 namespace pldm
21 {
22 namespace responder
23 {
24 namespace platform
25 {
26 
27 using namespace pldm::utils;
28 using namespace pldm::responder::pdr_utils;
29 
30 using generatePDR =
31     std::function<void(const pldm::utils::DBusHandler& dBusIntf,
32                        const Json& json, 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 
47 // EventEntry = <uint8_t> - EventState <uint8_t> - SensorOffset <uint16_t> -
48 // SensorID
49 using EventEntry = uint32_t;
50 struct DBusInfo
51 {
52     pldm::utils::DBusMapping dBusValues;
53     pldm::utils::PropertyValue dBusPropertyValue;
54 };
55 
56 class Handler : public CmdHandler
57 {
58   public:
59     Handler(const pldm::utils::DBusHandler* dBusIntf,
60             const std::string& pdrJsonsDir, const std::string& eventsJsonsDir,
61             pldm_pdr* repo, HostPDRHandler* hostPDRHandler,
62             fru::Handler* fruHandler, bool buildPDRLazily = false,
63             const std::optional<EventMap>& addOnHandlersMap = std::nullopt) :
64         pdrRepo(repo),
65         hostPDRHandler(hostPDRHandler), stateSensorHandler(eventsJsonsDir),
66         fruHandler(fruHandler), dBusIntf(dBusIntf), pdrJsonsDir(pdrJsonsDir),
67         pdrCreated(false)
68     {
69         if (!buildPDRLazily)
70         {
71             generateTerminusLocatorPDR(pdrRepo);
72             generate(*dBusIntf, pdrJsonsDir, pdrRepo);
73             pdrCreated = true;
74         }
75 
76         handlers.emplace(PLDM_GET_PDR,
77                          [this](const pldm_msg* request, size_t payloadLength) {
78                              return this->getPDR(request, payloadLength);
79                          });
80         handlers.emplace(PLDM_SET_NUMERIC_EFFECTER_VALUE,
81                          [this](const pldm_msg* request, size_t payloadLength) {
82                              return this->setNumericEffecterValue(
83                                  request, payloadLength);
84                          });
85         handlers.emplace(PLDM_SET_STATE_EFFECTER_STATES,
86                          [this](const pldm_msg* request, size_t payloadLength) {
87                              return this->setStateEffecterStates(request,
88                                                                  payloadLength);
89                          });
90         handlers.emplace(PLDM_PLATFORM_EVENT_MESSAGE,
91                          [this](const pldm_msg* request, size_t payloadLength) {
92                              return this->platformEventMessage(request,
93                                                                payloadLength);
94                          });
95 
96         // Default handler for PLDM Events
97         eventHandlers[PLDM_SENSOR_EVENT].emplace_back(
98             [this](const pldm_msg* request, size_t payloadLength,
99                    uint8_t formatVersion, uint8_t tid, size_t eventDataOffset) {
100                 return this->sensorEvent(request, payloadLength, formatVersion,
101                                          tid, eventDataOffset);
102             });
103         eventHandlers[PLDM_PDR_REPOSITORY_CHG_EVENT].emplace_back(
104             [this](const pldm_msg* request, size_t payloadLength,
105                    uint8_t formatVersion, uint8_t tid, size_t eventDataOffset) {
106                 return this->pldmPDRRepositoryChgEvent(request, payloadLength,
107                                                        formatVersion, tid,
108                                                        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      *         effecterId. If the same id is added, the previous dbusObjs will
141      *         be "over-written".
142      *
143      *  @param[in] effecterId - effecter id
144      *  @param[in] dbusObj - list of D-Bus object structure and list of D-Bus
145      *                       property value to attribute value
146      */
147     void addDbusObjMaps(
148         uint16_t effecterId,
149         std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps> dbusObj);
150 
151     /** @brief Retrieve an effecter id -> D-Bus objects mapping
152      *
153      *  @param[in] effecterId - effecter id
154      *
155      *  @return std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps> -
156      *          list of D-Bus object structure and list of D-Bus property value
157      *          to attribute value
158      */
159     const std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps>&
160         getDbusObjMaps(uint16_t effecterId) const;
161 
162     uint16_t getNextEffecterId()
163     {
164         return ++nextEffecterId;
165     }
166 
167     /** @brief Parse PDR JSONs and build PDR repository
168      *
169      *  @param[in] dBusIntf - The interface object
170      *  @param[in] dir - directory housing platform specific PDR JSON files
171      *  @param[in] repo - instance of concrete implementation of Repo
172      */
173     void generate(const pldm::utils::DBusHandler& dBusIntf,
174                   const std::string& dir, Repo& repo);
175 
176     /** @brief Parse PDR JSONs and build state effecter PDR repository
177      *
178      *  @param[in] json - platform specific PDR JSON files
179      *  @param[in] repo - instance of state effecter implementation of Repo
180      */
181     void generateStateEffecterRepo(const Json& json, Repo& repo);
182 
183     /** @brief map of PLDM event type to EventHandlers
184      *
185      */
186     EventMap eventHandlers;
187 
188     /** @brief Handler for GetPDR
189      *
190      *  @param[in] request - Request message payload
191      *  @param[in] payloadLength - Request payload length
192      *  @param[out] Response - Response message written here
193      */
194     Response getPDR(const pldm_msg* request, size_t payloadLength);
195 
196     /** @brief Handler for setNumericEffecterValue
197      *
198      *  @param[in] request - Request message
199      *  @param[in] payloadLength - Request payload length
200      *  @return Response - PLDM Response message
201      */
202     Response setNumericEffecterValue(const pldm_msg* request,
203                                      size_t payloadLength);
204 
205     /** @brief Handler for setStateEffecterStates
206      *
207      *  @param[in] request - Request message
208      *  @param[in] payloadLength - Request payload length
209      *  @return Response - PLDM Response message
210      */
211     Response setStateEffecterStates(const pldm_msg* request,
212                                     size_t payloadLength);
213 
214     /** @brief Handler for PlatformEventMessage
215      *
216      *  @param[in] request - Request message
217      *  @param[in] payloadLength - Request payload length
218      *  @return Response - PLDM Response message
219      */
220     Response platformEventMessage(const pldm_msg* request,
221                                   size_t payloadLength);
222 
223     /** @brief Handler for event class Sensor event
224      *
225      *  @param[in] request - Request message
226      *  @param[in] payloadLength - Request payload length
227      *  @param[in] formatVersion - Version of the event format
228      *  @param[in] tid - Terminus ID of the event's originator
229      *  @param[in] eventDataOffset - Offset of the event data in the request
230      *                               message
231      *  @return PLDM completion code
232      */
233     int sensorEvent(const pldm_msg* request, size_t payloadLength,
234                     uint8_t formatVersion, uint8_t tid, size_t eventDataOffset);
235 
236     /** @brief Handler for pldmPDRRepositoryChgEvent
237      *
238      *  @param[in] request - Request message
239      *  @param[in] payloadLength - Request payload length
240      *  @param[in] formatVersion - Version of the event format
241      *  @param[in] tid - Terminus ID of the event's originator
242      *  @param[in] eventDataOffset - Offset of the event data in the request
243      *                               message
244      *  @return PLDM completion code
245      */
246     int pldmPDRRepositoryChgEvent(const pldm_msg* request, size_t payloadLength,
247                                   uint8_t formatVersion, uint8_t tid,
248                                   size_t eventDataOffset);
249 
250     /** @brief Handler for extracting the PDR handles from changeEntries
251      *
252      *  @param[in] changeEntryData - ChangeEntry data from changeRecord
253      *  @param[in] changeEntryDataSize - total size of changeEntryData
254      *  @param[in] numberOfChangeEntries - total number of changeEntries to
255      *                                     extract
256      *  @param[out] pdrRecordHandles - std::vector where the extracted PDR
257      *                                 handles are placed
258      *  @return PLDM completion code
259      */
260     int getPDRRecordHandles(const ChangeEntry* changeEntryData,
261                             size_t changeEntryDataSize,
262                             size_t numberOfChangeEntries,
263                             PDRRecordHandles& pdrRecordHandles);
264 
265     /** @brief Handler for setting Sensor event data
266      *
267      *  @param[in] sensorId - sensorID value of the sensor
268      *  @param[in] sensorOffset - Identifies which state sensor within a
269      * composite state sensor the event is being returned for
270      *  @param[in] eventState - The event state value from the state change that
271      * triggered the event message
272      *  @return PLDM completion code
273      */
274     int setSensorEventData(uint16_t sensorId, uint8_t sensorOffset,
275                            uint8_t eventState);
276 
277     /** @brief Function to set the effecter requested by pldm requester
278      *  @param[in] dBusIntf - The interface object
279      *  @param[in] effecterId - Effecter ID sent by the requester to act on
280      *  @param[in] stateField - The state field data for each of the states,
281      * equal to composite effecter count in number
282      *  @return - Success or failure in setting the states. Returns failure in
283      * terms of PLDM completion codes if atleast one state fails to be set
284      */
285     template <class DBusInterface>
286     int setStateEffecterStatesHandler(
287         const DBusInterface& dBusIntf, uint16_t effecterId,
288         const std::vector<set_effecter_state_field>& stateField)
289     {
290         using namespace pldm::responder::pdr;
291         using namespace pldm::utils;
292         using StateSetNum = uint8_t;
293 
294         state_effecter_possible_states* states = nullptr;
295         pldm_state_effecter_pdr* pdr = nullptr;
296         uint8_t compEffecterCnt = stateField.size();
297 
298         std::unique_ptr<pldm_pdr, decltype(&pldm_pdr_destroy)>
299             stateEffecterPdrRepo(pldm_pdr_init(), pldm_pdr_destroy);
300         Repo stateEffecterPDRs(stateEffecterPdrRepo.get());
301         getRepoByType(pdrRepo, stateEffecterPDRs, PLDM_STATE_EFFECTER_PDR);
302         if (stateEffecterPDRs.empty())
303         {
304             std::cerr << "Failed to get record by PDR type\n";
305             return PLDM_PLATFORM_INVALID_EFFECTER_ID;
306         }
307 
308         PdrEntry pdrEntry{};
309         auto pdrRecord = stateEffecterPDRs.getFirstRecord(pdrEntry);
310         while (pdrRecord)
311         {
312             pdr = reinterpret_cast<pldm_state_effecter_pdr*>(pdrEntry.data);
313             if (pdr->effecter_id != effecterId)
314             {
315                 pdr = nullptr;
316                 pdrRecord =
317                     stateEffecterPDRs.getNextRecord(pdrRecord, pdrEntry);
318                 continue;
319             }
320 
321             states = reinterpret_cast<state_effecter_possible_states*>(
322                 pdr->possible_states);
323             if (compEffecterCnt > pdr->composite_effecter_count)
324             {
325                 std::cerr << "The requester sent wrong composite effecter"
326                           << " count for the effecter, EFFECTER_ID="
327                           << effecterId << "COMP_EFF_CNT=" << compEffecterCnt
328                           << "\n";
329                 return PLDM_ERROR_INVALID_DATA;
330             }
331             break;
332         }
333 
334         if (!pdr)
335         {
336             return PLDM_PLATFORM_INVALID_EFFECTER_ID;
337         }
338 
339         int rc = PLDM_SUCCESS;
340         try
341         {
342             const auto& [dbusMappings, dbusValMaps] =
343                 dbusObjMaps.at(effecterId);
344             for (uint8_t currState = 0; currState < compEffecterCnt;
345                  ++currState)
346             {
347                 std::vector<StateSetNum> allowed{};
348                 // computation is based on table 79 from DSP0248 v1.1.1
349                 uint8_t bitfieldIndex =
350                     stateField[currState].effecter_state / 8;
351                 uint8_t bit =
352                     stateField[currState].effecter_state - (8 * bitfieldIndex);
353                 if (states->possible_states_size < bitfieldIndex ||
354                     !(states->states[bitfieldIndex].byte & (1 << bit)))
355                 {
356                     std::cerr
357                         << "Invalid state set value, EFFECTER_ID=" << effecterId
358                         << " VALUE=" << stateField[currState].effecter_state
359                         << " COMPOSITE_EFFECTER_ID=" << currState
360                         << " DBUS_PATH=" << dbusMappings[currState].objectPath
361                         << "\n";
362                     rc = PLDM_PLATFORM_SET_EFFECTER_UNSUPPORTED_SENSORSTATE;
363                     break;
364                 }
365                 const DBusMapping& dbusMapping = dbusMappings[currState];
366                 const StatestoDbusVal& dbusValToMap = dbusValMaps[currState];
367 
368                 if (stateField[currState].set_request == PLDM_REQUEST_SET)
369                 {
370                     try
371                     {
372                         dBusIntf.setDbusProperty(
373                             dbusMapping,
374                             dbusValToMap.at(
375                                 stateField[currState].effecter_state));
376                     }
377                     catch (const std::exception& e)
378                     {
379                         std::cerr
380                             << "Error setting property, ERROR=" << e.what()
381                             << " PROPERTY=" << dbusMapping.propertyName
382                             << " INTERFACE="
383                             << dbusMapping.interface << " PATH="
384                             << dbusMapping.objectPath << "\n";
385                         return PLDM_ERROR;
386                     }
387                 }
388                 uint8_t* nextState =
389                     reinterpret_cast<uint8_t*>(states) +
390                     sizeof(state_effecter_possible_states) -
391                     sizeof(states->states) +
392                     (states->possible_states_size * sizeof(states->states));
393                 states = reinterpret_cast<state_effecter_possible_states*>(
394                     nextState);
395             }
396         }
397         catch (const std::out_of_range& e)
398         {
399             std::cerr << "the effecterId does not exist. effecter id: "
400                       << effecterId << e.what() << '\n';
401         }
402 
403         return rc;
404     }
405 
406     /** @brief Build BMC Terminus Locator PDR
407      *
408      *  @param[in] repo - instance of concrete implementation of Repo
409      */
410     void generateTerminusLocatorPDR(Repo& repo);
411 
412   private:
413     pdr_utils::Repo pdrRepo;
414     uint16_t nextEffecterId{};
415     DbusObjMaps dbusObjMaps{};
416     HostPDRHandler* hostPDRHandler;
417     events::StateSensorHandler stateSensorHandler;
418     fru::Handler* fruHandler;
419     const pldm::utils::DBusHandler* dBusIntf;
420     std::string pdrJsonsDir;
421     bool pdrCreated;
422 };
423 
424 } // namespace platform
425 } // namespace responder
426 } // namespace pldm
427