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