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