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