blob: daa28c49345bc3da44028acd4a810ebca226a9cd [file] [log] [blame]
Pavithra Barithaya9b1a0b62024-10-14 19:16:11 +05301#include "config.h"
2
Andrew Jefferya0010202024-06-27 10:49:11 +00003#include <libpldm/oem/ibm/state_set.h>
Ben Tynerbb90afc2022-12-14 20:50:33 -06004#include <libpldm/platform.h>
5#include <libpldm/pldm.h>
Pavithra Barithaya9b1a0b62024-10-14 19:16:11 +05306#include <libpldm/transport.h>
7#include <libpldm/transport/mctp-demux.h>
8#include <poll.h>
Ben Tynerbb90afc2022-12-14 20:50:33 -06009
10#include <util/dbus.hpp>
Pavithra Barithaya36b043e2024-10-14 15:37:53 +053011#include <util/pldm.hpp>
Ben Tynerbb90afc2022-12-14 20:50:33 -060012#include <util/trace.hpp>
13
14namespace util
15{
16namespace pldm
17{
Pavithra Barithaya36b043e2024-10-14 15:37:53 +053018
19class PLDMInstanceManager
20{
21 public:
22 // Singleton access method
23 static PLDMInstanceManager& getInstance()
24 {
25 static PLDMInstanceManager instance;
26 return instance;
27 }
28
29 bool getPldmInstanceID(uint8_t& pldmInstance, uint8_t tid);
30 void freePLDMInstanceID(pldm_instance_id_t instanceID, uint8_t tid);
31
Pavithra Barithaya9b1a0b62024-10-14 19:16:11 +053032 /**
33 * @brief setup PLDM transport for sending and receiving messages
34 *
35 * @param[in] eid - MCTP endpoint ID
36 * @return file descriptor on success and throw
37 * exception (xyz::openbmc_project::Common::Error::NotAllowed) on
38 * failures.
39 */
40 int openPLDM(mctp_eid_t eid);
41 /** @brief Opens the MCTP socket for sending and receiving messages.
42 *
43 * @param[in] eid - MCTP endpoint ID
44 */
45 int openMctpDemuxTransport(mctp_eid_t eid);
46
47 /** @brief Close the PLDM file */
48 void closePLDM();
49
50 /** @brief sending PLDM file */
51 bool sendPldm(const std::vector<uint8_t>& request, uint8_t mctpEid);
52
Pavithra Barithaya36b043e2024-10-14 15:37:53 +053053 private:
54 // Private constructor and destructor to prevent creating multiple instances
55 PLDMInstanceManager();
56 ~PLDMInstanceManager();
57
58 // Deleted copy constructor and assignment operator to prevent copying
59 PLDMInstanceManager(const PLDMInstanceManager&) = delete;
60 PLDMInstanceManager& operator=(const PLDMInstanceManager&) = delete;
61
62 // Private member for the instance database
63 pldm_instance_db* pldmInstanceIdDb;
Pavithra Barithaya9b1a0b62024-10-14 19:16:11 +053064
65 /** pldm transport instance */
66 struct pldm_transport* pldmTransport = NULL;
67
68 pldm_transport_mctp_demux* mctpDemux;
Pavithra Barithaya36b043e2024-10-14 15:37:53 +053069};
70
71PLDMInstanceManager::PLDMInstanceManager() : pldmInstanceIdDb(nullptr)
72{
73 // Initialize the database object directly in the constructor
74 auto rc = pldm_instance_db_init_default(&pldmInstanceIdDb);
75 if (rc)
76 {
77 trace::err("Error calling pldm_instance_db_init_default, rc = %d",
78 (unsigned)rc);
79 }
80}
81
82PLDMInstanceManager::~PLDMInstanceManager()
83{
84 // Directly destroy the database object in the destructor
85 if (pldmInstanceIdDb)
86 {
87 auto rc = pldm_instance_db_destroy(pldmInstanceIdDb);
88 if (rc)
89 {
90 trace::err("pldm_instance_db_destroy failed rc = %d", (unsigned)rc);
91 }
92 }
93}
94
95// Get the PLDM instance ID for the given terminus ID
96bool PLDMInstanceManager::getPldmInstanceID(uint8_t& pldmInstance, uint8_t tid)
97{
98 pldm_instance_id_t id;
99 int rc = pldm_instance_id_alloc(pldmInstanceIdDb, tid, &id);
100 if (rc == -EAGAIN)
101 {
102 std::this_thread::sleep_for(
103 std::chrono::milliseconds(100)); // Retry after 100ms
104 rc = pldm_instance_id_alloc(pldmInstanceIdDb, tid,
105 &id); // Retry allocation
106 }
107
108 if (rc)
109 {
110 trace::err("getPldmInstanceId: Failed to alloc ID for TID = %d, RC= %d",
111 (unsigned)tid, (unsigned)rc);
112 return false;
113 }
114
115 pldmInstance = id; // Return the allocated instance ID
116 trace::inf("Got instanceId: %d, for PLDM TID: %d", (unsigned)pldmInstance,
117 (unsigned)tid);
118 return true;
119}
120
121// Free the PLDM instance ID associated with the terminus ID
122void PLDMInstanceManager::freePLDMInstanceID(pldm_instance_id_t instanceID,
123 uint8_t tid)
124{
125 int rc = pldm_instance_id_free(pldmInstanceIdDb, tid, instanceID);
126 if (rc)
127 {
128 trace::err(
129 "pldm_instance_id_free failed to free id=%d of TID=%d with rc= %d",
130 (unsigned)instanceID, (unsigned)tid, (unsigned)rc);
131 }
132}
133
Pavithra Barithaya9b1a0b62024-10-14 19:16:11 +0530134int PLDMInstanceManager::openPLDM(mctp_eid_t eid)
135{
136 auto fd = -1;
137 if (pldmTransport)
138 {
139 trace::inf("open: pldmTransport already setup!");
140 return fd;
141 }
142 fd = openMctpDemuxTransport(eid);
143 if (fd < 0)
144 {
145 auto e = errno;
146 trace::err("openPLDM failed, fd = %d and error= %d", (unsigned)fd, e);
147 }
148 return fd;
149}
150
151int PLDMInstanceManager::openMctpDemuxTransport(mctp_eid_t eid)
152{
153 int rc = pldm_transport_mctp_demux_init(&mctpDemux);
154 if (rc)
155 {
156 trace::err(
157 "openMctpDemuxTransport: Failed to setup tid to eid mapping. rc = %d",
158 (unsigned)rc);
159 closePLDM();
160 return rc;
161 }
162
163 rc = pldm_transport_mctp_demux_map_tid(mctpDemux, eid, eid);
164 if (rc)
165 {
166 trace::err(
167 "openMctpDemuxTransport: Failed to setup tid to eid mapping. rc = %d",
168 (unsigned)rc);
169 closePLDM();
170 return rc;
171 }
172
173 pldmTransport = pldm_transport_mctp_demux_core(mctpDemux);
174 struct pollfd pollfd;
175 rc = pldm_transport_mctp_demux_init_pollfd(pldmTransport, &pollfd);
176 if (rc)
177 {
178 trace::err("openMctpDemuxTransport: Failed to get pollfd. rc= %d",
179 (unsigned)rc);
180 closePLDM();
181 return rc;
182 }
183 return pollfd.fd;
184}
185void PLDMInstanceManager::closePLDM()
186{
187 pldm_transport_mctp_demux_destroy(mctpDemux);
188 mctpDemux = NULL;
189 pldmTransport = NULL;
190}
191
Ben Tynerbb90afc2022-12-14 20:50:33 -0600192/** @brief Send PLDM request
193 *
194 * @param[in] request - the request data
195 * @param[in] mcptEid - the mctp endpoint ID
Ben Tynerbb90afc2022-12-14 20:50:33 -0600196 *
197 * @pre a mctp instance must have been
198 * @return true if send is successful false otherwise
199 */
Pavithra Barithaya9b1a0b62024-10-14 19:16:11 +0530200bool PLDMInstanceManager::sendPldm(const std::vector<uint8_t>& request,
201 uint8_t mctpEid)
Ben Tynerbb90afc2022-12-14 20:50:33 -0600202{
Pavithra Barithaya9b1a0b62024-10-14 19:16:11 +0530203 auto rc = openPLDM(mctpEid);
204 if (rc)
Ben Tynerbb90afc2022-12-14 20:50:33 -0600205 {
206 trace::err("failed to connect to pldm");
207 return false;
208 }
209
Pavithra Barithaya9b1a0b62024-10-14 19:16:11 +0530210 pldm_tid_t pldmTID = static_cast<pldm_tid_t>(mctpEid);
Ben Tynerbb90afc2022-12-14 20:50:33 -0600211 // send PLDM request
Pavithra Barithaya9b1a0b62024-10-14 19:16:11 +0530212 auto pldmRc = pldm_transport_send_msg(pldmTransport, pldmTID,
213 request.data(), request.size());
Ben Tynerbb90afc2022-12-14 20:50:33 -0600214
215 trace::inf("sent pldm request");
216
217 return pldmRc == PLDM_REQUESTER_SUCCESS ? true : false;
218}
219
220/** @brief Prepare a request for SetStateEffecterStates
221 *
222 * @param[in] effecterId - the effecter ID
223 * @param[in] effecterCount - composite effecter count
224 * @param[in] stateIdPos - position of the state set
225 * @param[in] stateSetValue - the value to set the state
226 * @param[in] mcptEid - the MCTP endpoint ID
227 *
228 * @return PLDM request message to be sent to host, empty message on error
229 */
Patrick Williamsa0c724d2024-08-16 15:21:54 -0400230std::vector<uint8_t> prepareSetEffecterReq(
231 uint16_t effecterId, uint8_t effecterCount, uint8_t stateIdPos,
232 uint8_t stateSetValue, uint8_t mctpEid)
Ben Tynerbb90afc2022-12-14 20:50:33 -0600233{
Pavithra Barithaya36b043e2024-10-14 15:37:53 +0530234 PLDMInstanceManager& manager = PLDMInstanceManager::getInstance();
235
Pavithra Barithaya08f25b22024-10-14 14:38:57 +0530236 // get pldm instance associated with the endpoint ID
237 uint8_t pldmInstanceID;
Pavithra Barithaya36b043e2024-10-14 15:37:53 +0530238 if (!manager.getPldmInstanceID(pldmInstanceID, mctpEid))
Ben Tynerbb90afc2022-12-14 20:50:33 -0600239 {
240 return std::vector<uint8_t>();
241 }
242
243 // form the request message
244 std::vector<uint8_t> request(
245 sizeof(pldm_msg_hdr) + sizeof(effecterId) + sizeof(effecterCount) +
246 (effecterCount * sizeof(set_effecter_state_field)));
247
248 // encode the state data with the change we want to elicit
249 std::vector<set_effecter_state_field> stateField;
250 for (uint8_t effecterPos = 0; effecterPos < effecterCount; effecterPos++)
251 {
252 if (effecterPos == stateIdPos)
253 {
254 stateField.emplace_back(
255 set_effecter_state_field{PLDM_REQUEST_SET, stateSetValue});
256 }
257 else
258 {
259 stateField.emplace_back(
260 set_effecter_state_field{PLDM_NO_CHANGE, 0});
261 }
262 }
263
264 // encode the message with state data
265 auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
Patrick Williams27dd6362023-05-10 07:51:20 -0500266 auto rc = encode_set_state_effecter_states_req(
Pavithra Barithaya08f25b22024-10-14 14:38:57 +0530267 pldmInstanceID, effecterId, effecterCount, stateField.data(),
268 requestMsg);
Ben Tynerbb90afc2022-12-14 20:50:33 -0600269
270 if (rc != PLDM_SUCCESS)
271 {
272 trace::err("encode set effecter states request failed");
Pavithra Barithaya36b043e2024-10-14 15:37:53 +0530273 manager.freePLDMInstanceID(pldmInstanceID, mctpEid);
Ben Tynerbb90afc2022-12-14 20:50:33 -0600274 request.clear();
275 }
276
277 return request;
278}
279
280/** @brief Return map of sensor ID to SBE instance
281 *
282 * @param[in] stateSetId - the state set ID of interest
283 * @param[out] sensorInstanceMap - map of sensor to SBE instance
284 * @param[out] sensorOffset - position of sensor with state set ID within map
285 *
286 * @return true if sensor info is available false otherwise
287 */
288bool fetchSensorInfo(uint16_t stateSetId,
289 std::map<uint16_t, unsigned int>& sensorInstanceMap,
290 uint8_t& sensorOffset)
291{
292 // get state sensor PDRs
293 std::vector<std::vector<uint8_t>> pdrs{};
294 if (!util::dbus::getStateSensorPdrs(pdrs, stateSetId))
295 {
296 return false;
297 }
298
299 // check for any PDRs available
300 if (!pdrs.size())
301 {
302 trace::err("state sensor PDRs not present");
303 return false;
304 }
305
306 // find the offset of specified sensor withing PDRs
307 bool offsetFound = false;
308 auto stateSensorPDR =
309 reinterpret_cast<const pldm_state_sensor_pdr*>(pdrs.front().data());
310 auto possibleStatesPtr = stateSensorPDR->possible_states;
311
312 for (auto offset = 0; offset < stateSensorPDR->composite_sensor_count;
313 offset++)
314 {
315 auto possibleStates =
316 reinterpret_cast<const state_sensor_possible_states*>(
317 possibleStatesPtr);
318
319 if (possibleStates->state_set_id == stateSetId)
320 {
321 sensorOffset = offset;
Patrick Williams27dd6362023-05-10 07:51:20 -0500322 offsetFound = true;
Ben Tynerbb90afc2022-12-14 20:50:33 -0600323 break;
324 }
325 possibleStatesPtr += sizeof(possibleStates->state_set_id) +
326 sizeof(possibleStates->possible_states_size) +
327 possibleStates->possible_states_size;
328 }
329
330 if (!offsetFound)
331 {
332 trace::err("state sensor not found");
333 return false;
334 }
335
336 // map sensor ID to equivelent 16 bit value
337 std::map<uint32_t, uint16_t> entityInstMap{};
338 for (auto& pdr : pdrs)
339 {
340 auto pdrPtr =
341 reinterpret_cast<const pldm_state_sensor_pdr*>(pdr.data());
342 uint32_t key = pdrPtr->sensor_id;
343 entityInstMap.emplace(key, static_cast<uint16_t>(pdrPtr->sensor_id));
344 }
345
346 // map sensor ID to zero based SBE instance
347 unsigned int position = 0;
Patrick Williams27dd6362023-05-10 07:51:20 -0500348 for (const auto& pair : entityInstMap)
Ben Tynerbb90afc2022-12-14 20:50:33 -0600349 {
350 sensorInstanceMap.emplace(pair.second, position);
351 position++;
352 }
353
354 return true;
355}
356
357/** @brief Return map of SBE instance to effecter ID
358 *
359 * @param[in] stateSetId - the state set ID of interest
360 * @param[out] instanceToEffecterMap - map of sbe instance to effecter ID
361 * @param[out] effecterCount - composite effecter count
362 * @param[out] stateIdPos - position of effecter with state set ID within map
363 *
364 * @return true if effector info is available false otherwise
365 */
366bool fetchEffecterInfo(uint16_t stateSetId,
367 std::map<unsigned int, uint16_t>& instanceToEffecterMap,
368 uint8_t& effecterCount, uint8_t& stateIdPos)
369{
370 // get state effecter PDRs
371 std::vector<std::vector<uint8_t>> pdrs{};
372 if (!util::dbus::getStateEffecterPdrs(pdrs, stateSetId))
373 {
374 return false;
375 }
376
377 // check for any PDRs available
378 if (!pdrs.size())
379 {
380 trace::err("state effecter PDRs not present");
381 return false;
382 }
383
384 // find the offset of specified effector within PDRs
385 bool offsetFound = false;
386 auto stateEffecterPDR =
387 reinterpret_cast<const pldm_state_effecter_pdr*>(pdrs.front().data());
388 auto possibleStatesPtr = stateEffecterPDR->possible_states;
389
390 for (auto offset = 0; offset < stateEffecterPDR->composite_effecter_count;
391 offset++)
392 {
393 auto possibleStates =
394 reinterpret_cast<const state_effecter_possible_states*>(
395 possibleStatesPtr);
396
397 if (possibleStates->state_set_id == stateSetId)
398 {
Patrick Williams27dd6362023-05-10 07:51:20 -0500399 stateIdPos = offset;
Ben Tynerbb90afc2022-12-14 20:50:33 -0600400 effecterCount = stateEffecterPDR->composite_effecter_count;
Patrick Williams27dd6362023-05-10 07:51:20 -0500401 offsetFound = true;
Ben Tynerbb90afc2022-12-14 20:50:33 -0600402 break;
403 }
404 possibleStatesPtr += sizeof(possibleStates->state_set_id) +
405 sizeof(possibleStates->possible_states_size) +
406 possibleStates->possible_states_size;
407 }
408
409 if (!offsetFound)
410 {
411 trace::err("state set effecter not found");
412 return false;
413 }
414
415 // map effecter ID to equivelent 16 bit value
416 std::map<uint32_t, uint16_t> entityInstMap{};
417 for (auto& pdr : pdrs)
418 {
419 auto pdrPtr =
420 reinterpret_cast<const pldm_state_effecter_pdr*>(pdr.data());
421 uint32_t key = pdrPtr->effecter_id;
422 entityInstMap.emplace(key, static_cast<uint16_t>(pdrPtr->effecter_id));
423 }
424
425 // map zero based SBE instance to effecter ID
426 unsigned int position = 0;
Patrick Williams27dd6362023-05-10 07:51:20 -0500427 for (const auto& pair : entityInstMap)
Ben Tynerbb90afc2022-12-14 20:50:33 -0600428 {
429 instanceToEffecterMap.emplace(position, pair.second);
430 position++;
431 }
432
433 return true;
434}
435
436/** @brief Reset SBE using HBRT PLDM interface */
437bool hresetSbe(unsigned int sbeInstance)
438{
439 trace::inf("requesting sbe hreset");
440
441 // get effecter info
442 std::map<unsigned int, uint16_t> sbeInstanceToEffecter;
Patrick Williams27dd6362023-05-10 07:51:20 -0500443 uint8_t SBEEffecterCount = 0;
Ben Tynerbb90afc2022-12-14 20:50:33 -0600444 uint8_t sbeMaintenanceStatePosition = 0;
445
446 if (!fetchEffecterInfo(PLDM_OEM_IBM_SBE_MAINTENANCE_STATE,
447 sbeInstanceToEffecter, SBEEffecterCount,
448 sbeMaintenanceStatePosition))
449 {
450 return false;
451 }
452
453 // find the state effecter ID for the given SBE instance
454 auto effecterEntry = sbeInstanceToEffecter.find(sbeInstance);
455 if (effecterEntry == sbeInstanceToEffecter.end())
456 {
457 trace::err("failed to find effecter for SBE");
458 return false;
459 }
460
461 // create request to HRESET the SBE
462 constexpr uint8_t hbrtMctpEid = 10; // HBRT MCTP EID
463
464 auto request = prepareSetEffecterReq(
465 effecterEntry->second, SBEEffecterCount, sbeMaintenanceStatePosition,
466 SBE_RETRY_REQUIRED, hbrtMctpEid);
467
468 if (request.empty())
469 {
470 trace::err("HRESET effecter request empty");
471 return false;
472 }
473
474 // get sensor info for validating sensor change
475 std::map<uint16_t, unsigned int> sensorToSbeInstance;
476 uint8_t sbeSensorOffset = 0;
477 if (!fetchSensorInfo(PLDM_OEM_IBM_SBE_HRESET_STATE, sensorToSbeInstance,
478 sbeSensorOffset))
479 {
Pavithra Barithaya36b043e2024-10-14 15:37:53 +0530480 PLDMInstanceManager& manager = PLDMInstanceManager::getInstance();
481 auto reqhdr = reinterpret_cast<const pldm_msg_hdr*>(&request);
482 manager.freePLDMInstanceID(reqhdr->instance_id, hbrtMctpEid);
Ben Tynerbb90afc2022-12-14 20:50:33 -0600483 return false;
484 }
485
486 // register signal change listener
487 std::string hresetStatus = "requested";
488 constexpr auto interface = "xyz.openbmc_project.PLDM.Event";
Patrick Williams27dd6362023-05-10 07:51:20 -0500489 constexpr auto path = "/xyz/openbmc_project/pldm";
490 constexpr auto member = "StateSensorEvent";
Ben Tynerbb90afc2022-12-14 20:50:33 -0600491
492 auto bus = sdbusplus::bus::new_default();
493 std::unique_ptr<sdbusplus::bus::match_t> match =
494 std::make_unique<sdbusplus::bus::match_t>(
495 bus,
496 sdbusplus::bus::match::rules::type::signal() +
497 sdbusplus::bus::match::rules::member(member) +
498 sdbusplus::bus::match::rules::path(path) +
499 sdbusplus::bus::match::rules::interface(interface),
500 [&](auto& msg) {
Patrick Williamsa0c724d2024-08-16 15:21:54 -0400501 uint8_t sensorTid{};
502 uint16_t sensorId{};
503 uint8_t msgSensorOffset{};
504 uint8_t eventState{};
505 uint8_t previousEventState{};
Ben Tynerbb90afc2022-12-14 20:50:33 -0600506
Patrick Williamsa0c724d2024-08-16 15:21:54 -0400507 // get sensor event details
508 msg.read(sensorTid, sensorId, msgSensorOffset, eventState,
509 previousEventState);
Ben Tynerbb90afc2022-12-14 20:50:33 -0600510
Patrick Williamsa0c724d2024-08-16 15:21:54 -0400511 // does sensor offset match?
512 if (sbeSensorOffset == msgSensorOffset)
Ben Tynerbb90afc2022-12-14 20:50:33 -0600513 {
Patrick Williamsa0c724d2024-08-16 15:21:54 -0400514 // does sensor ID match?
515 auto sensorEntry = sensorToSbeInstance.find(sensorId);
516 if (sensorEntry != sensorToSbeInstance.end())
Ben Tynerbb90afc2022-12-14 20:50:33 -0600517 {
Patrick Williamsa0c724d2024-08-16 15:21:54 -0400518 const uint8_t instance = sensorEntry->second;
519
520 // if instances matche check status
521 if (instance == sbeInstance)
522 {
523 if (eventState ==
524 static_cast<uint8_t>(SBE_HRESET_READY))
525 {
526 hresetStatus = "success";
527 }
528 else if (eventState ==
529 static_cast<uint8_t>(SBE_HRESET_FAILED))
530 {
531 hresetStatus = "fail";
532 }
533 }
Ben Tynerbb90afc2022-12-14 20:50:33 -0600534 }
535 }
Patrick Williamsa0c724d2024-08-16 15:21:54 -0400536 });
Ben Tynerbb90afc2022-12-14 20:50:33 -0600537
538 // send request to issue hreset of sbe
Pavithra Barithaya9b1a0b62024-10-14 19:16:11 +0530539 PLDMInstanceManager& manager = PLDMInstanceManager::getInstance();
540 if (!(manager.sendPldm(request, hbrtMctpEid)))
Ben Tynerbb90afc2022-12-14 20:50:33 -0600541 {
542 trace::err("send pldm request failed");
Pavithra Barithaya36b043e2024-10-14 15:37:53 +0530543 auto reqhdr = reinterpret_cast<const pldm_msg_hdr*>(&request);
544 manager.freePLDMInstanceID(reqhdr->instance_id, hbrtMctpEid);
545
Ben Tynerbb90afc2022-12-14 20:50:33 -0600546 return false;
547 }
548
549 // keep track of elapsed time
550 uint64_t timeRemaining = 60000000; // microseconds, 1 minute
551 std::chrono::steady_clock::time_point begin =
552 std::chrono::steady_clock::now();
553
554 // wait for status update or timeout
555 trace::inf("waiting on sbe hreset");
556 while ("requested" == hresetStatus && 0 != timeRemaining)
557 {
558 bus.wait(timeRemaining);
559 uint64_t timeElapsed =
560 std::chrono::duration_cast<std::chrono::microseconds>(
561 std::chrono::steady_clock::now() - begin)
562 .count();
563
564 timeRemaining =
565 timeElapsed > timeRemaining ? 0 : timeRemaining - timeElapsed;
566
567 bus.process_discard();
568 }
569
570 if (0 == timeRemaining)
571 {
572 trace::err("hreset timed out");
573 }
574
Pavithra Barithaya36b043e2024-10-14 15:37:53 +0530575 auto reqhdr = reinterpret_cast<const pldm_msg_hdr*>(&request);
576 manager.freePLDMInstanceID(reqhdr->instance_id, hbrtMctpEid);
Pavithra Barithaya9b1a0b62024-10-14 19:16:11 +0530577 manager.closePLDM();
Ben Tynerbb90afc2022-12-14 20:50:33 -0600578
579 return hresetStatus == "success" ? true : false;
580}
581
582} // namespace pldm
583} // namespace util