#include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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::error::xyz::openbmc_project::common; using Version = sdbusplus::server::xyz::openbmc_project::software::Version; using Activation = sdbusplus::server::xyz::openbmc_project::software::Activation; using BMC = sdbusplus::server::xyz::openbmc_project::state::BMC; namespace fs = std::filesystem; #ifdef ENABLE_I2C_WHITELIST_CHECK typedef struct { uint8_t busId; uint8_t targetAddr; uint8_t targetAddrMask; std::vector data; std::vector dataMask; } i2cControllerWRAllowlist; static std::vector& getWRAllowlist() { static std::vector wrAllowlist; return wrAllowlist; } static constexpr const char* i2cControllerWRAllowlistFile = "/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* targetAddrStr = "slaveAddr"; static constexpr const char* targetAddrMaskStr = "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 oemCmdStart = 192; 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) { lg2::error("Failed to fetch redundancy object from dbus, " "interface: {INTERFACE}, error: {ERROR}", "INTERFACE", redundancyIntf, "ERROR", e); elog(); } 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(redundancyPriorityProps.at("Priority")); auto purpose = std::get(versionProps.at("Purpose")); auto activation = std::get(activationProps.at("Activation")); auto version = std::get(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) { lg2::error("error message: {ERROR}", "ERROR", e); } } } if (!objectFound) { lg2::error("Could not found an BMC software Object"); elog(); } 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(variant) && BMC::convertBMCStateFromString(std::get(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::server::xyz::openbmc_project::control::power; 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 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(acpi_state::PowerState::override)) || (state == static_cast(acpi_state::PowerState::legacyOn)) || (state == static_cast(acpi_state::PowerState::legacyOff)) || (state == static_cast(acpi_state::PowerState::unknown)) || (state == static_cast(acpi_state::PowerState::noChange))) { return true; } else { return false; } } else if (type == acpi_state::PowerStateType::devPowerState) { if ((state <= static_cast(acpi_state::PowerState::s3D3)) || (state == static_cast(acpi_state::PowerState::unknown)) || (state == static_cast(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(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)) { lg2::error("set_acpi_power sys invalid input, S: {S}", "S", s); return ipmi::responseParmOutOfRange(); } // valid input if (s == static_cast(acpi_state::PowerState::noChange)) { lg2::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(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) { lg2::error("Failed in set ACPI system property: {ERROR}", "ERROR", e); return ipmi::responseUnspecifiedError(); } } } else { lg2::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)) { lg2::error("set_acpi_power dev invalid input, S: {S}", "S", s); return ipmi::responseParmOutOfRange(); } // valid input if (s == static_cast(acpi_state::PowerState::noChange)) { lg2::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(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) { lg2::error("Failed in set ACPI device property: {ERROR}", "ERROR", e); return ipmi::responseUnspecifiedError(); } } } else { lg2::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 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(sysACPIVal)); sysAcpiState = static_cast(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(devACPIVal)); devAcpiState = static_cast(acpi_state::dbusToIPMI.at(devACPI)); } catch (const InternalFailure& e) { return ipmi::responseUnspecifiedError(); } return ipmi::responseSuccess(sysAcpiState, devAcpiState); } typedef struct { char major; char minor; uint8_t aux[4]; } Revision; /* Use regular expression searching matched pattern X.Y, and convert it to */ /* Major (X) and Minor (Y) version. */ /* Example: */ /* version = 2.14.0-dev */ /* ^ ^ */ /* | |---------------- Minor */ /* |------------------ Major */ /* */ /* Default regex string only tries to match Major and Minor version. */ /* */ /* To match more firmware version info, platforms need to define it own */ /* regex string to match more strings, and assign correct mapping index in */ /* matches array. */ /* */ /* matches[0]: matched index for major ver */ /* matches[1]: matched index for minor ver */ /* matches[2]: matched index for aux[0] (set 0 to skip) */ /* matches[3]: matched index for aux[1] (set 0 to skip) */ /* matches[4]: matched index for aux[2] (set 0 to skip) */ /* matches[5]: matched index for aux[3] (set 0 to skip) */ /* Example: */ /* regex = "([\d]+).([\d]+).([\d]+)-dev-([\d]+)-g([0-9a-fA-F]{2}) */ /* ([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})" */ /* matches = {1,2,5,6,7,8} */ /* version = 2.14.0-dev-750-g37a7c5ad1-dirty */ /* ^ ^ ^ ^ ^ ^ ^ ^ */ /* | | | | | | | | */ /* | | | | | | | |-- Aux byte 3 (0xAD), index 8 */ /* | | | | | | |---- Aux byte 2 (0xC5), index 7 */ /* | | | | | |------ Aux byte 1 (0xA7), index 6 */ /* | | | | |-------- Aux byte 0 (0x37), index 5 */ /* | | | |------------- Not used, index 4 */ /* | | |------------------- Not used, index 3 */ /* | |---------------------- Minor (14), index 2 */ /* |------------------------ Major (2), index 1 */ int convertVersion(std::string s, Revision& rev) { static const std::vector matches = { MAJOR_MATCH_INDEX, MINOR_MATCH_INDEX, AUX_0_MATCH_INDEX, AUX_1_MATCH_INDEX, AUX_2_MATCH_INDEX, AUX_3_MATCH_INDEX}; std::regex fw_regex(FW_VER_REGEX); std::smatch m; Revision r = {0}; size_t val; if (std::regex_search(s, m, fw_regex)) { if (m.size() < *std::max_element(matches.begin(), matches.end())) { // max index higher than match count return -1; } // convert major { std::string_view str = m[matches[0]].str(); auto [ptr, ec]{std::from_chars(str.begin(), str.end(), val)}; if (ec != std::errc() || ptr != str.begin() + str.size()) { // failed to convert major string return -1; } if (val >= 2000) { // For the platforms use year as major version, it would expect to // have major version between 0 - 99. If the major version is // greater than or equal to 2000, it is treated as a year and // converted to 0 - 99. r.major = val % 100; } else { r.major = val & 0x7F; } } // convert minor { std::string_view str = m[matches[1]].str(); auto [ptr, ec]{std::from_chars(str.begin(), str.end(), val)}; if (ec != std::errc() || ptr != str.begin() + str.size()) { // failed to convert minor string return -1; } r.minor = val & 0xFF; } // convert aux bytes { size_t i; for (i = 0; i < 4; i++) { if (matches[i + 2] == 0) { continue; } std::string_view str = m[matches[i + 2]].str(); auto [ptr, ec]{std::from_chars(str.begin(), str.end(), val, 16)}; if (ec != std::errc() || ptr != str.begin() + str.size()) { // failed to convert aux byte string break; } r.aux[i] = val & 0xFF; } if (i != 4) { // something wrong durign converting aux bytes return -1; } } // all matched rev = r; return 0; } return -1; } /* @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 ipmiAppGetDeviceId([[maybe_unused]] ipmi::Context::ptr ctx) { 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 defaultActivationSetting = true; const char* filename = "/usr/share/ipmi-providers/dev_id.json"; constexpr auto ipmiDevIdStateShift = 7; constexpr auto ipmiDevIdFw1Mask = ~(1 << ipmiDevIdStateShift); #ifdef GET_DBUS_ACTIVE_SOFTWARE static bool haveBMCVersion = false; if (!haveBMCVersion || !dev_id_initialized) { int r = -1; Revision rev = {0, 0, 0, 0}; try { auto version = getActiveSoftwareVersionInfo(ctx); r = convertVersion(version, rev); } catch (const std::exception& e) { lg2::error("error message: {ERROR}", "ERROR", e); } 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.aux, sizeof(rev.aux)); haveBMCVersion = true; } } #endif 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); #ifdef GET_DBUS_ACTIVE_SOFTWARE if (!(AUX_0_MATCH_INDEX || AUX_1_MATCH_INDEX || AUX_2_MATCH_INDEX || AUX_3_MATCH_INDEX)) #endif { 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 { lg2::error("Device ID JSON parser failure"); return ipmi::responseUnspecifiedError(); } } else { lg2::error("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 { // 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 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 uuid; if (rfc4122.size() == uuidRfc4122Length) { rfc4122.erase(std::remove(rfc4122.begin(), rfc4122.end(), '-'), rfc4122.end()); } if (rfc4122.size() != uuidHexLength) { elog(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(Argument::ARGUMENT_NAME("rfc4122"), Argument::ARGUMENT_VALUE(rfc4122.c_str())); } // check that exactly two ascii bytes were converted if (err != 2) { elog(Argument::ARGUMENT_NAME("rfc4122"), Argument::ARGUMENT_VALUE(rfc4122.c_str())); } uuid[uuidBinaryLength - (ind / 2) - 1] = static_cast(b); } return uuid; } auto ipmiAppGetDeviceGuid() -> ipmi::RspType> { // 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 uuid = rfc4122ToIpmi(systemUuid); return ipmi::responseSuccess(uuid); } auto ipmiAppGetBtCapabilities() -> ipmi::RspType { // 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::Context::ptr& ctx) -> ipmi::RspType> { static constexpr auto uuidInterface = "xyz.openbmc_project.Common.UUID"; static constexpr auto uuidProperty = "UUID"; // Get the Inventory object implementing BMC interface ipmi::DbusObjectInfo objectInfo{}; boost::system::error_code ec = ipmi::getDbusObject(ctx, uuidInterface, objectInfo); if (ec.value()) { lg2::error("Failed to locate System UUID object, " "interface: {INTERFACE}, error: {ERROR}", "INTERFACE", uuidInterface, "ERROR", ec.message()); } // Read UUID property value from bmcObject // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223 std::string rfc4122Uuid{}; ec = ipmi::getDbusProperty(ctx, objectInfo.second, objectInfo.first, uuidInterface, uuidProperty, rfc4122Uuid); if (ec.value()) { lg2::error("Failed to read System UUID property, " "interface: {INTERFACE}, property: {PROPERTY}, " "error: {ERROR}", "INTERFACE", uuidInterface, "PROPERTY", uuidProperty, "ERROR", ec.message()); return ipmi::responseUnspecifiedError(); } std::array uuid; try { // convert to IPMI format uuid = rfc4122ToIpmi(rfc4122Uuid); } catch (const InvalidArgument& e) { lg2::error("Failed in parsing BMC UUID property, " "interface: {INTERFACE}, property: {PROPERTY}, " "value: {VALUE}, error: {ERROR}", "INTERFACE", uuidInterface, "PROPERTY", uuidProperty, "VALUE", rfc4122Uuid, "ERROR", e); 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& busp, const std::string& service, const std::string& obj) { try { uint8_t sessionState = std::get(ipmi::getDbusProperty( *busp, service, obj, session::sessionIntf, "State")); if (sessionState == static_cast(session::State::active)) { ipmi::setDbusProperty( *busp, service, obj, session::sessionIntf, "State", static_cast(session::State::tearDownInProgress)); return ipmi::ccSuccess; } } catch (const std::exception& e) { lg2::error("Failed in getting session state property, " "service: {SERVICE}, object path: {OBJECT_PATH}, " "interface: {INTERFACE}, error: {ERROR}", "SERVICE", service, "OBJECT_PATH", obj, "INTERFACE", session::sessionIntf, "ERROR", e); return ipmi::ccUnspecifiedError; } return ipmi::ccInvalidFieldRequest; } ipmi::RspType<> ipmiAppCloseSession(uint32_t reqSessionId, std::optional 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) { lg2::error("Failed to fetch object from dbus, " "interface: {INTERFACE}, error: {ERROR}", "INTERFACE", session::sessionIntf, "ERROR", e); 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(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(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) { lg2::error("Failed to fetch state property, service: {SERVICE}, " "object path: {OBJECTPATH}, interface: {INTERFACE}, " "error: {ERROR}", "SERVICE", service, "OBJECTPATH", objPath, "INTERFACE", session::sessionIntf, "ERROR", ec.message()); 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 mac address * @param port - uint16_t remote port */ using SessionDetails = std::tuple, 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) { lg2::error("Failed to fetch state property, service: {SERVICE}, " "object path: {OBJECTPATH}, interface: {INTERFACE}, " "error: {ERROR}", "SERVICE", service, "OBJECTPATH", objPath, "INTERFACE", session::sessionIntf, "ERROR", ec.message()); return ipmi::ccUnspecifiedError; } sessionState = ipmi::mappedVariant( sessionProps, "State", static_cast(session::State::inactive)); if (sessionState == static_cast(session::State::active)) { sessionHandle = ipmi::mappedVariant(sessionProps, "SessionHandle", 0); std::get<0>(details) = ipmi::mappedVariant(sessionProps, "UserID", 0xff); // std::get<1>(details) = 0; // (default constructed to 0) std::get<2>(details) = ipmi::mappedVariant(sessionProps, "CurrentPrivilege", 0); // std::get<3>(details) = 0; // (default constructed to 0) std::get<4>(details) = ipmi::mappedVariant(sessionProps, "ChannelNum", 0xff); constexpr uint4_t rmcpPlusProtocol = 1; std::get<5>(details) = rmcpPlusProtocol; std::get<6>(details) = ipmi::mappedVariant(sessionProps, "RemoteIPAddr", 0); // std::get<7>(details) = {{0}}; // default constructed to all 0 std::get<8>(details) = ipmi::mappedVariant(sessionProps, "RemotePort", 0); } return ipmi::ccSuccess; } ipmi::RspType> 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) { lg2::error("Failed to fetch object from dbus, " "interface: {INTERFACE}, error: {ERROR}", "INTERFACE", session::sessionIntf, "ERROR", ec.message()); return ipmi::responseUnspecifiedError(); } uint8_t totalSessionCount = getTotalSessionCount(); uint8_t activeSessionCount = 0; uint8_t sessionHandle = session::defaultSessionHandle; uint8_t activeSessionHandle = 0; std::optional 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(session::State::active)) { activeSessionCount++; } if (index == sessionIndex || reqSessionId == sessionId || reqSessionHandle == sessionHandle) { SessionDetails details{}; completionCode = getSessionDetails(ctx, service, objectPath, sessionHandle, state, details); if (completionCode) { return ipmi::response(completionCode); } activeSessionHandle = sessionHandle; maybeDetails = std::move(details); } } if (state == static_cast(session::State::active) || state == static_cast(session::State::tearDownInProgress)) { return ipmi::responseSuccess(activeSessionHandle, totalSessionCount, activeSessionCount, maybeDetails); } return ipmi::responseInvalidFieldRequest(); } static std::unique_ptr 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 maxValidEncodingData = 0x02; static constexpr uint8_t setComplete = 0x0; static constexpr uint8_t setInProgress = 0x1; static uint8_t transferStatus = setComplete; static constexpr uint8_t configDataOverhead = 2; namespace ipmi { constexpr Cc ccParmNotSupported = 0x80; constexpr Cc ccSetInProgressActive = 0x81; static inline auto responseParmNotSupported() { return response(ccParmNotSupported); } static inline auto responseSetInProgressActive() { return response(ccSetInProgressActive); } } // namespace ipmi ipmi::RspType, // data1 / setSelector / ProgressStatus std::optional>> // 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) { 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->update(IPMI_SYSINFO_SYSTEM_NAME, sysInfoReadSystemName); } // Parameters other than Set In Progress are assumed to be strings. std::tuple ret = sysInfoParamStore->lookup(paramSelector); bool found = std::get<0>(ret); if (!found) { return ipmi::responseSensorInvalid(); } std::string& paramString = std::get<1>(ret); std::vector 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 configData) { if (paramSelector >= invalidParamSelectorStart && paramSelector <= invalidParamSelectorEnd) { return ipmi::responseInvalidFieldRequest(); } if (paramSelector >= oemCmdStart) { 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) { lg2::error("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->update(IPMI_SYSINFO_SYSTEM_NAME, sysInfoReadSystemName); } // lookup std::tuple 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. { uint8_t encoding = configData.at(0); if (encoding > maxValidEncodingData) { return ipmi::responseInvalidFieldRequest(); } size_t stringLen = configData.at(1); // string length 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 convertStringToData(const std::string& command) { std::istringstream iss(command); std::string token; std::vector dataValue; while (std::getline(iss, token, ' ')) { dataValue.emplace_back( static_cast(std::stoul(token, nullptr, base_16))); } return dataValue; } static bool populateI2CControllerWRAllowlist() { nlohmann::json data = nullptr; std::ifstream jsonFile(i2cControllerWRAllowlistFile); if (!jsonFile.good()) { lg2::warning("i2c allow list file not found! file name: {FILE_NAME}", "FILE_NAME", i2cControllerWRAllowlistFile); return false; } try { data = nlohmann::json::parse(jsonFile, nullptr, false); } catch (const nlohmann::json::parse_error& e) { lg2::error("Corrupted i2c allow list config file, " "file name: {FILE_NAME}, error: {ERROR}", "FILE_NAME", i2cControllerWRAllowlistFile, "ERROR", e); 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(); std::vector& allowlist = getWRAllowlist(); for (const auto& it : filters.items()) { nlohmann::json filter = it.value(); if (filter.is_null()) { lg2::error( "Corrupted I2C controller write read allowlist config file, " "file name: {FILE_NAME}", "FILE_NAME", i2cControllerWRAllowlistFile); return false; } const std::vector& writeData = convertStringToData(filter[cmdStr].get()); const std::vector& writeDataMask = convertStringToData(filter[cmdMaskStr].get()); if (writeDataMask.size() != writeData.size()) { lg2::error("I2C controller write read allowlist filter " "mismatch for command & mask size"); return false; } allowlist.push_back( {static_cast(std::stoul( filter[busIdStr].get(), nullptr, base_16)), static_cast( std::stoul(filter[targetAddrStr].get(), nullptr, base_16)), static_cast( std::stoul(filter[targetAddrMaskStr].get(), nullptr, base_16)), writeData, writeDataMask}); } if (allowlist.size() != filters.size()) { lg2::error( "I2C controller write read allowlist filter size mismatch"); return false; } } catch (const std::exception& e) { lg2::error("I2C controller write read allowlist " "unexpected exception: {ERROR}", "ERROR", e); return false; } return true; } static inline bool isWriteDataAllowlisted(const std::vector& data, const std::vector& dataMask, const std::vector& writeData) { std::vector processedDataBuf(data.size()); std::vector processedReqBuf(dataMask.size()); std::transform(writeData.begin(), writeData.end(), dataMask.begin(), processedReqBuf.begin(), std::bit_or()); std::transform(data.begin(), data.end(), dataMask.begin(), processedDataBuf.begin(), std::bit_or()); return (processedDataBuf == processedReqBuf); } static bool isCmdAllowlisted(uint8_t busId, uint8_t targetAddr, std::vector& writeData) { std::vector& allowList = getWRAllowlist(); for (const auto& wlEntry : allowList) { if ((busId == wlEntry.busId) && ((targetAddr | wlEntry.targetAddrMask) == (wlEntry.targetAddr | wlEntry.targetAddrMask))) { const std::vector& 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 (isWriteDataAllowlisted(wlEntry.data, dataMask, writeData)) { return true; } } } return false; } #else static bool populateI2CControllerWRAllowlist() { lg2::info("I2C_WHITELIST_CHECK is disabled, do not populate allowlist"); return true; } #endif // ENABLE_I2C_WHITELIST_CHECK /** @brief implements controller 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 targetAddr - target 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> ipmiControllerWriteRead( [[maybe_unused]] bool isPrivateBus, uint3_t busId, [[maybe_unused]] uint4_t channelNum, bool reserved, uint7_t targetAddr, uint8_t readCount, std::vector writeData) { if (reserved) { return ipmi::responseInvalidFieldRequest(); } const size_t writeCount = writeData.size(); if (!readCount && !writeCount) { lg2::error("Controller write read command: Read & write count are 0"); return ipmi::responseInvalidFieldRequest(); } #ifdef ENABLE_I2C_WHITELIST_CHECK if (!isCmdAllowlisted(static_cast(busId), static_cast(targetAddr), writeData)) { lg2::error("Controller write read request blocked!, " "bus: {BUS}, addr: {ADDR}", "BUS", static_cast(busId), "ADDR", lg2::hex, static_cast(targetAddr)); } #endif // ENABLE_I2C_WHITELIST_CHECK std::vector readBuf(readCount); std::string i2cBus = "/dev/i2c-" + std::to_string(static_cast(busId)); ipmi::Cc ret = ipmi::i2cWriteRead(i2cBus, static_cast(targetAddr), writeData, readBuf); if (ret != ipmi::ccSuccess) { return ipmi::response(ret); } return ipmi::responseSuccess(readBuf); } void register_netfn_app_functions() { // ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, ipmi::app::cmdGetDeviceId, ipmi::Privilege::User, ipmiAppGetDeviceId); // ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, ipmi::app::cmdGetBtIfaceCapabilities, ipmi::Privilege::User, ipmiAppGetBtCapabilities); // 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); // 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); // ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, ipmi::app::cmdGetWatchdogTimer, ipmi::Privilege::User, ipmiGetWatchdogTimer); // ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, ipmi::app::cmdGetSelfTestResults, ipmi::Privilege::User, ipmiAppGetSelfTestResults); // ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, ipmi::app::cmdGetDeviceGuid, ipmi::Privilege::User, ipmiAppGetDeviceGuid); // ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, ipmi::app::cmdSetAcpiPowerState, ipmi::Privilege::Admin, ipmiSetAcpiPowerState); // 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 Controller write read allowlist if (populateI2CControllerWRAllowlist()) { // Note: For security reasons, registering controller 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, ipmiControllerWriteRead); } // ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, ipmi::app::cmdGetSystemGuid, ipmi::Privilege::User, ipmiAppGetSystemGuid); // ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, ipmi::app::cmdGetChannelCipherSuites, ipmi::Privilege::None, getChannelCipherSuites); // ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, ipmi::app::cmdGetSystemInfoParameters, ipmi::Privilege::User, ipmiAppGetSystemInfo); // ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, ipmi::app::cmdSetSystemInfoParameters, ipmi::Privilege::Admin, ipmiAppSetSystemInfo); return; }