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