Add Set Front Panel Button Enables command
This change overrides the chassis status and set front panel
button enables commands to use the new Buttons interface.
Tested:
ipmitool raw 0 0xa 7
ipmitool chassis status
System Power : on
Power Overload : false
Power Interlock : inactive
Main Power Fault : false
Power Control Fault : false
Power Restore Policy : always-off
Last Power Event :
Chassis Intrusion : inactive
Front-Panel Lockout : inactive
Drive Fault : false
Cooling/Fan Fault : false
Sleep Button Disable : not allowed
Diag Button Disable : allowed
Reset Button Disable : allowed
Power Button Disable : allowed
Sleep Button Disabled: false
Diag Button Disabled : true
Reset Button Disabled: true
Power Button Disabled: true
ipmitool raw 0 0xa 0
ipmitool chassis status
System Power : on
Power Overload : false
Power Interlock : inactive
Main Power Fault : false
Power Control Fault : false
Power Restore Policy : always-off
Last Power Event :
Chassis Intrusion : inactive
Front-Panel Lockout : inactive
Drive Fault : false
Cooling/Fan Fault : false
Sleep Button Disable : not allowed
Diag Button Disable : allowed
Reset Button Disable : allowed
Power Button Disable : allowed
Sleep Button Disabled: false
Diag Button Disabled : false
Reset Button Disabled: false
Power Button Disabled: false
Change-Id: I12b6c19483404bee2d481e08260d878bd5aa99c5
Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
diff --git a/src/chassiscommands.cpp b/src/chassiscommands.cpp
index 72439f7..2fa9e23 100644
--- a/src/chassiscommands.cpp
+++ b/src/chassiscommands.cpp
@@ -16,29 +16,43 @@
#include "xyz/openbmc_project/Common/error.hpp"
#include <fstream>
+#include <iostream>
#include <ipmid/api.hpp>
#include <ipmid/utils.hpp>
#include <nlohmann/json.hpp>
#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
#include <regex>
#include <sdbusplus/timer.hpp>
+#include <stdexcept>
+#include <string_view>
+#include <xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp>
-namespace ipmi
+using namespace phosphor::logging;
+
+namespace ipmi::chassis
{
-const static constexpr char *idButtonPath =
- "/xyz/openbmc_project/chassis/buttons/id";
-const static constexpr char *idButtonInterface =
- "xyz.openbmc_project.Chassis.Buttons";
-const static constexpr char *idButtonProp = "ButtonPressed";
+static constexpr const char* buttonIntf = "xyz.openbmc_project.Chassis.Buttons";
-const static constexpr char *ledService =
+const static constexpr char* idButtonPath =
+ "/xyz/openbmc_project/chassis/buttons/id";
+static constexpr const char* powerButtonPath =
+ "/xyz/openbmc_project/chassis/buttons/power";
+static constexpr const char* resetButtonPath =
+ "/xyz/openbmc_project/chassis/buttons/reset";
+static constexpr const char* interruptButtonPath =
+ "/xyz/openbmc_project/chassis/buttons/nmi";
+
+const static constexpr char* idButtonProp = "ButtonPressed";
+
+const static constexpr char* ledService =
"xyz.openbmc_project.LED.GroupManager";
-const static constexpr char *ledIDOnObj =
+const static constexpr char* ledIDOnObj =
"/xyz/openbmc_project/led/groups/enclosure_identify";
-const static constexpr char *ledIDBlinkObj =
+const static constexpr char* ledIDBlinkObj =
"/xyz/openbmc_project/led/groups/enclosure_identify_blink";
-const static constexpr char *ledInterface = "xyz.openbmc_project.Led.Group";
-const static constexpr char *ledProp = "Asserted";
+const static constexpr char* ledInterface = "xyz.openbmc_project.Led.Group";
+const static constexpr char* ledProp = "Asserted";
constexpr size_t defaultIdentifyTimeOut = 15;
@@ -51,7 +65,7 @@
static ipmi::ServiceCache LEDService(ledInterface, ledIDBlinkObj);
-void enclosureIdentifyLed(const char *objName, bool isIdLedOn)
+void enclosureIdentifyLed(const char* objName, bool isIdLedOn)
{
auto bus = getSdBus();
@@ -61,15 +75,14 @@
setDbusProperty(*bus, service, objName, ledInterface, ledProp,
isIdLedOn);
}
- catch (const std::exception &e)
+ catch (const std::exception& e)
{
- phosphor::logging::log<phosphor::logging::level::ERR>(
- "enclosureIdentifyLed: can't set property",
- phosphor::logging::entry("ERR=%s", e.what()));
+ log<level::ERR>("enclosureIdentifyLed: can't set property",
+ entry("ERR=%s", e.what()));
}
}
-bool getIDState(const char *objName, bool &state)
+bool getIDState(const char* objName, bool& state)
{
auto bus = getSdBus();
@@ -80,12 +93,10 @@
getDbusProperty(*bus, service, objName, ledInterface, ledProp);
state = std::get<bool>(enabled);
}
- catch (sdbusplus::exception::SdBusError &e)
+ catch (sdbusplus::exception::SdBusError& e)
{
- phosphor::logging::log<phosphor::logging::level::ERR>(
- "Fail to get property",
- phosphor::logging::entry("PATH=%s", objName),
- phosphor::logging::entry("ERROR=%s", e.what()));
+ log<level::ERR>("Fail to get property", entry("PATH=%s", objName),
+ entry("ERROR=%s", e.what()));
return false;
}
return true;
@@ -96,7 +107,7 @@
enclosureIdentifyLed(ledIDBlinkObj, false);
}
-void idButtonPropChanged(sdbusplus::message::message &msg)
+void idButtonPropChanged(sdbusplus::message::message& msg)
{
bool asserted = false;
bool buttonPressed = false;
@@ -106,7 +117,7 @@
std::string iface;
msg.read(iface, props, inval);
- for (const auto &t : props)
+ for (const auto& t : props)
{
auto key = t.first;
auto value = t.second;
@@ -122,8 +133,7 @@
{
if (identifyTimer->isRunning())
{
- phosphor::logging::log<phosphor::logging::level::INFO>(
- "ID timer is running");
+ log<level::INFO>("ID timer is running");
}
// make sure timer is stopped
@@ -189,10 +199,307 @@
return ipmi::responseSuccess();
}
+namespace power_policy
+{
+/* helper function for Get Chassis Status Command
+ */
+std::optional<uint2_t> getPowerRestorePolicy()
+{
+ constexpr const char* powerRestorePath =
+ "/xyz/openbmc_project/control/host0/power_restore_policy";
+ constexpr const char* powerRestoreIntf =
+ "xyz.openbmc_project.Control.Power.RestorePolicy";
+ uint2_t restorePolicy = 0;
+ std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
+
+ try
+ {
+ auto service =
+ ipmi::getService(*busp, powerRestoreIntf, powerRestorePath);
+
+ ipmi::Value result =
+ ipmi::getDbusProperty(*busp, service, powerRestorePath,
+ powerRestoreIntf, "PowerRestorePolicy");
+ auto powerRestore = sdbusplus::xyz::openbmc_project::Control::Power::
+ server::RestorePolicy::convertPolicyFromString(
+ std::get<std::string>(result));
+
+ using namespace sdbusplus::xyz::openbmc_project::Control::Power::server;
+ switch (powerRestore)
+ {
+ case RestorePolicy::Policy::AlwaysOff:
+ restorePolicy = 0x00;
+ break;
+ case RestorePolicy::Policy::Restore:
+ restorePolicy = 0x01;
+ break;
+ case RestorePolicy::Policy::AlwaysOn:
+ restorePolicy = 0x02;
+ break;
+ }
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>("Failed to fetch PowerRestorePolicy property",
+ entry("ERROR=%s", e.what()),
+ entry("PATH=%s", powerRestorePath),
+ entry("INTERFACE=%s", powerRestoreIntf));
+ return std::nullopt;
+ }
+ return std::make_optional(restorePolicy);
+}
+
+/*
+ * getPowerStatus
+ * helper function for Get Chassis Status Command
+ * return - optional value for pgood (no value on error)
+ */
+std::optional<bool> getPowerStatus()
+{
+ bool powerGood = false;
+ std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
+ try
+ {
+ constexpr const char* chassisStatePath =
+ "/xyz/openbmc_project/state/chassis0";
+ constexpr const char* chassisStateIntf =
+ "xyz.openbmc_project.State.Chassis";
+ auto service =
+ ipmi::getService(*busp, chassisStateIntf, chassisStatePath);
+
+ ipmi::Value variant =
+ ipmi::getDbusProperty(*busp, service, chassisStatePath,
+ chassisStateIntf, "CurrentPowerState");
+ std::string powerState = std::get<std::string>(variant);
+ if (powerState == "xyz.openbmc_project.State.Chassis.PowerState.On")
+ {
+ powerGood = true;
+ }
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>("Failed to fetch power state property",
+ entry("ERROR=%s", e.what()));
+ return std::nullopt;
+ }
+ return std::make_optional(powerGood);
+}
+
+/*
+ * getACFailStatus
+ * helper function for Get Chassis Status Command
+ * return - bool value for ACFail (false on error)
+ */
+bool getACFailStatus()
+{
+ constexpr const char* powerControlObj =
+ "/xyz/openbmc_project/Chassis/Control/Power0";
+ constexpr const char* powerControlIntf =
+ "xyz.openbmc_project.Chassis.Control.Power";
+ bool acFail = false;
+ std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
+ try
+ {
+ auto service =
+ ipmi::getService(*bus, powerControlIntf, powerControlObj);
+
+ ipmi::Value variant = ipmi::getDbusProperty(
+ *bus, service, powerControlObj, powerControlIntf, "PFail");
+ acFail = std::get<bool>(variant);
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>("Failed to fetch PFail property",
+ entry("ERROR=%s", e.what()),
+ entry("PATH=%s", powerControlObj),
+ entry("INTERFACE=%s", powerControlIntf));
+ }
+ return acFail;
+}
+} // namespace power_policy
+
+static std::optional<bool> getButtonEnabled(const std::string& buttonPath)
+{
+ bool buttonDisabled = false;
+ std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
+ try
+ {
+ auto service = ipmi::getService(*getSdBus(), buttonIntf, buttonPath);
+ ipmi::Value disabled = ipmi::getDbusProperty(
+ *busp, service, buttonPath, buttonIntf, "ButtonMasked");
+ buttonDisabled = std::get<bool>(disabled);
+ }
+ catch (sdbusplus::exception::SdBusError& e)
+ {
+ log<level::ERR>("Fail to get button disabled property",
+ entry("PATH=%s", buttonPath.c_str()),
+ entry("ERROR=%s", e.what()));
+ return std::nullopt;
+ }
+ return std::make_optional(buttonDisabled);
+}
+
+static bool setButtonEnabled(const std::string& buttonPath, const bool disabled)
+{
+ try
+ {
+ auto service = ipmi::getService(*getSdBus(), buttonIntf, buttonPath);
+ ipmi::setDbusProperty(*getSdBus(), service, buttonPath, buttonIntf,
+ "ButtonMasked", disabled);
+ }
+ catch (std::exception& e)
+ {
+ log<level::ERR>("Failed to set button disabled",
+ entry("EXCEPTION=%s, REQUEST=%x", e.what(), disabled));
+ return -1;
+ }
+
+ return 0;
+}
+
+//----------------------------------------------------------------------
+// Get Chassis Status commands
+//----------------------------------------------------------------------
+ipmi::RspType<bool, // Power is on
+ bool, // Power overload
+ bool, // Interlock
+ bool, // power fault
+ bool, // power control fault
+ uint2_t, // power restore policy
+ bool, // reserved
+
+ bool, // AC failed
+ bool, // last power down caused by a Power overload
+ bool, // last power down caused by a power interlock
+ bool, // last power down caused by power fault
+ bool, // last ‘Power is on’ state was entered via IPMI command
+ uint3_t, // reserved
+
+ bool, // Chassis intrusion active
+ bool, // Front Panel Lockout active
+ bool, // Drive Fault
+ bool, // Cooling/fan fault detected
+ uint2_t, // Chassis Identify State
+ bool, // Chassis Identify command and state info supported
+ bool, // reserved
+
+ bool, // Power off button disabled
+ bool, // Reset button disabled
+ bool, // Diagnostic Interrupt button disabled
+ bool, // Standby (sleep) button disabled
+ bool, // Power off button disable allowed
+ bool, // Reset button disable allowed
+ bool, // Diagnostic Interrupt button disable allowed
+ bool // Standby (sleep) button disable allowed
+ >
+ ipmiGetChassisStatus()
+{
+ std::optional<uint2_t> restorePolicy =
+ power_policy::getPowerRestorePolicy();
+ std::optional<bool> powerGood = power_policy::getPowerStatus();
+ if (!restorePolicy || !powerGood)
+ {
+ return ipmi::responseUnspecifiedError();
+ }
+
+ // Front Panel Button Capabilities and disable/enable status(Optional)
+ std::optional<bool> powerButtonReading = getButtonEnabled(powerButtonPath);
+ // allow disable if the interface is present
+ bool powerButtonDisableAllow = static_cast<bool>(powerButtonReading);
+ // default return the button is enabled (not disabled)
+ bool powerButtonDisabled = false;
+ if (powerButtonDisableAllow)
+ {
+ // return the real value of the button status, if present
+ powerButtonDisabled = *powerButtonReading;
+ }
+
+ std::optional<bool> resetButtonReading = getButtonEnabled(resetButtonPath);
+ // allow disable if the interface is present
+ bool resetButtonDisableAllow = static_cast<bool>(resetButtonReading);
+ // default return the button is enabled (not disabled)
+ bool resetButtonDisabled = false;
+ if (resetButtonDisableAllow)
+ {
+ // return the real value of the button status, if present
+ resetButtonDisabled = *resetButtonReading;
+ }
+
+ std::optional<bool> interruptButtonReading =
+ getButtonEnabled(interruptButtonPath);
+ // allow disable if the interface is present
+ bool interruptButtonDisableAllow =
+ static_cast<bool>(interruptButtonReading);
+ // default return the button is enabled (not disabled)
+ bool interruptButtonDisabled = false;
+ if (interruptButtonDisableAllow)
+ {
+ // return the real value of the button status, if present
+ interruptButtonDisabled = *interruptButtonReading;
+ }
+
+ bool powerDownAcFailed = power_policy::getACFailStatus();
+
+ // This response has a lot of hard-coded, unsupported fields
+ // They are set to false or 0
+ constexpr bool powerOverload = false;
+ constexpr bool chassisInterlock = false;
+ constexpr bool powerFault = false;
+ constexpr bool powerControlFault = false;
+ constexpr bool powerDownOverload = false;
+ constexpr bool powerDownInterlock = false;
+ constexpr bool powerDownPowerFault = false;
+ constexpr bool powerStatusIPMI = false;
+ constexpr bool chassisIntrusionActive = false;
+ constexpr bool frontPanelLockoutActive = false;
+ constexpr bool driveFault = false;
+ constexpr bool coolingFanFault = false;
+ // chassisIdentifySupport set because this command is implemented
+ constexpr bool chassisIdentifySupport = true;
+ uint2_t chassisIdentifyState = 0;
+ constexpr bool sleepButtonDisabled = false;
+ constexpr bool sleepButtonDisableAllow = false;
+
+ return ipmi::responseSuccess(
+ *powerGood, powerOverload, chassisInterlock, powerFault,
+ powerControlFault, *restorePolicy,
+ false, // reserved
+
+ powerDownAcFailed, powerDownOverload, powerDownInterlock,
+ powerDownPowerFault, powerStatusIPMI,
+ uint3_t(0), // reserved
+
+ chassisIntrusionActive, frontPanelLockoutActive, driveFault,
+ coolingFanFault, chassisIdentifyState, chassisIdentifySupport,
+ false, // reserved
+
+ powerButtonDisabled, resetButtonDisabled, interruptButtonDisabled,
+ sleepButtonDisabled, powerButtonDisableAllow, resetButtonDisableAllow,
+ interruptButtonDisableAllow, sleepButtonDisableAllow);
+}
+
+ipmi::RspType<> ipmiSetFrontPanelButtonEnables(bool disablePowerButton,
+ bool disableResetButton,
+ bool disableInterruptButton,
+ bool disableSleepButton,
+ uint4_t reserved)
+{
+ bool error = false;
+
+ error |= setButtonEnabled(powerButtonPath, disablePowerButton);
+ error |= setButtonEnabled(resetButtonPath, disableResetButton);
+ error |= setButtonEnabled(interruptButtonPath, disableInterruptButton);
+
+ if (error)
+ {
+ return ipmi::responseUnspecifiedError();
+ }
+ return ipmi::responseSuccess();
+}
+
static void registerChassisFunctions(void)
{
- phosphor::logging::log<phosphor::logging::level::INFO>(
- "Registering Chassis commands");
+ log<level::INFO>("Registering Chassis commands");
createIdentifyTimer();
@@ -204,7 +511,7 @@
matchPtr = std::make_unique<sdbusplus::bus::match_t>(
*bus,
sdbusplus::bus::match::rules::propertiesChanged(idButtonPath,
- idButtonInterface),
+ buttonIntf),
std::bind(idButtonPropChanged, std::placeholders::_1));
}
@@ -212,6 +519,15 @@
ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnChassis,
ipmi::chassis::cmdChassisIdentify,
ipmi::Privilege::Operator, ipmiChassisIdentify);
+ // <Get Chassis Status>
+ ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnChassis,
+ ipmi::chassis::cmdGetChassisStatus,
+ ipmi::Privilege::User, ipmiGetChassisStatus);
+ // <Set Front Panel Enables>
+ ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnChassis,
+ ipmi::chassis::cmdSetFrontPanelButtonEnables,
+ ipmi::Privilege::User,
+ ipmiSetFrontPanelButtonEnables);
}
-} // namespace ipmi
+} // namespace ipmi::chassis