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