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