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