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