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