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