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