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