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