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