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     };
76     using namespace phosphor::logging;
77     using namespace pldm::responder::pdr;
78     using namespace pldm::responder::effecter::dbus_mapping;
79 
80     state_effecter_possible_states* states = nullptr;
81     pldm_state_effecter_pdr* pdr = nullptr;
82     uint8_t compEffecterCnt = stateField.size();
83     uint32_t recordHndl{};
84     Repo& pdrRepo = get(PDR_JSONS_DIR);
85     pdr::Entry pdrEntry{};
86 
87     while (!pdr)
88     {
89         pdrEntry = pdrRepo.at(recordHndl);
90         pldm_pdr_hdr* header = reinterpret_cast<pldm_pdr_hdr*>(pdrEntry.data());
91         if (header->type != PLDM_STATE_EFFECTER_PDR)
92         {
93             recordHndl = pdrRepo.getNextRecordHandle(recordHndl);
94             if (recordHndl)
95             {
96                 continue;
97             }
98             return PLDM_PLATFORM_INVALID_EFFECTER_ID;
99         }
100         pdr = reinterpret_cast<pldm_state_effecter_pdr*>(pdrEntry.data());
101         recordHndl = pdr->hdr.record_handle;
102         if (pdr->effecter_id == effecterId)
103         {
104             states = reinterpret_cast<state_effecter_possible_states*>(
105                 pdr->possible_states);
106             if (compEffecterCnt > pdr->composite_effecter_count)
107             {
108                 log<level::ERR>("The requester sent wrong composite effecter "
109                                 "count for the effecter",
110                                 entry("EFFECTER_ID=%d", effecterId),
111                                 entry("COMP_EFF_CNT=%d", compEffecterCnt));
112                 return PLDM_ERROR_INVALID_DATA;
113             }
114             break;
115         }
116         recordHndl = pdrRepo.getNextRecordHandle(recordHndl);
117         if (!recordHndl)
118         {
119             return PLDM_PLATFORM_INVALID_EFFECTER_ID;
120         }
121         pdr = nullptr;
122     }
123 
124     std::map<StateSetId, std::function<int(const std::string& objPath,
125                                            const uint8_t currState)>>
126         effecterToDbusEntries = {
127             {PLDM_BOOT_PROGRESS_STATE,
128              [&](const std::string& objPath, const uint8_t currState) {
129                  auto stateSet =
130                      stateNumToDbusProp.find(PLDM_BOOT_PROGRESS_STATE);
131                  if (stateSet == stateNumToDbusProp.end())
132                  {
133                      log<level::ERR>("Couldn't find D-Bus mapping for "
134                                      "PLDM_BOOT_PROGRESS_STATE",
135                                      entry("EFFECTER_ID=%d", effecterId));
136                      return PLDM_ERROR;
137                  }
138                  auto iter = stateSet->second.find(
139                      stateField[currState].effecter_state);
140                  if (iter == stateSet->second.end())
141                  {
142                      log<level::ERR>(
143                          "Invalid state field passed or field not "
144                          "found for PLDM_BOOT_PROGRESS_STATE",
145                          entry("EFFECTER_ID=%d", effecterId),
146                          entry("FIELD=%d",
147                                stateField[currState].effecter_state),
148                          entry("OBJECT_PATH=%s", objPath.c_str()));
149                      return PLDM_ERROR_INVALID_DATA;
150                  }
151                  auto dbusProp = "OperatingSystemState";
152                  std::variant<std::string> value{
153                      std::get<std::string>(iter->second)};
154                  auto dbusInterface =
155                      "xyz.openbmc_project.State.OperatingSystem.Status";
156                  try
157                  {
158                      dBusIntf.setDbusProperty(objPath.c_str(), dbusProp,
159                                               dbusInterface, value);
160                  }
161                  catch (const std::exception& e)
162                  {
163                      log<level::ERR>("Error setting property",
164                                      entry("ERROR=%s", e.what()),
165                                      entry("PROPERTY=%s", dbusProp),
166                                      entry("INTERFACE=%s", dbusInterface),
167                                      entry("PATH=%s", objPath.c_str()));
168                      return PLDM_ERROR;
169                  }
170                  return PLDM_SUCCESS;
171              }}};
172 
173     int rc = PLDM_SUCCESS;
174     auto paths = get(effecterId);
175     for (uint8_t currState = 0; currState < compEffecterCnt; ++currState)
176     {
177         std::vector<StateSetNum> allowed{};
178         // computation is based on table 79 from DSP0248 v1.1.1
179         uint8_t bitfieldIndex = stateField[currState].effecter_state / 8;
180         uint8_t bit =
181             stateField[currState].effecter_state - (8 * bitfieldIndex);
182         if (states->possible_states_size < bitfieldIndex ||
183             !(states->states[bitfieldIndex].byte & (1 << bit)))
184         {
185             log<level::ERR>(
186                 "Invalid state set value", entry("EFFECTER_ID=%d", effecterId),
187                 entry("VALUE=%d", stateField[currState].effecter_state),
188                 entry("COMPOSITE_EFFECTER_ID=%d", currState),
189                 entry("DBUS_PATH=%c", paths[currState].c_str()));
190             rc = PLDM_PLATFORM_SET_EFFECTER_UNSUPPORTED_SENSORSTATE;
191             break;
192         }
193         auto iter = effecterToDbusEntries.find(states->state_set_id);
194         if (iter == effecterToDbusEntries.end())
195         {
196             uint16_t setId = states->state_set_id;
197             log<level::ERR>(
198                 "Did not find the state set for the state effecter pdr  ",
199                 entry("STATE=%d", setId), entry("EFFECTER_ID=%d", effecterId));
200             rc = PLDM_PLATFORM_INVALID_STATE_VALUE;
201             break;
202         }
203         if (stateField[currState].set_request == PLDM_REQUEST_SET)
204         {
205             rc = iter->second(paths[currState], currState);
206             if (rc != PLDM_SUCCESS)
207             {
208                 break;
209             }
210         }
211         uint8_t* nextState =
212             reinterpret_cast<uint8_t*>(states) +
213             sizeof(state_effecter_possible_states) - sizeof(states->states) +
214             (states->possible_states_size * sizeof(states->states));
215         states = reinterpret_cast<state_effecter_possible_states*>(nextState);
216     }
217     return rc;
218 }
219 
220 } // namespace responder
221 } // namespace pldm
222