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