| #include <arpa/inet.h> | 
 | #include <fcntl.h> | 
 | #include <limits.h> | 
 | #include <linux/i2c-dev.h> | 
 | #include <linux/i2c.h> | 
 | #include <mapper.h> | 
 | #include <sys/ioctl.h> | 
 | #include <sys/stat.h> | 
 | #include <sys/types.h> | 
 | #include <systemd/sd-bus.h> | 
 | #include <unistd.h> | 
 |  | 
 | #include <algorithm> | 
 | #include <app/channel.hpp> | 
 | #include <app/watchdog.hpp> | 
 | #include <apphandler.hpp> | 
 | #include <array> | 
 | #include <charconv> | 
 | #include <cstddef> | 
 | #include <cstdint> | 
 | #include <filesystem> | 
 | #include <fstream> | 
 | #include <ipmid/api.hpp> | 
 | #include <ipmid/sessiondef.hpp> | 
 | #include <ipmid/sessionhelper.hpp> | 
 | #include <ipmid/types.hpp> | 
 | #include <ipmid/utils.hpp> | 
 | #include <memory> | 
 | #include <nlohmann/json.hpp> | 
 | #include <phosphor-logging/elog-errors.hpp> | 
 | #include <phosphor-logging/log.hpp> | 
 | #include <sdbusplus/message/types.hpp> | 
 | #include <string> | 
 | #include <string_view> | 
 | #include <sys_info_param.hpp> | 
 | #include <tuple> | 
 | #include <vector> | 
 | #include <xyz/openbmc_project/Common/error.hpp> | 
 | #include <xyz/openbmc_project/Control/Power/ACPIPowerState/server.hpp> | 
 | #include <xyz/openbmc_project/Software/Activation/server.hpp> | 
 | #include <xyz/openbmc_project/Software/Version/server.hpp> | 
 | #include <xyz/openbmc_project/State/BMC/server.hpp> | 
 |  | 
 | extern sd_bus* bus; | 
 |  | 
 | constexpr auto bmc_state_interface = "xyz.openbmc_project.State.BMC"; | 
 | constexpr auto bmc_state_property = "CurrentBMCState"; | 
 |  | 
 | static constexpr auto redundancyIntf = | 
 |     "xyz.openbmc_project.Software.RedundancyPriority"; | 
 | static constexpr auto versionIntf = "xyz.openbmc_project.Software.Version"; | 
 | static constexpr auto activationIntf = | 
 |     "xyz.openbmc_project.Software.Activation"; | 
 | static constexpr auto softwareRoot = "/xyz/openbmc_project/software"; | 
 |  | 
 | void register_netfn_app_functions() __attribute__((constructor)); | 
 |  | 
 | using namespace phosphor::logging; | 
 | using namespace sdbusplus::xyz::openbmc_project::Common::Error; | 
 | using Version = sdbusplus::xyz::openbmc_project::Software::server::Version; | 
 | using Activation = | 
 |     sdbusplus::xyz::openbmc_project::Software::server::Activation; | 
 | using BMC = sdbusplus::xyz::openbmc_project::State::server::BMC; | 
 | namespace fs = std::filesystem; | 
 |  | 
 | #ifdef ENABLE_I2C_WHITELIST_CHECK | 
 | typedef struct | 
 | { | 
 |     uint8_t busId; | 
 |     uint8_t slaveAddr; | 
 |     uint8_t slaveAddrMask; | 
 |     std::vector<uint8_t> data; | 
 |     std::vector<uint8_t> dataMask; | 
 | } i2cMasterWRWhitelist; | 
 |  | 
 | static std::vector<i2cMasterWRWhitelist>& getWRWhitelist() | 
 | { | 
 |     static std::vector<i2cMasterWRWhitelist> wrWhitelist; | 
 |     return wrWhitelist; | 
 | } | 
 |  | 
 | static constexpr const char* i2cMasterWRWhitelistFile = | 
 |     "/usr/share/ipmi-providers/master_write_read_white_list.json"; | 
 |  | 
 | static constexpr const char* filtersStr = "filters"; | 
 | static constexpr const char* busIdStr = "busId"; | 
 | static constexpr const char* slaveAddrStr = "slaveAddr"; | 
 | static constexpr const char* slaveAddrMaskStr = "slaveAddrMask"; | 
 | static constexpr const char* cmdStr = "command"; | 
 | static constexpr const char* cmdMaskStr = "commandMask"; | 
 | static constexpr int base_16 = 16; | 
 | #endif // ENABLE_I2C_WHITELIST_CHECK | 
 | static constexpr uint8_t maxIPMIWriteReadSize = 255; | 
 | static constexpr uint8_t oemCmdStart = 192; | 
 | static constexpr uint8_t oemCmdEnd = 255; | 
 | static constexpr uint8_t invalidParamSelectorStart = 8; | 
 | static constexpr uint8_t invalidParamSelectorEnd = 191; | 
 |  | 
 | /** | 
 |  * @brief Returns the Version info from primary s/w object | 
 |  * | 
 |  * Get the Version info from the active s/w object which is having high | 
 |  * "Priority" value(a smaller number is a higher priority) and "Purpose" | 
 |  * is "BMC" from the list of all s/w objects those are implementing | 
 |  * RedundancyPriority interface from the given softwareRoot path. | 
 |  * | 
 |  * @return On success returns the Version info from primary s/w object. | 
 |  * | 
 |  */ | 
 | std::string getActiveSoftwareVersionInfo(ipmi::Context::ptr ctx) | 
 | { | 
 |     std::string revision{}; | 
 |     ipmi::ObjectTree objectTree; | 
 |     try | 
 |     { | 
 |         objectTree = | 
 |             ipmi::getAllDbusObjects(*ctx->bus, softwareRoot, redundancyIntf); | 
 |     } | 
 |     catch (const sdbusplus::exception_t& e) | 
 |     { | 
 |         log<level::ERR>("Failed to fetch redundancy object from dbus", | 
 |                         entry("INTERFACE=%s", redundancyIntf), | 
 |                         entry("ERRMSG=%s", e.what())); | 
 |         elog<InternalFailure>(); | 
 |     } | 
 |  | 
 |     auto objectFound = false; | 
 |     for (auto& softObject : objectTree) | 
 |     { | 
 |         auto service = | 
 |             ipmi::getService(*ctx->bus, redundancyIntf, softObject.first); | 
 |         auto objValueTree = | 
 |             ipmi::getManagedObjects(*ctx->bus, service, softwareRoot); | 
 |  | 
 |         auto minPriority = 0xFF; | 
 |         for (const auto& objIter : objValueTree) | 
 |         { | 
 |             try | 
 |             { | 
 |                 auto& intfMap = objIter.second; | 
 |                 auto& redundancyPriorityProps = intfMap.at(redundancyIntf); | 
 |                 auto& versionProps = intfMap.at(versionIntf); | 
 |                 auto& activationProps = intfMap.at(activationIntf); | 
 |                 auto priority = | 
 |                     std::get<uint8_t>(redundancyPriorityProps.at("Priority")); | 
 |                 auto purpose = | 
 |                     std::get<std::string>(versionProps.at("Purpose")); | 
 |                 auto activation = | 
 |                     std::get<std::string>(activationProps.at("Activation")); | 
 |                 auto version = | 
 |                     std::get<std::string>(versionProps.at("Version")); | 
 |                 if ((Version::convertVersionPurposeFromString(purpose) == | 
 |                      Version::VersionPurpose::BMC) && | 
 |                     (Activation::convertActivationsFromString(activation) == | 
 |                      Activation::Activations::Active)) | 
 |                 { | 
 |                     if (priority < minPriority) | 
 |                     { | 
 |                         minPriority = priority; | 
 |                         objectFound = true; | 
 |                         revision = std::move(version); | 
 |                     } | 
 |                 } | 
 |             } | 
 |             catch (const std::exception& e) | 
 |             { | 
 |                 log<level::ERR>(e.what()); | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     if (!objectFound) | 
 |     { | 
 |         log<level::ERR>("Could not found an BMC software Object"); | 
 |         elog<InternalFailure>(); | 
 |     } | 
 |  | 
 |     return revision; | 
 | } | 
 |  | 
 | bool getCurrentBmcState() | 
 | { | 
 |     sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; | 
 |  | 
 |     // Get the Inventory object implementing the BMC interface | 
 |     ipmi::DbusObjectInfo bmcObject = | 
 |         ipmi::getDbusObject(bus, bmc_state_interface); | 
 |     auto variant = | 
 |         ipmi::getDbusProperty(bus, bmcObject.second, bmcObject.first, | 
 |                               bmc_state_interface, bmc_state_property); | 
 |  | 
 |     return std::holds_alternative<std::string>(variant) && | 
 |            BMC::convertBMCStateFromString(std::get<std::string>(variant)) == | 
 |                BMC::BMCState::Ready; | 
 | } | 
 |  | 
 | bool getCurrentBmcStateWithFallback(const bool fallbackAvailability) | 
 | { | 
 |     try | 
 |     { | 
 |         return getCurrentBmcState(); | 
 |     } | 
 |     catch (...) | 
 |     { | 
 |         // Nothing provided the BMC interface, therefore return whatever was | 
 |         // configured as the default. | 
 |         return fallbackAvailability; | 
 |     } | 
 | } | 
 |  | 
 | namespace acpi_state | 
 | { | 
 | using namespace sdbusplus::xyz::openbmc_project::Control::Power::server; | 
 |  | 
 | const static constexpr char* acpiObjPath = | 
 |     "/xyz/openbmc_project/control/host0/acpi_power_state"; | 
 | const static constexpr char* acpiInterface = | 
 |     "xyz.openbmc_project.Control.Power.ACPIPowerState"; | 
 | const static constexpr char* sysACPIProp = "SysACPIStatus"; | 
 | const static constexpr char* devACPIProp = "DevACPIStatus"; | 
 |  | 
 | enum class PowerStateType : uint8_t | 
 | { | 
 |     sysPowerState = 0x00, | 
 |     devPowerState = 0x01, | 
 | }; | 
 |  | 
 | // Defined in 20.6 of ipmi doc | 
 | enum class PowerState : uint8_t | 
 | { | 
 |     s0G0D0 = 0x00, | 
 |     s1D1 = 0x01, | 
 |     s2D2 = 0x02, | 
 |     s3D3 = 0x03, | 
 |     s4 = 0x04, | 
 |     s5G2 = 0x05, | 
 |     s4S5 = 0x06, | 
 |     g3 = 0x07, | 
 |     sleep = 0x08, | 
 |     g1Sleep = 0x09, | 
 |     override = 0x0a, | 
 |     legacyOn = 0x20, | 
 |     legacyOff = 0x21, | 
 |     unknown = 0x2a, | 
 |     noChange = 0x7f, | 
 | }; | 
 |  | 
 | static constexpr uint8_t stateChanged = 0x80; | 
 |  | 
 | std::map<ACPIPowerState::ACPI, PowerState> dbusToIPMI = { | 
 |     {ACPIPowerState::ACPI::S0_G0_D0, PowerState::s0G0D0}, | 
 |     {ACPIPowerState::ACPI::S1_D1, PowerState::s1D1}, | 
 |     {ACPIPowerState::ACPI::S2_D2, PowerState::s2D2}, | 
 |     {ACPIPowerState::ACPI::S3_D3, PowerState::s3D3}, | 
 |     {ACPIPowerState::ACPI::S4, PowerState::s4}, | 
 |     {ACPIPowerState::ACPI::S5_G2, PowerState::s5G2}, | 
 |     {ACPIPowerState::ACPI::S4_S5, PowerState::s4S5}, | 
 |     {ACPIPowerState::ACPI::G3, PowerState::g3}, | 
 |     {ACPIPowerState::ACPI::SLEEP, PowerState::sleep}, | 
 |     {ACPIPowerState::ACPI::G1_SLEEP, PowerState::g1Sleep}, | 
 |     {ACPIPowerState::ACPI::OVERRIDE, PowerState::override}, | 
 |     {ACPIPowerState::ACPI::LEGACY_ON, PowerState::legacyOn}, | 
 |     {ACPIPowerState::ACPI::LEGACY_OFF, PowerState::legacyOff}, | 
 |     {ACPIPowerState::ACPI::Unknown, PowerState::unknown}}; | 
 |  | 
 | bool isValidACPIState(acpi_state::PowerStateType type, uint8_t state) | 
 | { | 
 |     if (type == acpi_state::PowerStateType::sysPowerState) | 
 |     { | 
 |         if ((state <= static_cast<uint8_t>(acpi_state::PowerState::override)) || | 
 |             (state == static_cast<uint8_t>(acpi_state::PowerState::legacyOn)) || | 
 |             (state == | 
 |              static_cast<uint8_t>(acpi_state::PowerState::legacyOff)) || | 
 |             (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) || | 
 |             (state == static_cast<uint8_t>(acpi_state::PowerState::noChange))) | 
 |         { | 
 |             return true; | 
 |         } | 
 |         else | 
 |         { | 
 |             return false; | 
 |         } | 
 |     } | 
 |     else if (type == acpi_state::PowerStateType::devPowerState) | 
 |     { | 
 |         if ((state <= static_cast<uint8_t>(acpi_state::PowerState::s3D3)) || | 
 |             (state == static_cast<uint8_t>(acpi_state::PowerState::unknown)) || | 
 |             (state == static_cast<uint8_t>(acpi_state::PowerState::noChange))) | 
 |         { | 
 |             return true; | 
 |         } | 
 |         else | 
 |         { | 
 |             return false; | 
 |         } | 
 |     } | 
 |     else | 
 |     { | 
 |         return false; | 
 |     } | 
 |     return false; | 
 | } | 
 | } // namespace acpi_state | 
 |  | 
 | /** @brief implements Set ACPI Power State command | 
 |  * @param sysAcpiState - ACPI system power state to set | 
 |  * @param devAcpiState - ACPI device power state to set | 
 |  * | 
 |  * @return IPMI completion code on success | 
 |  **/ | 
 | ipmi::RspType<> ipmiSetAcpiPowerState(uint8_t sysAcpiState, | 
 |                                       uint8_t devAcpiState) | 
 | { | 
 |     auto s = static_cast<uint8_t>(acpi_state::PowerState::unknown); | 
 |  | 
 |     sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; | 
 |  | 
 |     auto value = acpi_state::ACPIPowerState::ACPI::Unknown; | 
 |  | 
 |     if (sysAcpiState & acpi_state::stateChanged) | 
 |     { | 
 |         // set system power state | 
 |         s = sysAcpiState & ~acpi_state::stateChanged; | 
 |  | 
 |         if (!acpi_state::isValidACPIState( | 
 |                 acpi_state::PowerStateType::sysPowerState, s)) | 
 |         { | 
 |             log<level::ERR>("set_acpi_power sys invalid input", | 
 |                             entry("S=%x", s)); | 
 |             return ipmi::responseParmOutOfRange(); | 
 |         } | 
 |  | 
 |         // valid input | 
 |         if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange)) | 
 |         { | 
 |             log<level::DEBUG>("No change for system power state"); | 
 |         } | 
 |         else | 
 |         { | 
 |             auto found = std::find_if( | 
 |                 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(), | 
 |                 [&s](const auto& iter) { | 
 |                     return (static_cast<uint8_t>(iter.second) == s); | 
 |                 }); | 
 |  | 
 |             value = found->first; | 
 |  | 
 |             try | 
 |             { | 
 |                 auto acpiObject = | 
 |                     ipmi::getDbusObject(bus, acpi_state::acpiInterface); | 
 |                 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first, | 
 |                                       acpi_state::acpiInterface, | 
 |                                       acpi_state::sysACPIProp, | 
 |                                       convertForMessage(value)); | 
 |             } | 
 |             catch (const InternalFailure& e) | 
 |             { | 
 |                 log<level::ERR>("Failed in set ACPI system property", | 
 |                                 entry("EXCEPTION=%s", e.what())); | 
 |                 return ipmi::responseUnspecifiedError(); | 
 |             } | 
 |         } | 
 |     } | 
 |     else | 
 |     { | 
 |         log<level::DEBUG>("Do not change system power state"); | 
 |     } | 
 |  | 
 |     if (devAcpiState & acpi_state::stateChanged) | 
 |     { | 
 |         // set device power state | 
 |         s = devAcpiState & ~acpi_state::stateChanged; | 
 |         if (!acpi_state::isValidACPIState( | 
 |                 acpi_state::PowerStateType::devPowerState, s)) | 
 |         { | 
 |             log<level::ERR>("set_acpi_power dev invalid input", | 
 |                             entry("S=%x", s)); | 
 |             return ipmi::responseParmOutOfRange(); | 
 |         } | 
 |  | 
 |         // valid input | 
 |         if (s == static_cast<uint8_t>(acpi_state::PowerState::noChange)) | 
 |         { | 
 |             log<level::DEBUG>("No change for device power state"); | 
 |         } | 
 |         else | 
 |         { | 
 |             auto found = std::find_if( | 
 |                 acpi_state::dbusToIPMI.begin(), acpi_state::dbusToIPMI.end(), | 
 |                 [&s](const auto& iter) { | 
 |                     return (static_cast<uint8_t>(iter.second) == s); | 
 |                 }); | 
 |  | 
 |             value = found->first; | 
 |  | 
 |             try | 
 |             { | 
 |                 auto acpiObject = | 
 |                     ipmi::getDbusObject(bus, acpi_state::acpiInterface); | 
 |                 ipmi::setDbusProperty(bus, acpiObject.second, acpiObject.first, | 
 |                                       acpi_state::acpiInterface, | 
 |                                       acpi_state::devACPIProp, | 
 |                                       convertForMessage(value)); | 
 |             } | 
 |             catch (const InternalFailure& e) | 
 |             { | 
 |                 log<level::ERR>("Failed in set ACPI device property", | 
 |                                 entry("EXCEPTION=%s", e.what())); | 
 |                 return ipmi::responseUnspecifiedError(); | 
 |             } | 
 |         } | 
 |     } | 
 |     else | 
 |     { | 
 |         log<level::DEBUG>("Do not change device power state"); | 
 |     } | 
 |     return ipmi::responseSuccess(); | 
 | } | 
 |  | 
 | /** | 
 |  *  @brief implements the get ACPI power state command | 
 |  * | 
 |  *  @return IPMI completion code plus response data on success. | 
 |  *   -  ACPI system power state | 
 |  *   -  ACPI device power state | 
 |  **/ | 
 | ipmi::RspType<uint8_t, // acpiSystemPowerState | 
 |               uint8_t  // acpiDevicePowerState | 
 |               > | 
 |     ipmiGetAcpiPowerState() | 
 | { | 
 |     uint8_t sysAcpiState; | 
 |     uint8_t devAcpiState; | 
 |  | 
 |     sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; | 
 |  | 
 |     try | 
 |     { | 
 |         auto acpiObject = ipmi::getDbusObject(bus, acpi_state::acpiInterface); | 
 |  | 
 |         auto sysACPIVal = ipmi::getDbusProperty( | 
 |             bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface, | 
 |             acpi_state::sysACPIProp); | 
 |         auto sysACPI = acpi_state::ACPIPowerState::convertACPIFromString( | 
 |             std::get<std::string>(sysACPIVal)); | 
 |         sysAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(sysACPI)); | 
 |  | 
 |         auto devACPIVal = ipmi::getDbusProperty( | 
 |             bus, acpiObject.second, acpiObject.first, acpi_state::acpiInterface, | 
 |             acpi_state::devACPIProp); | 
 |         auto devACPI = acpi_state::ACPIPowerState::convertACPIFromString( | 
 |             std::get<std::string>(devACPIVal)); | 
 |         devAcpiState = static_cast<uint8_t>(acpi_state::dbusToIPMI.at(devACPI)); | 
 |     } | 
 |     catch (const InternalFailure& e) | 
 |     { | 
 |         return ipmi::responseUnspecifiedError(); | 
 |     } | 
 |  | 
 |     return ipmi::responseSuccess(sysAcpiState, devAcpiState); | 
 | } | 
 |  | 
 | typedef struct | 
 | { | 
 |     char major; | 
 |     char minor; | 
 |     uint16_t d[2]; | 
 | } Revision; | 
 |  | 
 | /* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will     */ | 
 | /* return -1 if not in those formats, this routine knows how to parse       */ | 
 | /* version = v0.6-19-gf363f61-dirty                                         */ | 
 | /*            ^ ^ ^^          ^                                             */ | 
 | /*            | |  |----------|-- additional details                        */ | 
 | /*            | |---------------- Minor                                     */ | 
 | /*            |------------------ Major                                     */ | 
 | /* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715                        */ | 
 | /*                ^ ^  ^^ ^                                                 */ | 
 | /*                | |  |--|---------- additional details                    */ | 
 | /*                | |---------------- Minor                                 */ | 
 | /*                |------------------ Major                                 */ | 
 | /* Additional details : If the option group exists it will force Auxiliary  */ | 
 | /* Firmware Revision Information 4th byte to 1 indicating the build was     */ | 
 | /* derived with additional edits                                            */ | 
 | int convertVersion(std::string_view s, Revision& rev) | 
 | { | 
 |     std::string_view token; | 
 |     uint16_t commits; | 
 |  | 
 |     auto location = s.find_first_of('v'); | 
 |     if (location != std::string::npos) | 
 |     { | 
 |         s = s.substr(location + 1); | 
 |     } | 
 |  | 
 |     if (!s.empty()) | 
 |     { | 
 |         location = s.find_first_of("."); | 
 |         if (location != std::string::npos) | 
 |         { | 
 |             std::string_view majorView = s.substr(0, location); | 
 |             auto [ptr, ec]{ | 
 |                 std::from_chars(majorView.begin(), majorView.end(), rev.major)}; | 
 |             if (ec != std::errc()) | 
 |             { | 
 |                 throw std::runtime_error( | 
 |                     "failed to convert major string to uint8_t: " + | 
 |                     std::make_error_code(ec).message()); | 
 |             } | 
 |             if (ptr != majorView.begin() + majorView.size()) | 
 |             { | 
 |                 throw std::runtime_error( | 
 |                     "converted invalid characters in major string"); | 
 |             } | 
 |             token = s.substr(location + 1); | 
 |         } | 
 |  | 
 |         if (!token.empty()) | 
 |         { | 
 |             location = token.find_first_of(".-"); | 
 |             if (location != std::string::npos) | 
 |             { | 
 |                 std::string_view minorView = token.substr(0, location); | 
 |                 auto [ptr, ec]{std::from_chars(minorView.begin(), | 
 |                                                minorView.end(), rev.minor)}; | 
 |                 if (ec != std::errc()) | 
 |                 { | 
 |                     throw std::runtime_error( | 
 |                         "failed to convert minor string to uint8_t: " + | 
 |                         std::make_error_code(ec).message()); | 
 |                 } | 
 |                 if (ptr != minorView.begin() + minorView.size()) | 
 |                 { | 
 |                     throw std::runtime_error( | 
 |                         "converted invalid characters in minor string"); | 
 |                 } | 
 |                 token = token.substr(location + 1); | 
 |             } | 
 |         } | 
 |  | 
 |         // Capture the number of commits on top of the minor tag. | 
 |         // I'm using BE format like the ipmi spec asked for | 
 |         location = token.find_first_of(".-"); | 
 |         if (!token.empty()) | 
 |         { | 
 |             std::string_view commitView = token.substr(0, location); | 
 |             auto [ptr, ec]{std::from_chars(commitView.begin(), commitView.end(), | 
 |                                            commits, 16)}; | 
 |             if (ec != std::errc()) | 
 |             { | 
 |                 throw std::runtime_error( | 
 |                     "failed to convert commit string to uint16_t: " + | 
 |                     std::make_error_code(ec).message()); | 
 |             } | 
 |             if (ptr != commitView.begin() + commitView.size()) | 
 |             { | 
 |                 throw std::runtime_error( | 
 |                     "converted invalid characters in commit string"); | 
 |             } | 
 |             rev.d[0] = (commits >> 8) | (commits << 8); | 
 |  | 
 |             // commit number we skip | 
 |             location = token.find_first_of(".-"); | 
 |             if (location != std::string::npos) | 
 |             { | 
 |                 token = token.substr(location + 1); | 
 |             } | 
 |         } | 
 |         else | 
 |         { | 
 |             rev.d[0] = 0; | 
 |         } | 
 |  | 
 |         if (location != std::string::npos) | 
 |         { | 
 |             token = token.substr(location + 1); | 
 |         } | 
 |  | 
 |         // Any value of the optional parameter forces it to 1 | 
 |         location = token.find_first_of(".-"); | 
 |         if (location != std::string::npos) | 
 |         { | 
 |             token = token.substr(location + 1); | 
 |         } | 
 |         commits = (!token.empty()) ? 1 : 0; | 
 |  | 
 |         // We do this operation to get this displayed in least significant bytes | 
 |         // of ipmitool device id command. | 
 |         rev.d[1] = (commits >> 8) | (commits << 8); | 
 |     } | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | /* @brief: Implement the Get Device ID IPMI command per the IPMI spec | 
 |  *  @param[in] ctx - shared_ptr to an IPMI context struct | 
 |  * | 
 |  *  @returns IPMI completion code plus response data | 
 |  *   - Device ID (manufacturer defined) | 
 |  *   - Device revision[4 bits]; reserved[3 bits]; SDR support[1 bit] | 
 |  *   - FW revision major[7 bits] (binary encoded); available[1 bit] | 
 |  *   - FW Revision minor (BCD encoded) | 
 |  *   - IPMI version (0x02 for IPMI 2.0) | 
 |  *   - device support (bitfield of supported options) | 
 |  *   - MFG IANA ID (3 bytes) | 
 |  *   - product ID (2 bytes) | 
 |  *   - AUX info (4 bytes) | 
 |  */ | 
 | ipmi::RspType<uint8_t,  // Device ID | 
 |               uint8_t,  // Device Revision | 
 |               uint8_t,  // Firmware Revision Major | 
 |               uint8_t,  // Firmware Revision minor | 
 |               uint8_t,  // IPMI version | 
 |               uint8_t,  // Additional device support | 
 |               uint24_t, // MFG ID | 
 |               uint16_t, // Product ID | 
 |               uint32_t  // AUX info | 
 |               > | 
 |     ipmiAppGetDeviceId([[maybe_unused]] ipmi::Context::ptr ctx) | 
 | { | 
 |     int r = -1; | 
 |     Revision rev = {0, 0, 0, 0}; | 
 |     static struct | 
 |     { | 
 |         uint8_t id; | 
 |         uint8_t revision; | 
 |         uint8_t fw[2]; | 
 |         uint8_t ipmiVer; | 
 |         uint8_t addnDevSupport; | 
 |         uint24_t manufId; | 
 |         uint16_t prodId; | 
 |         uint32_t aux; | 
 |     } devId; | 
 |     static bool dev_id_initialized = false; | 
 |     static bool haveBMCVersion = false; | 
 |     static bool defaultActivationSetting = true; | 
 |     const char* filename = "/usr/share/ipmi-providers/dev_id.json"; | 
 |     constexpr auto ipmiDevIdStateShift = 7; | 
 |     constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift); | 
 |     if (!haveBMCVersion || !dev_id_initialized) | 
 |     { | 
 |         try | 
 |         { | 
 |             auto version = getActiveSoftwareVersionInfo(ctx); | 
 |             r = convertVersion(version, rev); | 
 |         } | 
 |         catch (const std::exception& e) | 
 |         { | 
 |             log<level::ERR>(e.what()); | 
 |         } | 
 |  | 
 |         if (r >= 0) | 
 |         { | 
 |             // bit7 identifies if the device is available | 
 |             // 0=normal operation | 
 |             // 1=device firmware, SDR update, | 
 |             // or self-initialization in progress. | 
 |             // The availability may change in run time, so mask here | 
 |             // and initialize later. | 
 |             devId.fw[0] = rev.major & ipmiDevIdFw1Mask; | 
 |  | 
 |             rev.minor = (rev.minor > 99 ? 99 : rev.minor); | 
 |             devId.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16; | 
 |             std::memcpy(&devId.aux, rev.d, 4); | 
 |             haveBMCVersion = true; | 
 |         } | 
 |     } | 
 |     if (!dev_id_initialized) | 
 |     { | 
 |         // IPMI Spec version 2.0 | 
 |         devId.ipmiVer = 2; | 
 |  | 
 |         std::ifstream devIdFile(filename); | 
 |         if (devIdFile.is_open()) | 
 |         { | 
 |             auto data = nlohmann::json::parse(devIdFile, nullptr, false); | 
 |             if (!data.is_discarded()) | 
 |             { | 
 |                 devId.id = data.value("id", 0); | 
 |                 devId.revision = data.value("revision", 0); | 
 |                 devId.addnDevSupport = data.value("addn_dev_support", 0); | 
 |                 devId.manufId = data.value("manuf_id", 0); | 
 |                 devId.prodId = data.value("prod_id", 0); | 
 |                 devId.aux = data.value("aux", 0); | 
 |  | 
 |                 if (data.contains("firmware_revision")) | 
 |                 { | 
 |                     const auto& firmwareRevision = data.at("firmware_revision"); | 
 |                     if (firmwareRevision.contains("major")) | 
 |                     { | 
 |                         firmwareRevision.at("major").get_to(devId.fw[0]); | 
 |                     } | 
 |                     if (firmwareRevision.contains("minor")) | 
 |                     { | 
 |                         firmwareRevision.at("minor").get_to(devId.fw[1]); | 
 |                     } | 
 |                 } | 
 |  | 
 |                 // Set the availablitity of the BMC. | 
 |                 defaultActivationSetting = data.value("availability", true); | 
 |  | 
 |                 // Don't read the file every time if successful | 
 |                 dev_id_initialized = true; | 
 |             } | 
 |             else | 
 |             { | 
 |                 log<level::ERR>("Device ID JSON parser failure"); | 
 |                 return ipmi::responseUnspecifiedError(); | 
 |             } | 
 |         } | 
 |         else | 
 |         { | 
 |             log<level::ERR>("Device ID file not found"); | 
 |             return ipmi::responseUnspecifiedError(); | 
 |         } | 
 |     } | 
 |  | 
 |     // Set availability to the actual current BMC state | 
 |     devId.fw[0] &= ipmiDevIdFw1Mask; | 
 |     if (!getCurrentBmcStateWithFallback(defaultActivationSetting)) | 
 |     { | 
 |         devId.fw[0] |= (1 << ipmiDevIdStateShift); | 
 |     } | 
 |  | 
 |     return ipmi::responseSuccess( | 
 |         devId.id, devId.revision, devId.fw[0], devId.fw[1], devId.ipmiVer, | 
 |         devId.addnDevSupport, devId.manufId, devId.prodId, devId.aux); | 
 | } | 
 |  | 
 | auto ipmiAppGetSelfTestResults() -> ipmi::RspType<uint8_t, uint8_t> | 
 | { | 
 |     // Byte 2: | 
 |     //  55h - No error. | 
 |     //  56h - Self Test function not implemented in this controller. | 
 |     //  57h - Corrupted or inaccesssible data or devices. | 
 |     //  58h - Fatal hardware error. | 
 |     //  FFh - reserved. | 
 |     //  all other: Device-specific 'internal failure'. | 
 |     //  Byte 3: | 
 |     //      For byte 2 = 55h, 56h, FFh:     00h | 
 |     //      For byte 2 = 58h, all other:    Device-specific | 
 |     //      For byte 2 = 57h:   self-test error bitfield. | 
 |     //      Note: returning 57h does not imply that all test were run. | 
 |     //      [7] 1b = Cannot access SEL device. | 
 |     //      [6] 1b = Cannot access SDR Repository. | 
 |     //      [5] 1b = Cannot access BMC FRU device. | 
 |     //      [4] 1b = IPMB signal lines do not respond. | 
 |     //      [3] 1b = SDR Repository empty. | 
 |     //      [2] 1b = Internal Use Area of BMC FRU corrupted. | 
 |     //      [1] 1b = controller update 'boot block' firmware corrupted. | 
 |     //      [0] 1b = controller operational firmware corrupted. | 
 |     constexpr uint8_t notImplemented = 0x56; | 
 |     constexpr uint8_t zero = 0; | 
 |     return ipmi::responseSuccess(notImplemented, zero); | 
 | } | 
 |  | 
 | static constexpr size_t uuidBinaryLength = 16; | 
 | static std::array<uint8_t, uuidBinaryLength> rfc4122ToIpmi(std::string rfc4122) | 
 | { | 
 |     using Argument = xyz::openbmc_project::Common::InvalidArgument; | 
 |     // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223 | 
 |     // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte | 
 |     // order | 
 |     // Ex: 0x2332fc2c40e66298e511f2782395a361 | 
 |     constexpr size_t uuidHexLength = (2 * uuidBinaryLength); | 
 |     constexpr size_t uuidRfc4122Length = (uuidHexLength + 4); | 
 |     std::array<uint8_t, uuidBinaryLength> uuid; | 
 |     if (rfc4122.size() == uuidRfc4122Length) | 
 |     { | 
 |         rfc4122.erase(std::remove(rfc4122.begin(), rfc4122.end(), '-'), | 
 |                       rfc4122.end()); | 
 |     } | 
 |     if (rfc4122.size() != uuidHexLength) | 
 |     { | 
 |         elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"), | 
 |                               Argument::ARGUMENT_VALUE(rfc4122.c_str())); | 
 |     } | 
 |     for (size_t ind = 0; ind < uuidHexLength; ind += 2) | 
 |     { | 
 |         char v[3]; | 
 |         v[0] = rfc4122[ind]; | 
 |         v[1] = rfc4122[ind + 1]; | 
 |         v[2] = 0; | 
 |         size_t err; | 
 |         long b; | 
 |         try | 
 |         { | 
 |             b = std::stoul(v, &err, 16); | 
 |         } | 
 |         catch (const std::exception& e) | 
 |         { | 
 |             elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"), | 
 |                                   Argument::ARGUMENT_VALUE(rfc4122.c_str())); | 
 |         } | 
 |         // check that exactly two ascii bytes were converted | 
 |         if (err != 2) | 
 |         { | 
 |             elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"), | 
 |                                   Argument::ARGUMENT_VALUE(rfc4122.c_str())); | 
 |         } | 
 |         uuid[uuidBinaryLength - (ind / 2) - 1] = static_cast<uint8_t>(b); | 
 |     } | 
 |     return uuid; | 
 | } | 
 |  | 
 | auto ipmiAppGetDeviceGuid() | 
 |     -> ipmi::RspType<std::array<uint8_t, uuidBinaryLength>> | 
 | { | 
 |     // return a fixed GUID based on /etc/machine-id | 
 |     // This should match the /redfish/v1/Managers/bmc's UUID data | 
 |  | 
 |     // machine specific application ID (for BMC ID) | 
 |     // generated by systemd-id128 -p new as per man page | 
 |     static constexpr sd_id128_t bmcUuidAppId = SD_ID128_MAKE( | 
 |         e0, e1, 73, 76, 64, 61, 47, da, a5, 0c, d0, cc, 64, 12, 45, 78); | 
 |  | 
 |     sd_id128_t bmcUuid; | 
 |     // create the UUID from /etc/machine-id via the systemd API | 
 |     sd_id128_get_machine_app_specific(bmcUuidAppId, &bmcUuid); | 
 |  | 
 |     char bmcUuidCstr[SD_ID128_STRING_MAX]; | 
 |     std::string systemUuid = sd_id128_to_string(bmcUuid, bmcUuidCstr); | 
 |  | 
 |     std::array<uint8_t, uuidBinaryLength> uuid = rfc4122ToIpmi(systemUuid); | 
 |     return ipmi::responseSuccess(uuid); | 
 | } | 
 |  | 
 | auto ipmiAppGetBtCapabilities() | 
 |     -> ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t, uint8_t> | 
 | { | 
 |     // Per IPMI 2.0 spec, the input and output buffer size must be the max | 
 |     // buffer size minus one byte to allocate space for the length byte. | 
 |     constexpr uint8_t nrOutstanding = 0x01; | 
 |     constexpr uint8_t inputBufferSize = MAX_IPMI_BUFFER - 1; | 
 |     constexpr uint8_t outputBufferSize = MAX_IPMI_BUFFER - 1; | 
 |     constexpr uint8_t transactionTime = 0x0A; | 
 |     constexpr uint8_t nrRetries = 0x01; | 
 |  | 
 |     return ipmi::responseSuccess(nrOutstanding, inputBufferSize, | 
 |                                  outputBufferSize, transactionTime, nrRetries); | 
 | } | 
 |  | 
 | auto ipmiAppGetSystemGuid() -> ipmi::RspType<std::array<uint8_t, 16>> | 
 | { | 
 |     static constexpr auto uuidInterface = "xyz.openbmc_project.Common.UUID"; | 
 |     static constexpr auto uuidProperty = "UUID"; | 
 |  | 
 |     ipmi::Value propValue; | 
 |     try | 
 |     { | 
 |         // Get the Inventory object implementing BMC interface | 
 |         auto busPtr = getSdBus(); | 
 |         auto objectInfo = ipmi::getDbusObject(*busPtr, uuidInterface); | 
 |  | 
 |         // Read UUID property value from bmcObject | 
 |         // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223 | 
 |         propValue = | 
 |             ipmi::getDbusProperty(*busPtr, objectInfo.second, objectInfo.first, | 
 |                                   uuidInterface, uuidProperty); | 
 |     } | 
 |     catch (const InternalFailure& e) | 
 |     { | 
 |         log<level::ERR>("Failed in reading BMC UUID property", | 
 |                         entry("INTERFACE=%s", uuidInterface), | 
 |                         entry("PROPERTY=%s", uuidProperty)); | 
 |         return ipmi::responseUnspecifiedError(); | 
 |     } | 
 |     std::array<uint8_t, 16> uuid; | 
 |     std::string rfc4122Uuid = std::get<std::string>(propValue); | 
 |     try | 
 |     { | 
 |         // convert to IPMI format | 
 |         uuid = rfc4122ToIpmi(rfc4122Uuid); | 
 |     } | 
 |     catch (const InvalidArgument& e) | 
 |     { | 
 |         log<level::ERR>("Failed in parsing BMC UUID property", | 
 |                         entry("INTERFACE=%s", uuidInterface), | 
 |                         entry("PROPERTY=%s", uuidProperty), | 
 |                         entry("VALUE=%s", rfc4122Uuid.c_str())); | 
 |         return ipmi::responseUnspecifiedError(); | 
 |     } | 
 |     return ipmi::responseSuccess(uuid); | 
 | } | 
 |  | 
 | /** | 
 |  * @brief set the session state as teardown | 
 |  * | 
 |  * This function is to set the session state to tear down in progress if the | 
 |  * state is active. | 
 |  * | 
 |  * @param[in] busp - Dbus obj | 
 |  * @param[in] service - service name | 
 |  * @param[in] obj - object path | 
 |  * | 
 |  * @return success completion code if it sets the session state to | 
 |  * tearDownInProgress else return the corresponding error completion code. | 
 |  **/ | 
 | uint8_t setSessionState(std::shared_ptr<sdbusplus::asio::connection>& busp, | 
 |                         const std::string& service, const std::string& obj) | 
 | { | 
 |     try | 
 |     { | 
 |         uint8_t sessionState = std::get<uint8_t>(ipmi::getDbusProperty( | 
 |             *busp, service, obj, session::sessionIntf, "State")); | 
 |  | 
 |         if (sessionState == static_cast<uint8_t>(session::State::active)) | 
 |         { | 
 |             ipmi::setDbusProperty( | 
 |                 *busp, service, obj, session::sessionIntf, "State", | 
 |                 static_cast<uint8_t>(session::State::tearDownInProgress)); | 
 |             return ipmi::ccSuccess; | 
 |         } | 
 |     } | 
 |     catch (const std::exception& e) | 
 |     { | 
 |         log<level::ERR>("Failed in getting session state property", | 
 |                         entry("service=%s", service.c_str()), | 
 |                         entry("object path=%s", obj.c_str()), | 
 |                         entry("interface=%s", session::sessionIntf)); | 
 |         return ipmi::ccUnspecifiedError; | 
 |     } | 
 |  | 
 |     return ipmi::ccInvalidFieldRequest; | 
 | } | 
 |  | 
 | ipmi::RspType<> ipmiAppCloseSession(uint32_t reqSessionId, | 
 |                                     std::optional<uint8_t> requestSessionHandle) | 
 | { | 
 |     auto busp = getSdBus(); | 
 |     uint8_t reqSessionHandle = | 
 |         requestSessionHandle.value_or(session::defaultSessionHandle); | 
 |  | 
 |     if (reqSessionId == session::sessionZero && | 
 |         reqSessionHandle == session::defaultSessionHandle) | 
 |     { | 
 |         return ipmi::response(session::ccInvalidSessionId); | 
 |     } | 
 |  | 
 |     if (reqSessionId == session::sessionZero && | 
 |         reqSessionHandle == session::invalidSessionHandle) | 
 |     { | 
 |         return ipmi::response(session::ccInvalidSessionHandle); | 
 |     } | 
 |  | 
 |     if (reqSessionId != session::sessionZero && | 
 |         reqSessionHandle != session::defaultSessionHandle) | 
 |     { | 
 |         return ipmi::response(ipmi::ccInvalidFieldRequest); | 
 |     } | 
 |  | 
 |     try | 
 |     { | 
 |         ipmi::ObjectTree objectTree = ipmi::getAllDbusObjects( | 
 |             *busp, session::sessionManagerRootPath, session::sessionIntf); | 
 |  | 
 |         for (auto& objectTreeItr : objectTree) | 
 |         { | 
 |             const std::string obj = objectTreeItr.first; | 
 |  | 
 |             if (isSessionObjectMatched(obj, reqSessionId, reqSessionHandle)) | 
 |             { | 
 |                 auto& serviceMap = objectTreeItr.second; | 
 |  | 
 |                 // Session id and session handle are unique for each session. | 
 |                 // Session id and handler are retrived from the object path and | 
 |                 // object path will be unique for each session. Checking if | 
 |                 // multiple objects exist with same object path under multiple | 
 |                 // services. | 
 |                 if (serviceMap.size() != 1) | 
 |                 { | 
 |                     return ipmi::responseUnspecifiedError(); | 
 |                 } | 
 |  | 
 |                 auto itr = serviceMap.begin(); | 
 |                 const std::string service = itr->first; | 
 |                 return ipmi::response(setSessionState(busp, service, obj)); | 
 |             } | 
 |         } | 
 |     } | 
 |     catch (const sdbusplus::exception_t& e) | 
 |     { | 
 |         log<level::ERR>("Failed to fetch object from dbus", | 
 |                         entry("INTERFACE=%s", session::sessionIntf), | 
 |                         entry("ERRMSG=%s", e.what())); | 
 |         return ipmi::responseUnspecifiedError(); | 
 |     } | 
 |  | 
 |     return ipmi::responseInvalidFieldRequest(); | 
 | } | 
 |  | 
 | uint8_t getTotalSessionCount() | 
 | { | 
 |     uint8_t count = 0, ch = 0; | 
 |  | 
 |     while (ch < ipmi::maxIpmiChannels && | 
 |            count < session::maxNetworkInstanceSupported) | 
 |     { | 
 |         ipmi::ChannelInfo chInfo{}; | 
 |         ipmi::getChannelInfo(ch, chInfo); | 
 |         if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) == | 
 |             ipmi::EChannelMediumType::lan8032) | 
 |         { | 
 |             count++; | 
 |         } | 
 |         ch++; | 
 |     } | 
 |     return count * session::maxSessionCountPerChannel; | 
 | } | 
 |  | 
 | /** | 
 |  * @brief get session info request data. | 
 |  * | 
 |  * This function validates the request data and retrive request session id, | 
 |  * session handle. | 
 |  * | 
 |  * @param[in] ctx - context of current session. | 
 |  * @param[in] sessionIndex - request session index | 
 |  * @param[in] payload - input payload | 
 |  * @param[in] reqSessionId - unpacked session Id will be asigned | 
 |  * @param[in] reqSessionHandle - unpacked session handle will be asigned | 
 |  * | 
 |  * @return success completion code if request data is valid | 
 |  * else return the correcponding error completion code. | 
 |  **/ | 
 | uint8_t getSessionInfoRequestData(const ipmi::Context::ptr ctx, | 
 |                                   const uint8_t sessionIndex, | 
 |                                   ipmi::message::Payload& payload, | 
 |                                   uint32_t& reqSessionId, | 
 |                                   uint8_t& reqSessionHandle) | 
 | { | 
 |     if ((sessionIndex > session::maxSessionCountPerChannel) && | 
 |         (sessionIndex < session::searchSessionByHandle)) | 
 |     { | 
 |         return ipmi::ccInvalidFieldRequest; | 
 |     } | 
 |  | 
 |     switch (sessionIndex) | 
 |     { | 
 |         case session::searchCurrentSession: | 
 |  | 
 |             ipmi::ChannelInfo chInfo; | 
 |             ipmi::getChannelInfo(ctx->channel, chInfo); | 
 |  | 
 |             if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) != | 
 |                 ipmi::EChannelMediumType::lan8032) | 
 |             { | 
 |                 return ipmi::ccInvalidFieldRequest; | 
 |             } | 
 |  | 
 |             if (!payload.fullyUnpacked()) | 
 |             { | 
 |                 return ipmi::ccReqDataLenInvalid; | 
 |             } | 
 |             // Check if current sessionId is 0, sessionId 0 is reserved. | 
 |             if (ctx->sessionId == session::sessionZero) | 
 |             { | 
 |                 return session::ccInvalidSessionId; | 
 |             } | 
 |             reqSessionId = ctx->sessionId; | 
 |             break; | 
 |  | 
 |         case session::searchSessionByHandle: | 
 |  | 
 |             if ((payload.unpack(reqSessionHandle)) || | 
 |                 (!payload.fullyUnpacked())) | 
 |             { | 
 |                 return ipmi::ccReqDataLenInvalid; | 
 |             } | 
 |  | 
 |             if ((reqSessionHandle == session::sessionZero) || | 
 |                 ((reqSessionHandle & session::multiIntfaceSessionHandleMask) > | 
 |                  session::maxSessionCountPerChannel)) | 
 |             { | 
 |                 return session::ccInvalidSessionHandle; | 
 |             } | 
 |             break; | 
 |  | 
 |         case session::searchSessionById: | 
 |  | 
 |             if ((payload.unpack(reqSessionId)) || (!payload.fullyUnpacked())) | 
 |             { | 
 |                 return ipmi::ccReqDataLenInvalid; | 
 |             } | 
 |  | 
 |             if (reqSessionId == session::sessionZero) | 
 |             { | 
 |                 return session::ccInvalidSessionId; | 
 |             } | 
 |             break; | 
 |  | 
 |         default: | 
 |             if (!payload.fullyUnpacked()) | 
 |             { | 
 |                 return ipmi::ccReqDataLenInvalid; | 
 |             } | 
 |             break; | 
 |     } | 
 |     return ipmi::ccSuccess; | 
 | } | 
 |  | 
 | uint8_t getSessionState(ipmi::Context::ptr ctx, const std::string& service, | 
 |                         const std::string& objPath, uint8_t& sessionState) | 
 | { | 
 |     boost::system::error_code ec = ipmi::getDbusProperty( | 
 |         ctx, service, objPath, session::sessionIntf, "State", sessionState); | 
 |     if (ec) | 
 |     { | 
 |         log<level::ERR>("Failed to fetch state property ", | 
 |                         entry("SERVICE=%s", service.c_str()), | 
 |                         entry("OBJECTPATH=%s", objPath.c_str()), | 
 |                         entry("INTERFACE=%s", session::sessionIntf), | 
 |                         entry("ERRMSG=%s", ec.message().c_str())); | 
 |         return ipmi::ccUnspecifiedError; | 
 |     } | 
 |     return ipmi::ccSuccess; | 
 | } | 
 |  | 
 | static constexpr uint8_t macAddrLen = 6; | 
 | /** Alias SessionDetails - contain the optional information about an | 
 |  *        RMCP+ session. | 
 |  * | 
 |  *  @param userID - uint6_t session user ID (0-63) | 
 |  *  @param reserved - uint2_t reserved | 
 |  *  @param privilege - uint4_t session privilege (0-5) | 
 |  *  @param reserved - uint4_t reserved | 
 |  *  @param channel - uint4_t session channel number | 
 |  *  @param protocol - uint4_t session protocol | 
 |  *  @param remoteIP - uint32_t remote IP address | 
 |  *  @param macAddr - std::array<uint8_t, 6> mac address | 
 |  *  @param port - uint16_t remote port | 
 |  */ | 
 | using SessionDetails = | 
 |     std::tuple<uint2_t, uint6_t, uint4_t, uint4_t, uint4_t, uint4_t, uint32_t, | 
 |                std::array<uint8_t, macAddrLen>, uint16_t>; | 
 |  | 
 | /** @brief get session details for a given session | 
 |  * | 
 |  *  @param[in] ctx - ipmi::Context pointer for accessing D-Bus | 
 |  *  @param[in] service - D-Bus service name to fetch details from | 
 |  *  @param[in] objPath - D-Bus object path for session | 
 |  *  @param[out] sessionHandle - return session handle for session | 
 |  *  @param[out] sessionState - return session state for session | 
 |  *  @param[out] details - return a SessionDetails tuple containing other | 
 |  *                        session info | 
 |  *  @return - ipmi::Cc success or error code | 
 |  */ | 
 | ipmi::Cc getSessionDetails(ipmi::Context::ptr ctx, const std::string& service, | 
 |                            const std::string& objPath, uint8_t& sessionHandle, | 
 |                            uint8_t& sessionState, SessionDetails& details) | 
 | { | 
 |     ipmi::PropertyMap sessionProps; | 
 |     boost::system::error_code ec = ipmi::getAllDbusProperties( | 
 |         ctx, service, objPath, session::sessionIntf, sessionProps); | 
 |  | 
 |     if (ec) | 
 |     { | 
 |         log<level::ERR>("Failed to fetch state property ", | 
 |                         entry("SERVICE=%s", service.c_str()), | 
 |                         entry("OBJECTPATH=%s", objPath.c_str()), | 
 |                         entry("INTERFACE=%s", session::sessionIntf), | 
 |                         entry("ERRMSG=%s", ec.message().c_str())); | 
 |         return ipmi::ccUnspecifiedError; | 
 |     } | 
 |  | 
 |     sessionState = ipmi::mappedVariant<uint8_t>( | 
 |         sessionProps, "State", static_cast<uint8_t>(session::State::inactive)); | 
 |     if (sessionState == static_cast<uint8_t>(session::State::active)) | 
 |     { | 
 |         sessionHandle = | 
 |             ipmi::mappedVariant<uint8_t>(sessionProps, "SessionHandle", 0); | 
 |         std::get<0>(details) = | 
 |             ipmi::mappedVariant<uint8_t>(sessionProps, "UserID", 0xff); | 
 |         // std::get<1>(details) = 0; // (default constructed to 0) | 
 |         std::get<2>(details) = | 
 |             ipmi::mappedVariant<uint8_t>(sessionProps, "CurrentPrivilege", 0); | 
 |         // std::get<3>(details) = 0; // (default constructed to 0) | 
 |         std::get<4>(details) = | 
 |             ipmi::mappedVariant<uint8_t>(sessionProps, "ChannelNum", 0xff); | 
 |         constexpr uint4_t rmcpPlusProtocol = 1; | 
 |         std::get<5>(details) = rmcpPlusProtocol; | 
 |         std::get<6>(details) = | 
 |             ipmi::mappedVariant<uint32_t>(sessionProps, "RemoteIPAddr", 0); | 
 |         // std::get<7>(details) = {{0}}; // default constructed to all 0 | 
 |         std::get<8>(details) = | 
 |             ipmi::mappedVariant<uint16_t>(sessionProps, "RemotePort", 0); | 
 |     } | 
 |  | 
 |     return ipmi::ccSuccess; | 
 | } | 
 |  | 
 | ipmi::RspType<uint8_t, // session handle, | 
 |               uint8_t, // total session count | 
 |               uint8_t, // active session count | 
 |               std::optional<SessionDetails>> | 
 |     ipmiAppGetSessionInfo(ipmi::Context::ptr ctx, uint8_t sessionIndex, | 
 |                           ipmi::message::Payload& payload) | 
 | { | 
 |     uint32_t reqSessionId = 0; | 
 |     uint8_t reqSessionHandle = session::defaultSessionHandle; | 
 |     // initializing state to 0xff as 0 represents state as inactive. | 
 |     uint8_t state = 0xFF; | 
 |  | 
 |     uint8_t completionCode = getSessionInfoRequestData( | 
 |         ctx, sessionIndex, payload, reqSessionId, reqSessionHandle); | 
 |  | 
 |     if (completionCode) | 
 |     { | 
 |         return ipmi::response(completionCode); | 
 |     } | 
 |     ipmi::ObjectTree objectTree; | 
 |     boost::system::error_code ec = ipmi::getAllDbusObjects( | 
 |         ctx, session::sessionManagerRootPath, session::sessionIntf, objectTree); | 
 |     if (ec) | 
 |     { | 
 |         log<level::ERR>("Failed to fetch object from dbus", | 
 |                         entry("INTERFACE=%s", session::sessionIntf), | 
 |                         entry("ERRMSG=%s", ec.message().c_str())); | 
 |         return ipmi::responseUnspecifiedError(); | 
 |     } | 
 |  | 
 |     uint8_t totalSessionCount = getTotalSessionCount(); | 
 |     uint8_t activeSessionCount = 0; | 
 |     uint8_t sessionHandle = session::defaultSessionHandle; | 
 |     std::optional<SessionDetails> maybeDetails; | 
 |     uint8_t index = 0; | 
 |     for (auto& objectTreeItr : objectTree) | 
 |     { | 
 |         uint32_t sessionId = 0; | 
 |         std::string objectPath = objectTreeItr.first; | 
 |  | 
 |         if (!parseCloseSessionInputPayload(objectPath, sessionId, | 
 |                                            sessionHandle)) | 
 |         { | 
 |             continue; | 
 |         } | 
 |         index++; | 
 |         auto& serviceMap = objectTreeItr.second; | 
 |         auto itr = serviceMap.begin(); | 
 |  | 
 |         if (serviceMap.size() != 1) | 
 |         { | 
 |             return ipmi::responseUnspecifiedError(); | 
 |         } | 
 |  | 
 |         std::string service = itr->first; | 
 |         uint8_t sessionState = 0; | 
 |         completionCode = | 
 |             getSessionState(ctx, service, objectPath, sessionState); | 
 |         if (completionCode) | 
 |         { | 
 |             return ipmi::response(completionCode); | 
 |         } | 
 |  | 
 |         if (sessionState == static_cast<uint8_t>(session::State::active)) | 
 |         { | 
 |             activeSessionCount++; | 
 |         } | 
 |  | 
 |         if (index != sessionIndex && reqSessionId != sessionId && | 
 |             reqSessionHandle != sessionHandle) | 
 |         { | 
 |             continue; | 
 |         } | 
 |  | 
 |         SessionDetails details{}; | 
 |         completionCode = getSessionDetails(ctx, service, objectPath, | 
 |                                            sessionHandle, state, details); | 
 |  | 
 |         if (completionCode) | 
 |         { | 
 |             return ipmi::response(completionCode); | 
 |         } | 
 |         maybeDetails = std::move(details); | 
 |         break; | 
 |     } | 
 |  | 
 |     if (state == static_cast<uint8_t>(session::State::active) || | 
 |         state == static_cast<uint8_t>(session::State::tearDownInProgress)) | 
 |     { | 
 |         return ipmi::responseSuccess(sessionHandle, totalSessionCount, | 
 |                                      activeSessionCount, maybeDetails); | 
 |     } | 
 |  | 
 |     return ipmi::responseInvalidFieldRequest(); | 
 | } | 
 |  | 
 | static std::unique_ptr<SysInfoParamStore> sysInfoParamStore; | 
 |  | 
 | static std::string sysInfoReadSystemName() | 
 | { | 
 |     // Use the BMC hostname as the "System Name." | 
 |     char hostname[HOST_NAME_MAX + 1] = {}; | 
 |     if (gethostname(hostname, HOST_NAME_MAX) != 0) | 
 |     { | 
 |         perror("System info parameter: system name"); | 
 |     } | 
 |     return hostname; | 
 | } | 
 |  | 
 | static constexpr uint8_t paramRevision = 0x11; | 
 | static constexpr size_t configParameterLength = 16; | 
 |  | 
 | static constexpr size_t smallChunkSize = 14; | 
 | static constexpr size_t fullChunkSize = 16; | 
 | static constexpr uint8_t progressMask = 0x3; | 
 |  | 
 | static constexpr uint8_t setComplete = 0x0; | 
 | static constexpr uint8_t setInProgress = 0x1; | 
 | static constexpr uint8_t commitWrite = 0x2; | 
 | static uint8_t transferStatus = setComplete; | 
 |  | 
 | static constexpr uint8_t configDataOverhead = 2; | 
 |  | 
 | // For EFI based system, 256 bytes is recommended. | 
 | static constexpr size_t maxBytesPerParameter = 256; | 
 |  | 
 | namespace ipmi | 
 | { | 
 | constexpr Cc ccParmNotSupported = 0x80; | 
 | constexpr Cc ccSetInProgressActive = 0x81; | 
 | constexpr Cc ccSystemInfoParameterSetReadOnly = 0x82; | 
 |  | 
 | static inline auto responseParmNotSupported() | 
 | { | 
 |     return response(ccParmNotSupported); | 
 | } | 
 | static inline auto responseSetInProgressActive() | 
 | { | 
 |     return response(ccSetInProgressActive); | 
 | } | 
 | static inline auto responseSystemInfoParameterSetReadOnly() | 
 | { | 
 |     return response(ccSystemInfoParameterSetReadOnly); | 
 | } | 
 | } // namespace ipmi | 
 |  | 
 | ipmi::RspType<uint8_t,                // Parameter revision | 
 |               std::optional<uint8_t>, // data1 / setSelector / ProgressStatus | 
 |               std::optional<std::vector<uint8_t>>> // data2-17 | 
 |     ipmiAppGetSystemInfo(uint7_t reserved, bool getRevision, | 
 |                          uint8_t paramSelector, uint8_t setSelector, | 
 |                          uint8_t BlockSelector) | 
 | { | 
 |     if (reserved || (paramSelector >= invalidParamSelectorStart && | 
 |                      paramSelector <= invalidParamSelectorEnd)) | 
 |     { | 
 |         return ipmi::responseInvalidFieldRequest(); | 
 |     } | 
 |     if ((paramSelector >= oemCmdStart) && (paramSelector <= oemCmdEnd)) | 
 |     { | 
 |         return ipmi::responseParmNotSupported(); | 
 |     } | 
 |     if (getRevision) | 
 |     { | 
 |         return ipmi::responseSuccess(paramRevision, std::nullopt, std::nullopt); | 
 |     } | 
 |  | 
 |     if (paramSelector == 0) | 
 |     { | 
 |         return ipmi::responseSuccess(paramRevision, transferStatus, | 
 |                                      std::nullopt); | 
 |     } | 
 |  | 
 |     if (BlockSelector != 0) // 00h if parameter does not require a block number | 
 |     { | 
 |         return ipmi::responseParmNotSupported(); | 
 |     } | 
 |  | 
 |     if (sysInfoParamStore == nullptr) | 
 |     { | 
 |         sysInfoParamStore = std::make_unique<SysInfoParamStore>(); | 
 |         sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME, | 
 |                                   sysInfoReadSystemName); | 
 |     } | 
 |  | 
 |     // Parameters other than Set In Progress are assumed to be strings. | 
 |     std::tuple<bool, std::string> ret = | 
 |         sysInfoParamStore->lookup(paramSelector); | 
 |     bool found = std::get<0>(ret); | 
 |     if (!found) | 
 |     { | 
 |         return ipmi::responseSensorInvalid(); | 
 |     } | 
 |     std::string& paramString = std::get<1>(ret); | 
 |     std::vector<uint8_t> configData; | 
 |     size_t count = 0; | 
 |     if (setSelector == 0) | 
 |     {                               // First chunk has only 14 bytes. | 
 |         configData.emplace_back(0); // encoding | 
 |         configData.emplace_back(paramString.length()); // string length | 
 |         count = std::min(paramString.length(), smallChunkSize); | 
 |         configData.resize(count + configDataOverhead); | 
 |         std::copy_n(paramString.begin(), count, | 
 |                     configData.begin() + configDataOverhead); // 14 bytes chunk | 
 |  | 
 |         // Append zero's to remaining bytes | 
 |         if (configData.size() < configParameterLength) | 
 |         { | 
 |             std::fill_n(std::back_inserter(configData), | 
 |                         configParameterLength - configData.size(), 0x00); | 
 |         } | 
 |     } | 
 |     else | 
 |     { | 
 |         size_t offset = (setSelector * fullChunkSize) - configDataOverhead; | 
 |         if (offset >= paramString.length()) | 
 |         { | 
 |             return ipmi::responseParmOutOfRange(); | 
 |         } | 
 |         count = std::min(paramString.length() - offset, fullChunkSize); | 
 |         configData.resize(count); | 
 |         std::copy_n(paramString.begin() + offset, count, | 
 |                     configData.begin()); // 16 bytes chunk | 
 |     } | 
 |     return ipmi::responseSuccess(paramRevision, setSelector, configData); | 
 | } | 
 |  | 
 | ipmi::RspType<> ipmiAppSetSystemInfo(uint8_t paramSelector, uint8_t data1, | 
 |                                      std::vector<uint8_t> configData) | 
 | { | 
 |     if (paramSelector >= invalidParamSelectorStart && | 
 |         paramSelector <= invalidParamSelectorEnd) | 
 |     { | 
 |         return ipmi::responseInvalidFieldRequest(); | 
 |     } | 
 |     if ((paramSelector >= oemCmdStart) && (paramSelector <= oemCmdEnd)) | 
 |     { | 
 |         return ipmi::responseParmNotSupported(); | 
 |     } | 
 |  | 
 |     if (paramSelector == 0) | 
 |     { | 
 |         // attempt to set the 'set in progress' value (in parameter #0) | 
 |         // when not in the set complete state. | 
 |         if ((transferStatus != setComplete) && (data1 == setInProgress)) | 
 |         { | 
 |             return ipmi::responseSetInProgressActive(); | 
 |         } | 
 |         // only following 2 states are supported | 
 |         if (data1 > setInProgress) | 
 |         { | 
 |             phosphor::logging::log<phosphor::logging::level::ERR>( | 
 |                 "illegal SetInProgress status"); | 
 |             return ipmi::responseInvalidFieldRequest(); | 
 |         } | 
 |  | 
 |         transferStatus = data1 & progressMask; | 
 |         return ipmi::responseSuccess(); | 
 |     } | 
 |  | 
 |     if (configData.size() > configParameterLength) | 
 |     { | 
 |         return ipmi::responseInvalidFieldRequest(); | 
 |     } | 
 |  | 
 |     // Append zero's to remaining bytes | 
 |     if (configData.size() < configParameterLength) | 
 |     { | 
 |         fill_n(back_inserter(configData), | 
 |                (configParameterLength - configData.size()), 0x00); | 
 |     } | 
 |  | 
 |     if (!sysInfoParamStore) | 
 |     { | 
 |         sysInfoParamStore = std::make_unique<SysInfoParamStore>(); | 
 |         sysInfoParamStore->update(IPMI_SYSINFO_SYSTEM_NAME, | 
 |                                   sysInfoReadSystemName); | 
 |     } | 
 |  | 
 |     // lookup | 
 |     std::tuple<bool, std::string> ret = | 
 |         sysInfoParamStore->lookup(paramSelector); | 
 |     bool found = std::get<0>(ret); | 
 |     std::string& paramString = std::get<1>(ret); | 
 |     if (!found) | 
 |     { | 
 |         // parameter does not exist. Init new | 
 |         paramString = ""; | 
 |     } | 
 |  | 
 |     uint8_t setSelector = data1; | 
 |     size_t count = 0; | 
 |     if (setSelector == 0) // First chunk has only 14 bytes. | 
 |     { | 
 |         size_t stringLen = configData.at(1); // string length | 
 |         // maxBytesPerParamter is 256. It will always be greater than stringLen | 
 |         // (unit8_t) if maxBytes changes in future, then following line is | 
 |         // needed. | 
 |         // stringLen = std::min(stringLen, maxBytesPerParameter); | 
 |         count = std::min(stringLen, smallChunkSize); | 
 |         count = std::min(count, configData.size()); | 
 |         paramString.resize(stringLen); // reserve space | 
 |         std::copy_n(configData.begin() + configDataOverhead, count, | 
 |                     paramString.begin()); | 
 |     } | 
 |     else | 
 |     { | 
 |         size_t offset = (setSelector * fullChunkSize) - configDataOverhead; | 
 |         if (offset >= paramString.length()) | 
 |         { | 
 |             return ipmi::responseParmOutOfRange(); | 
 |         } | 
 |         count = std::min(paramString.length() - offset, configData.size()); | 
 |         std::copy_n(configData.begin(), count, paramString.begin() + offset); | 
 |     } | 
 |     sysInfoParamStore->update(paramSelector, paramString); | 
 |     return ipmi::responseSuccess(); | 
 | } | 
 |  | 
 | #ifdef ENABLE_I2C_WHITELIST_CHECK | 
 | inline std::vector<uint8_t> convertStringToData(const std::string& command) | 
 | { | 
 |     std::istringstream iss(command); | 
 |     std::string token; | 
 |     std::vector<uint8_t> dataValue; | 
 |     while (std::getline(iss, token, ' ')) | 
 |     { | 
 |         dataValue.emplace_back( | 
 |             static_cast<uint8_t>(std::stoul(token, nullptr, base_16))); | 
 |     } | 
 |     return dataValue; | 
 | } | 
 |  | 
 | static bool populateI2CMasterWRWhitelist() | 
 | { | 
 |     nlohmann::json data = nullptr; | 
 |     std::ifstream jsonFile(i2cMasterWRWhitelistFile); | 
 |  | 
 |     if (!jsonFile.good()) | 
 |     { | 
 |         log<level::WARNING>("i2c white list file not found!", | 
 |                             entry("FILE_NAME: %s", i2cMasterWRWhitelistFile)); | 
 |         return false; | 
 |     } | 
 |  | 
 |     try | 
 |     { | 
 |         data = nlohmann::json::parse(jsonFile, nullptr, false); | 
 |     } | 
 |     catch (const nlohmann::json::parse_error& e) | 
 |     { | 
 |         log<level::ERR>("Corrupted i2c white list config file", | 
 |                         entry("FILE_NAME: %s", i2cMasterWRWhitelistFile), | 
 |                         entry("MSG: %s", e.what())); | 
 |         return false; | 
 |     } | 
 |  | 
 |     try | 
 |     { | 
 |         // Example JSON Structure format | 
 |         // "filters": [ | 
 |         //    { | 
 |         //      "Description": "Allow full read - ignore first byte write value | 
 |         //      for 0x40 to 0x4F", | 
 |         //      "busId": "0x01", | 
 |         //      "slaveAddr": "0x40", | 
 |         //      "slaveAddrMask": "0x0F", | 
 |         //      "command": "0x00", | 
 |         //      "commandMask": "0xFF" | 
 |         //    }, | 
 |         //    { | 
 |         //      "Description": "Allow full read - first byte match 0x05 and | 
 |         //      ignore second byte", | 
 |         //      "busId": "0x01", | 
 |         //      "slaveAddr": "0x57", | 
 |         //      "slaveAddrMask": "0x00", | 
 |         //      "command": "0x05 0x00", | 
 |         //      "commandMask": "0x00 0xFF" | 
 |         //    },] | 
 |  | 
 |         nlohmann::json filters = data[filtersStr].get<nlohmann::json>(); | 
 |         std::vector<i2cMasterWRWhitelist>& whitelist = getWRWhitelist(); | 
 |         for (const auto& it : filters.items()) | 
 |         { | 
 |             nlohmann::json filter = it.value(); | 
 |             if (filter.is_null()) | 
 |             { | 
 |                 log<level::ERR>( | 
 |                     "Corrupted I2C master write read whitelist config file", | 
 |                     entry("FILE_NAME: %s", i2cMasterWRWhitelistFile)); | 
 |                 return false; | 
 |             } | 
 |             const std::vector<uint8_t>& writeData = | 
 |                 convertStringToData(filter[cmdStr].get<std::string>()); | 
 |             const std::vector<uint8_t>& writeDataMask = | 
 |                 convertStringToData(filter[cmdMaskStr].get<std::string>()); | 
 |             if (writeDataMask.size() != writeData.size()) | 
 |             { | 
 |                 log<level::ERR>("I2C master write read whitelist filter " | 
 |                                 "mismatch for command & mask size"); | 
 |                 return false; | 
 |             } | 
 |             whitelist.push_back( | 
 |                 {static_cast<uint8_t>(std::stoul( | 
 |                      filter[busIdStr].get<std::string>(), nullptr, base_16)), | 
 |                  static_cast<uint8_t>( | 
 |                      std::stoul(filter[slaveAddrStr].get<std::string>(), | 
 |                                 nullptr, base_16)), | 
 |                  static_cast<uint8_t>( | 
 |                      std::stoul(filter[slaveAddrMaskStr].get<std::string>(), | 
 |                                 nullptr, base_16)), | 
 |                  writeData, writeDataMask}); | 
 |         } | 
 |         if (whitelist.size() != filters.size()) | 
 |         { | 
 |             log<level::ERR>( | 
 |                 "I2C master write read whitelist filter size mismatch"); | 
 |             return false; | 
 |         } | 
 |     } | 
 |     catch (const std::exception& e) | 
 |     { | 
 |         log<level::ERR>("I2C master write read whitelist unexpected exception", | 
 |                         entry("ERROR=%s", e.what())); | 
 |         return false; | 
 |     } | 
 |     return true; | 
 | } | 
 |  | 
 | static inline bool isWriteDataWhitelisted(const std::vector<uint8_t>& data, | 
 |                                           const std::vector<uint8_t>& dataMask, | 
 |                                           const std::vector<uint8_t>& writeData) | 
 | { | 
 |     std::vector<uint8_t> processedDataBuf(data.size()); | 
 |     std::vector<uint8_t> processedReqBuf(dataMask.size()); | 
 |     std::transform(writeData.begin(), writeData.end(), dataMask.begin(), | 
 |                    processedReqBuf.begin(), std::bit_or<uint8_t>()); | 
 |     std::transform(data.begin(), data.end(), dataMask.begin(), | 
 |                    processedDataBuf.begin(), std::bit_or<uint8_t>()); | 
 |  | 
 |     return (processedDataBuf == processedReqBuf); | 
 | } | 
 |  | 
 | static bool isCmdWhitelisted(uint8_t busId, uint8_t slaveAddr, | 
 |                              std::vector<uint8_t>& writeData) | 
 | { | 
 |     std::vector<i2cMasterWRWhitelist>& whiteList = getWRWhitelist(); | 
 |     for (const auto& wlEntry : whiteList) | 
 |     { | 
 |         if ((busId == wlEntry.busId) && | 
 |             ((slaveAddr | wlEntry.slaveAddrMask) == | 
 |              (wlEntry.slaveAddr | wlEntry.slaveAddrMask))) | 
 |         { | 
 |             const std::vector<uint8_t>& dataMask = wlEntry.dataMask; | 
 |             // Skip as no-match, if requested write data is more than the | 
 |             // write data mask size | 
 |             if (writeData.size() > dataMask.size()) | 
 |             { | 
 |                 continue; | 
 |             } | 
 |             if (isWriteDataWhitelisted(wlEntry.data, dataMask, writeData)) | 
 |             { | 
 |                 return true; | 
 |             } | 
 |         } | 
 |     } | 
 |     return false; | 
 | } | 
 | #else | 
 | static bool populateI2CMasterWRWhitelist() | 
 | { | 
 |     log<level::INFO>( | 
 |         "I2C_WHITELIST_CHECK is disabled, do not populate whitelist"); | 
 |     return true; | 
 | } | 
 | #endif // ENABLE_I2C_WHITELIST_CHECK | 
 |  | 
 | /** @brief implements master write read IPMI command which can be used for | 
 |  * low-level I2C/SMBus write, read or write-read access | 
 |  *  @param isPrivateBus -to indicate private bus usage | 
 |  *  @param busId - bus id | 
 |  *  @param channelNum - channel number | 
 |  *  @param reserved - skip 1 bit | 
 |  *  @param slaveAddr - slave address | 
 |  *  @param read count - number of bytes to be read | 
 |  *  @param writeData - data to be written | 
 |  * | 
 |  *  @returns IPMI completion code plus response data | 
 |  *   - readData - i2c response data | 
 |  */ | 
 | ipmi::RspType<std::vector<uint8_t>> | 
 |     ipmiMasterWriteRead([[maybe_unused]] bool isPrivateBus, uint3_t busId, | 
 |                         [[maybe_unused]] uint4_t channelNum, bool reserved, | 
 |                         uint7_t slaveAddr, uint8_t readCount, | 
 |                         std::vector<uint8_t> writeData) | 
 | { | 
 |     if (reserved) | 
 |     { | 
 |         return ipmi::responseInvalidFieldRequest(); | 
 |     } | 
 |     if (readCount > maxIPMIWriteReadSize) | 
 |     { | 
 |         log<level::ERR>("Master write read command: Read count exceeds limit"); | 
 |         return ipmi::responseParmOutOfRange(); | 
 |     } | 
 |     const size_t writeCount = writeData.size(); | 
 |     if (!readCount && !writeCount) | 
 |     { | 
 |         log<level::ERR>("Master write read command: Read & write count are 0"); | 
 |         return ipmi::responseInvalidFieldRequest(); | 
 |     } | 
 | #ifdef ENABLE_I2C_WHITELIST_CHECK | 
 |     if (!isCmdWhitelisted(static_cast<uint8_t>(busId), | 
 |                           static_cast<uint8_t>(slaveAddr), writeData)) | 
 |     { | 
 |         log<level::ERR>("Master write read request blocked!", | 
 |                         entry("BUS=%d", static_cast<uint8_t>(busId)), | 
 |                         entry("ADDR=0x%x", static_cast<uint8_t>(slaveAddr))); | 
 |         return ipmi::responseInvalidFieldRequest(); | 
 |     } | 
 | #endif // ENABLE_I2C_WHITELIST_CHECK | 
 |     std::vector<uint8_t> readBuf(readCount); | 
 |     std::string i2cBus = | 
 |         "/dev/i2c-" + std::to_string(static_cast<uint8_t>(busId)); | 
 |  | 
 |     ipmi::Cc ret = ipmi::i2cWriteRead(i2cBus, static_cast<uint8_t>(slaveAddr), | 
 |                                       writeData, readBuf); | 
 |     if (ret != ipmi::ccSuccess) | 
 |     { | 
 |         return ipmi::response(ret); | 
 |     } | 
 |     return ipmi::responseSuccess(readBuf); | 
 | } | 
 |  | 
 | void register_netfn_app_functions() | 
 | { | 
 |     // <Get Device ID> | 
 |     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, | 
 |                           ipmi::app::cmdGetDeviceId, ipmi::Privilege::User, | 
 |                           ipmiAppGetDeviceId); | 
 |  | 
 |     // <Get BT Interface Capabilities> | 
 |     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, | 
 |                           ipmi::app::cmdGetBtIfaceCapabilities, | 
 |                           ipmi::Privilege::User, ipmiAppGetBtCapabilities); | 
 |  | 
 |     // <Reset Watchdog Timer> | 
 |     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, | 
 |                           ipmi::app::cmdResetWatchdogTimer, | 
 |                           ipmi::Privilege::Operator, ipmiAppResetWatchdogTimer); | 
 |  | 
 |     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, | 
 |                           ipmi::app::cmdGetSessionInfo, ipmi::Privilege::User, | 
 |                           ipmiAppGetSessionInfo); | 
 |  | 
 |     // <Set Watchdog Timer> | 
 |     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, | 
 |                           ipmi::app::cmdSetWatchdogTimer, | 
 |                           ipmi::Privilege::Operator, ipmiSetWatchdogTimer); | 
 |  | 
 |     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, | 
 |                           ipmi::app::cmdCloseSession, ipmi::Privilege::Callback, | 
 |                           ipmiAppCloseSession); | 
 |  | 
 |     // <Get Watchdog Timer> | 
 |     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, | 
 |                           ipmi::app::cmdGetWatchdogTimer, ipmi::Privilege::User, | 
 |                           ipmiGetWatchdogTimer); | 
 |  | 
 |     // <Get Self Test Results> | 
 |     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, | 
 |                           ipmi::app::cmdGetSelfTestResults, | 
 |                           ipmi::Privilege::User, ipmiAppGetSelfTestResults); | 
 |  | 
 |     // <Get Device GUID> | 
 |     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, | 
 |                           ipmi::app::cmdGetDeviceGuid, ipmi::Privilege::User, | 
 |                           ipmiAppGetDeviceGuid); | 
 |  | 
 |     // <Set ACPI Power State> | 
 |     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, | 
 |                           ipmi::app::cmdSetAcpiPowerState, | 
 |                           ipmi::Privilege::Admin, ipmiSetAcpiPowerState); | 
 |     // <Get ACPI Power State> | 
 |     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, | 
 |                           ipmi::app::cmdGetAcpiPowerState, | 
 |                           ipmi::Privilege::User, ipmiGetAcpiPowerState); | 
 |  | 
 |     // Note: For security reason, this command will be registered only when | 
 |     // there are proper I2C Master write read whitelist | 
 |     if (populateI2CMasterWRWhitelist()) | 
 |     { | 
 |         // Note: For security reasons, registering master write read as admin | 
 |         // privilege command, even though IPMI 2.0 specification allows it as | 
 |         // operator privilege. | 
 |         ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, | 
 |                               ipmi::app::cmdMasterWriteRead, | 
 |                               ipmi::Privilege::Admin, ipmiMasterWriteRead); | 
 |     } | 
 |  | 
 |     // <Get System GUID Command> | 
 |     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, | 
 |                           ipmi::app::cmdGetSystemGuid, ipmi::Privilege::User, | 
 |                           ipmiAppGetSystemGuid); | 
 |  | 
 |     // <Get Channel Cipher Suites Command> | 
 |     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, | 
 |                           ipmi::app::cmdGetChannelCipherSuites, | 
 |                           ipmi::Privilege::None, getChannelCipherSuites); | 
 |  | 
 |     // <Get System Info Command> | 
 |     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, | 
 |                           ipmi::app::cmdGetSystemInfoParameters, | 
 |                           ipmi::Privilege::User, ipmiAppGetSystemInfo); | 
 |     // <Set System Info Command> | 
 |     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, | 
 |                           ipmi::app::cmdSetSystemInfoParameters, | 
 |                           ipmi::Privilege::Admin, ipmiAppSetSystemInfo); | 
 |     return; | 
 | } |