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