1 #include "config.h"
2
3 #include <libpldm/oem/ibm/state_set.h>
4 #include <libpldm/platform.h>
5 #include <libpldm/pldm.h>
6 #include <libpldm/transport.h>
7 #include <libpldm/transport/af-mctp.h>
8 #include <libpldm/transport/mctp-demux.h>
9 #include <poll.h>
10
11 #include <util/dbus.hpp>
12 #include <util/pldm.hpp>
13 #include <util/trace.hpp>
14
15 namespace util
16 {
17 namespace pldm
18 {
19
20 class PLDMInstanceManager
21 {
22 public:
23 // Singleton access method
getInstance()24 static PLDMInstanceManager& getInstance()
25 {
26 static PLDMInstanceManager instance;
27 return instance;
28 }
29
30 bool getPldmInstanceID(uint8_t& pldmInstance, uint8_t tid);
31 void freePLDMInstanceID(pldm_instance_id_t instanceID, uint8_t tid);
32
33 /**
34 * @brief setup PLDM transport for sending and receiving messages
35 *
36 * @param[in] eid - MCTP endpoint ID
37 * @return file descriptor on success and throw
38 * exception (xyz::openbmc_project::Common::Error::NotAllowed) on
39 * failures.
40 */
41 int openPLDM(mctp_eid_t eid);
42 /** @brief Opens the MCTP socket for sending and receiving messages.
43 *
44 * @param[in] eid - MCTP endpoint ID
45 */
46 int openMctpDemuxTransport(mctp_eid_t eid);
47
48 /** @brief Close the PLDM file */
49 void closePLDM();
50
51 /** @brief sending PLDM file */
52 bool sendPldm(const std::vector<uint8_t>& request, uint8_t mctpEid);
53
54 /** @brief Opens the MCTP AF_MCTP for sending and receiving messages.
55 *
56 * @param[in] eid - MCTP endpoint ID
57 */
58 int openAfMctpTransport(mctp_eid_t eid);
59
60 union TransportImpl
61 {
62 pldm_transport_mctp_demux* mctpDemux;
63 pldm_transport_af_mctp* afMctp;
64 };
65
66 private:
67 // Private constructor and destructor to prevent creating multiple instances
68 PLDMInstanceManager();
69 ~PLDMInstanceManager();
70
71 // Deleted copy constructor and assignment operator to prevent copying
72 PLDMInstanceManager(const PLDMInstanceManager&) = delete;
73 PLDMInstanceManager& operator=(const PLDMInstanceManager&) = delete;
74
75 // Private member for the instance database
76 pldm_instance_db* pldmInstanceIdDb;
77
78 /** pldm transport instance */
79 struct pldm_transport* pldmTransport = NULL;
80
81 // type of transport implementation instance
82 TransportImpl impl;
83 };
84
PLDMInstanceManager()85 PLDMInstanceManager::PLDMInstanceManager() : pldmInstanceIdDb(nullptr)
86 {
87 // Initialize the database object directly in the constructor
88 auto rc = pldm_instance_db_init_default(&pldmInstanceIdDb);
89 if (rc)
90 {
91 trace::err("Error calling pldm_instance_db_init_default, rc = %d",
92 (unsigned)rc);
93 }
94 }
95
~PLDMInstanceManager()96 PLDMInstanceManager::~PLDMInstanceManager()
97 {
98 // Directly destroy the database object in the destructor
99 if (pldmInstanceIdDb)
100 {
101 auto rc = pldm_instance_db_destroy(pldmInstanceIdDb);
102 if (rc)
103 {
104 trace::err("pldm_instance_db_destroy failed rc = %d", (unsigned)rc);
105 }
106 }
107 }
108
109 // Get the PLDM instance ID for the given terminus ID
getPldmInstanceID(uint8_t & pldmInstance,uint8_t tid)110 bool PLDMInstanceManager::getPldmInstanceID(uint8_t& pldmInstance, uint8_t tid)
111 {
112 pldm_instance_id_t id;
113 int rc = pldm_instance_id_alloc(pldmInstanceIdDb, tid, &id);
114 if (rc == -EAGAIN)
115 {
116 std::this_thread::sleep_for(
117 std::chrono::milliseconds(100)); // Retry after 100ms
118 rc = pldm_instance_id_alloc(pldmInstanceIdDb, tid,
119 &id); // Retry allocation
120 }
121
122 if (rc)
123 {
124 trace::err("getPldmInstanceId: Failed to alloc ID for TID = %d, RC= %d",
125 (unsigned)tid, (unsigned)rc);
126 return false;
127 }
128
129 pldmInstance = id; // Return the allocated instance ID
130 trace::inf("Got instanceId: %d, for PLDM TID: %d", (unsigned)pldmInstance,
131 (unsigned)tid);
132 return true;
133 }
134
135 // Free the PLDM instance ID associated with the terminus ID
freePLDMInstanceID(pldm_instance_id_t instanceID,uint8_t tid)136 void PLDMInstanceManager::freePLDMInstanceID(pldm_instance_id_t instanceID,
137 uint8_t tid)
138 {
139 int rc = pldm_instance_id_free(pldmInstanceIdDb, tid, instanceID);
140 if (rc)
141 {
142 trace::err(
143 "pldm_instance_id_free failed to free id=%d of TID=%d with rc= %d",
144 (unsigned)instanceID, (unsigned)tid, (unsigned)rc);
145 }
146 }
147
openPLDM(mctp_eid_t eid)148 int PLDMInstanceManager::openPLDM(mctp_eid_t eid)
149 {
150 auto fd = -1;
151 if (pldmTransport)
152 {
153 trace::inf("open: pldmTransport already setup!");
154 return fd;
155 }
156 #if defined(PLDM_TRANSPORT_WITH_MCTP_DEMUX)
157 fd = openMctpDemuxTransport(eid);
158 #elif defined(PLDM_TRANSPORT_WITH_AF_MCTP)
159 fd = openAfMctpTransport(eid);
160 #else
161 trace::err("open: No valid transport defined!");
162 #endif
163 if (fd < 0)
164 {
165 auto e = errno;
166 trace::err("openPLDM failed, fd = %d and error= %d", (unsigned)fd, e);
167 }
168 return fd;
169 }
170
openMctpDemuxTransport(mctp_eid_t eid)171 [[maybe_unused]] int PLDMInstanceManager::openMctpDemuxTransport(mctp_eid_t eid)
172 {
173 impl.mctpDemux = nullptr;
174 int rc = pldm_transport_mctp_demux_init(&impl.mctpDemux);
175 if (rc)
176 {
177 trace::err(
178 "openMctpDemuxTransport: Failed to setup tid to eid mapping. rc = %d",
179 (unsigned)rc);
180 closePLDM();
181 return rc;
182 }
183
184 rc = pldm_transport_mctp_demux_map_tid(impl.mctpDemux, eid, eid);
185 if (rc)
186 {
187 trace::err(
188 "openMctpDemuxTransport: Failed to setup tid to eid mapping. rc = %d",
189 (unsigned)rc);
190 closePLDM();
191 return rc;
192 }
193
194 pldmTransport = pldm_transport_mctp_demux_core(impl.mctpDemux);
195 struct pollfd pollfd;
196 rc = pldm_transport_mctp_demux_init_pollfd(pldmTransport, &pollfd);
197 if (rc)
198 {
199 trace::err("openMctpDemuxTransport: Failed to get pollfd. rc= %d",
200 (unsigned)rc);
201 closePLDM();
202 return rc;
203 }
204 return pollfd.fd;
205 }
206
openAfMctpTransport(mctp_eid_t eid)207 [[maybe_unused]] int PLDMInstanceManager::openAfMctpTransport(mctp_eid_t eid)
208 {
209 impl.afMctp = nullptr;
210 int rc = pldm_transport_af_mctp_init(&impl.afMctp);
211 if (rc)
212 {
213 trace::err(
214 "openAfMctpTransport: Failed to init AF MCTP transport. rc = %d",
215 (unsigned)rc);
216 return rc;
217 }
218 rc = pldm_transport_af_mctp_map_tid(impl.afMctp, eid, eid);
219 if (rc)
220 {
221 trace::err(
222 "openAfMctpTransport: Failed to setup tid to eid mapping. rc = %d",
223 (unsigned)rc);
224 closePLDM();
225 return rc;
226 }
227 pldmTransport = pldm_transport_af_mctp_core(impl.afMctp);
228 struct pollfd pollfd;
229 rc = pldm_transport_af_mctp_init_pollfd(pldmTransport, &pollfd);
230 if (rc)
231 {
232 trace::err("openAfMctpTransport: Failed to get pollfd. rc = %d",
233 (unsigned)rc);
234 closePLDM();
235 return rc;
236 }
237 return pollfd.fd;
238 }
239
closePLDM()240 void PLDMInstanceManager::closePLDM()
241 {
242 #if defined(PLDM_TRANSPORT_WITH_MCTP_DEMUX)
243 pldm_transport_mctp_demux_destroy(impl.mctpDemux);
244 impl.mctpDemux = nullptr;
245 #elif defined(PLDM_TRANSPORT_WITH_AF_MCTP)
246 pldm_transport_af_mctp_destroy(impl.afMctp);
247 impl.afMctp = nullptr;
248 #endif
249 pldmTransport = NULL;
250 }
251
252 /** @brief Send PLDM request
253 *
254 * @param[in] request - the request data
255 * @param[in] mcptEid - the mctp endpoint ID
256 *
257 * @pre a mctp instance must have been
258 * @return true if send is successful false otherwise
259 */
sendPldm(const std::vector<uint8_t> & request,uint8_t mctpEid)260 bool PLDMInstanceManager::sendPldm(const std::vector<uint8_t>& request,
261 uint8_t mctpEid)
262 {
263 auto rc = openPLDM(mctpEid);
264 if (rc)
265 {
266 trace::err("failed to connect to pldm");
267 return false;
268 }
269
270 pldm_tid_t pldmTID = static_cast<pldm_tid_t>(mctpEid);
271 // send PLDM request
272 auto pldmRc = pldm_transport_send_msg(pldmTransport, pldmTID,
273 request.data(), request.size());
274
275 trace::inf("sent pldm request");
276
277 return pldmRc == PLDM_REQUESTER_SUCCESS ? true : false;
278 }
279
280 /** @brief Prepare a request for SetStateEffecterStates
281 *
282 * @param[in] effecterId - the effecter ID
283 * @param[in] effecterCount - composite effecter count
284 * @param[in] stateIdPos - position of the state set
285 * @param[in] stateSetValue - the value to set the state
286 * @param[in] mcptEid - the MCTP endpoint ID
287 *
288 * @return PLDM request message to be sent to host, empty message on error
289 */
prepareSetEffecterReq(uint16_t effecterId,uint8_t effecterCount,uint8_t stateIdPos,uint8_t stateSetValue,uint8_t mctpEid)290 std::vector<uint8_t> prepareSetEffecterReq(
291 uint16_t effecterId, uint8_t effecterCount, uint8_t stateIdPos,
292 uint8_t stateSetValue, uint8_t mctpEid)
293 {
294 PLDMInstanceManager& manager = PLDMInstanceManager::getInstance();
295
296 // get pldm instance associated with the endpoint ID
297 uint8_t pldmInstanceID;
298 if (!manager.getPldmInstanceID(pldmInstanceID, mctpEid))
299 {
300 return std::vector<uint8_t>();
301 }
302
303 // form the request message
304 std::vector<uint8_t> request(
305 sizeof(pldm_msg_hdr) + sizeof(effecterId) + sizeof(effecterCount) +
306 (effecterCount * sizeof(set_effecter_state_field)));
307
308 // encode the state data with the change we want to elicit
309 std::vector<set_effecter_state_field> stateField;
310 for (uint8_t effecterPos = 0; effecterPos < effecterCount; effecterPos++)
311 {
312 if (effecterPos == stateIdPos)
313 {
314 stateField.emplace_back(
315 set_effecter_state_field{PLDM_REQUEST_SET, stateSetValue});
316 }
317 else
318 {
319 stateField.emplace_back(
320 set_effecter_state_field{PLDM_NO_CHANGE, 0});
321 }
322 }
323
324 // encode the message with state data
325 auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
326 auto rc = encode_set_state_effecter_states_req(
327 pldmInstanceID, effecterId, effecterCount, stateField.data(),
328 requestMsg);
329
330 if (rc != PLDM_SUCCESS)
331 {
332 trace::err("encode set effecter states request failed");
333 manager.freePLDMInstanceID(pldmInstanceID, mctpEid);
334 request.clear();
335 }
336
337 return request;
338 }
339
340 /** @brief Return map of sensor ID to SBE instance
341 *
342 * @param[in] stateSetId - the state set ID of interest
343 * @param[out] sensorInstanceMap - map of sensor to SBE instance
344 * @param[out] sensorOffset - position of sensor with state set ID within map
345 *
346 * @return true if sensor info is available false otherwise
347 */
fetchSensorInfo(uint16_t stateSetId,std::map<uint16_t,unsigned int> & sensorInstanceMap,uint8_t & sensorOffset)348 bool fetchSensorInfo(uint16_t stateSetId,
349 std::map<uint16_t, unsigned int>& sensorInstanceMap,
350 uint8_t& sensorOffset)
351 {
352 // get state sensor PDRs
353 std::vector<std::vector<uint8_t>> pdrs{};
354 if (!util::dbus::getStateSensorPdrs(pdrs, stateSetId))
355 {
356 return false;
357 }
358
359 // check for any PDRs available
360 if (!pdrs.size())
361 {
362 trace::err("state sensor PDRs not present");
363 return false;
364 }
365
366 // find the offset of specified sensor withing PDRs
367 bool offsetFound = false;
368 auto stateSensorPDR =
369 reinterpret_cast<const pldm_state_sensor_pdr*>(pdrs.front().data());
370 auto possibleStatesPtr = stateSensorPDR->possible_states;
371
372 for (auto offset = 0; offset < stateSensorPDR->composite_sensor_count;
373 offset++)
374 {
375 auto possibleStates =
376 reinterpret_cast<const state_sensor_possible_states*>(
377 possibleStatesPtr);
378
379 if (possibleStates->state_set_id == stateSetId)
380 {
381 sensorOffset = offset;
382 offsetFound = true;
383 break;
384 }
385 possibleStatesPtr += sizeof(possibleStates->state_set_id) +
386 sizeof(possibleStates->possible_states_size) +
387 possibleStates->possible_states_size;
388 }
389
390 if (!offsetFound)
391 {
392 trace::err("state sensor not found");
393 return false;
394 }
395
396 // map sensor ID to equivelent 16 bit value
397 std::map<uint32_t, uint16_t> entityInstMap{};
398 for (auto& pdr : pdrs)
399 {
400 auto pdrPtr =
401 reinterpret_cast<const pldm_state_sensor_pdr*>(pdr.data());
402 uint32_t key = pdrPtr->sensor_id;
403 entityInstMap.emplace(key, static_cast<uint16_t>(pdrPtr->sensor_id));
404 }
405
406 // map sensor ID to zero based SBE instance
407 unsigned int position = 0;
408 for (const auto& pair : entityInstMap)
409 {
410 sensorInstanceMap.emplace(pair.second, position);
411 position++;
412 }
413
414 return true;
415 }
416
417 /** @brief Return map of SBE instance to effecter ID
418 *
419 * @param[in] stateSetId - the state set ID of interest
420 * @param[out] instanceToEffecterMap - map of sbe instance to effecter ID
421 * @param[out] effecterCount - composite effecter count
422 * @param[out] stateIdPos - position of effecter with state set ID within map
423 *
424 * @return true if effector info is available false otherwise
425 */
fetchEffecterInfo(uint16_t stateSetId,std::map<unsigned int,uint16_t> & instanceToEffecterMap,uint8_t & effecterCount,uint8_t & stateIdPos)426 bool fetchEffecterInfo(uint16_t stateSetId,
427 std::map<unsigned int, uint16_t>& instanceToEffecterMap,
428 uint8_t& effecterCount, uint8_t& stateIdPos)
429 {
430 // get state effecter PDRs
431 std::vector<std::vector<uint8_t>> pdrs{};
432 if (!util::dbus::getStateEffecterPdrs(pdrs, stateSetId))
433 {
434 return false;
435 }
436
437 // check for any PDRs available
438 if (!pdrs.size())
439 {
440 trace::err("state effecter PDRs not present");
441 return false;
442 }
443
444 // find the offset of specified effector within PDRs
445 bool offsetFound = false;
446 auto stateEffecterPDR =
447 reinterpret_cast<const pldm_state_effecter_pdr*>(pdrs.front().data());
448 auto possibleStatesPtr = stateEffecterPDR->possible_states;
449
450 for (auto offset = 0; offset < stateEffecterPDR->composite_effecter_count;
451 offset++)
452 {
453 auto possibleStates =
454 reinterpret_cast<const state_effecter_possible_states*>(
455 possibleStatesPtr);
456
457 if (possibleStates->state_set_id == stateSetId)
458 {
459 stateIdPos = offset;
460 effecterCount = stateEffecterPDR->composite_effecter_count;
461 offsetFound = true;
462 break;
463 }
464 possibleStatesPtr += sizeof(possibleStates->state_set_id) +
465 sizeof(possibleStates->possible_states_size) +
466 possibleStates->possible_states_size;
467 }
468
469 if (!offsetFound)
470 {
471 trace::err("state set effecter not found");
472 return false;
473 }
474
475 // map effecter ID to equivelent 16 bit value
476 std::map<uint32_t, uint16_t> entityInstMap{};
477 for (auto& pdr : pdrs)
478 {
479 auto pdrPtr =
480 reinterpret_cast<const pldm_state_effecter_pdr*>(pdr.data());
481 uint32_t key = pdrPtr->effecter_id;
482 entityInstMap.emplace(key, static_cast<uint16_t>(pdrPtr->effecter_id));
483 }
484
485 // map zero based SBE instance to effecter ID
486 unsigned int position = 0;
487 for (const auto& pair : entityInstMap)
488 {
489 instanceToEffecterMap.emplace(position, pair.second);
490 position++;
491 }
492
493 return true;
494 }
495
496 /** @brief Reset SBE using HBRT PLDM interface */
hresetSbe(unsigned int sbeInstance)497 bool hresetSbe(unsigned int sbeInstance)
498 {
499 trace::inf("requesting sbe hreset");
500
501 // get effecter info
502 std::map<unsigned int, uint16_t> sbeInstanceToEffecter;
503 uint8_t SBEEffecterCount = 0;
504 uint8_t sbeMaintenanceStatePosition = 0;
505
506 if (!fetchEffecterInfo(PLDM_OEM_IBM_SBE_MAINTENANCE_STATE,
507 sbeInstanceToEffecter, SBEEffecterCount,
508 sbeMaintenanceStatePosition))
509 {
510 return false;
511 }
512
513 // find the state effecter ID for the given SBE instance
514 auto effecterEntry = sbeInstanceToEffecter.find(sbeInstance);
515 if (effecterEntry == sbeInstanceToEffecter.end())
516 {
517 trace::err("failed to find effecter for SBE");
518 return false;
519 }
520
521 // create request to HRESET the SBE
522 constexpr uint8_t hbrtMctpEid = 10; // HBRT MCTP EID
523
524 auto request = prepareSetEffecterReq(
525 effecterEntry->second, SBEEffecterCount, sbeMaintenanceStatePosition,
526 SBE_RETRY_REQUIRED, hbrtMctpEid);
527
528 if (request.empty())
529 {
530 trace::err("HRESET effecter request empty");
531 return false;
532 }
533
534 // get sensor info for validating sensor change
535 std::map<uint16_t, unsigned int> sensorToSbeInstance;
536 uint8_t sbeSensorOffset = 0;
537 if (!fetchSensorInfo(PLDM_OEM_IBM_SBE_HRESET_STATE, sensorToSbeInstance,
538 sbeSensorOffset))
539 {
540 PLDMInstanceManager& manager = PLDMInstanceManager::getInstance();
541 auto reqhdr = reinterpret_cast<const pldm_msg_hdr*>(&request);
542 manager.freePLDMInstanceID(reqhdr->instance_id, hbrtMctpEid);
543 return false;
544 }
545
546 // register signal change listener
547 std::string hresetStatus = "requested";
548 constexpr auto interface = "xyz.openbmc_project.PLDM.Event";
549 constexpr auto path = "/xyz/openbmc_project/pldm";
550 constexpr auto member = "StateSensorEvent";
551
552 auto bus = sdbusplus::bus::new_default();
553 std::unique_ptr<sdbusplus::bus::match_t> match =
554 std::make_unique<sdbusplus::bus::match_t>(
555 bus,
556 sdbusplus::bus::match::rules::type::signal() +
557 sdbusplus::bus::match::rules::member(member) +
558 sdbusplus::bus::match::rules::path(path) +
559 sdbusplus::bus::match::rules::interface(interface),
560 [&](auto& msg) {
561 uint8_t sensorTid{};
562 uint16_t sensorId{};
563 uint8_t msgSensorOffset{};
564 uint8_t eventState{};
565 uint8_t previousEventState{};
566
567 // get sensor event details
568 msg.read(sensorTid, sensorId, msgSensorOffset, eventState,
569 previousEventState);
570
571 // does sensor offset match?
572 if (sbeSensorOffset == msgSensorOffset)
573 {
574 // does sensor ID match?
575 auto sensorEntry = sensorToSbeInstance.find(sensorId);
576 if (sensorEntry != sensorToSbeInstance.end())
577 {
578 const uint8_t instance = sensorEntry->second;
579
580 // if instances matche check status
581 if (instance == sbeInstance)
582 {
583 if (eventState ==
584 static_cast<uint8_t>(SBE_HRESET_READY))
585 {
586 hresetStatus = "success";
587 }
588 else if (eventState ==
589 static_cast<uint8_t>(SBE_HRESET_FAILED))
590 {
591 hresetStatus = "fail";
592 }
593 }
594 }
595 }
596 });
597
598 // send request to issue hreset of sbe
599 PLDMInstanceManager& manager = PLDMInstanceManager::getInstance();
600 if (!(manager.sendPldm(request, hbrtMctpEid)))
601 {
602 trace::err("send pldm request failed");
603 auto reqhdr = reinterpret_cast<const pldm_msg_hdr*>(&request);
604 manager.freePLDMInstanceID(reqhdr->instance_id, hbrtMctpEid);
605
606 return false;
607 }
608
609 // keep track of elapsed time
610 uint64_t timeRemaining = 60000000; // microseconds, 1 minute
611 std::chrono::steady_clock::time_point begin =
612 std::chrono::steady_clock::now();
613
614 // wait for status update or timeout
615 trace::inf("waiting on sbe hreset");
616 while ("requested" == hresetStatus && 0 != timeRemaining)
617 {
618 bus.wait(timeRemaining);
619 uint64_t timeElapsed =
620 std::chrono::duration_cast<std::chrono::microseconds>(
621 std::chrono::steady_clock::now() - begin)
622 .count();
623
624 timeRemaining =
625 timeElapsed > timeRemaining ? 0 : timeRemaining - timeElapsed;
626
627 bus.process_discard();
628 }
629
630 if (0 == timeRemaining)
631 {
632 trace::err("hreset timed out");
633 }
634
635 auto reqhdr = reinterpret_cast<const pldm_msg_hdr*>(&request);
636 manager.freePLDMInstanceID(reqhdr->instance_id, hbrtMctpEid);
637 manager.closePLDM();
638
639 return hresetStatus == "success" ? true : false;
640 }
641
642 } // namespace pldm
643 } // namespace util
644