1 #pragma once
2 
3 #include "config.h"
4 
5 #include "handler.hpp"
6 #include "libpldmresponder/pdr.hpp"
7 #include "utils.hpp"
8 
9 #include <stdint.h>
10 
11 #include <map>
12 
13 #include "libpldm/platform.h"
14 #include "libpldm/states.h"
15 
16 namespace pldm
17 {
18 namespace responder
19 {
20 namespace platform
21 {
22 
23 class Handler : public CmdHandler
24 {
25   public:
26     Handler()
27     {
28         handlers.emplace(PLDM_GET_PDR,
29                          [this](const pldm_msg* request, size_t payloadLength) {
30                              return this->getPDR(request, payloadLength);
31                          });
32         handlers.emplace(PLDM_SET_STATE_EFFECTER_STATES,
33                          [this](const pldm_msg* request, size_t payloadLength) {
34                              return this->setStateEffecterStates(request,
35                                                                  payloadLength);
36                          });
37     }
38 
39     /** @brief Handler for GetPDR
40      *
41      *  @param[in] request - Request message payload
42      *  @param[in] payloadLength - Request payload length
43      *  @param[out] Response - Response message written here
44      */
45     Response getPDR(const pldm_msg* request, size_t payloadLength);
46 
47     /** @brief Handler for setStateEffecterStates
48      *
49      *  @param[in] request - Request message
50      *  @param[in] payloadLength - Request payload length
51      *  @return Response - PLDM Response message
52      */
53     Response setStateEffecterStates(const pldm_msg* request,
54                                     size_t payloadLength);
55 
56     /** @brief Function to set the effecter requested by pldm requester
57      *  @param[in] dBusIntf - The interface object
58      *  @param[in] effecterId - Effecter ID sent by the requester to act on
59      *  @param[in] stateField - The state field data for each of the states,
60      * equal to composite effecter count in number
61      *  @return - Success or failure in setting the states. Returns failure in
62      * terms of PLDM completion codes if atleast one state fails to be set
63      */
64     template <class DBusInterface>
65     int setStateEffecterStatesHandler(
66         const DBusInterface& dBusIntf, effecter::Id effecterId,
67         const std::vector<set_effecter_state_field>& stateField)
68     {
69         using namespace std::string_literals;
70         using DBusProperty = std::variant<std::string, bool>;
71         using StateSetId = uint16_t;
72         using StateSetNum = uint8_t;
73         using PropertyMap =
74             std::map<StateSetId, std::map<StateSetNum, DBusProperty>>;
75         static const PropertyMap stateNumToDbusProp = {
76             {PLDM_BOOT_PROGRESS_STATE,
77              {{PLDM_BOOT_NOT_ACTIVE,
78                "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus."
79                "Standby"s},
80               {PLDM_BOOT_COMPLETED,
81                "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus."
82                "BootComplete"s}}},
83             {PLDM_SYSTEM_POWER_STATE,
84              {{PLDM_OFF_SOFT_GRACEFUL,
85                "xyz.openbmc_project.State.Chassis.Transition.Off"s}}}};
86         using namespace pldm::responder::pdr;
87         using namespace pldm::responder::effecter::dbus_mapping;
88 
89         state_effecter_possible_states* states = nullptr;
90         pldm_state_effecter_pdr* pdr = nullptr;
91         uint8_t compEffecterCnt = stateField.size();
92         uint32_t recordHndl{};
93         Repo& pdrRepo = get(PDR_JSONS_DIR);
94         pdr::Entry pdrEntry{};
95 
96         while (!pdr)
97         {
98             pdrEntry = pdrRepo.at(recordHndl);
99             pldm_pdr_hdr* header =
100                 reinterpret_cast<pldm_pdr_hdr*>(pdrEntry.data());
101             if (header->type != PLDM_STATE_EFFECTER_PDR)
102             {
103                 recordHndl = pdrRepo.getNextRecordHandle(recordHndl);
104                 if (recordHndl)
105                 {
106                     continue;
107                 }
108                 return PLDM_PLATFORM_INVALID_EFFECTER_ID;
109             }
110             pdr = reinterpret_cast<pldm_state_effecter_pdr*>(pdrEntry.data());
111             recordHndl = pdr->hdr.record_handle;
112             if (pdr->effecter_id == effecterId)
113             {
114                 states = reinterpret_cast<state_effecter_possible_states*>(
115                     pdr->possible_states);
116                 if (compEffecterCnt > pdr->composite_effecter_count)
117                 {
118                     std::cerr
119                         << "The requester sent wrong composite effecter"
120                         << " count for the effecter, EFFECTER_ID=" << effecterId
121                         << "COMP_EFF_CNT=" << compEffecterCnt << "\n";
122                     return PLDM_ERROR_INVALID_DATA;
123                 }
124                 break;
125             }
126             recordHndl = pdrRepo.getNextRecordHandle(recordHndl);
127             if (!recordHndl)
128             {
129                 return PLDM_PLATFORM_INVALID_EFFECTER_ID;
130             }
131             pdr = nullptr;
132         }
133 
134         std::map<StateSetId, std::function<int(const std::string& objPath,
135                                                const uint8_t currState)>>
136             effecterToDbusEntries = {
137                 {PLDM_BOOT_PROGRESS_STATE,
138                  [&](const std::string& objPath, const uint8_t currState) {
139                      auto stateSet =
140                          stateNumToDbusProp.find(PLDM_BOOT_PROGRESS_STATE);
141                      if (stateSet == stateNumToDbusProp.end())
142                      {
143                          std::cerr << "Couldn't find D-Bus mapping for "
144                                    << "PLDM_BOOT_PROGRESS_STATE, EFFECTER_ID="
145                                    << effecterId << "\n";
146                          return PLDM_ERROR;
147                      }
148                      auto iter = stateSet->second.find(
149                          stateField[currState].effecter_state);
150                      if (iter == stateSet->second.end())
151                      {
152                          std::cerr << "Invalid state field passed or field not "
153                                    << "found for PLDM_BOOT_PROGRESS_STATE, "
154                                       "EFFECTER_ID="
155                                    << effecterId << " FIELD="
156                                    << stateField[currState].effecter_state
157                                    << " OBJECT_PATH=" << objPath.c_str()
158                                    << "\n";
159                          return PLDM_ERROR_INVALID_DATA;
160                      }
161                      auto dbusProp = "OperatingSystemState";
162                      std::variant<std::string> value{
163                          std::get<std::string>(iter->second)};
164                      auto dbusInterface =
165                          "xyz.openbmc_project.State.OperatingSystem.Status";
166                      try
167                      {
168                          dBusIntf.setDbusProperty(objPath.c_str(), dbusProp,
169                                                   dbusInterface, value);
170                      }
171                      catch (const std::exception& e)
172                      {
173                          std::cerr
174                              << "Error setting property, ERROR=" << e.what()
175                              << " PROPERTY=" << dbusProp
176                              << " INTERFACE=" << dbusInterface
177                              << " PATH=" << objPath.c_str() << "\n";
178                          return PLDM_ERROR;
179                      }
180                      return PLDM_SUCCESS;
181                  }},
182                 {PLDM_SYSTEM_POWER_STATE,
183                  [&](const std::string& objPath, const uint8_t currState) {
184                      auto stateSet =
185                          stateNumToDbusProp.find(PLDM_SYSTEM_POWER_STATE);
186                      if (stateSet == stateNumToDbusProp.end())
187                      {
188                          std::cerr << "Couldn't find D-Bus mapping for "
189                                    << "PLDM_SYSTEM_POWER_STATE, EFFECTER_ID="
190                                    << effecterId << "\n";
191                          return PLDM_ERROR;
192                      }
193                      auto iter = stateSet->second.find(
194                          stateField[currState].effecter_state);
195                      if (iter == stateSet->second.end())
196                      {
197                          std::cerr << "Invalid state field passed or field not "
198                                    << "found for PLDM_SYSTEM_POWER_STATE, "
199                                       "EFFECTER_ID="
200                                    << effecterId << " FIELD="
201                                    << stateField[currState].effecter_state
202                                    << " OBJECT_PATH=" << objPath.c_str()
203                                    << "\n";
204                          return PLDM_ERROR_INVALID_DATA;
205                      }
206                      auto dbusProp = "RequestedPowerTransition";
207                      std::variant<std::string> value{
208                          std::get<std::string>(iter->second)};
209                      auto dbusInterface = "xyz.openbmc_project.State.Chassis";
210                      try
211                      {
212                          dBusIntf.setDbusProperty(objPath.c_str(), dbusProp,
213                                                   dbusInterface, value);
214                      }
215                      catch (const std::exception& e)
216                      {
217                          std::cerr
218                              << "Error setting property, ERROR=" << e.what()
219                              << " PROPERTY=" << dbusProp
220                              << " INTERFACE=" << dbusInterface
221                              << " PATH=" << objPath.c_str() << "\n";
222                          return PLDM_ERROR;
223                      }
224                      return PLDM_SUCCESS;
225                  }}};
226 
227         int rc = PLDM_SUCCESS;
228         auto paths = get(effecterId);
229         for (uint8_t currState = 0; currState < compEffecterCnt; ++currState)
230         {
231             std::vector<StateSetNum> allowed{};
232             // computation is based on table 79 from DSP0248 v1.1.1
233             uint8_t bitfieldIndex = stateField[currState].effecter_state / 8;
234             uint8_t bit =
235                 stateField[currState].effecter_state - (8 * bitfieldIndex);
236             if (states->possible_states_size < bitfieldIndex ||
237                 !(states->states[bitfieldIndex].byte & (1 << bit)))
238             {
239                 std::cerr << "Invalid state set value, EFFECTER_ID="
240                           << effecterId
241                           << " VALUE=" << stateField[currState].effecter_state
242                           << " COMPOSITE_EFFECTER_ID=" << currState
243                           << " DBUS_PATH=" << paths[currState].c_str() << "\n";
244                 rc = PLDM_PLATFORM_SET_EFFECTER_UNSUPPORTED_SENSORSTATE;
245                 break;
246             }
247             auto iter = effecterToDbusEntries.find(states->state_set_id);
248             if (iter == effecterToDbusEntries.end())
249             {
250                 uint16_t setId = states->state_set_id;
251                 std::cerr << "Did not find the state set for the"
252                           << " state effecter pdr, STATE=" << setId
253                           << " EFFECTER_ID=" << effecterId << "\n";
254                 rc = PLDM_PLATFORM_INVALID_STATE_VALUE;
255                 break;
256             }
257             if (stateField[currState].set_request == PLDM_REQUEST_SET)
258             {
259                 rc = iter->second(paths[currState], currState);
260                 if (rc != PLDM_SUCCESS)
261                 {
262                     break;
263                 }
264             }
265             uint8_t* nextState =
266                 reinterpret_cast<uint8_t*>(states) +
267                 sizeof(state_effecter_possible_states) -
268                 sizeof(states->states) +
269                 (states->possible_states_size * sizeof(states->states));
270             states =
271                 reinterpret_cast<state_effecter_possible_states*>(nextState);
272         }
273         return rc;
274     }
275 };
276 
277 } // namespace platform
278 } // namespace responder
279 } // namespace pldm
280