1bb90afc7SBen Tyner #include <libpldm/platform.h> 2bb90afc7SBen Tyner #include <libpldm/pldm.h> 3bb90afc7SBen Tyner #include <libpldm/state_set_oem_ibm.h> 4bb90afc7SBen Tyner 5bb90afc7SBen Tyner #include <util/dbus.hpp> 6bb90afc7SBen Tyner #include <util/trace.hpp> 7bb90afc7SBen Tyner 8bb90afc7SBen Tyner namespace util 9bb90afc7SBen Tyner { 10bb90afc7SBen Tyner namespace pldm 11bb90afc7SBen Tyner { 12bb90afc7SBen Tyner /** @brief Send PLDM request 13bb90afc7SBen Tyner * 14bb90afc7SBen Tyner * @param[in] request - the request data 15bb90afc7SBen Tyner * @param[in] mcptEid - the mctp endpoint ID 16bb90afc7SBen Tyner * @param[out] pldmFd - pldm socket file descriptor 17bb90afc7SBen Tyner * 18bb90afc7SBen Tyner * @pre a mctp instance must have been 19bb90afc7SBen Tyner * @return true if send is successful false otherwise 20bb90afc7SBen Tyner */ 21bb90afc7SBen Tyner bool sendPldm(const std::vector<uint8_t>& request, uint8_t mctpEid, int& pldmFd) 22bb90afc7SBen Tyner { 23bb90afc7SBen Tyner // connect to socket 24bb90afc7SBen Tyner pldmFd = pldm_open(); 25bb90afc7SBen Tyner if (-1 == pldmFd) 26bb90afc7SBen Tyner { 27bb90afc7SBen Tyner trace::err("failed to connect to pldm"); 28bb90afc7SBen Tyner return false; 29bb90afc7SBen Tyner } 30bb90afc7SBen Tyner 31bb90afc7SBen Tyner // send PLDM request 32bb90afc7SBen Tyner auto pldmRc = pldm_send(mctpEid, pldmFd, request.data(), request.size()); 33bb90afc7SBen Tyner 34bb90afc7SBen Tyner trace::inf("sent pldm request"); 35bb90afc7SBen Tyner 36bb90afc7SBen Tyner return pldmRc == PLDM_REQUESTER_SUCCESS ? true : false; 37bb90afc7SBen Tyner } 38bb90afc7SBen Tyner 39bb90afc7SBen Tyner /** @brief Prepare a request for SetStateEffecterStates 40bb90afc7SBen Tyner * 41bb90afc7SBen Tyner * @param[in] effecterId - the effecter ID 42bb90afc7SBen Tyner * @param[in] effecterCount - composite effecter count 43bb90afc7SBen Tyner * @param[in] stateIdPos - position of the state set 44bb90afc7SBen Tyner * @param[in] stateSetValue - the value to set the state 45bb90afc7SBen Tyner * @param[in] mcptEid - the MCTP endpoint ID 46bb90afc7SBen Tyner * 47bb90afc7SBen Tyner * @return PLDM request message to be sent to host, empty message on error 48bb90afc7SBen Tyner */ 49bb90afc7SBen Tyner std::vector<uint8_t> prepareSetEffecterReq(uint16_t effecterId, 50bb90afc7SBen Tyner uint8_t effecterCount, 51bb90afc7SBen Tyner uint8_t stateIdPos, 52bb90afc7SBen Tyner uint8_t stateSetValue, 53bb90afc7SBen Tyner uint8_t mctpEid) 54bb90afc7SBen Tyner { 55bb90afc7SBen Tyner // get mctp instance associated with the endpoint ID 56bb90afc7SBen Tyner uint8_t mctpInstance; 57bb90afc7SBen Tyner if (!util::dbus::getMctpInstance(mctpInstance, mctpEid)) 58bb90afc7SBen Tyner { 59bb90afc7SBen Tyner return std::vector<uint8_t>(); 60bb90afc7SBen Tyner } 61bb90afc7SBen Tyner 62bb90afc7SBen Tyner // form the request message 63bb90afc7SBen Tyner std::vector<uint8_t> request( 64bb90afc7SBen Tyner sizeof(pldm_msg_hdr) + sizeof(effecterId) + sizeof(effecterCount) + 65bb90afc7SBen Tyner (effecterCount * sizeof(set_effecter_state_field))); 66bb90afc7SBen Tyner 67bb90afc7SBen Tyner // encode the state data with the change we want to elicit 68bb90afc7SBen Tyner std::vector<set_effecter_state_field> stateField; 69bb90afc7SBen Tyner for (uint8_t effecterPos = 0; effecterPos < effecterCount; effecterPos++) 70bb90afc7SBen Tyner { 71bb90afc7SBen Tyner if (effecterPos == stateIdPos) 72bb90afc7SBen Tyner { 73bb90afc7SBen Tyner stateField.emplace_back( 74bb90afc7SBen Tyner set_effecter_state_field{PLDM_REQUEST_SET, stateSetValue}); 75bb90afc7SBen Tyner } 76bb90afc7SBen Tyner else 77bb90afc7SBen Tyner { 78bb90afc7SBen Tyner stateField.emplace_back( 79bb90afc7SBen Tyner set_effecter_state_field{PLDM_NO_CHANGE, 0}); 80bb90afc7SBen Tyner } 81bb90afc7SBen Tyner } 82bb90afc7SBen Tyner 83bb90afc7SBen Tyner // encode the message with state data 84bb90afc7SBen Tyner auto requestMsg = reinterpret_cast<pldm_msg*>(request.data()); 85bb90afc7SBen Tyner auto rc = encode_set_state_effecter_states_req( 86bb90afc7SBen Tyner mctpInstance, effecterId, effecterCount, stateField.data(), requestMsg); 87bb90afc7SBen Tyner 88bb90afc7SBen Tyner if (rc != PLDM_SUCCESS) 89bb90afc7SBen Tyner { 90bb90afc7SBen Tyner trace::err("encode set effecter states request failed"); 91bb90afc7SBen Tyner request.clear(); 92bb90afc7SBen Tyner } 93bb90afc7SBen Tyner 94bb90afc7SBen Tyner return request; 95bb90afc7SBen Tyner } 96bb90afc7SBen Tyner 97bb90afc7SBen Tyner /** @brief Return map of sensor ID to SBE instance 98bb90afc7SBen Tyner * 99bb90afc7SBen Tyner * @param[in] stateSetId - the state set ID of interest 100bb90afc7SBen Tyner * @param[out] sensorInstanceMap - map of sensor to SBE instance 101bb90afc7SBen Tyner * @param[out] sensorOffset - position of sensor with state set ID within map 102bb90afc7SBen Tyner * 103bb90afc7SBen Tyner * @return true if sensor info is available false otherwise 104bb90afc7SBen Tyner */ 105bb90afc7SBen Tyner bool fetchSensorInfo(uint16_t stateSetId, 106bb90afc7SBen Tyner std::map<uint16_t, unsigned int>& sensorInstanceMap, 107bb90afc7SBen Tyner uint8_t& sensorOffset) 108bb90afc7SBen Tyner { 109bb90afc7SBen Tyner // get state sensor PDRs 110bb90afc7SBen Tyner std::vector<std::vector<uint8_t>> pdrs{}; 111bb90afc7SBen Tyner if (!util::dbus::getStateSensorPdrs(pdrs, stateSetId)) 112bb90afc7SBen Tyner { 113bb90afc7SBen Tyner return false; 114bb90afc7SBen Tyner } 115bb90afc7SBen Tyner 116bb90afc7SBen Tyner // check for any PDRs available 117bb90afc7SBen Tyner if (!pdrs.size()) 118bb90afc7SBen Tyner { 119bb90afc7SBen Tyner trace::err("state sensor PDRs not present"); 120bb90afc7SBen Tyner return false; 121bb90afc7SBen Tyner } 122bb90afc7SBen Tyner 123bb90afc7SBen Tyner // find the offset of specified sensor withing PDRs 124bb90afc7SBen Tyner bool offsetFound = false; 125bb90afc7SBen Tyner auto stateSensorPDR = 126bb90afc7SBen Tyner reinterpret_cast<const pldm_state_sensor_pdr*>(pdrs.front().data()); 127bb90afc7SBen Tyner auto possibleStatesPtr = stateSensorPDR->possible_states; 128bb90afc7SBen Tyner 129bb90afc7SBen Tyner for (auto offset = 0; offset < stateSensorPDR->composite_sensor_count; 130bb90afc7SBen Tyner offset++) 131bb90afc7SBen Tyner { 132bb90afc7SBen Tyner auto possibleStates = 133bb90afc7SBen Tyner reinterpret_cast<const state_sensor_possible_states*>( 134bb90afc7SBen Tyner possibleStatesPtr); 135bb90afc7SBen Tyner 136bb90afc7SBen Tyner if (possibleStates->state_set_id == stateSetId) 137bb90afc7SBen Tyner { 138bb90afc7SBen Tyner sensorOffset = offset; 139bb90afc7SBen Tyner offsetFound = true; 140bb90afc7SBen Tyner break; 141bb90afc7SBen Tyner } 142bb90afc7SBen Tyner possibleStatesPtr += sizeof(possibleStates->state_set_id) + 143bb90afc7SBen Tyner sizeof(possibleStates->possible_states_size) + 144bb90afc7SBen Tyner possibleStates->possible_states_size; 145bb90afc7SBen Tyner } 146bb90afc7SBen Tyner 147bb90afc7SBen Tyner if (!offsetFound) 148bb90afc7SBen Tyner { 149bb90afc7SBen Tyner trace::err("state sensor not found"); 150bb90afc7SBen Tyner return false; 151bb90afc7SBen Tyner } 152bb90afc7SBen Tyner 153bb90afc7SBen Tyner // map sensor ID to equivelent 16 bit value 154bb90afc7SBen Tyner std::map<uint32_t, uint16_t> entityInstMap{}; 155bb90afc7SBen Tyner for (auto& pdr : pdrs) 156bb90afc7SBen Tyner { 157bb90afc7SBen Tyner auto pdrPtr = 158bb90afc7SBen Tyner reinterpret_cast<const pldm_state_sensor_pdr*>(pdr.data()); 159bb90afc7SBen Tyner uint32_t key = pdrPtr->sensor_id; 160bb90afc7SBen Tyner entityInstMap.emplace(key, static_cast<uint16_t>(pdrPtr->sensor_id)); 161bb90afc7SBen Tyner } 162bb90afc7SBen Tyner 163bb90afc7SBen Tyner // map sensor ID to zero based SBE instance 164bb90afc7SBen Tyner unsigned int position = 0; 165*27dd6368SPatrick Williams for (const auto& pair : entityInstMap) 166bb90afc7SBen Tyner { 167bb90afc7SBen Tyner sensorInstanceMap.emplace(pair.second, position); 168bb90afc7SBen Tyner position++; 169bb90afc7SBen Tyner } 170bb90afc7SBen Tyner 171bb90afc7SBen Tyner return true; 172bb90afc7SBen Tyner } 173bb90afc7SBen Tyner 174bb90afc7SBen Tyner /** @brief Return map of SBE instance to effecter ID 175bb90afc7SBen Tyner * 176bb90afc7SBen Tyner * @param[in] stateSetId - the state set ID of interest 177bb90afc7SBen Tyner * @param[out] instanceToEffecterMap - map of sbe instance to effecter ID 178bb90afc7SBen Tyner * @param[out] effecterCount - composite effecter count 179bb90afc7SBen Tyner * @param[out] stateIdPos - position of effecter with state set ID within map 180bb90afc7SBen Tyner * 181bb90afc7SBen Tyner * @return true if effector info is available false otherwise 182bb90afc7SBen Tyner */ 183bb90afc7SBen Tyner bool fetchEffecterInfo(uint16_t stateSetId, 184bb90afc7SBen Tyner std::map<unsigned int, uint16_t>& instanceToEffecterMap, 185bb90afc7SBen Tyner uint8_t& effecterCount, uint8_t& stateIdPos) 186bb90afc7SBen Tyner { 187bb90afc7SBen Tyner // get state effecter PDRs 188bb90afc7SBen Tyner std::vector<std::vector<uint8_t>> pdrs{}; 189bb90afc7SBen Tyner if (!util::dbus::getStateEffecterPdrs(pdrs, stateSetId)) 190bb90afc7SBen Tyner { 191bb90afc7SBen Tyner return false; 192bb90afc7SBen Tyner } 193bb90afc7SBen Tyner 194bb90afc7SBen Tyner // check for any PDRs available 195bb90afc7SBen Tyner if (!pdrs.size()) 196bb90afc7SBen Tyner { 197bb90afc7SBen Tyner trace::err("state effecter PDRs not present"); 198bb90afc7SBen Tyner return false; 199bb90afc7SBen Tyner } 200bb90afc7SBen Tyner 201bb90afc7SBen Tyner // find the offset of specified effector within PDRs 202bb90afc7SBen Tyner bool offsetFound = false; 203bb90afc7SBen Tyner auto stateEffecterPDR = 204bb90afc7SBen Tyner reinterpret_cast<const pldm_state_effecter_pdr*>(pdrs.front().data()); 205bb90afc7SBen Tyner auto possibleStatesPtr = stateEffecterPDR->possible_states; 206bb90afc7SBen Tyner 207bb90afc7SBen Tyner for (auto offset = 0; offset < stateEffecterPDR->composite_effecter_count; 208bb90afc7SBen Tyner offset++) 209bb90afc7SBen Tyner { 210bb90afc7SBen Tyner auto possibleStates = 211bb90afc7SBen Tyner reinterpret_cast<const state_effecter_possible_states*>( 212bb90afc7SBen Tyner possibleStatesPtr); 213bb90afc7SBen Tyner 214bb90afc7SBen Tyner if (possibleStates->state_set_id == stateSetId) 215bb90afc7SBen Tyner { 216bb90afc7SBen Tyner stateIdPos = offset; 217bb90afc7SBen Tyner effecterCount = stateEffecterPDR->composite_effecter_count; 218bb90afc7SBen Tyner offsetFound = true; 219bb90afc7SBen Tyner break; 220bb90afc7SBen Tyner } 221bb90afc7SBen Tyner possibleStatesPtr += sizeof(possibleStates->state_set_id) + 222bb90afc7SBen Tyner sizeof(possibleStates->possible_states_size) + 223bb90afc7SBen Tyner possibleStates->possible_states_size; 224bb90afc7SBen Tyner } 225bb90afc7SBen Tyner 226bb90afc7SBen Tyner if (!offsetFound) 227bb90afc7SBen Tyner { 228bb90afc7SBen Tyner trace::err("state set effecter not found"); 229bb90afc7SBen Tyner return false; 230bb90afc7SBen Tyner } 231bb90afc7SBen Tyner 232bb90afc7SBen Tyner // map effecter ID to equivelent 16 bit value 233bb90afc7SBen Tyner std::map<uint32_t, uint16_t> entityInstMap{}; 234bb90afc7SBen Tyner for (auto& pdr : pdrs) 235bb90afc7SBen Tyner { 236bb90afc7SBen Tyner auto pdrPtr = 237bb90afc7SBen Tyner reinterpret_cast<const pldm_state_effecter_pdr*>(pdr.data()); 238bb90afc7SBen Tyner uint32_t key = pdrPtr->effecter_id; 239bb90afc7SBen Tyner entityInstMap.emplace(key, static_cast<uint16_t>(pdrPtr->effecter_id)); 240bb90afc7SBen Tyner } 241bb90afc7SBen Tyner 242bb90afc7SBen Tyner // map zero based SBE instance to effecter ID 243bb90afc7SBen Tyner unsigned int position = 0; 244*27dd6368SPatrick Williams for (const auto& pair : entityInstMap) 245bb90afc7SBen Tyner { 246bb90afc7SBen Tyner instanceToEffecterMap.emplace(position, pair.second); 247bb90afc7SBen Tyner position++; 248bb90afc7SBen Tyner } 249bb90afc7SBen Tyner 250bb90afc7SBen Tyner return true; 251bb90afc7SBen Tyner } 252bb90afc7SBen Tyner 253bb90afc7SBen Tyner /** @brief Reset SBE using HBRT PLDM interface */ 254bb90afc7SBen Tyner bool hresetSbe(unsigned int sbeInstance) 255bb90afc7SBen Tyner { 256bb90afc7SBen Tyner trace::inf("requesting sbe hreset"); 257bb90afc7SBen Tyner 258bb90afc7SBen Tyner // get effecter info 259bb90afc7SBen Tyner std::map<unsigned int, uint16_t> sbeInstanceToEffecter; 260bb90afc7SBen Tyner uint8_t SBEEffecterCount = 0; 261bb90afc7SBen Tyner uint8_t sbeMaintenanceStatePosition = 0; 262bb90afc7SBen Tyner 263bb90afc7SBen Tyner if (!fetchEffecterInfo(PLDM_OEM_IBM_SBE_MAINTENANCE_STATE, 264bb90afc7SBen Tyner sbeInstanceToEffecter, SBEEffecterCount, 265bb90afc7SBen Tyner sbeMaintenanceStatePosition)) 266bb90afc7SBen Tyner { 267bb90afc7SBen Tyner return false; 268bb90afc7SBen Tyner } 269bb90afc7SBen Tyner 270bb90afc7SBen Tyner // find the state effecter ID for the given SBE instance 271bb90afc7SBen Tyner auto effecterEntry = sbeInstanceToEffecter.find(sbeInstance); 272bb90afc7SBen Tyner if (effecterEntry == sbeInstanceToEffecter.end()) 273bb90afc7SBen Tyner { 274bb90afc7SBen Tyner trace::err("failed to find effecter for SBE"); 275bb90afc7SBen Tyner return false; 276bb90afc7SBen Tyner } 277bb90afc7SBen Tyner 278bb90afc7SBen Tyner // create request to HRESET the SBE 279bb90afc7SBen Tyner constexpr uint8_t hbrtMctpEid = 10; // HBRT MCTP EID 280bb90afc7SBen Tyner 281bb90afc7SBen Tyner auto request = prepareSetEffecterReq( 282bb90afc7SBen Tyner effecterEntry->second, SBEEffecterCount, sbeMaintenanceStatePosition, 283bb90afc7SBen Tyner SBE_RETRY_REQUIRED, hbrtMctpEid); 284bb90afc7SBen Tyner 285bb90afc7SBen Tyner if (request.empty()) 286bb90afc7SBen Tyner { 287bb90afc7SBen Tyner trace::err("HRESET effecter request empty"); 288bb90afc7SBen Tyner return false; 289bb90afc7SBen Tyner } 290bb90afc7SBen Tyner 291bb90afc7SBen Tyner // get sensor info for validating sensor change 292bb90afc7SBen Tyner std::map<uint16_t, unsigned int> sensorToSbeInstance; 293bb90afc7SBen Tyner uint8_t sbeSensorOffset = 0; 294bb90afc7SBen Tyner if (!fetchSensorInfo(PLDM_OEM_IBM_SBE_HRESET_STATE, sensorToSbeInstance, 295bb90afc7SBen Tyner sbeSensorOffset)) 296bb90afc7SBen Tyner { 297bb90afc7SBen Tyner return false; 298bb90afc7SBen Tyner } 299bb90afc7SBen Tyner 300bb90afc7SBen Tyner // register signal change listener 301bb90afc7SBen Tyner std::string hresetStatus = "requested"; 302bb90afc7SBen Tyner constexpr auto interface = "xyz.openbmc_project.PLDM.Event"; 303bb90afc7SBen Tyner constexpr auto path = "/xyz/openbmc_project/pldm"; 304bb90afc7SBen Tyner constexpr auto member = "StateSensorEvent"; 305bb90afc7SBen Tyner 306bb90afc7SBen Tyner auto bus = sdbusplus::bus::new_default(); 307bb90afc7SBen Tyner std::unique_ptr<sdbusplus::bus::match_t> match = 308bb90afc7SBen Tyner std::make_unique<sdbusplus::bus::match_t>( 309bb90afc7SBen Tyner bus, 310bb90afc7SBen Tyner sdbusplus::bus::match::rules::type::signal() + 311bb90afc7SBen Tyner sdbusplus::bus::match::rules::member(member) + 312bb90afc7SBen Tyner sdbusplus::bus::match::rules::path(path) + 313bb90afc7SBen Tyner sdbusplus::bus::match::rules::interface(interface), 314bb90afc7SBen Tyner [&](auto& msg) { 315bb90afc7SBen Tyner uint8_t sensorTid{}; 316bb90afc7SBen Tyner uint16_t sensorId{}; 317bb90afc7SBen Tyner uint8_t msgSensorOffset{}; 318bb90afc7SBen Tyner uint8_t eventState{}; 319bb90afc7SBen Tyner uint8_t previousEventState{}; 320bb90afc7SBen Tyner 321bb90afc7SBen Tyner // get sensor event details 322bb90afc7SBen Tyner msg.read(sensorTid, sensorId, msgSensorOffset, eventState, 323bb90afc7SBen Tyner previousEventState); 324bb90afc7SBen Tyner 325bb90afc7SBen Tyner // does sensor offset match? 326bb90afc7SBen Tyner if (sbeSensorOffset == msgSensorOffset) 327bb90afc7SBen Tyner { 328bb90afc7SBen Tyner // does sensor ID match? 329bb90afc7SBen Tyner auto sensorEntry = sensorToSbeInstance.find(sensorId); 330bb90afc7SBen Tyner if (sensorEntry != sensorToSbeInstance.end()) 331bb90afc7SBen Tyner { 332bb90afc7SBen Tyner const uint8_t instance = sensorEntry->second; 333bb90afc7SBen Tyner 334bb90afc7SBen Tyner // if instances matche check status 335bb90afc7SBen Tyner if (instance == sbeInstance) 336bb90afc7SBen Tyner { 337*27dd6368SPatrick Williams if (eventState == static_cast<uint8_t>(SBE_HRESET_READY)) 338bb90afc7SBen Tyner { 339bb90afc7SBen Tyner hresetStatus = "success"; 340bb90afc7SBen Tyner } 341bb90afc7SBen Tyner else if (eventState == 342bb90afc7SBen Tyner static_cast<uint8_t>(SBE_HRESET_FAILED)) 343bb90afc7SBen Tyner { 344bb90afc7SBen Tyner hresetStatus = "fail"; 345bb90afc7SBen Tyner } 346bb90afc7SBen Tyner } 347bb90afc7SBen Tyner } 348bb90afc7SBen Tyner } 349bb90afc7SBen Tyner }); 350bb90afc7SBen Tyner 351bb90afc7SBen Tyner // send request to issue hreset of sbe 352bb90afc7SBen Tyner int pldmFd = -1; // mctp socket file descriptor 353bb90afc7SBen Tyner if (!sendPldm(request, hbrtMctpEid, pldmFd)) 354bb90afc7SBen Tyner { 355bb90afc7SBen Tyner trace::err("send pldm request failed"); 356bb90afc7SBen Tyner if (-1 != pldmFd) 357bb90afc7SBen Tyner { 358bb90afc7SBen Tyner close(pldmFd); 359bb90afc7SBen Tyner } 360bb90afc7SBen Tyner return false; 361bb90afc7SBen Tyner } 362bb90afc7SBen Tyner 363bb90afc7SBen Tyner // keep track of elapsed time 364bb90afc7SBen Tyner uint64_t timeRemaining = 60000000; // microseconds, 1 minute 365bb90afc7SBen Tyner std::chrono::steady_clock::time_point begin = 366bb90afc7SBen Tyner std::chrono::steady_clock::now(); 367bb90afc7SBen Tyner 368bb90afc7SBen Tyner // wait for status update or timeout 369bb90afc7SBen Tyner trace::inf("waiting on sbe hreset"); 370bb90afc7SBen Tyner while ("requested" == hresetStatus && 0 != timeRemaining) 371bb90afc7SBen Tyner { 372bb90afc7SBen Tyner bus.wait(timeRemaining); 373bb90afc7SBen Tyner uint64_t timeElapsed = 374bb90afc7SBen Tyner std::chrono::duration_cast<std::chrono::microseconds>( 375bb90afc7SBen Tyner std::chrono::steady_clock::now() - begin) 376bb90afc7SBen Tyner .count(); 377bb90afc7SBen Tyner 378bb90afc7SBen Tyner timeRemaining = 379bb90afc7SBen Tyner timeElapsed > timeRemaining ? 0 : timeRemaining - timeElapsed; 380bb90afc7SBen Tyner 381bb90afc7SBen Tyner bus.process_discard(); 382bb90afc7SBen Tyner } 383bb90afc7SBen Tyner 384bb90afc7SBen Tyner if (0 == timeRemaining) 385bb90afc7SBen Tyner { 386bb90afc7SBen Tyner trace::err("hreset timed out"); 387bb90afc7SBen Tyner } 388bb90afc7SBen Tyner 389bb90afc7SBen Tyner close(pldmFd); // close pldm socket 390bb90afc7SBen Tyner 391bb90afc7SBen Tyner return hresetStatus == "success" ? true : false; 392bb90afc7SBen Tyner } 393bb90afc7SBen Tyner 394bb90afc7SBen Tyner } // namespace pldm 395bb90afc7SBen Tyner } // namespace util 396