diff options
-rw-r--r-- | apphandler.cpp | 264 | ||||
-rw-r--r-- | apphandler.hpp | 1 | ||||
-rw-r--r-- | host-ipmid-whitelist.conf | 2 |
3 files changed, 266 insertions, 1 deletions
diff --git a/apphandler.cpp b/apphandler.cpp index ac06f50..370e0c3 100644 --- a/apphandler.cpp +++ b/apphandler.cpp @@ -29,6 +29,7 @@ #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> @@ -179,16 +180,273 @@ bool getCurrentBmcState() variant_ns::get<std::string>(variant)) == BMC::BMCState::Ready; } +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; + +struct ACPIState +{ + uint8_t sysACPIState; + uint8_t devACPIState; +} __attribute__((packed)); + +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 + ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context) { + auto s = static_cast<uint8_t>(acpi_state::PowerState::unknown); ipmi_ret_t rc = IPMI_CC_OK; + + sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; + + auto value = acpi_state::ACPIPowerState::ACPI::Unknown; + + auto* req = reinterpret_cast<acpi_state::ACPIState*>(request); + + if (*data_len != sizeof(acpi_state::ACPIState)) + { + log<level::ERR>("set_acpi invalid len"); + *data_len = 0; + return IPMI_CC_REQ_DATA_LEN_INVALID; + } + *data_len = 0; - log<level::DEBUG>("IPMI SET ACPI STATE Ignoring for now\n"); + if (req->sysACPIState & acpi_state::stateChanged) + { + // set system power state + s = req->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_CC_PARM_OUT_OF_RANGE; + } + + // 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_CC_UNSPECIFIED_ERROR; + } + } + } + else + { + log<level::DEBUG>("Do not change system power state"); + } + + if (req->devACPIState & acpi_state::stateChanged) + { + // set device power state + s = req->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_CC_PARM_OUT_OF_RANGE; + } + + // 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_CC_UNSPECIFIED_ERROR; + } + } + } + else + { + log<level::DEBUG>("Do not change device power state"); + } + + return rc; +} + +ipmi_ret_t ipmi_app_get_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + ipmi_request_t request, + ipmi_response_t response, + ipmi_data_len_t data_len, + ipmi_context_t context) +{ + ipmi_ret_t rc = IPMI_CC_OK; + + auto* res = reinterpret_cast<acpi_state::ACPIState*>(response); + + sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; + + *data_len = 0; + + 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( + sysACPIVal.get<std::string>()); + res->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( + devACPIVal.get<std::string>()); + res->devACPIState = + static_cast<uint8_t>(acpi_state::dbusToIPMI.at(devACPI)); + + *data_len = sizeof(acpi_state::ACPIState); + } + catch (const InternalFailure& e) + { + log<level::ERR>("Failed in get ACPI property"); + return IPMI_CC_UNSPECIFIED_ERROR; + } return rc; } @@ -860,6 +1118,10 @@ void register_netfn_app_functions() ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL, ipmi_app_set_acpi_power_state, PRIVILEGE_ADMIN); + // <Get ACPI Power State> + ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_ACPI, NULL, + ipmi_app_get_acpi_power_state, PRIVILEGE_ADMIN); + // <Get Channel Access> ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHANNEL_ACCESS, NULL, ipmi_get_channel_access, PRIVILEGE_USER); diff --git a/apphandler.hpp b/apphandler.hpp index 3d377fb..d4dd8e8 100644 --- a/apphandler.hpp +++ b/apphandler.hpp @@ -9,6 +9,7 @@ enum ipmi_netfn_app_cmds IPMI_CMD_GET_DEVICE_ID = 0x01, IPMI_CMD_GET_SELF_TEST_RESULTS = 0x04, IPMI_CMD_SET_ACPI = 0x06, + IPMI_CMD_GET_ACPI = 0x07, IPMI_CMD_GET_DEVICE_GUID = 0x08, IPMI_CMD_RESET_WD = 0x22, IPMI_CMD_SET_WD = 0x24, diff --git a/host-ipmid-whitelist.conf b/host-ipmid-whitelist.conf index 2c37ac9..9dff10a 100644 --- a/host-ipmid-whitelist.conf +++ b/host-ipmid-whitelist.conf @@ -11,6 +11,8 @@ 0x04:0x30 //<Sensor/Event>:<Set Sensor Reading and Event Status> 0x06:0x01 //<App>:<Get Device ID> 0x06:0x04 //<App>:<Get Self Test Results> +0x06:0x06 //<App>:<Set ACPI Power State> +0x06:0x07 //<App>:<Get ACPI Power State> 0x06:0x08 //<App>:<Get Device GUID> 0x06:0x22 //<App>:<Reset Watchdog Timer> 0x06:0x24 //<App>:<Set Watchdog Timer> |