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