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