xref: /openbmc/openpower-occ-control/pldm.cpp (revision 37abe9be91df2bb173c9642a2740a425904d7921)
1  #include "pldm.hpp"
2  
3  #include "libpldm/instance-id.h"
4  
5  #include "file.hpp"
6  
7  #include <libpldm/entity.h>
8  #include <libpldm/oem/ibm/state_set.h>
9  #include <libpldm/platform.h>
10  #include <libpldm/state_set.h>
11  #include <libpldm/transport.h>
12  #include <libpldm/transport/af-mctp.h>
13  #include <libpldm/transport/mctp-demux.h>
14  #include <poll.h>
15  
16  #include <phosphor-logging/lg2.hpp>
17  #include <sdbusplus/bus.hpp>
18  #include <sdeventplus/clock.hpp>
19  #include <sdeventplus/exception.hpp>
20  #include <sdeventplus/source/io.hpp>
21  #include <sdeventplus/source/time.hpp>
22  
23  #include <algorithm>
24  
25  namespace pldm
26  {
27  
28  using namespace sdeventplus;
29  using namespace sdeventplus::source;
30  constexpr auto clockId = sdeventplus::ClockId::RealTime;
31  using Clock = sdeventplus::Clock<clockId>;
32  using Timer = Time<clockId>;
33  bool Interface::throttleTraces = false;
34  enum pldm_msg_type Interface::msgType = MSG_UNDEFINED;
35  open_power::occ::instanceID Interface::resetInstance = 0;
36  
fetchSensorInfo(uint16_t stateSetId,SensorToInstance & sensorInstanceMap,SensorOffset & sensorOffset)37  void Interface::fetchSensorInfo(uint16_t stateSetId,
38                                  SensorToInstance& sensorInstanceMap,
39                                  SensorOffset& sensorOffset)
40  {
41      PdrList pdrs{};
42      static bool tracedError = false;
43  
44      auto& bus = open_power::occ::utils::getBus();
45      try
46      {
47          auto method = bus.new_method_call(
48              "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
49              "xyz.openbmc_project.PLDM.PDR", "FindStateSensorPDR");
50          method.append(tid, static_cast<uint16_t>(PLDM_ENTITY_PROC), stateSetId);
51  
52          auto responseMsg = bus.call(method);
53          responseMsg.read(pdrs);
54      }
55      catch (const sdbusplus::exception_t& e)
56      {
57          if (!tracedError)
58          {
59              lg2::error(
60                  "fetchSensorInfo: Failed to find stateSetID:{ID} PDR: {ERR}",
61                  "ID", stateSetId, "ERR", e.what());
62              tracedError = true;
63          }
64      }
65  
66      if (pdrs.empty())
67      {
68          if (!tracedError)
69          {
70              lg2::error("fetchSensorInfo: state sensor PDRs ({ID}) not present",
71                         "ID", stateSetId);
72              tracedError = true;
73          }
74          return;
75      }
76  
77      // Found PDR
78      if (tracedError)
79      {
80          lg2::info("fetchSensorInfo: found {NUM} PDRs", "NUM", pdrs.size());
81          tracedError = false;
82      }
83  
84      bool offsetFound = false;
85      auto stateSensorPDR =
86          reinterpret_cast<const pldm_state_sensor_pdr*>(pdrs.front().data());
87      auto possibleStatesPtr = stateSensorPDR->possible_states;
88      for (auto offset = 0; offset < stateSensorPDR->composite_sensor_count;
89           offset++)
90      {
91          auto possibleStates =
92              reinterpret_cast<const state_sensor_possible_states*>(
93                  possibleStatesPtr);
94  
95          if (possibleStates->state_set_id == stateSetId)
96          {
97              sensorOffset = offset;
98              offsetFound = true;
99              break;
100          }
101          possibleStatesPtr += sizeof(possibleStates->state_set_id) +
102                               sizeof(possibleStates->possible_states_size) +
103                               possibleStates->possible_states_size;
104      }
105  
106      if (!offsetFound)
107      {
108          lg2::error("pldm: state sensor PDR not found");
109          return;
110      }
111  
112      // To order SensorID based on the EntityInstance.
113      // Note that when a proc is on a DCM, the PDRs for these sensors
114      // could have the same instance IDs but different container IDs.
115      std::map<uint32_t, SensorID> entityInstMap{};
116      for (auto& pdr : pdrs)
117      {
118          auto pdrPtr =
119              reinterpret_cast<const pldm_state_sensor_pdr*>(pdr.data());
120          uint32_t key = pdrPtr->sensor_id;
121          entityInstMap.emplace(key, static_cast<SensorID>(pdrPtr->sensor_id));
122      }
123  
124      open_power::occ::instanceID count = start;
125      for (const auto& pair : entityInstMap)
126      {
127          sensorInstanceMap.emplace(pair.second, count);
128          count++;
129      }
130  }
131  
sensorEvent(sdbusplus::message_t & msg)132  void Interface::sensorEvent(sdbusplus::message_t& msg)
133  {
134      if (!isOCCSensorCacheValid())
135      {
136          fetchSensorInfo(PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS,
137                          sensorToOCCInstance, OCCSensorOffset);
138      }
139  
140      if (sensorToSBEInstance.empty())
141      {
142          fetchSensorInfo(PLDM_OEM_IBM_SBE_HRESET_STATE, sensorToSBEInstance,
143                          SBESensorOffset);
144      }
145  
146      TerminusID sensorTid{};
147      SensorID sensorId{};
148      SensorOffset msgSensorOffset{};
149      EventState eventState{};
150      EventState previousEventState{};
151  
152      msg.read(sensorTid, sensorId, msgSensorOffset, eventState,
153               previousEventState);
154  
155      if (msgSensorOffset == OCCSensorOffset)
156      {
157          auto sensorEntry = sensorToOCCInstance.find(sensorId);
158  
159          if (sensorEntry != sensorToOCCInstance.end())
160          {
161              const uint8_t instance = sensorEntry->second;
162              bool validEvent = true;
163              bool isRunning = false;
164              if (eventState ==
165                  static_cast<EventState>(
166                      PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_IN_SERVICE))
167              {
168                  lg2::info("PLDM: OCC{INST} is RUNNING", "INST", instance);
169                  isRunning = true;
170              }
171              else if (eventState ==
172                       static_cast<EventState>(
173                           PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_STOPPED))
174              {
175                  lg2::info("PLDM: OCC{INST} has now STOPPED", "INST", instance);
176              }
177              else if (eventState ==
178                       static_cast<EventState>(
179                           PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_DORMANT))
180              {
181                  lg2::error(
182                      "PLDM: OCC{INST} has now STOPPED and system is in SAFE MODE",
183                      "INST", instance);
184  
185                  // Setting safe mode true
186                  safeModeCallBack(true);
187              }
188              else
189              {
190                  lg2::warning(
191                      "PLDM: Unexpected OCC Active sensor state {STATE} for OCC{INST}",
192                      "STATE", eventState, "INST", instance);
193                  validEvent = false;
194              }
195              if (validEvent)
196              {
197                  if ((pldmFd > 0) && (instance == pldmResponseOcc))
198                  {
199                      // Waiting for a response for this OCC, can stop waiting
200                      pldmClose();
201                  }
202                  occActiveCallBack(instance, isRunning);
203              }
204              return;
205          }
206      }
207  
208      if (msgSensorOffset == SBESensorOffset)
209      {
210          auto sensorEntry = sensorToSBEInstance.find(sensorId);
211  
212          if (sensorEntry != sensorToSBEInstance.end())
213          {
214              const uint8_t instance = sensorEntry->second;
215              auto match = std::find(outstandingHResets.begin(),
216                                     outstandingHResets.end(), instance);
217              if (match != outstandingHResets.end())
218              {
219                  outstandingHResets.erase(match);
220                  if (eventState == static_cast<EventState>(SBE_HRESET_NOT_READY))
221                  {
222                      lg2::error("pldm: HRESET is NOT READY (OCC{INST})", "INST",
223                                 instance);
224                      // Stop OCC comm - OCC not usable until it becomes READY
225                      occActiveCallBack(instance, false);
226                      // Collect SBE FFDC
227                      sbeCallBack(instance, false);
228                      // Try PM Complex reset
229                      lg2::error(
230                          "sensorEvent: Requesting OCC reset for OCC{INST}",
231                          "INST", instance);
232                      resetOCC(resetInstance);
233                  }
234                  else if (eventState ==
235                           static_cast<EventState>(SBE_HRESET_READY))
236                  {
237                      sbeCallBack(instance, true);
238                  }
239                  else if (eventState ==
240                           static_cast<EventState>(SBE_HRESET_FAILED))
241                  {
242                      sbeCallBack(instance, false);
243                  }
244                  else
245                  {
246                      if (eventState ==
247                          static_cast<EventState>(SBE_HRESET_FAILED))
248                          lg2::error(
249                              "pldm: Unexpected HRESET state {STATE} (OCC{INST})",
250                              "STATE", eventState, "INST", instance);
251                      sbeCallBack(instance, false);
252                  }
253              }
254              // else request was not from us
255          }
256      }
257  }
258  
hostStateEvent(sdbusplus::message_t & msg)259  void Interface::hostStateEvent(sdbusplus::message_t& msg)
260  {
261      std::map<std::string, std::variant<std::string>> properties{};
262      std::string interface;
263      msg.read(interface, properties);
264      const auto stateEntry = properties.find("CurrentHostState");
265      if (stateEntry != properties.end())
266      {
267          auto stateEntryValue = stateEntry->second;
268          auto propVal = std::get<std::string>(stateEntryValue);
269          if (propVal == "xyz.openbmc_project.State.Host.HostState.Off")
270          {
271              clearData();
272          }
273      }
274  }
275  
clearData()276  void Interface::clearData()
277  {
278      if (!sensorToOCCInstance.empty())
279      {
280          lg2::info("clearData: Clearing sensorToOCCInstance ({NUM} entries)",
281                    "NUM", sensorToOCCInstance.size());
282          for (auto entry : sensorToOCCInstance)
283          {
284              lg2::info("clearData: OCC{INST} / sensorID: {ID}", "INST",
285                        entry.second, "ID", lg2::hex, entry.first);
286              occActiveCallBack(entry.second, false);
287          }
288          sensorToOCCInstance.clear();
289      }
290      if (!occInstanceToEffecter.empty())
291      {
292          lg2::debug("clearData: Clearing occInstanceToEffecter ({NUM} entries)",
293                     "NUM", occInstanceToEffecter.size());
294          occInstanceToEffecter.clear();
295      }
296      if (!sensorToSBEInstance.empty())
297      {
298          lg2::debug("clearData: Clearing sensorToSBEInstance ({NUM} entries)",
299                     "NUM", sensorToSBEInstance.size());
300          sensorToSBEInstance.clear();
301      }
302      if (!sbeInstanceToEffecter.empty())
303      {
304          lg2::debug("clearData: Clearing sbeInstanceToEffecter ({NUM} entries)",
305                     "NUM", sbeInstanceToEffecter.size());
306          sbeInstanceToEffecter.clear();
307      }
308  }
309  
fetchEffecterInfo(uint16_t stateSetId,InstanceToEffecter & instanceToEffecterMap,CompositeEffecterCount & effecterCount,uint8_t & stateIdPos)310  void Interface::fetchEffecterInfo(
311      uint16_t stateSetId, InstanceToEffecter& instanceToEffecterMap,
312      CompositeEffecterCount& effecterCount, uint8_t& stateIdPos)
313  {
314      PdrList pdrs{};
315  
316      auto& bus = open_power::occ::utils::getBus();
317      try
318      {
319          auto method = bus.new_method_call(
320              "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
321              "xyz.openbmc_project.PLDM.PDR", "FindStateEffecterPDR");
322          method.append(tid, static_cast<uint16_t>(PLDM_ENTITY_PROC), stateSetId);
323  
324          auto responseMsg = bus.call(method);
325          responseMsg.read(pdrs);
326      }
327      catch (const sdbusplus::exception_t& e)
328      {
329          lg2::error("pldm: Failed to fetch the state effecter PDRs: {ERR}",
330                     "ERR", e.what());
331      }
332  
333      if (!pdrs.size())
334      {
335          lg2::error("pldm: state effecter PDRs not present");
336          return;
337      }
338  
339      bool offsetFound = false;
340      auto stateEffecterPDR =
341          reinterpret_cast<const pldm_state_effecter_pdr*>(pdrs.front().data());
342      auto possibleStatesPtr = stateEffecterPDR->possible_states;
343      for (auto offset = 0; offset < stateEffecterPDR->composite_effecter_count;
344           offset++)
345      {
346          auto possibleStates =
347              reinterpret_cast<const state_effecter_possible_states*>(
348                  possibleStatesPtr);
349  
350          if (possibleStates->state_set_id == stateSetId)
351          {
352              stateIdPos = offset;
353              effecterCount = stateEffecterPDR->composite_effecter_count;
354              offsetFound = true;
355              break;
356          }
357          possibleStatesPtr += sizeof(possibleStates->state_set_id) +
358                               sizeof(possibleStates->possible_states_size) +
359                               possibleStates->possible_states_size;
360      }
361  
362      if (!offsetFound)
363      {
364          return;
365      }
366  
367      std::map<uint32_t, EffecterID> entityInstMap{};
368      for (auto& pdr : pdrs)
369      {
370          auto pdrPtr =
371              reinterpret_cast<const pldm_state_effecter_pdr*>(pdr.data());
372          uint32_t key = pdrPtr->effecter_id;
373          entityInstMap.emplace(key, static_cast<SensorID>(pdrPtr->effecter_id));
374      }
375  
376      open_power::occ::instanceID position = start;
377      for (const auto& pair : entityInstMap)
378      {
379          instanceToEffecterMap.emplace(position, pair.second);
380          position++;
381      }
382  }
383  
prepareSetEffecterReq(EffecterID effecterId,CompositeEffecterCount effecterCount,uint8_t stateIdPos,uint8_t stateSetValue)384  std::vector<uint8_t> Interface::prepareSetEffecterReq(
385      EffecterID effecterId, CompositeEffecterCount effecterCount,
386      uint8_t stateIdPos, uint8_t stateSetValue)
387  {
388      if (!getPldmInstanceId())
389      {
390          return std::vector<uint8_t>();
391      }
392  
393      std::vector<uint8_t> request(
394          sizeof(pldm_msg_hdr) + sizeof(effecterId) + sizeof(effecterCount) +
395          (effecterCount * sizeof(set_effecter_state_field)));
396      auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
397      std::vector<set_effecter_state_field> stateField;
398  
399      for (uint8_t effecterPos = 0; effecterPos < effecterCount; effecterPos++)
400      {
401          if (effecterPos == stateIdPos)
402          {
403              stateField.emplace_back(
404                  set_effecter_state_field{PLDM_REQUEST_SET, stateSetValue});
405          }
406          else
407          {
408              stateField.emplace_back(
409                  set_effecter_state_field{PLDM_NO_CHANGE, 0});
410          }
411      }
412      auto rc = encode_set_state_effecter_states_req(
413          pldmInstanceID.value(), effecterId, effecterCount, stateField.data(),
414          requestMsg);
415      if (rc != PLDM_SUCCESS)
416      {
417          lg2::error("encode set effecter states request returned error rc={RC}",
418                     "RC", rc);
419          request.clear();
420      }
421      return request;
422  }
423  
resetOCC(open_power::occ::instanceID occInstanceId)424  void Interface::resetOCC(open_power::occ::instanceID occInstanceId)
425  {
426      if (open_power::occ::utils::isHostRunning())
427      {
428          if (!isPDREffecterCacheValid())
429          {
430              fetchEffecterInfo(PLDM_STATE_SET_BOOT_RESTART_CAUSE,
431                                occInstanceToEffecter, OCCEffecterCount,
432                                bootRestartPosition);
433          }
434  
435          // Find the matching effecter for the OCC instance
436          auto effecterEntry = occInstanceToEffecter.find(occInstanceId);
437          if (effecterEntry == occInstanceToEffecter.end())
438          {
439              lg2::error(
440                  "pldm: Failed to find a matching effecter for OCC instance {INST}",
441                  "INST", occInstanceId);
442  
443              return;
444          }
445  
446          // Prepare the SetStateEffecterStates request to reset the OCC
447          auto request = prepareSetEffecterReq(
448              effecterEntry->second, OCCEffecterCount, bootRestartPosition,
449              PLDM_STATE_SET_BOOT_RESTART_CAUSE_WARM_RESET);
450  
451          if (request.empty())
452          {
453              lg2::error("pldm: SetStateEffecterStates OCC reset request empty");
454              return;
455          }
456  
457          // Send request to reset the OCCs/PM Complex (and wait for response)
458          msgType = MSG_OCC_RESET;
459          resetInstance = occInstanceId;
460          sendPldm(request, occInstanceId, true);
461      }
462      else
463      {
464          lg2::error("resetOCC: HOST is not running (OCC{INST})", "INST",
465                     occInstanceId);
466          clearData();
467      }
468  }
469  
sendHRESET(open_power::occ::instanceID sbeInstanceId)470  void Interface::sendHRESET(open_power::occ::instanceID sbeInstanceId)
471  {
472      if (open_power::occ::utils::isHostRunning())
473      {
474          if (sbeInstanceToEffecter.empty())
475          {
476              fetchEffecterInfo(PLDM_OEM_IBM_SBE_MAINTENANCE_STATE,
477                                sbeInstanceToEffecter, SBEEffecterCount,
478                                sbeMaintenanceStatePosition);
479          }
480  
481          auto effecterEntry = sbeInstanceToEffecter.find(sbeInstanceId);
482          if (effecterEntry == sbeInstanceToEffecter.end())
483          {
484              lg2::error(
485                  "pldm: Failed to find a matching effecter for SBE instance {INST}",
486                  "INST", sbeInstanceId);
487              return;
488          }
489  
490          // Prepare the SetStateEffecterStates request to HRESET the SBE
491          auto request = prepareSetEffecterReq(
492              effecterEntry->second, SBEEffecterCount,
493              sbeMaintenanceStatePosition, SBE_RETRY_REQUIRED);
494  
495          if (request.empty())
496          {
497              lg2::error("pldm: SetStateEffecterStates HRESET request empty");
498              return;
499          }
500  
501          // Send request to issue HRESET of SBE (and wait for response)
502          msgType = MSG_HRESET;
503          resetInstance = sbeInstanceId;
504          sendPldm(request, sbeInstanceId, true);
505          outstandingHResets.insert(sbeInstanceId);
506      }
507      else
508      {
509          lg2::error("sendHRESET: HOST is not running (OCC{INST})", "INST",
510                     sbeInstanceId);
511          clearData();
512      }
513  }
514  
getPldmInstanceId()515  bool Interface::getPldmInstanceId()
516  {
517      pldm_instance_id_t id;
518      if (!pldmInstanceID)
519      {
520          // Request new instance ID
521          int rc = pldm_instance_id_alloc(pldmInstanceIdDb, tid, &id);
522          if (rc == -EAGAIN)
523          {
524              std::this_thread::sleep_for(std::chrono::milliseconds(100));
525              rc = pldm_instance_id_alloc(pldmInstanceIdDb, tid, &id);
526          }
527  
528          if (rc)
529          {
530              lg2::error(
531                  "getPldmInstanceId: Failed to alloc ID for TID {TID}. RC{RC}",
532                  "TID", tid, "RC", rc);
533              return false;
534          }
535          pldmInstanceID.emplace(id);
536          if (!throttleTraces)
537          {
538              lg2::info("got id {ID} and set PldmInstanceId to {INST}", "ID", id,
539                        "INST", pldmInstanceID.value());
540          }
541      }
542      return true;
543  }
544  
freePldmInstanceId()545  void Interface::freePldmInstanceId()
546  {
547      if (pldmInstanceID)
548      {
549          int rc = pldm_instance_id_free(pldmInstanceIdDb, tid,
550                                         pldmInstanceID.value());
551          if (rc)
552          {
553              lg2::error(
554                  "freePldmInstanceId: Failed to free ID {ID} for TID {TID}. RC{RC}",
555                  "ID", pldmInstanceID.value(), "TID", tid, "RC", rc);
556              return;
557          }
558          if (!throttleTraces)
559          {
560              lg2::info("Freed PLDM instance ID {ID}", "ID",
561                        pldmInstanceID.value());
562          }
563          pldmInstanceID = std::nullopt;
564      }
565  }
566  
openMctpDemuxTransport()567  [[maybe_unused]] int Interface::openMctpDemuxTransport()
568  {
569      impl.mctpDemux = nullptr;
570      int rc = pldm_transport_mctp_demux_init(&impl.mctpDemux);
571      if (rc)
572      {
573          lg2::error(
574              "openMctpDemuxTransport: Failed to init MCTP demux transport, errno={ERR}/{STR}",
575              "ERR", rc, "STR", strerror(rc));
576          return -1;
577      }
578  
579      if (pldm_transport_mctp_demux_map_tid(impl.mctpDemux, mctpEid, mctpEid))
580      {
581          lg2::error(
582              "openMctpDemuxTransport: Failed to setup tid to eid mapping, errno={ERR}/{STR}",
583              "ERR", errno, "STR", strerror(errno));
584          pldmClose();
585          return -1;
586      }
587      pldmTransport = pldm_transport_mctp_demux_core(impl.mctpDemux);
588  
589      struct pollfd pollfd;
590      if (pldm_transport_mctp_demux_init_pollfd(pldmTransport, &pollfd))
591      {
592          lg2::error(
593              "openMctpDemuxTransport: Failed to get pollfd , errno={ERR}/{STR}",
594              "ERR", errno, "STR", strerror(errno));
595          pldmClose();
596          return -1;
597      }
598      pldmFd = pollfd.fd;
599      if (!throttleTraces)
600      {
601          lg2::info("openMctpDemuxTransport: pldmFd has fd={FD}", "FD", pldmFd);
602      }
603      return 0;
604  }
605  
openAfMctpTransport()606  [[maybe_unused]] int Interface::openAfMctpTransport()
607  {
608      impl.afMctp = nullptr;
609      int rc = pldm_transport_af_mctp_init(&impl.afMctp);
610      if (rc)
611      {
612          lg2::error(
613              "openAfMctpTransport: Failed to init af MCTP transport, errno={ERR}/{STR}",
614              "ERR", rc, "STR", strerror(rc));
615          return -1;
616      }
617  
618      if (pldm_transport_af_mctp_map_tid(impl.afMctp, mctpEid, mctpEid))
619      {
620          lg2::error(
621              "openAfMctpTransport: Failed to setup tid to eid mapping, errno={ERR}/{STR}",
622              "ERR", errno, "STR", strerror(errno));
623          pldmClose();
624          return -1;
625      }
626      pldmTransport = pldm_transport_af_mctp_core(impl.afMctp);
627  
628      struct pollfd pollfd;
629      if (pldm_transport_af_mctp_init_pollfd(pldmTransport, &pollfd))
630      {
631          lg2::error(
632              "openAfMctpTransport: Failed to get pollfd , errno={ERR}/{STR}",
633              "ERR", errno, "STR", strerror(errno));
634          pldmClose();
635          return -1;
636      }
637      pldmFd = pollfd.fd;
638      if (!throttleTraces)
639      {
640          lg2::info("openAfMctpTransport: pldmFd has fd={FD}", "FD", pldmFd);
641      }
642      return 0;
643  }
644  
pldmOpen()645  int Interface::pldmOpen()
646  {
647      if (pldmTransport)
648      {
649          lg2::error("pldmOpen: pldmTransport already setup!, errno={ERR}/{STR}",
650                     "ERR", errno, "STR", strerror(errno));
651          return -1;
652      }
653  #if defined(PLDM_TRANSPORT_WITH_MCTP_DEMUX)
654      return openMctpDemuxTransport();
655  #elif defined(PLDM_TRANSPORT_WITH_AF_MCTP)
656      return openAfMctpTransport();
657  #else
658      lg2::error("pldmOpen: Undefined pldmTransport!, errno={ERR}/{STR}", "ERR",
659                 errno, "STR", strerror(errno));
660      return -1;
661  #endif
662  
663      return 0;
664  }
665  
sendPldm(const std::vector<uint8_t> & request,const uint8_t instance,const bool rspExpected)666  void Interface::sendPldm(const std::vector<uint8_t>& request,
667                           const uint8_t instance, const bool rspExpected)
668  {
669      if (!pldmInstanceID)
670      {
671          lg2::error("sendPldm: No PLDM Instance ID found!");
672          return;
673      }
674  
675      auto rc = pldmOpen();
676      if (rc)
677      {
678          if (!throttleTraces)
679          {
680              lg2::error("sendPldm: pldmOpen failed rc={RC}", "RC", rc);
681          }
682          freePldmInstanceId();
683          return;
684      }
685  
686      pldm_tid_t pldmTID = static_cast<pldm_tid_t>(mctpEid);
687      // Send the PLDM request message to HBRT
688      if (rspExpected)
689      {
690          // Register callback when response is available
691          registerPldmRspCallback();
692  
693          using namespace std::literals::chrono_literals;
694          std::chrono::duration timeout = 8s;
695          if ((msgType == MSG_OCC_RESET) || (msgType == MSG_HRESET))
696          {
697              timeout = 30s;
698          }
699  
700          // Send PLDM request
701          if (!throttleTraces)
702          {
703              lg2::info("sendPldm: calling pldm_transport_send_msg(OCC{INST}, "
704                        "instance:{ID}, {LEN} bytes, timeout {TO})",
705                        "INST", instance, "ID", pldmInstanceID.value(), "LEN",
706                        request.size(), "TO", timeout.count());
707          }
708          pldmResponseReceived = false;
709          pldmResponseTimeout = false;
710          pldmResponseOcc = instance;
711          auto pldmRc = pldm_transport_send_msg(pldmTransport, pldmTID,
712                                                request.data(), request.size());
713          auto sendErrno = errno;
714          if (pldmRc != PLDM_REQUESTER_SUCCESS)
715          {
716              lg2::error(
717                  "sendPldm: pldm_transport_send_msg failed with rc={RC} and errno={ERR}/{STR}",
718                  "RC",
719                  static_cast<std::underlying_type_t<pldm_requester_error_codes>>(
720                      pldmRc),
721                  "ERR", sendErrno, "STR", strerror(sendErrno));
722              pldmClose();
723              return;
724          }
725  
726          // start timer waiting for the response
727          pldmRspTimer.restartOnce(timeout);
728  
729          // Wait for response/timeout
730      }
731      else // not expecting the response
732      {
733          if (!throttleTraces)
734          {
735              lg2::info(
736                  "sendPldm: calling pldm_transport_send_msg(mctpID:{ID}, fd:{FD}, "
737                  "{LEN} bytes) for OCC{INST}",
738                  "ID", mctpEid, "FD", pldmFd, "LEN", request.size(), "INST",
739                  instance);
740          }
741          auto rc = pldm_transport_send_msg(pldmTransport, pldmTID,
742                                            request.data(), request.size());
743          auto sendErrno = errno;
744          if (rc)
745          {
746              lg2::error(
747                  "sendPldm: pldm_transport_send_msg(mctpID:{ID}, fd:{FD}, {LEN} bytes) "
748                  "failed with rc={RC} and errno={ERR}/{STR}",
749                  "ID", mctpEid, "FD", pldmFd, "LEN", request.size(), "RC",
750                  static_cast<std::underlying_type_t<pldm_requester_error_codes>>(
751                      rc),
752                  "ERR", sendErrno, "STR", strerror(sendErrno));
753          }
754          pldmClose();
755      }
756  }
757  
758  // Attaches the FD to event loop and registers the callback handler
registerPldmRspCallback()759  void Interface::registerPldmRspCallback()
760  {
761      decltype(eventSource.get()) sourcePtr = nullptr;
762      int rc = 0;
763      if ((msgType == MSG_OCC_RESET) || (msgType == MSG_HRESET))
764      {
765          rc = sd_event_add_io(event.get(), &sourcePtr, pldmFd, EPOLLIN,
766                               pldmResetCallback, this);
767      }
768      else
769      {
770          rc = sd_event_add_io(event.get(), &sourcePtr, pldmFd, EPOLLIN,
771                               pldmRspCallback, this);
772      }
773      if (rc < 0)
774      {
775          lg2::error(
776              "registerPldmRspCallback: sd_event_add_io: Error({ERR})={STR} : fd={FD} (msgType={MSG})",
777              "ERR", rc, "STR", strerror(-rc), "FD", pldmFd, "MSG", msgType);
778      }
779      else
780      {
781          // puts sourcePtr in the event source.
782          eventSource.reset(sourcePtr);
783      }
784  }
785  
786  // Add a timer to the event loop, default 30s.
pldmRspExpired()787  void Interface::pldmRspExpired()
788  {
789      if (!pldmResponseReceived)
790      {
791          if (!throttleTraces)
792          {
793              lg2::warning(
794                  "pldmRspExpired: timerCallback - timeout waiting for pldm "
795                  "response to msg:{MSG} for OCC{INST}",
796                  "MSG", msgType, "INST", pldmResponseOcc);
797          }
798          pldmResponseTimeout = true;
799          if (pldmFd)
800          {
801              pldmClose();
802          }
803          if (msgType == MSG_OCC_RESET)
804          {
805              // reset not acked, try again
806              lg2::error("pldmRspExpired: retrying reset request for OCC{INST}",
807                         "INST", pldmResponseOcc);
808              resetOCC(pldmResponseOcc);
809          }
810      }
811      return;
812  };
813  
pldmClose()814  void Interface::pldmClose()
815  {
816      freePldmInstanceId();
817      if (pldmRspTimer.isEnabled())
818      {
819          // stop PLDM response timer
820          pldmRspTimer.setEnabled(false);
821      }
822  
823  #if defined(PLDM_TRANSPORT_WITH_MCTP_DEMUX)
824      pldm_transport_mctp_demux_destroy(impl.mctpDemux);
825      impl.mctpDemux = NULL;
826  #elif defined(PLDM_TRANSPORT_WITH_AF_MCTP)
827      pldm_transport_af_mctp_destroy(impl.afMctp);
828      impl.afMctp = NULL;
829  #endif
830      pldmFd = -1;
831      pldmTransport = NULL;
832      eventSource.reset();
833  }
834  
pldmRspCallback(sd_event_source *,int fd,uint32_t revents,void * userData)835  int Interface::pldmRspCallback(sd_event_source* /*es*/,
836                                 __attribute__((unused)) int fd, uint32_t revents,
837                                 void* userData)
838  {
839      if (!(revents & EPOLLIN))
840      {
841          lg2::info("pldmRspCallback - revents={NUM}", "NUM", lg2::hex, revents);
842          return -1;
843      }
844  
845      auto pldmIface = static_cast<Interface*>(userData);
846  
847      if (!pldmIface->pldmInstanceID)
848      {
849          lg2::error("pldmRspCallback: No outstanding PLDM Instance ID found");
850          return -1;
851      }
852  
853      uint8_t* responseMsg = nullptr;
854      size_t responseMsgSize{};
855      pldm_tid_t pldmTID = static_cast<pldm_tid_t>(mctpEid);
856  
857      if (!throttleTraces)
858      {
859          lg2::info(
860              "pldmRspCallback: calling pldm_transport_recv_msg() instance:{INST}",
861              "INST", pldmIface->pldmInstanceID.value());
862      }
863      auto rc = pldm_transport_recv_msg(pldmIface->pldmTransport, &pldmTID,
864                                        (void**)&responseMsg, &responseMsgSize);
865      int lastErrno = errno;
866      if (rc)
867      {
868          if (!throttleTraces)
869          {
870              lg2::error(
871                  "pldmRspCallback: pldm_transport_recv_msg failed with rc={RC}, errno={ERR}/{STR}",
872                  "RC",
873                  static_cast<std::underlying_type_t<pldm_requester_error_codes>>(
874                      rc),
875                  "ERR", lastErrno, "STR", strerror(lastErrno));
876          }
877          return -1;
878      }
879  
880      // We got the response for the PLDM request msg that was sent
881      if (!throttleTraces)
882      {
883          lg2::info(
884              "pldmRspCallback: pldm_transport_recv_msg() rsp was {LEN} bytes",
885              "LEN", responseMsgSize);
886      }
887  
888      if (pldmIface->pldmRspTimer.isEnabled())
889      {
890          // stop PLDM response timer
891          pldmIface->pldmRspTimer.setEnabled(false);
892      }
893  
894      // instance ID should be freed
895      pldmIface->pldmInstanceID = std::nullopt;
896  
897      // Set pointer to autodelete
898      std::unique_ptr<uint8_t, decltype(std::free)*> responseMsgPtr{
899          responseMsg, std::free};
900  
901      auto response = reinterpret_cast<pldm_msg*>(responseMsgPtr.get());
902      if (response->payload[0] != PLDM_SUCCESS)
903      {
904          lg2::error("pldmRspCallback: payload[0] was not success: {STATUS}",
905                     "STATUS", response->payload[0]);
906          pldmIface->pldmClose();
907          return -1;
908      }
909  
910      // Decode the response
911      uint8_t compCode = 0, sensorCount = 1;
912      get_sensor_state_field field[6];
913      responseMsgSize -= sizeof(pldm_msg_hdr);
914      auto msgRc = decode_get_state_sensor_readings_resp(
915          response, responseMsgSize, &compCode, &sensorCount, field);
916      if ((msgRc != PLDM_SUCCESS) || (compCode != PLDM_SUCCESS))
917      {
918          lg2::error(
919              "pldmRspCallback: decode_get_state_sensor_readings failed with rc={RC} and compCode={CC}",
920              "RC", msgRc, "CC", compCode);
921          pldmIface->pldmClose();
922          return -1;
923      }
924  
925      pldmIface->pldmClose();
926  
927      const uint8_t instance = pldmIface->pldmResponseOcc;
928      const uint8_t occSensorState = field[0].present_state;
929      pldmIface->pldmResponseReceived = true;
930  
931      if (occSensorState == PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_IN_SERVICE)
932      {
933          lg2::info("pldmRspCallback: OCC{INST} is RUNNING", "INST", instance);
934          pldmIface->occActiveCallBack(instance, true);
935      }
936      else if (occSensorState ==
937               PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_DORMANT)
938      {
939          lg2::error(
940              "pldmRspCallback: OCC{INST} has now STOPPED and system is in SAFE MODE",
941              "INST", instance);
942  
943          // Setting safe mode true
944          pldmIface->safeModeCallBack(true);
945  
946          pldmIface->occActiveCallBack(instance, false);
947      }
948      else if (occSensorState ==
949               PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_STOPPED)
950      {
951          lg2::info("pldmRspCallback: OCC{INST} is not running", "INST",
952                    instance);
953          pldmIface->occActiveCallBack(instance, false);
954      }
955      else
956      {
957          const size_t rspLength = responseMsgSize + sizeof(pldm_msg_hdr);
958          std::vector<std::uint8_t> pldmResponse(rspLength);
959          memcpy(&pldmResponse[0], reinterpret_cast<std::uint8_t*>(response),
960                 rspLength);
961          if (!throttleTraces)
962          {
963              lg2::warning(
964                  "pldmRspCallback: Unexpected State: {STATE} - PLDM response "
965                  "({LEN} bytes) for OCC{INST}:",
966                  "STATE", occSensorState, "LEN", rspLength, "INST", instance);
967              dump_hex(pldmResponse);
968          }
969      }
970  
971      return 0;
972  };
973  
pldmResetCallback(sd_event_source *,int fd,uint32_t revents,void * userData)974  int Interface::pldmResetCallback(sd_event_source* /*es*/,
975                                   __attribute__((unused)) int fd,
976                                   uint32_t revents, void* userData)
977  {
978      if (!(revents & EPOLLIN))
979      {
980          lg2::info("pldmResetCallback - revents={NUM}", "NUM", lg2::hex,
981                    revents);
982          return -1;
983      }
984  
985      auto pldmIface = static_cast<Interface*>(userData);
986  
987      if (!pldmIface->pldmInstanceID)
988      {
989          lg2::error("pldmResetCallback: No outstanding PLDM Instance ID found");
990          return -1;
991      }
992  
993      uint8_t* responseMsg = nullptr;
994      size_t responseMsgSize{};
995      pldm_tid_t pldmTID = static_cast<pldm_tid_t>(mctpEid);
996  
997      if (!throttleTraces)
998      {
999          lg2::info(
1000              "pldmResetCallback: calling pldm_transport_recv_msg() instance:{ID}",
1001              "ID", pldmIface->pldmInstanceID.value());
1002      }
1003      auto rc = pldm_transport_recv_msg(pldmIface->pldmTransport, &pldmTID,
1004                                        (void**)&responseMsg, &responseMsgSize);
1005      int lastErrno = errno;
1006      if (rc)
1007      {
1008          if (!throttleTraces)
1009          {
1010              lg2::error(
1011                  "pldmResetCallback: pldm_transport_recv_msg failed with rc={RC}, errno={ERR}/{STR}",
1012                  "RC",
1013                  static_cast<std::underlying_type_t<pldm_requester_error_codes>>(
1014                      rc),
1015                  "ERR", lastErrno, "STR", strerror(lastErrno));
1016          }
1017          return -1;
1018      }
1019  
1020      // We got the response for the PLDM request msg that was sent
1021      if (!throttleTraces)
1022      {
1023          lg2::info(
1024              "pldmResetCallback: pldm_transport_recv_msg() rsp was {LEN} bytes",
1025              "LEN", responseMsgSize);
1026      }
1027  
1028      if (pldmIface->pldmRspTimer.isEnabled())
1029      {
1030          // stop PLDM response timer
1031          pldmIface->pldmRspTimer.setEnabled(false);
1032      }
1033  
1034      // instance ID should be freed
1035      pldmIface->pldmInstanceID = std::nullopt;
1036  
1037      // Set pointer to autodelete
1038      std::unique_ptr<uint8_t, decltype(std::free)*> responseMsgPtr{
1039          responseMsg, std::free};
1040  
1041      auto response = reinterpret_cast<pldm_msg*>(responseMsgPtr.get());
1042      if (response->payload[0] != PLDM_SUCCESS)
1043      {
1044          lg2::error(
1045              "pldmResetCallback: Reset FAILED ({MSG}) - payload[0] was not success: {STATUS}",
1046              "MSG", msgType, "STATUS", response->payload[0]);
1047          pldmIface->pldmClose();
1048  
1049          if (msgType == MSG_OCC_RESET)
1050          {
1051              // Retry reset request
1052              lg2::error(
1053                  "pldmResetCallback: retrying reset request for OCC{INST}",
1054                  "INST", resetInstance);
1055              pldmIface->resetOCC(resetInstance);
1056          }
1057          return -1;
1058      }
1059      else
1060      {
1061          lg2::info("pldmResetCallback: Reset has been successfully started");
1062      }
1063  
1064      pldmIface->pldmClose();
1065  
1066      pldmIface->pldmResponseReceived = true;
1067  
1068      return 0;
1069  }
1070  
1071  std::vector<uint8_t>
encodeGetStateSensorRequest(uint8_t instance,uint16_t sensorId)1072      Interface::encodeGetStateSensorRequest(uint8_t instance, uint16_t sensorId)
1073  {
1074      if (!getPldmInstanceId())
1075      {
1076          lg2::error("encodeGetStateSensorRequest: failed to getPldmInstanceId");
1077          return std::vector<uint8_t>();
1078      }
1079  
1080      bitfield8_t sRearm = {0};
1081      const size_t msgSize =
1082          sizeof(pldm_msg_hdr) + PLDM_GET_STATE_SENSOR_READINGS_REQ_BYTES;
1083      std::vector<uint8_t> request(msgSize);
1084  
1085      auto msg = reinterpret_cast<pldm_msg*>(request.data());
1086      auto msgRc = encode_get_state_sensor_readings_req(pldmInstanceID.value(),
1087                                                        sensorId, sRearm, 0, msg);
1088      if (msgRc != PLDM_SUCCESS)
1089      {
1090          lg2::error(
1091              "encodeGetStateSensorRequest: Failed to encode sensorId:{ID} for OCC{INST} (rc={RC})",
1092              "ID", lg2::hex, sensorId, "INST", instance, "RC", msgRc);
1093      }
1094      return request;
1095  }
1096  
1097  // Initiate query of the specified OCC Active Sensor
checkActiveSensor(uint8_t instance)1098  void Interface::checkActiveSensor(uint8_t instance)
1099  {
1100      static bool tracedOnce = false;
1101      if (pldmFd > 0)
1102      {
1103          if (!throttleTraces && !tracedOnce)
1104          {
1105              lg2::warning(
1106                  "checkActiveSensor: already waiting on OCC{INST} (fd={FD})",
1107                  "INST", pldmResponseOcc, "FD", pldmFd);
1108              tracedOnce = true;
1109          }
1110          return;
1111      }
1112      tracedOnce = false;
1113  
1114      if (!isOCCSensorCacheValid())
1115      {
1116          fetchSensorInfo(PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS,
1117                          sensorToOCCInstance, OCCSensorOffset);
1118      }
1119  
1120      // look up sensor id (key) based on instance
1121      auto entry = std::find_if(
1122          sensorToOCCInstance.begin(), sensorToOCCInstance.end(),
1123          [instance](const auto& entry) { return instance == entry.second; });
1124      if (entry != sensorToOCCInstance.end())
1125      {
1126          // Query the OCC Active Sensor state for this instance
1127          if (!throttleTraces)
1128          {
1129              lg2::info("checkActiveSensor: OCC{INST} / sensorID: {ID}", "INST",
1130                        instance, "ID", lg2::hex, entry->first);
1131          }
1132  
1133          // Encode GetStateSensorReadings PLDM message
1134          auto request = encodeGetStateSensorRequest(instance, entry->first);
1135          if (request.empty())
1136          {
1137              return;
1138          }
1139  
1140          // Send request to PLDM and setup callback for response
1141          msgType = MSG_SENSOR_STATUS;
1142          sendPldm(request, instance, true);
1143      }
1144      else
1145      {
1146          if (!throttleTraces)
1147          {
1148              lg2::error(
1149                  "checkActiveSensor: Unable to find PLDM sensor for OCC{INST}",
1150                  "INST", instance);
1151              lg2::info(
1152                  "checkActiveSensor: fetching STATE_SET_OPERATIONAL_RUNNING_STATUS");
1153          }
1154          fetchSensorInfo(PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS,
1155                          sensorToOCCInstance, OCCSensorOffset);
1156      }
1157  }
1158  
setTraceThrottle(const bool throttle)1159  void Interface::setTraceThrottle(const bool throttle)
1160  {
1161      if (throttle != throttleTraces)
1162      {
1163          if (throttle)
1164          {
1165              lg2::warning("PLDM traces being throttled");
1166          }
1167          else
1168          {
1169              lg2::info("PLDM traces no longer being throttled");
1170          }
1171          throttleTraces = throttle;
1172      }
1173  }
1174  
1175  } // namespace pldm
1176