xref: /openbmc/openpower-occ-control/pldm.cpp (revision d7542c83)
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(
319     uint16_t stateSetId, InstanceToEffecter& instanceToEffecterMap,
320     CompositeEffecterCount& effecterCount, uint8_t& stateIdPos)
321 {
322     PdrList pdrs{};
323 
324     auto& bus = open_power::occ::utils::getBus();
325     try
326     {
327         auto method = bus.new_method_call(
328             "xyz.openbmc_project.PLDM", "/xyz/openbmc_project/pldm",
329             "xyz.openbmc_project.PLDM.PDR", "FindStateEffecterPDR");
330         method.append(tid, static_cast<uint16_t>(PLDM_ENTITY_PROC), stateSetId);
331 
332         auto responseMsg = bus.call(method);
333         responseMsg.read(pdrs);
334     }
335     catch (const sdbusplus::exception_t& e)
336     {
337         log<level::ERR>("pldm: Failed to fetch the state effecter PDRs",
338                         entry("ERROR=%s", e.what()));
339     }
340 
341     if (!pdrs.size())
342     {
343         log<level::ERR>("pldm: state effecter PDRs not present");
344         return;
345     }
346 
347     bool offsetFound = false;
348     auto stateEffecterPDR =
349         reinterpret_cast<const pldm_state_effecter_pdr*>(pdrs.front().data());
350     auto possibleStatesPtr = stateEffecterPDR->possible_states;
351     for (auto offset = 0; offset < stateEffecterPDR->composite_effecter_count;
352          offset++)
353     {
354         auto possibleStates =
355             reinterpret_cast<const state_effecter_possible_states*>(
356                 possibleStatesPtr);
357 
358         if (possibleStates->state_set_id == stateSetId)
359         {
360             stateIdPos = offset;
361             effecterCount = stateEffecterPDR->composite_effecter_count;
362             offsetFound = true;
363             break;
364         }
365         possibleStatesPtr += sizeof(possibleStates->state_set_id) +
366                              sizeof(possibleStates->possible_states_size) +
367                              possibleStates->possible_states_size;
368     }
369 
370     if (!offsetFound)
371     {
372         return;
373     }
374 
375     std::map<uint32_t, EffecterID> entityInstMap{};
376     for (auto& pdr : pdrs)
377     {
378         auto pdrPtr =
379             reinterpret_cast<const pldm_state_effecter_pdr*>(pdr.data());
380         uint32_t key = pdrPtr->effecter_id;
381         entityInstMap.emplace(key, static_cast<SensorID>(pdrPtr->effecter_id));
382     }
383 
384     open_power::occ::instanceID position = start;
385     for (const auto& pair : entityInstMap)
386     {
387         instanceToEffecterMap.emplace(position, pair.second);
388         position++;
389     }
390 }
391 
392 std::vector<uint8_t> Interface::prepareSetEffecterReq(
393     EffecterID effecterId, CompositeEffecterCount effecterCount,
394     uint8_t stateIdPos, uint8_t stateSetValue)
395 {
396     if (!getPldmInstanceId())
397     {
398         return std::vector<uint8_t>();
399     }
400 
401     std::vector<uint8_t> request(
402         sizeof(pldm_msg_hdr) + sizeof(effecterId) + sizeof(effecterCount) +
403         (effecterCount * sizeof(set_effecter_state_field)));
404     auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
405     std::vector<set_effecter_state_field> stateField;
406 
407     for (uint8_t effecterPos = 0; effecterPos < effecterCount; effecterPos++)
408     {
409         if (effecterPos == stateIdPos)
410         {
411             stateField.emplace_back(
412                 set_effecter_state_field{PLDM_REQUEST_SET, stateSetValue});
413         }
414         else
415         {
416             stateField.emplace_back(
417                 set_effecter_state_field{PLDM_NO_CHANGE, 0});
418         }
419     }
420     auto rc = encode_set_state_effecter_states_req(
421         pldmInstanceID.value(), effecterId, effecterCount, stateField.data(),
422         requestMsg);
423     if (rc != PLDM_SUCCESS)
424     {
425         log<level::ERR>("encode set effecter states request returned error ",
426                         entry("RC=%d", rc));
427         request.clear();
428     }
429     return request;
430 }
431 
432 void Interface::resetOCC(open_power::occ::instanceID occInstanceId)
433 {
434     if (open_power::occ::utils::isHostRunning())
435     {
436         if (!isPDREffecterCacheValid())
437         {
438             fetchEffecterInfo(PLDM_STATE_SET_BOOT_RESTART_CAUSE,
439                               occInstanceToEffecter, OCCEffecterCount,
440                               bootRestartPosition);
441         }
442 
443         // Find the matching effecter for the OCC instance
444         auto effecterEntry = occInstanceToEffecter.find(occInstanceId);
445         if (effecterEntry == occInstanceToEffecter.end())
446         {
447             log<level::ERR>(
448                 std::format(
449                     "pldm: Failed to find a matching effecter for OCC instance {}",
450                     occInstanceId)
451                     .c_str());
452 
453             return;
454         }
455 
456         // Prepare the SetStateEffecterStates request to reset the OCC
457         auto request = prepareSetEffecterReq(
458             effecterEntry->second, OCCEffecterCount, bootRestartPosition,
459             PLDM_STATE_SET_BOOT_RESTART_CAUSE_WARM_RESET);
460 
461         if (request.empty())
462         {
463             log<level::ERR>(
464                 "pldm: SetStateEffecterStates OCC reset request empty");
465             return;
466         }
467 
468         // Send request to reset the OCCs/PM Complex (ignore response)
469         sendPldm(request, occInstanceId, false);
470     }
471     else
472     {
473         log<level::ERR>(
474             std::format("resetOCC: HOST is not running (OCC{})", occInstanceId)
475                 .c_str());
476         clearData();
477     }
478 }
479 
480 void Interface::sendHRESET(open_power::occ::instanceID sbeInstanceId)
481 {
482     if (open_power::occ::utils::isHostRunning())
483     {
484         if (sbeInstanceToEffecter.empty())
485         {
486             fetchEffecterInfo(PLDM_OEM_IBM_SBE_MAINTENANCE_STATE,
487                               sbeInstanceToEffecter, SBEEffecterCount,
488                               sbeMaintenanceStatePosition);
489         }
490 
491         auto effecterEntry = sbeInstanceToEffecter.find(sbeInstanceId);
492         if (effecterEntry == sbeInstanceToEffecter.end())
493         {
494             log<level::ERR>(
495                 "pldm: Failed to find a matching effecter for SBE instance",
496                 entry("SBE=%d", sbeInstanceId));
497             return;
498         }
499 
500         // Prepare the SetStateEffecterStates request to HRESET the SBE
501         auto request = prepareSetEffecterReq(
502             effecterEntry->second, SBEEffecterCount,
503             sbeMaintenanceStatePosition, SBE_RETRY_REQUIRED);
504 
505         if (request.empty())
506         {
507             log<level::ERR>(
508                 "pldm: SetStateEffecterStates HRESET request empty");
509             return;
510         }
511 
512         // Send request to issue HRESET of SBE (ignore response)
513         sendPldm(request, sbeInstanceId, false);
514         outstandingHResets.insert(sbeInstanceId);
515     }
516     else
517     {
518         log<level::ERR>(std::format("sendHRESET: HOST is not running (OCC{})",
519                                     sbeInstanceId)
520                             .c_str());
521         clearData();
522     }
523 }
524 
525 bool Interface::getPldmInstanceId()
526 {
527     pldm_instance_id_t id;
528     if (!pldmInstanceID)
529     {
530         // Request new instance ID
531         int rc = pldm_instance_id_alloc(pldmInstanceIdDb, tid, &id);
532         if (rc == -EAGAIN)
533         {
534             std::this_thread::sleep_for(std::chrono::milliseconds(100));
535             rc = pldm_instance_id_alloc(pldmInstanceIdDb, tid, &id);
536         }
537 
538         if (rc)
539         {
540             log<level::ERR>(
541                 std::format(
542                     "getPldmInstanceId: Failed to alloc ID for TID {}. RC{}",
543                     tid, rc)
544                     .c_str());
545             return false;
546         }
547         pldmInstanceID.emplace(id);
548         if (!throttleTraces)
549         {
550             log<level::INFO>(
551                 std::format("got id {} and set PldmInstanceId to {}", id,
552                             pldmInstanceID.value())
553                     .c_str());
554         }
555     }
556     return true;
557 }
558 
559 void Interface::freePldmInstanceId()
560 {
561     if (pldmInstanceID)
562     {
563         int rc = pldm_instance_id_free(pldmInstanceIdDb, tid,
564                                        pldmInstanceID.value());
565         if (rc)
566         {
567             log<level::ERR>(
568                 std::format(
569                     "freePldmInstanceId: Failed to free ID {} for TID {}. RC{}",
570                     pldmInstanceID.value(), tid, rc)
571                     .c_str());
572             return;
573         }
574         if (!throttleTraces)
575         {
576             log<level::INFO>(
577                 std::format("Freed PLDM instance ID {}", pldmInstanceID.value())
578                     .c_str());
579         }
580         pldmInstanceID = std::nullopt;
581     }
582 }
583 
584 int Interface::openMctpDemuxTransport()
585 {
586     mctpDemux = nullptr;
587     int rc = pldm_transport_mctp_demux_init(&mctpDemux);
588     if (rc)
589     {
590         log<level::ERR>(
591             std::format(
592                 "openMctpDemuxTransport: Failed to init MCTP demux transport, errno={}/{}",
593                 rc, strerror(rc))
594                 .c_str());
595         return -1;
596     }
597 
598     if (pldm_transport_mctp_demux_map_tid(mctpDemux, mctpEid, mctpEid))
599     {
600         log<level::ERR>(
601             std::format(
602                 "openMctpDemuxTransport: Failed to setup tid to eid mapping, errno={}/{}",
603                 errno, strerror(errno))
604                 .c_str());
605         pldmClose();
606         return -1;
607     }
608     pldmTransport = pldm_transport_mctp_demux_core(mctpDemux);
609 
610     struct pollfd pollfd;
611     if (pldm_transport_mctp_demux_init_pollfd(pldmTransport, &pollfd))
612     {
613         log<level::ERR>(
614             std::format(
615                 "openMctpDemuxTransport: Failed to get pollfd , errno={}/{}",
616                 errno, strerror(errno))
617                 .c_str());
618         pldmClose();
619         return -1;
620     }
621     pldmFd = pollfd.fd;
622     if (!throttleTraces)
623     {
624         log<level::INFO>(
625             std::format("openMctpDemuxTransport: pldmFd has fd={}", pldmFd)
626                 .c_str());
627     }
628     return 0;
629 }
630 
631 int Interface::pldmOpen()
632 {
633     if (pldmTransport)
634     {
635         log<level::ERR>(
636             std::format("pldmOpen: pldmTransport already setup!, errno={}/{}",
637                         errno, strerror(errno))
638                 .c_str());
639         return -1;
640     }
641     return openMctpDemuxTransport();
642 }
643 
644 void Interface::sendPldm(const std::vector<uint8_t>& request,
645                          const uint8_t instance, const bool rspExpected)
646 {
647     if (!pldmInstanceID)
648     {
649         log<level::ERR>("sendPldm: No PLDM Instance ID found!");
650         return;
651     }
652 
653     auto rc = pldmOpen();
654     if (rc)
655     {
656         log<level::ERR>(
657             std::format("sendPldm: pldmOpen failed rc={}", rc).c_str());
658         freePldmInstanceId();
659         return;
660     }
661 
662     pldm_tid_t pldmTID = static_cast<pldm_tid_t>(mctpEid);
663     // Send the PLDM request message to HBRT
664     if (rspExpected)
665     {
666         // Register callback when response is available
667         registerPldmRspCallback();
668 
669         // Send PLDM request
670         if (!throttleTraces)
671         {
672             log<level::INFO>(
673                 std::format(
674                     "sendPldm: calling pldm_transport_send_msg(OCC{}, instance:{}, {} bytes)",
675                     instance, pldmInstanceID.value(), request.size())
676                     .c_str());
677         }
678         pldmResponseReceived = false;
679         pldmResponseTimeout = false;
680         pldmResponseOcc = instance;
681         auto pldmRc = pldm_transport_send_msg(pldmTransport, pldmTID,
682                                               request.data(), request.size());
683         auto sendErrno = errno;
684         if (pldmRc != PLDM_REQUESTER_SUCCESS)
685         {
686             log<level::ERR>(
687                 std::format(
688                     "sendPldm: pldm_transport_send_msg failed with rc={} and errno={}/{}",
689                     static_cast<
690                         std::underlying_type_t<pldm_requester_error_codes>>(
691                         pldmRc),
692                     sendErrno, strerror(sendErrno))
693                     .c_str());
694             pldmClose();
695             return;
696         }
697 
698         // start timer waiting for the response
699         using namespace std::literals::chrono_literals;
700         pldmRspTimer.restartOnce(8s);
701 
702         // Wait for response/timeout
703     }
704     else // not expecting the response
705     {
706         if (!throttleTraces)
707         {
708             log<level::INFO>(
709                 std::format(
710                     "sendPldm: calling pldm_transport_send_msg(mctpID:{}, fd:{}, {} bytes) for OCC{}",
711                     mctpEid, pldmFd, request.size(), instance)
712                     .c_str());
713         }
714         auto rc = pldm_transport_send_msg(pldmTransport, pldmTID,
715                                           request.data(), request.size());
716         auto sendErrno = errno;
717         if (rc)
718         {
719             log<level::ERR>(
720                 std::format(
721                     "sendPldm: pldm_transport_send_msg(mctpID:{}, fd:{}, {} bytes) failed with rc={} and errno={}/{}",
722                     mctpEid, pldmFd, request.size(),
723                     static_cast<
724                         std::underlying_type_t<pldm_requester_error_codes>>(rc),
725                     sendErrno, strerror(sendErrno))
726                     .c_str());
727         }
728         pldmClose();
729     }
730 }
731 
732 // Attaches the FD to event loop and registers the callback handler
733 void Interface::registerPldmRspCallback()
734 {
735     decltype(eventSource.get()) sourcePtr = nullptr;
736     auto rc = sd_event_add_io(event.get(), &sourcePtr, pldmFd, EPOLLIN,
737                               pldmRspCallback, this);
738     if (rc < 0)
739     {
740         log<level::ERR>(
741             std::format(
742                 "registerPldmRspCallback: sd_event_add_io: Error({})={} : fd={}",
743                 rc, strerror(-rc), pldmFd)
744                 .c_str());
745     }
746     else
747     {
748         // puts sourcePtr in the event source.
749         eventSource.reset(sourcePtr);
750     }
751 }
752 
753 // Add a timer to the event loop, default 30s.
754 void Interface::pldmRspExpired()
755 {
756     if (!pldmResponseReceived)
757     {
758         if (!throttleTraces)
759         {
760             log<level::WARNING>(
761                 std::format(
762                     "pldmRspExpired: timerCallback - timeout waiting for pldm response for OCC{}",
763                     pldmResponseOcc)
764                     .c_str());
765         }
766         pldmResponseTimeout = true;
767         if (pldmFd)
768         {
769             pldmClose();
770         }
771     }
772     return;
773 };
774 
775 void Interface::pldmClose()
776 {
777     freePldmInstanceId();
778     if (pldmRspTimer.isEnabled())
779     {
780         // stop PLDM response timer
781         pldmRspTimer.setEnabled(false);
782     }
783 
784     pldm_transport_mctp_demux_destroy(mctpDemux);
785     mctpDemux = NULL;
786     pldmFd = -1;
787     pldmTransport = NULL;
788     eventSource.reset();
789 }
790 
791 int Interface::pldmRspCallback(sd_event_source* /*es*/,
792                                __attribute__((unused)) int fd, uint32_t revents,
793                                void* userData)
794 {
795     if (!(revents & EPOLLIN))
796     {
797         log<level::INFO>(
798             std::format("pldmRspCallback - revents={:08X}", revents).c_str());
799         return -1;
800     }
801 
802     auto pldmIface = static_cast<Interface*>(userData);
803 
804     if (!pldmIface->pldmInstanceID)
805     {
806         log<level::ERR>(
807             "pldmRspCallback: No outstanding PLDM Instance ID found");
808         return -1;
809     }
810 
811     uint8_t* responseMsg = nullptr;
812     size_t responseMsgSize{};
813     pldm_tid_t pldmTID = static_cast<pldm_tid_t>(mctpEid);
814 
815     if (!throttleTraces)
816     {
817         log<level::INFO>(
818             std::format(
819                 "pldmRspCallback: calling pldm_transport_recv_msg() instance:{}",
820                 pldmIface->pldmInstanceID.value())
821                 .c_str());
822     }
823     auto rc = pldm_transport_recv_msg(pldmIface->pldmTransport, &pldmTID,
824                                       (void**)&responseMsg, &responseMsgSize);
825     int lastErrno = errno;
826     if (rc)
827     {
828         if (!throttleTraces)
829         {
830             log<level::ERR>(
831                 std::format(
832                     "pldmRspCallback: pldm_transport_recv_msg failed with rc={}, errno={}/{}",
833                     static_cast<
834                         std::underlying_type_t<pldm_requester_error_codes>>(rc),
835                     lastErrno, strerror(lastErrno))
836                     .c_str());
837         }
838         return -1;
839     }
840 
841     // We got the response for the PLDM request msg that was sent
842     if (!throttleTraces)
843     {
844         log<level::INFO>(
845             std::format(
846                 "pldmRspCallback: pldm_transport_recv_msg() rsp was {} bytes",
847                 responseMsgSize)
848                 .c_str());
849     }
850 
851     if (pldmIface->pldmRspTimer.isEnabled())
852     {
853         // stop PLDM response timer
854         pldmIface->pldmRspTimer.setEnabled(false);
855     }
856 
857     // instance ID should be freed
858     pldmIface->pldmInstanceID = std::nullopt;
859 
860     // Set pointer to autodelete
861     std::unique_ptr<uint8_t, decltype(std::free)*> responseMsgPtr{
862         responseMsg, std::free};
863 
864     auto response = reinterpret_cast<pldm_msg*>(responseMsgPtr.get());
865     if (response->payload[0] != PLDM_SUCCESS)
866     {
867         log<level::ERR>(
868             std::format("pldmRspCallback: payload[0] was not success: {}",
869                         response->payload[0])
870                 .c_str());
871         pldmIface->pldmClose();
872         return -1;
873     }
874 
875     // Decode the response
876     uint8_t compCode = 0, sensorCount = 1;
877     get_sensor_state_field field[6];
878     responseMsgSize -= sizeof(pldm_msg_hdr);
879     auto msgRc = decode_get_state_sensor_readings_resp(
880         response, responseMsgSize, &compCode, &sensorCount, field);
881     if ((msgRc != PLDM_SUCCESS) || (compCode != PLDM_SUCCESS))
882     {
883         log<level::ERR>(
884             std::format(
885                 "pldmRspCallback: decode_get_state_sensor_readings failed with rc={} and compCode={}",
886                 msgRc, compCode)
887                 .c_str());
888         pldmIface->pldmClose();
889         return -1;
890     }
891 
892     pldmIface->pldmClose();
893 
894     const uint8_t instance = pldmIface->pldmResponseOcc;
895     const uint8_t occSensorState = field[0].present_state;
896     pldmIface->pldmResponseReceived = true;
897 
898     if (occSensorState == PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_IN_SERVICE)
899     {
900         log<level::INFO>(
901             std::format("pldmRspCallback: OCC{} is RUNNING", instance).c_str());
902         pldmIface->callBack(instance, true);
903     }
904     else if (occSensorState ==
905              PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_DORMANT)
906     {
907         log<level::ERR>(
908             std::format(
909                 "pldmRspCallback: OCC{} has now STOPPED and system is in SAFE MODE",
910                 instance)
911                 .c_str());
912 
913         // Setting safe mode true
914         pldmIface->safeModeCallBack(true);
915 
916         pldmIface->callBack(instance, false);
917     }
918     else if (occSensorState ==
919              PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_STOPPED)
920     {
921         log<level::INFO>(
922             std::format("pldmRspCallback: OCC{} is not running", instance)
923                 .c_str());
924         pldmIface->callBack(instance, false);
925     }
926     else
927     {
928         const size_t rspLength = responseMsgSize + sizeof(pldm_msg_hdr);
929         std::vector<std::uint8_t> pldmResponse(rspLength);
930         memcpy(&pldmResponse[0], reinterpret_cast<std::uint8_t*>(response),
931                rspLength);
932         if (!throttleTraces)
933         {
934             log<level::WARNING>(
935                 std::format(
936                     "pldmRspCallback: Unexpected State: {} - PLDM response ({} bytes) for OCC{}:",
937                     occSensorState, rspLength, instance)
938                     .c_str());
939             dump_hex(pldmResponse);
940         }
941     }
942 
943     return 0;
944 };
945 
946 std::vector<uint8_t>
947     Interface::encodeGetStateSensorRequest(uint8_t instance, uint16_t sensorId)
948 {
949     if (!getPldmInstanceId())
950     {
951         log<level::ERR>(
952             "encodeGetStateSensorRequest: failed to getPldmInstanceId");
953         return std::vector<uint8_t>();
954     }
955 
956     bitfield8_t sRearm = {0};
957     const size_t msgSize =
958         sizeof(pldm_msg_hdr) + PLDM_GET_STATE_SENSOR_READINGS_REQ_BYTES;
959     std::vector<uint8_t> request(msgSize);
960 
961     auto msg = reinterpret_cast<pldm_msg*>(request.data());
962     auto msgRc = encode_get_state_sensor_readings_req(pldmInstanceID.value(),
963                                                       sensorId, sRearm, 0, msg);
964     if (msgRc != PLDM_SUCCESS)
965     {
966         log<level::ERR>(
967             std::format(
968                 "encodeGetStateSensorRequest: Failed to encode sensorId:0x{:08X} for OCC{} (rc={})",
969                 sensorId, instance, msgRc)
970                 .c_str());
971     }
972     return request;
973 }
974 
975 // Initiate query of the specified OCC Active Sensor
976 void Interface::checkActiveSensor(uint8_t instance)
977 {
978     static bool tracedOnce = false;
979     if (pldmFd > 0)
980     {
981         if (!throttleTraces && !tracedOnce)
982         {
983             log<level::WARNING>(
984                 std::format(
985                     "checkActiveSensor: already waiting on OCC{} (fd={})",
986                     pldmResponseOcc, pldmFd)
987                     .c_str());
988             tracedOnce = true;
989         }
990         return;
991     }
992     tracedOnce = false;
993 
994     if (!isOCCSensorCacheValid())
995     {
996         fetchSensorInfo(PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS,
997                         sensorToOCCInstance, OCCSensorOffset);
998     }
999 
1000     // look up sensor id (key) based on instance
1001     auto entry = std::find_if(
1002         sensorToOCCInstance.begin(), sensorToOCCInstance.end(),
1003         [instance](const auto& entry) { return instance == entry.second; });
1004     if (entry != sensorToOCCInstance.end())
1005     {
1006         // Query the OCC Active Sensor state for this instance
1007         if (!throttleTraces)
1008         {
1009             log<level::INFO>(
1010                 std::format("checkActiveSensor: OCC{} / sensorID: 0x{:04X}",
1011                             instance, entry->first)
1012                     .c_str());
1013         }
1014 
1015         // Encode GetStateSensorReadings PLDM message
1016         auto request = encodeGetStateSensorRequest(instance, entry->first);
1017         if (request.empty())
1018         {
1019             return;
1020         }
1021 
1022         // Send request to PLDM and setup callback for response
1023         sendPldm(request, instance, true);
1024     }
1025     else
1026     {
1027         if (!throttleTraces)
1028         {
1029             log<level::ERR>(
1030                 std::format(
1031                     "checkActiveSensor: Unable to find PLDM sensor for OCC{}",
1032                     instance)
1033                     .c_str());
1034             log<level::INFO>(
1035                 "checkActiveSensor: fetching STATE_SET_OPERATIONAL_RUNNING_STATUS");
1036         }
1037         fetchSensorInfo(PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS,
1038                         sensorToOCCInstance, OCCSensorOffset);
1039     }
1040 }
1041 
1042 void Interface::setTraceThrottle(const bool throttle)
1043 {
1044     if (throttle != throttleTraces)
1045     {
1046         if (throttle)
1047         {
1048             log<level::WARNING>("PLDM traces being throttled");
1049         }
1050         else
1051         {
1052             log<level::INFO>("PLDM traces no longer being throttled");
1053         }
1054         throttleTraces = throttle;
1055     }
1056 }
1057 
1058 } // namespace pldm
1059