xref: /openbmc/openpower-hw-diags/util/pldm.cpp (revision 316acdacb382ec27a6ede244e8fda3847db37fa7)
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