xref: /openbmc/openpower-hw-diags/util/pldm.cpp (revision 08f25b21)
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  */
sendPldm(const std::vector<uint8_t> & request,uint8_t mctpEid,int & pldmFd)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  */
prepareSetEffecterReq(uint16_t effecterId,uint8_t effecterCount,uint8_t stateIdPos,uint8_t stateSetValue,uint8_t mctpEid)49 std::vector<uint8_t> prepareSetEffecterReq(
50     uint16_t effecterId, uint8_t effecterCount, uint8_t stateIdPos,
51     uint8_t stateSetValue, uint8_t mctpEid)
52 {
53     // get pldm instance associated with the endpoint ID
54     uint8_t pldmInstanceID;
55     if (!util::dbus::getPldmInstanceID(pldmInstanceID, mctpEid))
56     {
57         return std::vector<uint8_t>();
58     }
59 
60     // form the request message
61     std::vector<uint8_t> request(
62         sizeof(pldm_msg_hdr) + sizeof(effecterId) + sizeof(effecterCount) +
63         (effecterCount * sizeof(set_effecter_state_field)));
64 
65     // encode the state data with the change we want to elicit
66     std::vector<set_effecter_state_field> stateField;
67     for (uint8_t effecterPos = 0; effecterPos < effecterCount; effecterPos++)
68     {
69         if (effecterPos == stateIdPos)
70         {
71             stateField.emplace_back(
72                 set_effecter_state_field{PLDM_REQUEST_SET, stateSetValue});
73         }
74         else
75         {
76             stateField.emplace_back(
77                 set_effecter_state_field{PLDM_NO_CHANGE, 0});
78         }
79     }
80 
81     // encode the message with state data
82     auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
83     auto rc = encode_set_state_effecter_states_req(
84         pldmInstanceID, effecterId, effecterCount, stateField.data(),
85         requestMsg);
86 
87     if (rc != PLDM_SUCCESS)
88     {
89         trace::err("encode set effecter states request failed");
90         request.clear();
91     }
92 
93     return request;
94 }
95 
96 /** @brief Return map of sensor ID to SBE instance
97  *
98  *  @param[in] stateSetId - the state set ID of interest
99  *  @param[out] sensorInstanceMap - map of sensor to SBE instance
100  *  @param[out] sensorOffset - position of sensor with state set ID within map
101  *
102  *  @return true if sensor info is available false otherwise
103  */
fetchSensorInfo(uint16_t stateSetId,std::map<uint16_t,unsigned int> & sensorInstanceMap,uint8_t & sensorOffset)104 bool fetchSensorInfo(uint16_t stateSetId,
105                      std::map<uint16_t, unsigned int>& sensorInstanceMap,
106                      uint8_t& sensorOffset)
107 {
108     // get state sensor PDRs
109     std::vector<std::vector<uint8_t>> pdrs{};
110     if (!util::dbus::getStateSensorPdrs(pdrs, stateSetId))
111     {
112         return false;
113     }
114 
115     // check for any PDRs available
116     if (!pdrs.size())
117     {
118         trace::err("state sensor PDRs not present");
119         return false;
120     }
121 
122     // find the offset of specified sensor withing PDRs
123     bool offsetFound = false;
124     auto stateSensorPDR =
125         reinterpret_cast<const pldm_state_sensor_pdr*>(pdrs.front().data());
126     auto possibleStatesPtr = stateSensorPDR->possible_states;
127 
128     for (auto offset = 0; offset < stateSensorPDR->composite_sensor_count;
129          offset++)
130     {
131         auto possibleStates =
132             reinterpret_cast<const state_sensor_possible_states*>(
133                 possibleStatesPtr);
134 
135         if (possibleStates->state_set_id == stateSetId)
136         {
137             sensorOffset = offset;
138             offsetFound = true;
139             break;
140         }
141         possibleStatesPtr += sizeof(possibleStates->state_set_id) +
142                              sizeof(possibleStates->possible_states_size) +
143                              possibleStates->possible_states_size;
144     }
145 
146     if (!offsetFound)
147     {
148         trace::err("state sensor not found");
149         return false;
150     }
151 
152     // map sensor ID to equivelent 16 bit value
153     std::map<uint32_t, uint16_t> entityInstMap{};
154     for (auto& pdr : pdrs)
155     {
156         auto pdrPtr =
157             reinterpret_cast<const pldm_state_sensor_pdr*>(pdr.data());
158         uint32_t key = pdrPtr->sensor_id;
159         entityInstMap.emplace(key, static_cast<uint16_t>(pdrPtr->sensor_id));
160     }
161 
162     // map sensor ID to zero based SBE instance
163     unsigned int position = 0;
164     for (const auto& pair : entityInstMap)
165     {
166         sensorInstanceMap.emplace(pair.second, position);
167         position++;
168     }
169 
170     return true;
171 }
172 
173 /** @brief Return map of SBE instance to effecter ID
174  *
175  *  @param[in] stateSetId - the state set ID of interest
176  *  @param[out] instanceToEffecterMap - map of sbe instance to effecter ID
177  *  @param[out] effecterCount - composite effecter count
178  *  @param[out] stateIdPos - position of effecter with state set ID within map
179  *
180  *  @return true if effector info is available false otherwise
181  */
fetchEffecterInfo(uint16_t stateSetId,std::map<unsigned int,uint16_t> & instanceToEffecterMap,uint8_t & effecterCount,uint8_t & stateIdPos)182 bool fetchEffecterInfo(uint16_t stateSetId,
183                        std::map<unsigned int, uint16_t>& instanceToEffecterMap,
184                        uint8_t& effecterCount, uint8_t& stateIdPos)
185 {
186     // get state effecter PDRs
187     std::vector<std::vector<uint8_t>> pdrs{};
188     if (!util::dbus::getStateEffecterPdrs(pdrs, stateSetId))
189     {
190         return false;
191     }
192 
193     // check for any PDRs available
194     if (!pdrs.size())
195     {
196         trace::err("state effecter PDRs not present");
197         return false;
198     }
199 
200     // find the offset of specified effector within PDRs
201     bool offsetFound = false;
202     auto stateEffecterPDR =
203         reinterpret_cast<const pldm_state_effecter_pdr*>(pdrs.front().data());
204     auto possibleStatesPtr = stateEffecterPDR->possible_states;
205 
206     for (auto offset = 0; offset < stateEffecterPDR->composite_effecter_count;
207          offset++)
208     {
209         auto possibleStates =
210             reinterpret_cast<const state_effecter_possible_states*>(
211                 possibleStatesPtr);
212 
213         if (possibleStates->state_set_id == stateSetId)
214         {
215             stateIdPos = offset;
216             effecterCount = stateEffecterPDR->composite_effecter_count;
217             offsetFound = true;
218             break;
219         }
220         possibleStatesPtr += sizeof(possibleStates->state_set_id) +
221                              sizeof(possibleStates->possible_states_size) +
222                              possibleStates->possible_states_size;
223     }
224 
225     if (!offsetFound)
226     {
227         trace::err("state set effecter not found");
228         return false;
229     }
230 
231     // map effecter ID to equivelent 16 bit value
232     std::map<uint32_t, uint16_t> entityInstMap{};
233     for (auto& pdr : pdrs)
234     {
235         auto pdrPtr =
236             reinterpret_cast<const pldm_state_effecter_pdr*>(pdr.data());
237         uint32_t key = pdrPtr->effecter_id;
238         entityInstMap.emplace(key, static_cast<uint16_t>(pdrPtr->effecter_id));
239     }
240 
241     // map zero based SBE instance to effecter ID
242     unsigned int position = 0;
243     for (const auto& pair : entityInstMap)
244     {
245         instanceToEffecterMap.emplace(position, pair.second);
246         position++;
247     }
248 
249     return true;
250 }
251 
252 /**  @brief Reset SBE using HBRT PLDM interface */
hresetSbe(unsigned int sbeInstance)253 bool hresetSbe(unsigned int sbeInstance)
254 {
255     trace::inf("requesting sbe hreset");
256 
257     // get effecter info
258     std::map<unsigned int, uint16_t> sbeInstanceToEffecter;
259     uint8_t SBEEffecterCount = 0;
260     uint8_t sbeMaintenanceStatePosition = 0;
261 
262     if (!fetchEffecterInfo(PLDM_OEM_IBM_SBE_MAINTENANCE_STATE,
263                            sbeInstanceToEffecter, SBEEffecterCount,
264                            sbeMaintenanceStatePosition))
265     {
266         return false;
267     }
268 
269     // find the state effecter ID for the given SBE instance
270     auto effecterEntry = sbeInstanceToEffecter.find(sbeInstance);
271     if (effecterEntry == sbeInstanceToEffecter.end())
272     {
273         trace::err("failed to find effecter for SBE");
274         return false;
275     }
276 
277     // create request to HRESET the SBE
278     constexpr uint8_t hbrtMctpEid = 10; // HBRT MCTP EID
279 
280     auto request = prepareSetEffecterReq(
281         effecterEntry->second, SBEEffecterCount, sbeMaintenanceStatePosition,
282         SBE_RETRY_REQUIRED, hbrtMctpEid);
283 
284     if (request.empty())
285     {
286         trace::err("HRESET effecter request empty");
287         return false;
288     }
289 
290     // get sensor info for validating sensor change
291     std::map<uint16_t, unsigned int> sensorToSbeInstance;
292     uint8_t sbeSensorOffset = 0;
293     if (!fetchSensorInfo(PLDM_OEM_IBM_SBE_HRESET_STATE, sensorToSbeInstance,
294                          sbeSensorOffset))
295     {
296         return false;
297     }
298 
299     // register signal change listener
300     std::string hresetStatus = "requested";
301     constexpr auto interface = "xyz.openbmc_project.PLDM.Event";
302     constexpr auto path = "/xyz/openbmc_project/pldm";
303     constexpr auto member = "StateSensorEvent";
304 
305     auto bus = sdbusplus::bus::new_default();
306     std::unique_ptr<sdbusplus::bus::match_t> match =
307         std::make_unique<sdbusplus::bus::match_t>(
308             bus,
309             sdbusplus::bus::match::rules::type::signal() +
310                 sdbusplus::bus::match::rules::member(member) +
311                 sdbusplus::bus::match::rules::path(path) +
312                 sdbusplus::bus::match::rules::interface(interface),
313             [&](auto& msg) {
314                 uint8_t sensorTid{};
315                 uint16_t sensorId{};
316                 uint8_t msgSensorOffset{};
317                 uint8_t eventState{};
318                 uint8_t previousEventState{};
319 
320                 // get sensor event details
321                 msg.read(sensorTid, sensorId, msgSensorOffset, eventState,
322                          previousEventState);
323 
324                 // does sensor offset match?
325                 if (sbeSensorOffset == msgSensorOffset)
326                 {
327                     // does sensor ID match?
328                     auto sensorEntry = sensorToSbeInstance.find(sensorId);
329                     if (sensorEntry != sensorToSbeInstance.end())
330                     {
331                         const uint8_t instance = sensorEntry->second;
332 
333                         // if instances matche check status
334                         if (instance == sbeInstance)
335                         {
336                             if (eventState ==
337                                 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