xref: /openbmc/openpower-hw-diags/util/pldm.cpp (revision 62dd15e8)
1 #include <libpldm/oem/ibm/state_set.h>
2 #include <libpldm/platform.h>
3 #include <libpldm/pldm.h>
4 
5 #include <util/dbus.hpp>
6 #include <util/trace.hpp>
7 
8 namespace util
9 {
10 namespace pldm
11 {
12 /** @brief Send PLDM request
13  *
14  * @param[in] request - the request data
15  * @param[in] mcptEid - the mctp endpoint ID
16  * @param[out] pldmFd - pldm socket file descriptor
17  *
18  * @pre a mctp instance must have been
19  * @return true if send is successful false otherwise
20  */
21 bool sendPldm(const std::vector<uint8_t>& request, uint8_t mctpEid, int& pldmFd)
22 {
23     // connect to socket
24     pldmFd = pldm_open();
25     if (-1 == pldmFd)
26     {
27         trace::err("failed to connect to pldm");
28         return false;
29     }
30 
31     // send PLDM request
32     auto pldmRc = pldm_send(mctpEid, pldmFd, request.data(), request.size());
33 
34     trace::inf("sent pldm request");
35 
36     return pldmRc == PLDM_REQUESTER_SUCCESS ? true : false;
37 }
38 
39 /** @brief Prepare a request for SetStateEffecterStates
40  *
41  *  @param[in] effecterId - the effecter ID
42  *  @param[in] effecterCount - composite effecter count
43  *  @param[in] stateIdPos - position of the state set
44  *  @param[in] stateSetValue - the value to set the state
45  *  @param[in] mcptEid - the MCTP endpoint ID
46  *
47  *  @return PLDM request message to be sent to host, empty message on error
48  */
49 std::vector<uint8_t> prepareSetEffecterReq(uint16_t effecterId,
50                                            uint8_t effecterCount,
51                                            uint8_t stateIdPos,
52                                            uint8_t stateSetValue,
53                                            uint8_t mctpEid)
54 {
55     // get mctp instance associated with the endpoint ID
56     uint8_t mctpInstance;
57     if (!util::dbus::getMctpInstance(mctpInstance, mctpEid))
58     {
59         return std::vector<uint8_t>();
60     }
61 
62     // form the request message
63     std::vector<uint8_t> request(
64         sizeof(pldm_msg_hdr) + sizeof(effecterId) + sizeof(effecterCount) +
65         (effecterCount * sizeof(set_effecter_state_field)));
66 
67     // encode the state data with the change we want to elicit
68     std::vector<set_effecter_state_field> stateField;
69     for (uint8_t effecterPos = 0; effecterPos < effecterCount; effecterPos++)
70     {
71         if (effecterPos == stateIdPos)
72         {
73             stateField.emplace_back(
74                 set_effecter_state_field{PLDM_REQUEST_SET, stateSetValue});
75         }
76         else
77         {
78             stateField.emplace_back(
79                 set_effecter_state_field{PLDM_NO_CHANGE, 0});
80         }
81     }
82 
83     // encode the message with state data
84     auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
85     auto rc = encode_set_state_effecter_states_req(
86         mctpInstance, effecterId, effecterCount, stateField.data(), requestMsg);
87 
88     if (rc != PLDM_SUCCESS)
89     {
90         trace::err("encode set effecter states request failed");
91         request.clear();
92     }
93 
94     return request;
95 }
96 
97 /** @brief Return map of sensor ID to SBE instance
98  *
99  *  @param[in] stateSetId - the state set ID of interest
100  *  @param[out] sensorInstanceMap - map of sensor to SBE instance
101  *  @param[out] sensorOffset - position of sensor with state set ID within map
102  *
103  *  @return true if sensor info is available false otherwise
104  */
105 bool fetchSensorInfo(uint16_t stateSetId,
106                      std::map<uint16_t, unsigned int>& sensorInstanceMap,
107                      uint8_t& sensorOffset)
108 {
109     // get state sensor PDRs
110     std::vector<std::vector<uint8_t>> pdrs{};
111     if (!util::dbus::getStateSensorPdrs(pdrs, stateSetId))
112     {
113         return false;
114     }
115 
116     // check for any PDRs available
117     if (!pdrs.size())
118     {
119         trace::err("state sensor PDRs not present");
120         return false;
121     }
122 
123     // find the offset of specified sensor withing PDRs
124     bool offsetFound = false;
125     auto stateSensorPDR =
126         reinterpret_cast<const pldm_state_sensor_pdr*>(pdrs.front().data());
127     auto possibleStatesPtr = stateSensorPDR->possible_states;
128 
129     for (auto offset = 0; offset < stateSensorPDR->composite_sensor_count;
130          offset++)
131     {
132         auto possibleStates =
133             reinterpret_cast<const state_sensor_possible_states*>(
134                 possibleStatesPtr);
135 
136         if (possibleStates->state_set_id == stateSetId)
137         {
138             sensorOffset = offset;
139             offsetFound = true;
140             break;
141         }
142         possibleStatesPtr += sizeof(possibleStates->state_set_id) +
143                              sizeof(possibleStates->possible_states_size) +
144                              possibleStates->possible_states_size;
145     }
146 
147     if (!offsetFound)
148     {
149         trace::err("state sensor not found");
150         return false;
151     }
152 
153     // map sensor ID to equivelent 16 bit value
154     std::map<uint32_t, uint16_t> entityInstMap{};
155     for (auto& pdr : pdrs)
156     {
157         auto pdrPtr =
158             reinterpret_cast<const pldm_state_sensor_pdr*>(pdr.data());
159         uint32_t key = pdrPtr->sensor_id;
160         entityInstMap.emplace(key, static_cast<uint16_t>(pdrPtr->sensor_id));
161     }
162 
163     // map sensor ID to zero based SBE instance
164     unsigned int position = 0;
165     for (const auto& pair : entityInstMap)
166     {
167         sensorInstanceMap.emplace(pair.second, position);
168         position++;
169     }
170 
171     return true;
172 }
173 
174 /** @brief Return map of SBE instance to effecter ID
175  *
176  *  @param[in] stateSetId - the state set ID of interest
177  *  @param[out] instanceToEffecterMap - map of sbe instance to effecter ID
178  *  @param[out] effecterCount - composite effecter count
179  *  @param[out] stateIdPos - position of effecter with state set ID within map
180  *
181  *  @return true if effector info is available false otherwise
182  */
183 bool fetchEffecterInfo(uint16_t stateSetId,
184                        std::map<unsigned int, uint16_t>& instanceToEffecterMap,
185                        uint8_t& effecterCount, uint8_t& stateIdPos)
186 {
187     // get state effecter PDRs
188     std::vector<std::vector<uint8_t>> pdrs{};
189     if (!util::dbus::getStateEffecterPdrs(pdrs, stateSetId))
190     {
191         return false;
192     }
193 
194     // check for any PDRs available
195     if (!pdrs.size())
196     {
197         trace::err("state effecter PDRs not present");
198         return false;
199     }
200 
201     // find the offset of specified effector within PDRs
202     bool offsetFound = false;
203     auto stateEffecterPDR =
204         reinterpret_cast<const pldm_state_effecter_pdr*>(pdrs.front().data());
205     auto possibleStatesPtr = stateEffecterPDR->possible_states;
206 
207     for (auto offset = 0; offset < stateEffecterPDR->composite_effecter_count;
208          offset++)
209     {
210         auto possibleStates =
211             reinterpret_cast<const state_effecter_possible_states*>(
212                 possibleStatesPtr);
213 
214         if (possibleStates->state_set_id == stateSetId)
215         {
216             stateIdPos = offset;
217             effecterCount = stateEffecterPDR->composite_effecter_count;
218             offsetFound = true;
219             break;
220         }
221         possibleStatesPtr += sizeof(possibleStates->state_set_id) +
222                              sizeof(possibleStates->possible_states_size) +
223                              possibleStates->possible_states_size;
224     }
225 
226     if (!offsetFound)
227     {
228         trace::err("state set effecter not found");
229         return false;
230     }
231 
232     // map effecter ID to equivelent 16 bit value
233     std::map<uint32_t, uint16_t> entityInstMap{};
234     for (auto& pdr : pdrs)
235     {
236         auto pdrPtr =
237             reinterpret_cast<const pldm_state_effecter_pdr*>(pdr.data());
238         uint32_t key = pdrPtr->effecter_id;
239         entityInstMap.emplace(key, static_cast<uint16_t>(pdrPtr->effecter_id));
240     }
241 
242     // map zero based SBE instance to effecter ID
243     unsigned int position = 0;
244     for (const auto& pair : entityInstMap)
245     {
246         instanceToEffecterMap.emplace(position, pair.second);
247         position++;
248     }
249 
250     return true;
251 }
252 
253 /**  @brief Reset SBE using HBRT PLDM interface */
254 bool hresetSbe(unsigned int sbeInstance)
255 {
256     trace::inf("requesting sbe hreset");
257 
258     // get effecter info
259     std::map<unsigned int, uint16_t> sbeInstanceToEffecter;
260     uint8_t SBEEffecterCount = 0;
261     uint8_t sbeMaintenanceStatePosition = 0;
262 
263     if (!fetchEffecterInfo(PLDM_OEM_IBM_SBE_MAINTENANCE_STATE,
264                            sbeInstanceToEffecter, SBEEffecterCount,
265                            sbeMaintenanceStatePosition))
266     {
267         return false;
268     }
269 
270     // find the state effecter ID for the given SBE instance
271     auto effecterEntry = sbeInstanceToEffecter.find(sbeInstance);
272     if (effecterEntry == sbeInstanceToEffecter.end())
273     {
274         trace::err("failed to find effecter for SBE");
275         return false;
276     }
277 
278     // create request to HRESET the SBE
279     constexpr uint8_t hbrtMctpEid = 10; // HBRT MCTP EID
280 
281     auto request = prepareSetEffecterReq(
282         effecterEntry->second, SBEEffecterCount, sbeMaintenanceStatePosition,
283         SBE_RETRY_REQUIRED, hbrtMctpEid);
284 
285     if (request.empty())
286     {
287         trace::err("HRESET effecter request empty");
288         return false;
289     }
290 
291     // get sensor info for validating sensor change
292     std::map<uint16_t, unsigned int> sensorToSbeInstance;
293     uint8_t sbeSensorOffset = 0;
294     if (!fetchSensorInfo(PLDM_OEM_IBM_SBE_HRESET_STATE, sensorToSbeInstance,
295                          sbeSensorOffset))
296     {
297         return false;
298     }
299 
300     // register signal change listener
301     std::string hresetStatus = "requested";
302     constexpr auto interface = "xyz.openbmc_project.PLDM.Event";
303     constexpr auto path = "/xyz/openbmc_project/pldm";
304     constexpr auto member = "StateSensorEvent";
305 
306     auto bus = sdbusplus::bus::new_default();
307     std::unique_ptr<sdbusplus::bus::match_t> match =
308         std::make_unique<sdbusplus::bus::match_t>(
309             bus,
310             sdbusplus::bus::match::rules::type::signal() +
311                 sdbusplus::bus::match::rules::member(member) +
312                 sdbusplus::bus::match::rules::path(path) +
313                 sdbusplus::bus::match::rules::interface(interface),
314             [&](auto& msg) {
315         uint8_t sensorTid{};
316         uint16_t sensorId{};
317         uint8_t msgSensorOffset{};
318         uint8_t eventState{};
319         uint8_t previousEventState{};
320 
321         // get sensor event details
322         msg.read(sensorTid, sensorId, msgSensorOffset, eventState,
323                  previousEventState);
324 
325         // does sensor offset match?
326         if (sbeSensorOffset == msgSensorOffset)
327         {
328             // does sensor ID match?
329             auto sensorEntry = sensorToSbeInstance.find(sensorId);
330             if (sensorEntry != sensorToSbeInstance.end())
331             {
332                 const uint8_t instance = sensorEntry->second;
333 
334                 // if instances matche check status
335                 if (instance == sbeInstance)
336                 {
337                     if (eventState == static_cast<uint8_t>(SBE_HRESET_READY))
338                     {
339                         hresetStatus = "success";
340                     }
341                     else if (eventState ==
342                              static_cast<uint8_t>(SBE_HRESET_FAILED))
343                     {
344                         hresetStatus = "fail";
345                     }
346                 }
347             }
348         }
349     });
350 
351     // send request to issue hreset of sbe
352     int pldmFd = -1; // mctp socket file descriptor
353     if (!sendPldm(request, hbrtMctpEid, pldmFd))
354     {
355         trace::err("send pldm request failed");
356         if (-1 != pldmFd)
357         {
358             close(pldmFd);
359         }
360         return false;
361     }
362 
363     // keep track of elapsed time
364     uint64_t timeRemaining = 60000000; // microseconds, 1 minute
365     std::chrono::steady_clock::time_point begin =
366         std::chrono::steady_clock::now();
367 
368     // wait for status update or timeout
369     trace::inf("waiting on sbe hreset");
370     while ("requested" == hresetStatus && 0 != timeRemaining)
371     {
372         bus.wait(timeRemaining);
373         uint64_t timeElapsed =
374             std::chrono::duration_cast<std::chrono::microseconds>(
375                 std::chrono::steady_clock::now() - begin)
376                 .count();
377 
378         timeRemaining =
379             timeElapsed > timeRemaining ? 0 : timeRemaining - timeElapsed;
380 
381         bus.process_discard();
382     }
383 
384     if (0 == timeRemaining)
385     {
386         trace::err("hreset timed out");
387     }
388 
389     close(pldmFd); // close pldm socket
390 
391     return hresetStatus == "success" ? true : false;
392 }
393 
394 } // namespace pldm
395 } // namespace util
396