xref: /openbmc/openpower-occ-control/pldm.cpp (revision ee36451ffd21f8f5c79c427236ca53110e6a56b0)
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         // Save header to confirm response matches
738         auto requestPtr = reinterpret_cast<const pldm_msg*>(&request[0]);
739         memcpy(&_requestHeader, requestPtr, sizeof(pldm_msg_hdr));
740 
741         // start timer waiting for the response
742         pldmRspTimer.restartOnce(timeout);
743 
744         // Wait for response/timeout
745     }
746     else // not expecting the response
747     {
748         if (!throttleTraces)
749         {
750             lg2::info(
751                 "sendPldm: calling pldm_transport_send_msg(mctpID:{ID}, fd:{FD}, "
752                 "{LEN} bytes) for OCC{INST}",
753                 "ID", mctpEid, "FD", pldmFd, "LEN", request.size(), "INST",
754                 instance);
755         }
756         auto rc = pldm_transport_send_msg(pldmTransport, pldmTID,
757                                           request.data(), request.size());
758         auto sendErrno = errno;
759         if (rc)
760         {
761             lg2::error(
762                 "sendPldm: pldm_transport_send_msg(mctpID:{ID}, fd:{FD}, {LEN} bytes) "
763                 "failed with rc={RC} and errno={ERR}/{STR}",
764                 "ID", mctpEid, "FD", pldmFd, "LEN", request.size(), "RC",
765                 static_cast<std::underlying_type_t<pldm_requester_error_codes>>(
766                     rc),
767                 "ERR", sendErrno, "STR", strerror(sendErrno));
768         }
769         pldmClose();
770     }
771 }
772 
773 // Attaches the FD to event loop and registers the callback handler
registerPldmRspCallback()774 void Interface::registerPldmRspCallback()
775 {
776     decltype(eventSource.get()) sourcePtr = nullptr;
777     int rc = 0;
778     if ((msgType == MSG_OCC_RESET) || (msgType == MSG_HRESET))
779     {
780         rc = sd_event_add_io(event.get(), &sourcePtr, pldmFd, EPOLLIN,
781                              pldmResetCallback, this);
782     }
783     else
784     {
785         rc = sd_event_add_io(event.get(), &sourcePtr, pldmFd, EPOLLIN,
786                              pldmRspCallback, this);
787     }
788     if (rc < 0)
789     {
790         lg2::error(
791             "registerPldmRspCallback: sd_event_add_io: Error({ERR})={STR} : fd={FD} (msgType={MSG})",
792             "ERR", rc, "STR", strerror(-rc), "FD", pldmFd, "MSG", msgType);
793     }
794     else
795     {
796         // puts sourcePtr in the event source.
797         eventSource.reset(sourcePtr);
798     }
799 }
800 
801 // Add a timer to the event loop, default 30s.
pldmRspExpired()802 void Interface::pldmRspExpired()
803 {
804     if (!pldmResponseReceived)
805     {
806         if (!throttleTraces)
807         {
808             lg2::warning(
809                 "pldmRspExpired: timerCallback - timeout waiting for pldm "
810                 "response to msg:{MSG} for OCC{INST}",
811                 "MSG", msgType, "INST", pldmResponseOcc);
812         }
813         pldmResponseTimeout = true;
814         if (pldmFd)
815         {
816             pldmClose();
817         }
818         if (msgType == MSG_OCC_RESET)
819         {
820             // reset not acked, try again
821             lg2::error("pldmRspExpired: retrying reset request for OCC{INST}",
822                        "INST", pldmResponseOcc);
823             resetOCC(pldmResponseOcc);
824         }
825     }
826     return;
827 };
828 
pldmClose()829 void Interface::pldmClose()
830 {
831     freePldmInstanceId();
832     if (pldmRspTimer.isEnabled())
833     {
834         // stop PLDM response timer
835         pldmRspTimer.setEnabled(false);
836     }
837 
838 #if defined(PLDM_TRANSPORT_WITH_MCTP_DEMUX)
839     pldm_transport_mctp_demux_destroy(impl.mctpDemux);
840     impl.mctpDemux = NULL;
841 #elif defined(PLDM_TRANSPORT_WITH_AF_MCTP)
842     pldm_transport_af_mctp_destroy(impl.afMctp);
843     impl.afMctp = NULL;
844 #endif
845     pldmFd = -1;
846     pldmTransport = NULL;
847     eventSource.reset();
848 }
849 
pldmRspCallback(sd_event_source *,int fd,uint32_t revents,void * userData)850 int Interface::pldmRspCallback(sd_event_source* /*es*/,
851                                __attribute__((unused)) int fd, uint32_t revents,
852                                void* userData)
853 {
854     if (!(revents & EPOLLIN))
855     {
856         lg2::info("pldmRspCallback - revents={NUM}", "NUM", lg2::hex, revents);
857         return -1;
858     }
859 
860     auto pldmIface = static_cast<Interface*>(userData);
861 
862     if (!pldmIface->pldmInstanceID)
863     {
864         lg2::error("pldmRspCallback: No outstanding PLDM Instance ID found");
865         return -1;
866     }
867 
868     uint8_t* responseMsg = nullptr;
869     size_t responseMsgSize{};
870     pldm_tid_t pldmTID = static_cast<pldm_tid_t>(mctpEid);
871 
872     if (!throttleTraces)
873     {
874         lg2::info(
875             "pldmRspCallback: calling pldm_transport_recv_msg() instance:{INST}",
876             "INST", pldmIface->pldmInstanceID.value());
877     }
878     auto rc = pldm_transport_recv_msg(pldmIface->pldmTransport, &pldmTID,
879                                       (void**)&responseMsg, &responseMsgSize);
880     int lastErrno = errno;
881     if (rc != PLDM_REQUESTER_SUCCESS)
882     {
883         if (!throttleTraces)
884         {
885             lg2::error(
886                 "pldmRspCallback: pldm_transport_recv_msg failed with rc={RC}, errno={ERR}/{STR}",
887                 "RC",
888                 static_cast<std::underlying_type_t<pldm_requester_error_codes>>(
889                     rc),
890                 "ERR", lastErrno, "STR", strerror(lastErrno));
891         }
892         return -1;
893     }
894 
895     // Verify the response header matches our request
896     struct pldm_msg_hdr* hdr = (struct pldm_msg_hdr*)responseMsg;
897     if ((pldmTID != mctpEid) ||
898         !pldm_msg_hdr_correlate_response(&pldmIface->_requestHeader, hdr))
899     {
900         // We got a response to someone else's message. Ignore it.
901         return 0;
902     }
903 
904     // We got the response for the PLDM request msg that was sent
905     if (!throttleTraces)
906     {
907         lg2::info(
908             "pldmRspCallback: pldm_transport_recv_msg() rsp was {LEN} bytes",
909             "LEN", responseMsgSize);
910     }
911 
912     if (pldmIface->pldmRspTimer.isEnabled())
913     {
914         // stop PLDM response timer
915         pldmIface->pldmRspTimer.setEnabled(false);
916     }
917 
918     // instance ID will get freed on pldmClose()
919 
920     // Set pointer to autodelete
921     std::unique_ptr<uint8_t, decltype(std::free)*> responseMsgPtr{
922         responseMsg, std::free};
923 
924     auto response = reinterpret_cast<pldm_msg*>(responseMsgPtr.get());
925     if (response->payload[0] != PLDM_SUCCESS)
926     {
927         lg2::error("pldmRspCallback: payload[0] was not success: {STATUS}",
928                    "STATUS", response->payload[0]);
929         pldmIface->pldmClose();
930         return -1;
931     }
932 
933     // Decode the response
934     uint8_t compCode = 0, sensorCount = 1;
935     get_sensor_state_field field[6];
936     responseMsgSize -= sizeof(pldm_msg_hdr);
937     auto msgRc = decode_get_state_sensor_readings_resp(
938         response, responseMsgSize, &compCode, &sensorCount, field);
939     if ((msgRc != PLDM_SUCCESS) || (compCode != PLDM_SUCCESS))
940     {
941         lg2::error(
942             "pldmRspCallback: decode_get_state_sensor_readings failed with rc={RC} and compCode={CC}",
943             "RC", msgRc, "CC", compCode);
944         pldmIface->pldmClose();
945         return -1;
946     }
947 
948     pldmIface->pldmClose();
949 
950     const uint8_t instance = pldmIface->pldmResponseOcc;
951     const uint8_t occSensorState = field[0].present_state;
952     pldmIface->pldmResponseReceived = true;
953 
954     if (occSensorState == PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_IN_SERVICE)
955     {
956         lg2::info("pldmRspCallback: OCC{INST} is RUNNING", "INST", instance);
957         pldmIface->occActiveCallBack(instance, true);
958     }
959     else if (occSensorState ==
960              PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_DORMANT)
961     {
962         lg2::error(
963             "pldmRspCallback: OCC{INST} has now STOPPED and system is in SAFE MODE",
964             "INST", instance);
965 
966         // Setting safe mode true
967         pldmIface->safeModeCallBack(true);
968 
969         pldmIface->occActiveCallBack(instance, false);
970     }
971     else if (occSensorState ==
972              PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS_STOPPED)
973     {
974         lg2::info("pldmRspCallback: OCC{INST} is not running", "INST",
975                   instance);
976         pldmIface->occActiveCallBack(instance, false);
977     }
978     else
979     {
980         const size_t rspLength = responseMsgSize + sizeof(pldm_msg_hdr);
981         std::vector<std::uint8_t> pldmResponse(rspLength);
982         memcpy(&pldmResponse[0], reinterpret_cast<std::uint8_t*>(response),
983                rspLength);
984         if (!throttleTraces)
985         {
986             lg2::warning(
987                 "pldmRspCallback: Unexpected State: {STATE} - PLDM response "
988                 "({LEN} bytes) for OCC{INST}:",
989                 "STATE", occSensorState, "LEN", rspLength, "INST", instance);
990             dump_hex(pldmResponse);
991         }
992     }
993 
994     return 0;
995 };
996 
pldmResetCallback(sd_event_source *,int fd,uint32_t revents,void * userData)997 int Interface::pldmResetCallback(sd_event_source* /*es*/,
998                                  __attribute__((unused)) int fd,
999                                  uint32_t revents, void* userData)
1000 {
1001     if (!(revents & EPOLLIN))
1002     {
1003         lg2::info("pldmResetCallback - revents={NUM}", "NUM", lg2::hex,
1004                   revents);
1005         return -1;
1006     }
1007 
1008     auto pldmIface = static_cast<Interface*>(userData);
1009 
1010     if (!pldmIface->pldmInstanceID)
1011     {
1012         lg2::error("pldmResetCallback: No outstanding PLDM Instance ID found");
1013         return -1;
1014     }
1015 
1016     uint8_t* responseMsg = nullptr;
1017     size_t responseMsgSize{};
1018     pldm_tid_t pldmTID = static_cast<pldm_tid_t>(mctpEid);
1019 
1020     if (!throttleTraces)
1021     {
1022         lg2::info(
1023             "pldmResetCallback: calling pldm_transport_recv_msg() instance:{ID}",
1024             "ID", pldmIface->pldmInstanceID.value());
1025     }
1026     auto rc = pldm_transport_recv_msg(pldmIface->pldmTransport, &pldmTID,
1027                                       (void**)&responseMsg, &responseMsgSize);
1028     int lastErrno = errno;
1029     if (rc != PLDM_REQUESTER_SUCCESS)
1030     {
1031         if (!throttleTraces)
1032         {
1033             lg2::error(
1034                 "pldmResetCallback: pldm_transport_recv_msg failed with rc={RC}, errno={ERR}/{STR}",
1035                 "RC",
1036                 static_cast<std::underlying_type_t<pldm_requester_error_codes>>(
1037                     rc),
1038                 "ERR", lastErrno, "STR", strerror(lastErrno));
1039         }
1040         return -1;
1041     }
1042 
1043     // Verify the response header matches our request
1044     struct pldm_msg_hdr* hdr = (struct pldm_msg_hdr*)responseMsg;
1045     if ((pldmTID != mctpEid) ||
1046         !pldm_msg_hdr_correlate_response(&pldmIface->_requestHeader, hdr))
1047     {
1048         // We got a response to someone else's message. Ignore it.
1049         return 0;
1050     }
1051 
1052     // We got the response for the PLDM request msg that was sent
1053     if (!throttleTraces)
1054     {
1055         lg2::info(
1056             "pldmResetCallback: pldm_transport_recv_msg() rsp was {LEN} bytes",
1057             "LEN", responseMsgSize);
1058     }
1059 
1060     if (pldmIface->pldmRspTimer.isEnabled())
1061     {
1062         // stop PLDM response timer
1063         pldmIface->pldmRspTimer.setEnabled(false);
1064     }
1065 
1066     // instance ID will get freed on pldmClose()
1067 
1068     // Set pointer to autodelete
1069     std::unique_ptr<uint8_t, decltype(std::free)*> responseMsgPtr{
1070         responseMsg, std::free};
1071 
1072     auto response = reinterpret_cast<pldm_msg*>(responseMsgPtr.get());
1073     if (response->payload[0] != PLDM_SUCCESS)
1074     {
1075         lg2::error(
1076             "pldmResetCallback: Reset FAILED ({MSG}) - payload[0] was not success: {STATUS}",
1077             "MSG", msgType, "STATUS", response->payload[0]);
1078         pldmIface->pldmClose();
1079 
1080         if (msgType == MSG_OCC_RESET)
1081         {
1082             // Retry reset request
1083             lg2::error(
1084                 "pldmResetCallback: retrying reset request for OCC{INST}",
1085                 "INST", resetInstance);
1086             pldmIface->resetOCC(resetInstance);
1087         }
1088         return -1;
1089     }
1090     else
1091     {
1092         lg2::info("pldmResetCallback: Reset has been successfully started");
1093     }
1094 
1095     pldmIface->pldmClose();
1096 
1097     pldmIface->pldmResponseReceived = true;
1098 
1099     return 0;
1100 }
1101 
encodeGetStateSensorRequest(uint8_t instance,uint16_t sensorId)1102 std::vector<uint8_t> Interface::encodeGetStateSensorRequest(uint8_t instance,
1103                                                             uint16_t sensorId)
1104 {
1105     if (!getPldmInstanceId())
1106     {
1107         lg2::error("encodeGetStateSensorRequest: failed to getPldmInstanceId");
1108         return std::vector<uint8_t>();
1109     }
1110 
1111     bitfield8_t sRearm = {0};
1112     const size_t msgSize =
1113         sizeof(pldm_msg_hdr) + PLDM_GET_STATE_SENSOR_READINGS_REQ_BYTES;
1114     std::vector<uint8_t> request(msgSize);
1115 
1116     auto msg = reinterpret_cast<pldm_msg*>(request.data());
1117     auto msgRc = encode_get_state_sensor_readings_req(pldmInstanceID.value(),
1118                                                       sensorId, sRearm, 0, msg);
1119     if (msgRc != PLDM_SUCCESS)
1120     {
1121         lg2::error(
1122             "encodeGetStateSensorRequest: Failed to encode sensorId:{ID} for OCC{INST} (rc={RC})",
1123             "ID", lg2::hex, sensorId, "INST", instance, "RC", msgRc);
1124     }
1125     return request;
1126 }
1127 
1128 // Initiate query of the specified OCC Active Sensor
checkActiveSensor(uint8_t instance)1129 void Interface::checkActiveSensor(uint8_t instance)
1130 {
1131     static bool tracedOnce = false;
1132     if (pldmFd > 0)
1133     {
1134         if (!throttleTraces && !tracedOnce)
1135         {
1136             lg2::warning(
1137                 "checkActiveSensor: already waiting on OCC{INST} (fd={FD})",
1138                 "INST", pldmResponseOcc, "FD", pldmFd);
1139             tracedOnce = true;
1140         }
1141         return;
1142     }
1143     tracedOnce = false;
1144 
1145     if (!isOCCSensorCacheValid())
1146     {
1147         fetchSensorInfo(PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS,
1148                         sensorToOCCInstance, OCCSensorOffset);
1149     }
1150 
1151     // look up sensor id (key) based on instance
1152     auto entry = std::find_if(
1153         sensorToOCCInstance.begin(), sensorToOCCInstance.end(),
1154         [instance](const auto& entry) { return instance == entry.second; });
1155     if (entry != sensorToOCCInstance.end())
1156     {
1157         // Query the OCC Active Sensor state for this instance
1158         if (!throttleTraces)
1159         {
1160             lg2::info("checkActiveSensor: OCC{INST} / sensorID: {ID}", "INST",
1161                       instance, "ID", lg2::hex, entry->first);
1162         }
1163 
1164         // Encode GetStateSensorReadings PLDM message
1165         auto request = encodeGetStateSensorRequest(instance, entry->first);
1166         if (request.empty())
1167         {
1168             return;
1169         }
1170 
1171         // Send request to PLDM and setup callback for response
1172         msgType = MSG_SENSOR_STATUS;
1173         sendPldm(request, instance, true);
1174     }
1175     else
1176     {
1177         if (!throttleTraces)
1178         {
1179             lg2::error(
1180                 "checkActiveSensor: Unable to find PLDM sensor for OCC{INST}",
1181                 "INST", instance);
1182             lg2::info(
1183                 "checkActiveSensor: fetching STATE_SET_OPERATIONAL_RUNNING_STATUS");
1184         }
1185         fetchSensorInfo(PLDM_STATE_SET_OPERATIONAL_RUNNING_STATUS,
1186                         sensorToOCCInstance, OCCSensorOffset);
1187     }
1188 }
1189 
setTraceThrottle(const bool throttle)1190 void Interface::setTraceThrottle(const bool throttle)
1191 {
1192     if (throttle != throttleTraces)
1193     {
1194         if (throttle)
1195         {
1196             lg2::warning("PLDM traces being throttled");
1197         }
1198         else
1199         {
1200             lg2::info("PLDM traces no longer being throttled");
1201         }
1202         throttleTraces = throttle;
1203     }
1204 }
1205 
1206 } // namespace pldm
1207