1 #pragma once
2 
3 #include "config.h"
4 
5 #include "handler.hpp"
6 #include "libpldmresponder/pdr.hpp"
7 #include "libpldmresponder/pdr_utils.hpp"
8 #include "utils.hpp"
9 
10 #include <stdint.h>
11 
12 #include <map>
13 
14 #include "libpldm/platform.h"
15 #include "libpldm/states.h"
16 
17 namespace pldm
18 {
19 namespace responder
20 {
21 namespace platform
22 {
23 
24 using namespace pldm::utils;
25 using namespace pldm::responder::pdr_utils;
26 
27 using generatePDR =
28     std::function<void(const Json& json, pdr_utils::RepoInterface& repo)>;
29 
30 using EffecterId = uint16_t;
31 using DbusObjMaps =
32     std::map<EffecterId,
33              std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps>>;
34 using DbusPath = std::string;
35 using EffecterObjs = std::vector<DbusPath>;
36 using EventType = uint8_t;
37 using EventHandler = std::function<int(
38     const pldm_msg* request, size_t payloadLength, uint8_t formatVersion,
39     uint8_t tid, size_t eventDataOffset)>;
40 using EventHandlers = std::vector<EventHandler>;
41 using EventMap = std::map<EventType, EventHandlers>;
42 
43 // EventEntry = <uint8_t> - EventState <uint8_t> - SensorOffset <uint16_t> -
44 // SensorID
45 using EventEntry = uint32_t;
46 struct DBusInfo
47 {
48     pldm::utils::DBusMapping dBusValues;
49     pldm::utils::PropertyValue dBusPropertyValue;
50 };
51 
52 class Handler : public CmdHandler
53 {
54   public:
55     Handler(const std::string& dir, pldm_pdr* repo,
56             const std::optional<EventMap>& addOnHandlersMap = std::nullopt) :
57         pdrRepo(repo)
58     {
59         generate(dir, pdrRepo);
60 
61         handlers.emplace(PLDM_GET_PDR,
62                          [this](const pldm_msg* request, size_t payloadLength) {
63                              return this->getPDR(request, payloadLength);
64                          });
65         handlers.emplace(PLDM_SET_STATE_EFFECTER_STATES,
66                          [this](const pldm_msg* request, size_t payloadLength) {
67                              return this->setStateEffecterStates(request,
68                                                                  payloadLength);
69                          });
70         handlers.emplace(PLDM_PLATFORM_EVENT_MESSAGE,
71                          [this](const pldm_msg* request, size_t payloadLength) {
72                              return this->platformEventMessage(request,
73                                                                payloadLength);
74                          });
75 
76         // Default handler for PLDM Events
77         eventHandlers[PLDM_SENSOR_EVENT].emplace_back(
78             [this](const pldm_msg* request, size_t payloadLength,
79                    uint8_t formatVersion, uint8_t tid, size_t eventDataOffset) {
80                 return this->sensorEvent(request, payloadLength, formatVersion,
81                                          tid, eventDataOffset);
82             });
83 
84         // Additional OEM event handlers for PLDM events, append it to the
85         // standard handlers
86         if (addOnHandlersMap)
87         {
88             auto addOnHandlers = addOnHandlersMap.value();
89             for (EventMap::iterator iter = addOnHandlers.begin();
90                  iter != addOnHandlers.end(); ++iter)
91             {
92                 auto search = eventHandlers.find(iter->first);
93                 if (search != eventHandlers.end())
94                 {
95                     search->second.insert(std::end(search->second),
96                                           std::begin(iter->second),
97                                           std::end(iter->second));
98                 }
99                 else
100                 {
101                     eventHandlers.emplace(iter->first, iter->second);
102                 }
103             }
104         }
105     }
106 
107     pdr_utils::Repo& getRepo()
108     {
109         return this->pdrRepo;
110     }
111 
112     /** @brief Add D-Bus mapping and value mapping(stateId to D-Bus) for the
113      *         effecterId. If the same id is added, the previous dbusObjs will
114      *         be "over-written".
115      *
116      *  @param[in] effecterId - effecter id
117      *  @param[in] dbusObj - list of D-Bus object structure and list of D-Bus
118      *                       property value to attribute value
119      */
120     void addDbusObjMaps(
121         uint16_t effecterId,
122         std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps> dbusObj);
123 
124     /** @brief Retrieve an effecter id -> D-Bus objects mapping
125      *
126      *  @param[in] effecterId - effecter id
127      *
128      *  @return std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps> -
129      *          list of D-Bus object structure and list of D-Bus property value
130      *          to attribute value
131      */
132     const std::tuple<pdr_utils::DbusMappings, pdr_utils::DbusValMaps>&
133         getDbusObjMaps(uint16_t effecterId) const;
134 
135     uint16_t getNextEffecterId()
136     {
137         return ++nextEffecterId;
138     }
139 
140     /** @brief Parse PDR JSONs and build PDR repository
141      *
142      *  @param[in] dir - directory housing platform specific PDR JSON files
143      *  @param[in] repo - instance of concrete implementation of Repo
144      */
145     void generate(const std::string& dir, Repo& repo);
146 
147     /** @brief Parse PDR JSONs and build state effecter PDR repository
148      *
149      *  @param[in] json - platform specific PDR JSON files
150      *  @param[in] repo - instance of state effecter implementation of Repo
151      */
152     void generateStateEffecterRepo(const Json& json, Repo& repo);
153 
154     /** @brief map of PLDM event type to EventHandlers
155      *
156      */
157     EventMap eventHandlers;
158 
159     /** @brief Handler for GetPDR
160      *
161      *  @param[in] request - Request message payload
162      *  @param[in] payloadLength - Request payload length
163      *  @param[out] Response - Response message written here
164      */
165     Response getPDR(const pldm_msg* request, size_t payloadLength);
166 
167     /** @brief Handler for setStateEffecterStates
168      *
169      *  @param[in] request - Request message
170      *  @param[in] payloadLength - Request payload length
171      *  @return Response - PLDM Response message
172      */
173     Response setStateEffecterStates(const pldm_msg* request,
174                                     size_t payloadLength);
175 
176     /** @brief Handler for PlatformEventMessage
177      *
178      *  @param[in] request - Request message
179      *  @param[in] payloadLength - Request payload length
180      *  @return Response - PLDM Response message
181      */
182     Response platformEventMessage(const pldm_msg* request,
183                                   size_t payloadLength);
184 
185     /** @brief Handler for event class Sensor event
186      *
187      *  @param[in] request - Request message
188      *  @param[in] payloadLength - Request payload length
189      *  @param[in] formatVersion - Version of the event format
190      *  @param[in] tid - Terminus ID of the event's originator
191      *  @param[in] eventDataOffset - Offset of the event data in the request
192      *                               message
193      *  @return PLDM completion code
194      */
195     int sensorEvent(const pldm_msg* request, size_t payloadLength,
196                     uint8_t formatVersion, uint8_t tid, size_t eventDataOffset);
197 
198     /** @brief Handler for setting Sensor event data
199      *
200      *  @param[in] sensorId - sensorID value of the sensor
201      *  @param[in] sensorOffset - Identifies which state sensor within a
202      * composite state sensor the event is being returned for
203      *  @param[in] eventState - The event state value from the state change that
204      * triggered the event message
205      *  @return PLDM completion code
206      */
207     int setSensorEventData(uint16_t sensorId, uint8_t sensorOffset,
208                            uint8_t eventState);
209 
210     /** @brief Function to set the effecter requested by pldm requester
211      *  @param[in] dBusIntf - The interface object
212      *  @param[in] effecterId - Effecter ID sent by the requester to act on
213      *  @param[in] stateField - The state field data for each of the states,
214      * equal to composite effecter count in number
215      *  @return - Success or failure in setting the states. Returns failure in
216      * terms of PLDM completion codes if atleast one state fails to be set
217      */
218     template <class DBusInterface>
219     int setStateEffecterStatesHandler(
220         const DBusInterface& dBusIntf, uint16_t effecterId,
221         const std::vector<set_effecter_state_field>& stateField)
222     {
223         using namespace pldm::responder::pdr;
224         using namespace pldm::utils;
225         using StateSetNum = uint8_t;
226 
227         state_effecter_possible_states* states = nullptr;
228         pldm_state_effecter_pdr* pdr = nullptr;
229         uint8_t compEffecterCnt = stateField.size();
230 
231         std::unique_ptr<pldm_pdr, decltype(&pldm_pdr_destroy)>
232             stateEffecterPdrRepo(pldm_pdr_init(), pldm_pdr_destroy);
233         Repo stateEffecterPDRs(stateEffecterPdrRepo.get());
234         getRepoByType(pdrRepo, stateEffecterPDRs, PLDM_STATE_EFFECTER_PDR);
235         if (stateEffecterPDRs.empty())
236         {
237             std::cerr << "Failed to get record by PDR type\n";
238             return PLDM_PLATFORM_INVALID_EFFECTER_ID;
239         }
240 
241         PdrEntry pdrEntry{};
242         auto pdrRecord = stateEffecterPDRs.getFirstRecord(pdrEntry);
243         while (pdrRecord)
244         {
245             pdr = reinterpret_cast<pldm_state_effecter_pdr*>(pdrEntry.data);
246             if (pdr->effecter_id != effecterId)
247             {
248                 pdr = nullptr;
249                 pdrRecord =
250                     stateEffecterPDRs.getNextRecord(pdrRecord, pdrEntry);
251                 continue;
252             }
253 
254             states = reinterpret_cast<state_effecter_possible_states*>(
255                 pdr->possible_states);
256             if (compEffecterCnt > pdr->composite_effecter_count)
257             {
258                 std::cerr << "The requester sent wrong composite effecter"
259                           << " count for the effecter, EFFECTER_ID="
260                           << effecterId << "COMP_EFF_CNT=" << compEffecterCnt
261                           << "\n";
262                 return PLDM_ERROR_INVALID_DATA;
263             }
264             break;
265         }
266 
267         if (!pdr)
268         {
269             return PLDM_PLATFORM_INVALID_EFFECTER_ID;
270         }
271 
272         int rc = PLDM_SUCCESS;
273         try
274         {
275             const auto& [dbusMappings, dbusValMaps] =
276                 dbusObjMaps.at(effecterId);
277             for (uint8_t currState = 0; currState < compEffecterCnt;
278                  ++currState)
279             {
280                 std::vector<StateSetNum> allowed{};
281                 // computation is based on table 79 from DSP0248 v1.1.1
282                 uint8_t bitfieldIndex =
283                     stateField[currState].effecter_state / 8;
284                 uint8_t bit =
285                     stateField[currState].effecter_state - (8 * bitfieldIndex);
286                 if (states->possible_states_size < bitfieldIndex ||
287                     !(states->states[bitfieldIndex].byte & (1 << bit)))
288                 {
289                     std::cerr
290                         << "Invalid state set value, EFFECTER_ID=" << effecterId
291                         << " VALUE=" << stateField[currState].effecter_state
292                         << " COMPOSITE_EFFECTER_ID=" << currState
293                         << " DBUS_PATH=" << dbusMappings[currState].objectPath
294                         << "\n";
295                     rc = PLDM_PLATFORM_SET_EFFECTER_UNSUPPORTED_SENSORSTATE;
296                     break;
297                 }
298                 const DBusMapping& dbusMapping = dbusMappings[currState];
299                 const StatestoDbusVal& dbusValToMap = dbusValMaps[currState];
300 
301                 if (stateField[currState].set_request == PLDM_REQUEST_SET)
302                 {
303                     try
304                     {
305                         dBusIntf.setDbusProperty(
306                             dbusMapping,
307                             dbusValToMap.at(
308                                 stateField[currState].effecter_state));
309                     }
310                     catch (const std::exception& e)
311                     {
312                         std::cerr
313                             << "Error setting property, ERROR=" << e.what()
314                             << " PROPERTY=" << dbusMapping.propertyName
315                             << " INTERFACE="
316                             << dbusMapping.interface << " PATH="
317                             << dbusMapping.objectPath << "\n";
318                         return PLDM_ERROR;
319                     }
320                 }
321                 uint8_t* nextState =
322                     reinterpret_cast<uint8_t*>(states) +
323                     sizeof(state_effecter_possible_states) -
324                     sizeof(states->states) +
325                     (states->possible_states_size * sizeof(states->states));
326                 states = reinterpret_cast<state_effecter_possible_states*>(
327                     nextState);
328             }
329         }
330         catch (const std::out_of_range& e)
331         {
332             std::cerr << "the effecterId does not exist. effecter id: "
333                       << effecterId << e.what() << '\n';
334         }
335 
336         return rc;
337     }
338 
339   private:
340     pdr_utils::Repo pdrRepo;
341     uint16_t nextEffecterId{};
342     DbusObjMaps dbusObjMaps{};
343 };
344 
345 } // namespace platform
346 } // namespace responder
347 } // namespace pldm
348