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