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