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