#include "config.h" #include "chassishandler.hpp" #include <arpa/inet.h> #include <endian.h> #include <limits.h> #include <mapper.h> #include <netinet/in.h> #include <array> #include <chrono> #include <cstring> #include <filesystem> #include <fstream> #include <future> #include <ipmid/api.hpp> #include <ipmid/types.hpp> #include <ipmid/utils.hpp> #include <map> #include <phosphor-logging/elog-errors.hpp> #include <phosphor-logging/log.hpp> #include <sdbusplus/bus.hpp> #include <sdbusplus/message/types.hpp> #include <sdbusplus/server/object.hpp> #include <sdbusplus/timer.hpp> #include <settings.hpp> #include <sstream> #include <string> #include <xyz/openbmc_project/Common/error.hpp> #include <xyz/openbmc_project/Control/Boot/Mode/server.hpp> #include <xyz/openbmc_project/Control/Boot/Source/server.hpp> #include <xyz/openbmc_project/Control/Boot/Type/server.hpp> #include <xyz/openbmc_project/Control/Power/RestorePolicy/server.hpp> #include <xyz/openbmc_project/State/Host/server.hpp> #include <xyz/openbmc_project/State/PowerOnHours/server.hpp> std::unique_ptr<phosphor::Timer> identifyTimer __attribute__((init_priority(101))); static ChassisIDState chassisIDState = ChassisIDState::reserved; static constexpr uint8_t setParmVersion = 0x01; constexpr size_t sizeVersion = 2; constexpr size_t DEFAULT_IDENTIFY_TIME_OUT = 15; // PetiBoot-Specific static constexpr uint8_t netConfInitialBytes[] = {0x80, 0x21, 0x70, 0x62, 0x21, 0x00, 0x01, 0x06}; static constexpr uint8_t oemParmStart = 96; static constexpr uint8_t oemParmEnd = 127; static constexpr size_t cookieOffset = 1; static constexpr size_t versionOffset = 5; static constexpr size_t addrSizeOffset = 8; static constexpr size_t macOffset = 9; static constexpr size_t addrTypeOffset = 16; static constexpr size_t ipAddrOffset = 17; static constexpr size_t encIdentifyObjectsSize = 1; static constexpr size_t chassisIdentifyReqLength = 2; static constexpr size_t identifyIntervalPos = 0; static constexpr size_t forceIdentifyPos = 1; namespace ipmi { constexpr Cc ccParmNotSupported = 0x80; static inline auto responseParmNotSupported() { return response(ccParmNotSupported); } } // namespace ipmi void register_netfn_chassis_functions() __attribute__((constructor)); // Host settings in dbus // Service name should be referenced by connection name got via object mapper const char* settings_object_name = "/org/openbmc/settings/host0"; const char* settings_intf_name = "org.freedesktop.DBus.Properties"; const char* identify_led_object_name = "/xyz/openbmc_project/led/groups/enclosure_identify"; constexpr auto SETTINGS_ROOT = "/"; constexpr auto SETTINGS_MATCH = "host0"; constexpr auto IP_INTERFACE = "xyz.openbmc_project.Network.IP"; constexpr auto MAC_INTERFACE = "xyz.openbmc_project.Network.MACAddress"; static constexpr auto chassisStateRoot = "/xyz/openbmc_project/state"; static constexpr auto chassisPOHStateIntf = "xyz.openbmc_project.State.PowerOnHours"; static constexpr auto pohCounterProperty = "POHCounter"; static constexpr auto match = "chassis0"; const static constexpr char chassisCapIntf[] = "xyz.openbmc_project.Control.ChassisCapabilities"; const static constexpr char chassisIntrusionProp[] = "ChassisIntrusionEnabled"; const static constexpr char chassisFrontPanelLockoutProp[] = "ChassisFrontPanelLockoutEnabled"; const static constexpr char chassisNMIProp[] = "ChassisNMIEnabled"; const static constexpr char chassisPowerInterlockProp[] = "ChassisPowerInterlockEnabled"; const static constexpr char chassisFRUDevAddrProp[] = "FRUDeviceAddress"; const static constexpr char chassisSDRDevAddrProp[] = "SDRDeviceAddress"; const static constexpr char chassisSELDevAddrProp[] = "SELDeviceAddress"; const static constexpr char chassisSMDevAddrProp[] = "SMDeviceAddress"; const static constexpr char chassisBridgeDevAddrProp[] = "BridgeDeviceAddress"; static constexpr uint8_t chassisCapFlagMask = 0x0f; static constexpr uint8_t chassisCapAddrMask = 0xfe; static constexpr const char* powerButtonIntf = "xyz.openbmc_project.Chassis.Buttons.Power"; static constexpr const char* powerButtonPath = "/xyz/openbmc_project/Chassis/Buttons/Power0"; static constexpr const char* resetButtonIntf = "xyz.openbmc_project.Chassis.Buttons.Reset"; static constexpr const char* resetButtonPath = "/xyz/openbmc_project/Chassis/Buttons/Reset0"; // Phosphor Host State manager namespace State = sdbusplus::xyz::openbmc_project::State::server; namespace fs = std::filesystem; using namespace phosphor::logging; using namespace sdbusplus::xyz::openbmc_project::Common::Error; using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server; namespace chassis { namespace internal { constexpr auto bootSettingsPath = "/xyz/openbmc_project/control/host0/boot"; constexpr auto bootEnableIntf = "xyz.openbmc_project.Object.Enable"; constexpr auto bootModeIntf = "xyz.openbmc_project.Control.Boot.Mode"; constexpr auto bootTypeIntf = "xyz.openbmc_project.Control.Boot.Type"; constexpr auto bootSourceIntf = "xyz.openbmc_project.Control.Boot.Source"; constexpr auto bootSettingsOneTimePath = "/xyz/openbmc_project/control/host0/boot/one_time"; constexpr auto bootOneTimeIntf = "xyz.openbmc_project.Object.Enable"; constexpr auto powerRestoreIntf = "xyz.openbmc_project.Control.Power.RestorePolicy"; sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection()); namespace cache { std::unique_ptr<settings::Objects> objectsPtr = nullptr; settings::Objects& getObjects() { if (objectsPtr == nullptr) { objectsPtr = std::make_unique<settings::Objects>( dbus, std::vector<std::string>{bootModeIntf, bootTypeIntf, bootSourceIntf, powerRestoreIntf}); } return *objectsPtr; } } // namespace cache } // namespace internal } // namespace chassis namespace poh { constexpr auto minutesPerCount = 60; } // namespace poh int getHostNetworkData(ipmi::message::Payload& payload) { ipmi::PropertyMap properties; int rc = 0; uint8_t addrSize = ipmi::network::IPV4_ADDRESS_SIZE_BYTE; try { // TODO There may be cases where an interface is implemented by multiple // objects,to handle such cases we are interested on that object // which are on interested busname. // Currenlty mapper doesn't give the readable busname(gives busid) // so we can't match with bus name so giving some object specific info // as SETTINGS_MATCH. // Later SETTINGS_MATCH will be replaced with busname. sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection()); auto ipObjectInfo = ipmi::getDbusObject(bus, IP_INTERFACE, SETTINGS_ROOT, SETTINGS_MATCH); auto macObjectInfo = ipmi::getDbusObject(bus, MAC_INTERFACE, SETTINGS_ROOT, SETTINGS_MATCH); properties = ipmi::getAllDbusProperties( bus, ipObjectInfo.second, ipObjectInfo.first, IP_INTERFACE); auto variant = ipmi::getDbusProperty(bus, macObjectInfo.second, macObjectInfo.first, MAC_INTERFACE, "MACAddress"); auto ipAddress = std::get<std::string>(properties["Address"]); auto gateway = std::get<std::string>(properties["Gateway"]); auto prefix = std::get<uint8_t>(properties["PrefixLength"]); uint8_t isStatic = (std::get<std::string>(properties["Origin"]) == "xyz.openbmc_project.Network.IP.AddressOrigin.Static") ? 1 : 0; auto MACAddress = std::get<std::string>(variant); // it is expected here that we should get the valid data // but we may also get the default values. // Validation of the data is done by settings. // // if mac address is default mac address then // don't send blank override. if ((MACAddress == ipmi::network::DEFAULT_MAC_ADDRESS)) { rc = -1; return rc; } // if addr is static then ipaddress,gateway,prefix // should not be default one,don't send blank override. if (isStatic) { if ((ipAddress == ipmi::network::DEFAULT_ADDRESS) || (gateway == ipmi::network::DEFAULT_ADDRESS) || (!prefix)) { rc = -1; return rc; } } std::string token; std::stringstream ss(MACAddress); // First pack macOffset no of bytes in payload. // Latter this PetiBoot-Specific data will be populated. std::vector<uint8_t> payloadInitialBytes(macOffset); payload.pack(payloadInitialBytes); while (std::getline(ss, token, ':')) { payload.pack(stoi(token, nullptr, 16)); } payload.pack(0x00); payload.pack(isStatic); uint8_t addressFamily = (std::get<std::string>(properties["Type"]) == "xyz.openbmc_project.Network.IP.Protocol.IPv4") ? AF_INET : AF_INET6; addrSize = (addressFamily == AF_INET) ? ipmi::network::IPV4_ADDRESS_SIZE_BYTE : ipmi::network::IPV6_ADDRESS_SIZE_BYTE; // ipaddress and gateway would be in IPv4 format std::vector<uint8_t> addrInBinary(addrSize); inet_pton(addressFamily, ipAddress.c_str(), reinterpret_cast<void*>(addrInBinary.data())); payload.pack(addrInBinary); payload.pack(prefix); std::vector<uint8_t> gatewayDetails(addrSize); inet_pton(addressFamily, gateway.c_str(), reinterpret_cast<void*>(gatewayDetails.data())); payload.pack(gatewayDetails); } catch (InternalFailure& e) { commit<InternalFailure>(); rc = -1; return rc; } // PetiBoot-Specific // If success then copy the first 9 bytes to the payload message // payload first 2 bytes contain the parameter values. Skip that 2 bytes. uint8_t skipFirstTwoBytes = 2; size_t payloadSize = payload.size(); uint8_t* configDataStartingAddress = payload.data() + skipFirstTwoBytes; if (payloadSize < skipFirstTwoBytes + sizeof(netConfInitialBytes)) { log<level::ERR>("Invalid net config "); rc = -1; return rc; } std::copy(netConfInitialBytes, netConfInitialBytes + sizeof(netConfInitialBytes), configDataStartingAddress); if (payloadSize < skipFirstTwoBytes + addrSizeOffset + sizeof(addrSize)) { log<level::ERR>("Invalid length of address size"); rc = -1; return rc; } std::copy(&addrSize, &(addrSize) + sizeof(addrSize), configDataStartingAddress + addrSizeOffset); #ifdef _IPMI_DEBUG_ std::printf("\n===Printing the IPMI Formatted Data========\n"); for (uint8_t pos = 0; pos < index; pos++) { std::printf("%02x ", payloadStartingAddress[pos]); } #endif return rc; } /** @brief convert IPv4 and IPv6 addresses from binary to text form. * @param[in] family - IPv4/Ipv6 * @param[in] data - req data pointer. * @param[in] offset - offset in the data. * @param[in] addrSize - size of the data which needs to be read from offset. * @returns address in text form. */ std::string getAddrStr(uint8_t family, uint8_t* data, uint8_t offset, uint8_t addrSize) { char ipAddr[INET6_ADDRSTRLEN] = {}; switch (family) { case AF_INET: { struct sockaddr_in addr4 { }; std::memcpy(&addr4.sin_addr.s_addr, &data[offset], addrSize); inet_ntop(AF_INET, &addr4.sin_addr, ipAddr, INET_ADDRSTRLEN); break; } case AF_INET6: { struct sockaddr_in6 addr6 { }; std::memcpy(&addr6.sin6_addr.s6_addr, &data[offset], addrSize); inet_ntop(AF_INET6, &addr6.sin6_addr, ipAddr, INET6_ADDRSTRLEN); break; } default: { return {}; } } return ipAddr; } ipmi::Cc setHostNetworkData(ipmi::message::Payload& data) { using namespace std::string_literals; std::string hostNetworkConfig; std::string mac("00:00:00:00:00:00"); std::string ipAddress, gateway; std::string addrOrigin{0}; uint8_t addrSize{0}; std::string addressOrigin = "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP"; std::string addressType = "xyz.openbmc_project.Network.IP.Protocol.IPv4"; uint8_t prefix{0}; uint8_t family = AF_INET; // cookie starts from second byte // version starts from sixth byte try { do { // cookie == 0x21 0x70 0x62 0x21 data.trailingOk = true; auto msgLen = data.size(); std::vector<uint8_t> msgPayloadBytes(msgLen); if (data.unpack(msgPayloadBytes) != 0 || !data.fullyUnpacked()) { log<level::ERR>( "Error in unpacking message of setHostNetworkData"); return ipmi::ccReqDataLenInvalid; } uint8_t* msgPayloadStartingPos = msgPayloadBytes.data(); constexpr size_t cookieSize = 4; if (msgLen < cookieOffset + cookieSize) { log<level::ERR>( "Error in cookie getting of setHostNetworkData"); return ipmi::ccReqDataLenInvalid; } if (std::equal(msgPayloadStartingPos + cookieOffset, msgPayloadStartingPos + cookieOffset + cookieSize, (netConfInitialBytes + cookieOffset)) != 0) { // all cookie == 0 if (std::all_of(msgPayloadStartingPos + cookieOffset, msgPayloadStartingPos + cookieOffset + cookieSize, [](int i) { return i == 0; }) == true) { // need to zero out the network settings. break; } log<level::ERR>("Invalid Cookie"); elog<InternalFailure>(); } // vesion == 0x00 0x01 if (msgLen < versionOffset + sizeVersion) { log<level::ERR>( "Error in version getting of setHostNetworkData"); return ipmi::ccReqDataLenInvalid; } if (std::equal(msgPayloadStartingPos + versionOffset, msgPayloadStartingPos + versionOffset + sizeVersion, (netConfInitialBytes + versionOffset)) != 0) { log<level::ERR>("Invalid Version"); elog<InternalFailure>(); } if (msgLen < macOffset + 6) { log<level::ERR>( "Error in mac address getting of setHostNetworkData"); return ipmi::ccReqDataLenInvalid; } std::stringstream result; std::copy((msgPayloadStartingPos + macOffset), (msgPayloadStartingPos + macOffset + 5), std::ostream_iterator<int>(result, ":")); mac = result.str(); if (msgLen < addrTypeOffset + sizeof(decltype(addrOrigin))) { log<level::ERR>( "Error in original address getting of setHostNetworkData"); return ipmi::ccReqDataLenInvalid; } std::copy(msgPayloadStartingPos + addrTypeOffset, msgPayloadStartingPos + addrTypeOffset + sizeof(decltype(addrOrigin)), std::ostream_iterator<int>(result, "")); addrOrigin = result.str(); if (!addrOrigin.empty()) { addressOrigin = "xyz.openbmc_project.Network.IP.AddressOrigin.Static"; } if (msgLen < addrSizeOffset + sizeof(decltype(addrSize))) { log<level::ERR>( "Error in address size getting of setHostNetworkData"); return ipmi::ccReqDataLenInvalid; } // Get the address size std::copy(msgPayloadStartingPos + addrSizeOffset, (msgPayloadStartingPos + addrSizeOffset + sizeof(decltype(addrSize))), &addrSize); uint8_t prefixOffset = ipAddrOffset + addrSize; if (msgLen < prefixOffset + sizeof(decltype(prefix))) { log<level::ERR>( "Error in prefix getting of setHostNetworkData"); return ipmi::ccReqDataLenInvalid; } std::copy(msgPayloadStartingPos + prefixOffset, (msgPayloadStartingPos + prefixOffset + sizeof(decltype(prefix))), &prefix); uint8_t gatewayOffset = prefixOffset + sizeof(decltype(prefix)); if (addrSize != ipmi::network::IPV4_ADDRESS_SIZE_BYTE) { addressType = "xyz.openbmc_project.Network.IP.Protocol.IPv6"; family = AF_INET6; } if (msgLen < ipAddrOffset + addrSize) { log<level::ERR>( "Error in IP address getting of setHostNetworkData"); return ipmi::ccReqDataLenInvalid; } ipAddress = getAddrStr(family, msgPayloadStartingPos, ipAddrOffset, addrSize); if (msgLen < gatewayOffset + addrSize) { log<level::ERR>( "Error in gateway address getting of setHostNetworkData"); return ipmi::ccReqDataLenInvalid; } gateway = getAddrStr(family, msgPayloadStartingPos, gatewayOffset, addrSize); } while (0); // Cookie == 0 or it is a valid cookie hostNetworkConfig += "ipaddress="s + ipAddress + ",prefix="s + std::to_string(prefix) + ",gateway="s + gateway + ",mac="s + mac + ",addressOrigin="s + addressOrigin; sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection()); auto ipObjectInfo = ipmi::getDbusObject(bus, IP_INTERFACE, SETTINGS_ROOT, SETTINGS_MATCH); auto macObjectInfo = ipmi::getDbusObject(bus, MAC_INTERFACE, SETTINGS_ROOT, SETTINGS_MATCH); // set the dbus property ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first, IP_INTERFACE, "Address", std::string(ipAddress)); ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first, IP_INTERFACE, "PrefixLength", prefix); ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first, IP_INTERFACE, "Origin", addressOrigin); ipmi::setDbusProperty(bus, ipObjectInfo.second, ipObjectInfo.first, IP_INTERFACE, "Gateway", std::string(gateway)); ipmi::setDbusProperty( bus, ipObjectInfo.second, ipObjectInfo.first, IP_INTERFACE, "Type", std::string("xyz.openbmc_project.Network.IP.Protocol.IPv4")); ipmi::setDbusProperty(bus, macObjectInfo.second, macObjectInfo.first, MAC_INTERFACE, "MACAddress", std::string(mac)); log<level::DEBUG>("Network configuration changed", entry("NETWORKCONFIG=%s", hostNetworkConfig.c_str())); } catch (sdbusplus::exception_t& e) { commit<InternalFailure>(); log<level::ERR>("Error in ipmiChassisSetSysBootOptions call"); return ipmi::ccUnspecifiedError; } return ipmi::ccSuccess; } uint32_t getPOHCounter() { sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; auto chassisStateObj = ipmi::getDbusObject(bus, chassisPOHStateIntf, chassisStateRoot, match); auto service = ipmi::getService(bus, chassisPOHStateIntf, chassisStateObj.first); auto propValue = ipmi::getDbusProperty(bus, service, chassisStateObj.first, chassisPOHStateIntf, pohCounterProperty); return std::get<uint32_t>(propValue); } /** @brief Implements the get chassis capabilities command * * @returns IPMI completion code plus response data * chassisCapFlags - chassis capability flag * chassisFRUInfoDevAddr - chassis FRU info Device Address * chassisSDRDevAddr - chassis SDR device address * chassisSELDevAddr - chassis SEL device address * chassisSMDevAddr - chassis system management device address * chassisBridgeDevAddr - chassis bridge device address */ ipmi::RspType<bool, // chassis intrusion sensor bool, // chassis Front panel lockout bool, // chassis NMI bool, // chassis power interlock uint4_t, // reserved uint8_t, // chassis FRU info Device Address uint8_t, // chassis SDR device address uint8_t, // chassis SEL device address uint8_t, // chassis system management device address uint8_t // chassis bridge device address > ipmiGetChassisCap() { ipmi::PropertyMap properties; try { sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; ipmi::DbusObjectInfo chassisCapObject = ipmi::getDbusObject(bus, chassisCapIntf); // capabilities flags // [7..4] - reserved // [3] – 1b = provides power interlock (IPM 1.5) // [2] – 1b = provides Diagnostic Interrupt (FP NMI) // [1] – 1b = provides “Front Panel Lockout†(indicates that the chassis // has capabilities // to lock out external power control and reset button or // front panel interfaces and/or detect tampering with those // interfaces). // [0] -1b = Chassis provides intrusion (physical security) sensor. // set to default value 0x0. properties = ipmi::getAllDbusProperties(bus, chassisCapObject.second, chassisCapObject.first, chassisCapIntf); } catch (std::exception& e) { log<level::ERR>("Failed to fetch Chassis Capability properties", entry("ERROR=%s", e.what())); return ipmi::responseUnspecifiedError(); } bool* chassisIntrusionFlag = std::get_if<bool>(&properties[chassisIntrusionProp]); if (chassisIntrusionFlag == nullptr) { log<level::ERR>("Error to get chassis Intrusion flags"); return ipmi::responseUnspecifiedError(); } bool* chassisFrontPanelFlag = std::get_if<bool>(&properties[chassisFrontPanelLockoutProp]); if (chassisFrontPanelFlag == nullptr) { log<level::ERR>("Error to get chassis intrusion flags"); return ipmi::responseUnspecifiedError(); } bool* chassisNMIFlag = std::get_if<bool>(&properties[chassisNMIProp]); if (chassisNMIFlag == nullptr) { log<level::ERR>("Error to get chassis NMI flags"); return ipmi::responseUnspecifiedError(); } bool* chassisPowerInterlockFlag = std::get_if<bool>(&properties[chassisPowerInterlockProp]); if (chassisPowerInterlockFlag == nullptr) { log<level::ERR>("Error to get chassis power interlock flags"); return ipmi::responseUnspecifiedError(); } uint8_t* chassisFRUInfoDevAddr = std::get_if<uint8_t>(&properties[chassisFRUDevAddrProp]); if (chassisFRUInfoDevAddr == nullptr) { log<level::ERR>("Error to get chassis FRU info device address"); return ipmi::responseUnspecifiedError(); } uint8_t* chassisSDRDevAddr = std::get_if<uint8_t>(&properties[chassisSDRDevAddrProp]); if (chassisSDRDevAddr == nullptr) { log<level::ERR>("Error to get chassis SDR device address"); return ipmi::responseUnspecifiedError(); } uint8_t* chassisSELDevAddr = std::get_if<uint8_t>(&properties[chassisSELDevAddrProp]); if (chassisSELDevAddr == nullptr) { log<level::ERR>("Error to get chassis SEL device address"); return ipmi::responseUnspecifiedError(); } uint8_t* chassisSMDevAddr = std::get_if<uint8_t>(&properties[chassisSMDevAddrProp]); if (chassisSMDevAddr == nullptr) { log<level::ERR>("Error to get chassis SM device address"); return ipmi::responseUnspecifiedError(); } uint8_t* chassisBridgeDevAddr = std::get_if<uint8_t>(&properties[chassisBridgeDevAddrProp]); if (chassisBridgeDevAddr == nullptr) { log<level::ERR>("Error to get chassis bridge device address"); return ipmi::responseUnspecifiedError(); } return ipmi::responseSuccess(*chassisIntrusionFlag, *chassisFrontPanelFlag, *chassisNMIFlag, *chassisPowerInterlockFlag, 0, *chassisFRUInfoDevAddr, *chassisSDRDevAddr, *chassisSELDevAddr, *chassisSMDevAddr, *chassisBridgeDevAddr); } /** @brief implements set chassis capalibities command * @param intrusion - chassis intrusion * @param fpLockout - frontpannel lockout * @param reserved1 - skip one bit * @param fruDeviceAddr - chassis FRU info Device Address * @param sdrDeviceAddr - chassis SDR device address * @param selDeviceAddr - chassis SEL device address * @param smDeviceAddr - chassis system management device address * @param bridgeDeviceAddr - chassis bridge device address * * @returns IPMI completion code */ ipmi::RspType<> ipmiSetChassisCap(bool intrusion, bool fpLockout, uint6_t reserved1, uint8_t fruDeviceAddr, uint8_t sdrDeviceAddr, uint8_t selDeviceAddr, uint8_t smDeviceAddr, uint8_t bridgeDeviceAddr) { // check input data if (reserved1 != 0) { log<level::ERR>("Unsupported request parameter"); return ipmi::responseInvalidFieldRequest(); } if ((fruDeviceAddr & ~chassisCapAddrMask) != 0) { log<level::ERR>("Unsupported request parameter(FRU Addr)", entry("REQ=0x%x", fruDeviceAddr)); return ipmi::responseInvalidFieldRequest(); } if ((sdrDeviceAddr & ~chassisCapAddrMask) != 0) { log<level::ERR>("Unsupported request parameter(SDR Addr)", entry("REQ=0x%x", sdrDeviceAddr)); return ipmi::responseInvalidFieldRequest(); } if ((selDeviceAddr & ~chassisCapAddrMask) != 0) { log<level::ERR>("Unsupported request parameter(SEL Addr)", entry("REQ=0x%x", selDeviceAddr)); return ipmi::responseInvalidFieldRequest(); } if ((smDeviceAddr & ~chassisCapAddrMask) != 0) { log<level::ERR>("Unsupported request parameter(SM Addr)", entry("REQ=0x%x", smDeviceAddr)); return ipmi::responseInvalidFieldRequest(); } if ((bridgeDeviceAddr & ~chassisCapAddrMask) != 0) { log<level::ERR>("Unsupported request parameter(Bridge Addr)", entry("REQ=0x%x", bridgeDeviceAddr)); return ipmi::responseInvalidFieldRequest(); } try { sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection()); ipmi::DbusObjectInfo chassisCapObject = ipmi::getDbusObject(bus, chassisCapIntf); ipmi::setDbusProperty(bus, chassisCapObject.second, chassisCapObject.first, chassisCapIntf, chassisIntrusionProp, intrusion); ipmi::setDbusProperty(bus, chassisCapObject.second, chassisCapObject.first, chassisCapIntf, chassisFrontPanelLockoutProp, fpLockout); ipmi::setDbusProperty(bus, chassisCapObject.second, chassisCapObject.first, chassisCapIntf, chassisFRUDevAddrProp, fruDeviceAddr); ipmi::setDbusProperty(bus, chassisCapObject.second, chassisCapObject.first, chassisCapIntf, chassisSDRDevAddrProp, sdrDeviceAddr); ipmi::setDbusProperty(bus, chassisCapObject.second, chassisCapObject.first, chassisCapIntf, chassisSELDevAddrProp, selDeviceAddr); ipmi::setDbusProperty(bus, chassisCapObject.second, chassisCapObject.first, chassisCapIntf, chassisSMDevAddrProp, smDeviceAddr); ipmi::setDbusProperty(bus, chassisCapObject.second, chassisCapObject.first, chassisCapIntf, chassisBridgeDevAddrProp, bridgeDeviceAddr); } catch (std::exception& e) { log<level::ERR>(e.what()); return ipmi::responseUnspecifiedError(); } return ipmi::responseSuccess(); } //------------------------------------------ // Calls into Host State Manager Dbus object //------------------------------------------ int initiate_state_transition(State::Host::Transition transition) { // OpenBMC Host State Manager dbus framework constexpr auto HOST_STATE_MANAGER_ROOT = "/xyz/openbmc_project/state/host0"; constexpr auto HOST_STATE_MANAGER_IFACE = "xyz.openbmc_project.State.Host"; constexpr auto DBUS_PROPERTY_IFACE = "org.freedesktop.DBus.Properties"; constexpr auto PROPERTY = "RequestedHostTransition"; // sd_bus error int rc = 0; char* busname = NULL; // SD Bus error report mechanism. sd_bus_error bus_error = SD_BUS_ERROR_NULL; // Gets a hook onto either a SYSTEM or SESSION bus sd_bus* bus_type = ipmid_get_sd_bus_connection(); rc = mapper_get_service(bus_type, HOST_STATE_MANAGER_ROOT, &busname); if (rc < 0) { log<level::ERR>( "Failed to get bus name", entry("ERRNO=0x%X, OBJPATH=%s", -rc, HOST_STATE_MANAGER_ROOT)); return rc; } // Convert to string equivalent of the passed in transition enum. auto request = State::convertForMessage(transition); rc = sd_bus_call_method(bus_type, // On the system bus busname, // Service to contact HOST_STATE_MANAGER_ROOT, // Object path DBUS_PROPERTY_IFACE, // Interface name "Set", // Method to be called &bus_error, // object to return error nullptr, // Response buffer if any "ssv", // Takes 3 arguments HOST_STATE_MANAGER_IFACE, PROPERTY, "s", request.c_str()); if (rc < 0) { log<level::ERR>("Failed to initiate transition", entry("ERRNO=0x%X, REQUEST=%s", -rc, request.c_str())); } else { log<level::INFO>("Transition request initiated successfully"); } sd_bus_error_free(&bus_error); free(busname); return rc; } //------------------------------------------ // Set Enabled property to inform NMI source // handling to trigger a NMI_OUT BSOD. //------------------------------------------ int setNmiProperty(const bool value) { constexpr const char* nmiSourceObjPath = "/xyz/openbmc_project/Chassis/Control/NMISource"; constexpr const char* nmiSourceIntf = "xyz.openbmc_project.Chassis.Control.NMISource"; std::string bmcSourceSignal = "xyz.openbmc_project.Chassis.Control." "NMISource.BMCSourceSignal.ChassisCmd"; std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus(); try { auto service = ipmi::getService(*busp, nmiSourceIntf, nmiSourceObjPath); ipmi::setDbusProperty(*busp, service, nmiSourceObjPath, nmiSourceIntf, "BMCSource", bmcSourceSignal); ipmi::setDbusProperty(*busp, service, nmiSourceObjPath, nmiSourceIntf, "Enabled", value); } catch (std::exception& e) { log<level::ERR>("Failed to trigger NMI_OUT", entry("EXCEPTION=%s", e.what())); return -1; } return 0; } namespace power_policy { using namespace sdbusplus::xyz::openbmc_project::Control::Power::server; using IpmiValue = uint8_t; using DbusValue = RestorePolicy::Policy; const std::map<DbusValue, IpmiValue> dbusToIpmi = { {RestorePolicy::Policy::AlwaysOff, 0x00}, {RestorePolicy::Policy::Restore, 0x01}, {RestorePolicy::Policy::AlwaysOn, 0x02}}; static constexpr uint8_t noChange = 0x03; static constexpr uint8_t allSupport = 0x01 | 0x02 | 0x04; /* helper function for Get Chassis Status Command */ std::optional<uint2_t> getPowerRestorePolicy() { uint2_t restorePolicy = 0; using namespace chassis::internal; settings::Objects& objects = cache::getObjects(); try { const auto& powerRestoreSetting = objects.map.at(powerRestoreIntf).front(); ipmi::Value result = ipmi::getDbusProperty( *getSdBus(), objects.service(powerRestoreSetting, powerRestoreIntf).c_str(), powerRestoreSetting.c_str(), powerRestoreIntf, "PowerRestorePolicy"); auto powerRestore = RestorePolicy::convertPolicyFromString( std::get<std::string>(result)); restorePolicy = dbusToIpmi.at(powerRestore); } catch (const std::exception& e) { log<level::ERR>( "Failed to fetch pgood property", entry("ERROR=%s", e.what()), entry("PATH=%s", objects.map.at(powerRestoreIntf).front().c_str()), entry("INTERFACE=%s", powerRestoreIntf)); cache::objectsPtr.reset(); 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 powerState = ipmi::getDbusProperty(*busp, service, chassisStatePath, chassisStateIntf, "CurrentPowerState"); powerGood = std::get<std::string>(powerState) == "xyz.openbmc_project.State.Chassis.PowerState.On"; } catch (const std::exception& e) { try { // FIXME: some legacy modules use the older path; try that next constexpr const char* legacyPwrCtrlObj = "/org/openbmc/control/power0"; constexpr const char* legacyPwrCtrlIntf = "org.openbmc.control.Power"; auto service = ipmi::getService(*busp, legacyPwrCtrlIntf, legacyPwrCtrlObj); ipmi::Value variant = ipmi::getDbusProperty( *busp, service, legacyPwrCtrlObj, legacyPwrCtrlIntf, "pgood"); powerGood = static_cast<bool>(std::get<int>(variant)); } catch (const std::exception& e) { log<level::ERR>("Failed to fetch pgood 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, const std::string& buttonIntf) { std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus(); bool buttonDisabled = false; try { auto service = ipmi::getService(*busp, buttonIntf, buttonPath); ipmi::Value enabled = ipmi::getDbusProperty(*busp, service, buttonPath, buttonIntf, "Enabled"); buttonDisabled = !std::get<bool>(enabled); } catch (sdbusplus::exception::SdBusError& e) { log<level::ERR>("Fail to get button Enabled property", entry("PATH=%s", buttonPath.c_str()), entry("ERROR=%s", e.what())); return std::nullopt; } return std::make_optional(buttonDisabled); } static bool setButtonEnabled(ipmi::Context::ptr& ctx, const std::string& buttonPath, const std::string& buttonIntf, bool enable) { std::string service; boost::system::error_code ec; ec = ipmi::getService(ctx, buttonIntf, buttonPath, service); if (!ec) { ec = ipmi::setDbusProperty(ctx, service, buttonPath, buttonIntf, "Enabled", enable); } if (ec) { log<level::ERR>("Fail to set button Enabled property", entry("SERVICE=%s", service.c_str()), entry("PATH=%s", buttonPath.c_str()), entry("ERROR=%s", ec.message().c_str())); return false; } return true; } //---------------------------------------------------------------------- // 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() { using namespace chassis::internal; 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, powerButtonIntf); // 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, resetButtonIntf); // 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; } 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 = types::enum_cast<uint2_t>(chassisIDState); constexpr bool diagButtonDisabled = false; constexpr bool sleepButtonDisabled = false; constexpr bool diagButtonDisableAllow = 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, diagButtonDisabled, sleepButtonDisabled, powerButtonDisableAllow, resetButtonDisableAllow, diagButtonDisableAllow, sleepButtonDisableAllow); } enum class IpmiRestartCause { Unknown = 0x0, RemoteCommand = 0x1, ResetButton = 0x2, PowerButton = 0x3, WatchdogTimer = 0x4, PowerPolicyAlwaysOn = 0x6, PowerPolicyPreviousState = 0x7, SoftReset = 0xa, }; static IpmiRestartCause restartCauseToIpmiRestartCause(State::Host::RestartCause cause) { switch (cause) { case State::Host::RestartCause::Unknown: { return IpmiRestartCause::Unknown; } case State::Host::RestartCause::RemoteCommand: { return IpmiRestartCause::RemoteCommand; } case State::Host::RestartCause::ResetButton: { return IpmiRestartCause::ResetButton; } case State::Host::RestartCause::PowerButton: { return IpmiRestartCause::PowerButton; } case State::Host::RestartCause::WatchdogTimer: { return IpmiRestartCause::WatchdogTimer; } case State::Host::RestartCause::PowerPolicyAlwaysOn: { return IpmiRestartCause::PowerPolicyAlwaysOn; } case State::Host::RestartCause::PowerPolicyPreviousState: { return IpmiRestartCause::PowerPolicyPreviousState; } case State::Host::RestartCause::SoftReset: { return IpmiRestartCause::SoftReset; } default: { return IpmiRestartCause::Unknown; } } } /* * getRestartCause * helper function for Get Host restart cause Command * return - optional value for RestartCause (no value on error) */ static std::optional<uint4_t> getRestartCause(ipmi::Context::ptr ctx) { constexpr const char* restartCausePath = "/xyz/openbmc_project/control/host0/restart_cause"; constexpr const char* restartCauseIntf = "xyz.openbmc_project.Control.Host.RestartCause"; std::string service; boost::system::error_code ec = ipmi::getService(ctx, restartCauseIntf, restartCausePath, service); if (!ec) { std::string restartCauseStr; ec = ipmi::getDbusProperty<std::string>( ctx, service, restartCausePath, restartCauseIntf, "RestartCause", restartCauseStr); if (!ec) { auto cause = State::Host::convertRestartCauseFromString(restartCauseStr); return types::enum_cast<uint4_t>( restartCauseToIpmiRestartCause(cause)); } } log<level::ERR>("Failed to fetch RestartCause property", entry("ERROR=%s", ec.message().c_str()), entry("PATH=%s", restartCausePath), entry("INTERFACE=%s", restartCauseIntf)); return std::nullopt; } ipmi::RspType<uint4_t, // Restart Cause uint4_t, // reserved uint8_t // channel number (not supported) > ipmiGetSystemRestartCause(ipmi::Context::ptr ctx) { std::optional<uint4_t> cause = getRestartCause(ctx); if (!cause) { return ipmi::responseUnspecifiedError(); } constexpr uint4_t reserved = 0; auto channel = static_cast<uint8_t>(ctx->channel); return ipmi::responseSuccess(cause.value(), reserved, channel); } //------------------------------------------------------------- // Send a command to SoftPowerOff application to stop any timer //------------------------------------------------------------- int stop_soft_off_timer() { constexpr auto iface = "org.freedesktop.DBus.Properties"; constexpr auto soft_off_iface = "xyz.openbmc_project.Ipmi.Internal." "SoftPowerOff"; constexpr auto property = "ResponseReceived"; constexpr auto value = "xyz.openbmc_project.Ipmi.Internal." "SoftPowerOff.HostResponse.HostShutdown"; // Get the system bus where most system services are provided. auto bus = ipmid_get_sd_bus_connection(); // Get the service name // TODO openbmc/openbmc#1661 - Mapper refactor // // See openbmc/openbmc#1743 for some details but high level summary is that // for now the code will directly call the soft off interface due to a // race condition with mapper usage // // char *busname = nullptr; // auto r = mapper_get_service(bus, SOFTOFF_OBJPATH, &busname); // if (r < 0) //{ // fprintf(stderr, "Failed to get %s bus name: %s\n", // SOFTOFF_OBJPATH, -r); // return r; //} // No error object or reply expected. int rc = sd_bus_call_method(bus, SOFTOFF_BUSNAME, SOFTOFF_OBJPATH, iface, "Set", nullptr, nullptr, "ssv", soft_off_iface, property, "s", value); if (rc < 0) { log<level::ERR>("Failed to set property in SoftPowerOff object", entry("ERRNO=0x%X", -rc)); } // TODO openbmc/openbmc#1661 - Mapper refactor // free(busname); return rc; } //---------------------------------------------------------------------- // Create file to indicate there is no need for softoff notification to host //---------------------------------------------------------------------- void indicate_no_softoff_needed() { fs::path path{HOST_INBAND_REQUEST_DIR}; if (!fs::is_directory(path)) { fs::create_directory(path); } // Add the host instance (default 0 for now) to the file name std::string file{HOST_INBAND_REQUEST_FILE}; auto size = std::snprintf(nullptr, 0, file.c_str(), 0); size++; // null std::unique_ptr<char[]> buf(new char[size]); std::snprintf(buf.get(), size, file.c_str(), 0); // Append file name to directory and create it path /= buf.get(); std::ofstream(path.c_str()); } /** @brief Implementation of chassis control command * * @param - chassisControl command byte * * @return Success or InvalidFieldRequest. */ ipmi::RspType<> ipmiChassisControl(uint8_t chassisControl) { int rc = 0; switch (chassisControl) { case CMD_POWER_ON: rc = initiate_state_transition(State::Host::Transition::On); break; case CMD_POWER_OFF: // This path would be hit in 2 conditions. // 1: When user asks for power off using ipmi chassis command 0x04 // 2: Host asking for power off post shutting down. // If it's a host requested power off, then need to nudge Softoff // application that it needs to stop the watchdog timer if running. // If it is a user requested power off, then this is not really // needed. But then we need to differentiate between user and host // calling this same command // For now, we are going ahead with trying to nudge the soft off and // interpret the failure to do so as a non softoff case rc = stop_soft_off_timer(); // Only request the Off transition if the soft power off // application is not running if (rc < 0) { // First create a file to indicate to the soft off application // that it should not run. Not doing this will result in State // manager doing a default soft power off when asked for power // off. indicate_no_softoff_needed(); // Now request the shutdown rc = initiate_state_transition(State::Host::Transition::Off); } else { log<level::INFO>("Soft off is running, so let shutdown target " "stop the host"); } break; case CMD_HARD_RESET: case CMD_POWER_CYCLE: // SPEC has a section that says certain implementations can trigger // PowerOn if power is Off when a command to power cycle is // requested // First create a file to indicate to the soft off application // that it should not run since this is a direct user initiated // power reboot request (i.e. a reboot request that is not // originating via a soft power off SMS request) indicate_no_softoff_needed(); rc = initiate_state_transition(State::Host::Transition::Reboot); break; case CMD_SOFT_OFF_VIA_OVER_TEMP: // Request Host State Manager to do a soft power off rc = initiate_state_transition(State::Host::Transition::Off); break; case CMD_PULSE_DIAGNOSTIC_INTR: rc = setNmiProperty(true); break; default: { log<level::ERR>("Invalid Chassis Control command", entry("CMD=0x%X", chassisControl)); return ipmi::responseInvalidFieldRequest(); } } return ((rc < 0) ? ipmi::responseUnspecifiedError() : ipmi::responseSuccess()); } /** @brief Return D-Bus connection string to enclosure identify LED object * * @param[in, out] connection - connection to D-Bus object * @return a IPMI return code */ std::string getEnclosureIdentifyConnection() { // lookup enclosure_identify group owner(s) in mapper auto mapperCall = chassis::internal::dbus.new_method_call( ipmi::MAPPER_BUS_NAME, ipmi::MAPPER_OBJ, ipmi::MAPPER_INTF, "GetObject"); mapperCall.append(identify_led_object_name); static const std::vector<std::string> interfaces = { "xyz.openbmc_project.Led.Group"}; mapperCall.append(interfaces); auto mapperReply = chassis::internal::dbus.call(mapperCall); if (mapperReply.is_method_error()) { log<level::ERR>("Chassis Identify: Error communicating to mapper."); elog<InternalFailure>(); } std::vector<std::pair<std::string, std::vector<std::string>>> mapperResp; mapperReply.read(mapperResp); if (mapperResp.size() != encIdentifyObjectsSize) { log<level::ERR>( "Invalid number of enclosure identify objects.", entry("ENC_IDENTITY_OBJECTS_SIZE=%d", mapperResp.size())); elog<InternalFailure>(); } auto pair = mapperResp[encIdentifyObjectsSize - 1]; return pair.first; } /** @brief Turn On/Off enclosure identify LED * * @param[in] flag - true to turn on LED, false to turn off * @return a IPMI return code */ void enclosureIdentifyLed(bool flag) { using namespace chassis::internal; std::string connection = std::move(getEnclosureIdentifyConnection()); auto msg = std::string("enclosureIdentifyLed(") + boost::lexical_cast<std::string>(flag) + ")"; log<level::DEBUG>(msg.c_str()); auto led = dbus.new_method_call(connection.c_str(), identify_led_object_name, "org.freedesktop.DBus.Properties", "Set"); led.append("xyz.openbmc_project.Led.Group", "Asserted", std::variant<bool>(flag)); auto ledReply = dbus.call(led); if (ledReply.is_method_error()) { log<level::ERR>("Chassis Identify: Error Setting State On/Off\n", entry("LED_STATE=%d", flag)); elog<InternalFailure>(); } } /** @brief Callback method to turn off LED */ void enclosureIdentifyLedOff() { try { chassisIDState = ChassisIDState::off; enclosureIdentifyLed(false); } catch (const InternalFailure& e) { report<InternalFailure>(); } } /** @brief Create timer to turn on and off the enclosure LED */ void createIdentifyTimer() { if (!identifyTimer) { identifyTimer = std::make_unique<phosphor::Timer>(enclosureIdentifyLedOff); } } ipmi::RspType<> ipmiChassisIdentify(std::optional<uint8_t> interval, std::optional<uint8_t> force) { uint8_t identifyInterval = interval.value_or(DEFAULT_IDENTIFY_TIME_OUT); bool forceIdentify = force.value_or(0) & 0x01; if (identifyInterval || forceIdentify) { // stop the timer if already started; // for force identify we should not turn off LED identifyTimer->stop(); try { chassisIDState = ChassisIDState::temporaryOn; enclosureIdentifyLed(true); } catch (const InternalFailure& e) { report<InternalFailure>(); return ipmi::responseResponseError(); } if (forceIdentify) { chassisIDState = ChassisIDState::indefiniteOn; return ipmi::responseSuccess(); } // start the timer auto time = std::chrono::duration_cast<std::chrono::microseconds>( std::chrono::seconds(identifyInterval)); identifyTimer->start(time); } else if (!identifyInterval) { identifyTimer->stop(); enclosureIdentifyLedOff(); } return ipmi::responseSuccess(); } namespace boot_options { using namespace sdbusplus::xyz::openbmc_project::Control::Boot::server; using IpmiValue = uint8_t; constexpr auto ipmiDefault = 0; std::map<IpmiValue, Type::Types> typeIpmiToDbus = {{0x00, Type::Types::Legacy}, {0x01, Type::Types::EFI}}; std::map<IpmiValue, Source::Sources> sourceIpmiToDbus = { {0x01, Source::Sources::Network}, {0x02, Source::Sources::Disk}, {0x05, Source::Sources::ExternalMedia}, {0x0f, Source::Sources::RemovableMedia}, {ipmiDefault, Source::Sources::Default}}; std::map<IpmiValue, Mode::Modes> modeIpmiToDbus = { #ifdef ENABLE_BOOT_FLAG_SAFE_MODE_SUPPORT {0x03, Mode::Modes::Safe}, #endif // ENABLE_BOOT_SAFE_MODE_SUPPORT {0x06, Mode::Modes::Setup}, {ipmiDefault, Mode::Modes::Regular}}; std::map<Type::Types, IpmiValue> typeDbusToIpmi = {{Type::Types::Legacy, 0x00}, {Type::Types::EFI, 0x01}}; std::map<Source::Sources, IpmiValue> sourceDbusToIpmi = { {Source::Sources::Network, 0x01}, {Source::Sources::Disk, 0x02}, {Source::Sources::ExternalMedia, 0x05}, {Source::Sources::RemovableMedia, 0x0f}, {Source::Sources::Default, ipmiDefault}}; std::map<Mode::Modes, IpmiValue> modeDbusToIpmi = { #ifdef ENABLE_BOOT_FLAG_SAFE_MODE_SUPPORT {Mode::Modes::Safe, 0x03}, #endif // ENABLE_BOOT_SAFE_MODE_SUPPORT {Mode::Modes::Setup, 0x06}, {Mode::Modes::Regular, ipmiDefault}}; } // namespace boot_options /** @brief Get the property value for boot source * @param[in] ctx - context pointer * @param[out] source - boot source value * @return On failure return IPMI error. */ static ipmi::Cc getBootSource(ipmi::Context::ptr& ctx, Source::Sources& source) { using namespace chassis::internal; std::string result; std::string service; boost::system::error_code ec = getService(ctx, bootSourceIntf, bootSettingsPath, service); if (!ec) { ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath, bootSourceIntf, "BootSource", result); if (!ec) { source = Source::convertSourcesFromString(result); return ipmi::ccSuccess; } } log<level::ERR>("Error in BootSource Get", entry("ERROR=%s", ec.message().c_str())); return ipmi::ccUnspecifiedError; } /** @brief Set the property value for boot source * @param[in] ctx - context pointer * @param[in] source - boot source value * @return On failure return IPMI error. */ static ipmi::Cc setBootSource(ipmi::Context::ptr& ctx, const Source::Sources& source) { using namespace chassis::internal; std::string service; boost::system::error_code ec = getService(ctx, bootSourceIntf, bootSettingsPath, service); if (!ec) { ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath, bootSourceIntf, "BootSource", convertForMessage(source)); if (!ec) { return ipmi::ccSuccess; } } log<level::ERR>("Error in BootSource Set", entry("ERROR=%s", ec.message().c_str())); return ipmi::ccUnspecifiedError; } /** @brief Get the property value for boot mode * @param[in] ctx - context pointer * @param[out] mode - boot mode value * @return On failure return IPMI error. */ static ipmi::Cc getBootMode(ipmi::Context::ptr& ctx, Mode::Modes& mode) { using namespace chassis::internal; std::string result; std::string service; boost::system::error_code ec = getService(ctx, bootModeIntf, bootSettingsPath, service); if (!ec) { ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath, bootModeIntf, "BootMode", result); if (!ec) { mode = Mode::convertModesFromString(result); return ipmi::ccSuccess; } } log<level::ERR>("Error in BootMode Get", entry("ERROR=%s", ec.message().c_str())); return ipmi::ccUnspecifiedError; } /** @brief Set the property value for boot mode * @param[in] ctx - context pointer * @param[in] mode - boot mode value * @return On failure return IPMI error. */ static ipmi::Cc setBootMode(ipmi::Context::ptr& ctx, const Mode::Modes& mode) { using namespace chassis::internal; std::string service; boost::system::error_code ec = getService(ctx, bootModeIntf, bootSettingsPath, service); if (!ec) { ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath, bootModeIntf, "BootMode", convertForMessage(mode)); if (!ec) { return ipmi::ccSuccess; } } log<level::ERR>("Error in BootMode Set", entry("ERROR=%s", ec.message().c_str())); return ipmi::ccUnspecifiedError; } /** @brief Get the property value for boot type * @param[in] ctx - context pointer * @param[out] type - boot type value * @return On failure return IPMI error. */ static ipmi::Cc getBootType(ipmi::Context::ptr& ctx, Type::Types& type) { using namespace chassis::internal; std::string result; std::string service; boost::system::error_code ec = getService(ctx, bootTypeIntf, bootSettingsPath, service); // Don't throw error if BootType interface is not present. // This interface is not relevant for some Host architectures // (for example POWER). In this case we don't won't IPMI to // return an error, but simply return bootType as EFI. type = Type::Types::EFI; if (!ec) { ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath, bootTypeIntf, "BootType", result); if (ec) { log<level::ERR>("Error in BootType Get", entry("ERROR=%s", ec.message().c_str())); return ipmi::ccUnspecifiedError; } type = Type::convertTypesFromString(result); } return ipmi::ccSuccess; } /** @brief Set the property value for boot type * @param[in] ctx - context pointer * @param[in] type - boot type value * @return On failure return IPMI error. */ static ipmi::Cc setBootType(ipmi::Context::ptr& ctx, const Type::Types& type) { using namespace chassis::internal; std::string service; boost::system::error_code ec = getService(ctx, bootTypeIntf, bootSettingsPath, service); if (!ec) { ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath, bootTypeIntf, "BootType", convertForMessage(type)); if (ec) { log<level::ERR>("Error in BootType Set", entry("ERROR=%s", ec.message().c_str())); return ipmi::ccUnspecifiedError; } } // Don't throw error if BootType interface is not present. // This interface is not relevant for some Host architectures // (for example POWER). In this case we don't won't IPMI to // return an error, but want to just skip this function. return ipmi::ccSuccess; } /** @brief Get the property value for boot override enable * @param[in] ctx - context pointer * @param[out] enable - boot override enable * @return On failure return IPMI error. */ static ipmi::Cc getBootEnable(ipmi::Context::ptr& ctx, bool& enable) { using namespace chassis::internal; std::string result; std::string service; boost::system::error_code ec = getService(ctx, bootEnableIntf, bootSettingsPath, service); if (!ec) { ec = ipmi::getDbusProperty(ctx, service, bootSettingsPath, bootEnableIntf, "Enabled", enable); if (!ec) { return ipmi::ccSuccess; } } log<level::ERR>("Error in Boot Override Enable Get", entry("ERROR=%s", ec.message().c_str())); return ipmi::ccUnspecifiedError; } /** @brief Set the property value for boot override enable * @param[in] ctx - context pointer * @param[in] enable - boot override enable * @return On failure return IPMI error. */ static ipmi::Cc setBootEnable(ipmi::Context::ptr& ctx, const bool& enable) { using namespace chassis::internal; std::string service; boost::system::error_code ec = getService(ctx, bootEnableIntf, bootSettingsPath, service); if (!ec) { ec = ipmi::setDbusProperty(ctx, service, bootSettingsPath, bootEnableIntf, "Enabled", enable); if (!ec) { return ipmi::ccSuccess; } } log<level::ERR>("Error in Boot Source Override Enable Set", entry("ERROR=%s", ec.message().c_str())); return ipmi::ccUnspecifiedError; } /** @brief Get the property value for boot override one-time * @param[in] ctx - context pointer * @param[out] onetime - boot override one-time * @return On failure return IPMI error. */ static ipmi::Cc getBootOneTime(ipmi::Context::ptr& ctx, bool& onetime) { using namespace chassis::internal; std::string result; std::string service; boost::system::error_code ec = getService(ctx, bootOneTimeIntf, bootSettingsOneTimePath, service); if (!ec) { ec = ipmi::getDbusProperty(ctx, service, bootSettingsOneTimePath, bootOneTimeIntf, "Enabled", onetime); if (!ec) { return ipmi::ccSuccess; } } log<level::ERR>("Error in Boot Override OneTime Get", entry("ERROR=%s", ec.message().c_str())); return ipmi::ccUnspecifiedError; } /** @brief Set the property value for boot override one-time * @param[in] ctx - context pointer * @param[in] onetime - boot override one-time * @return On failure return IPMI error. */ static ipmi::Cc setBootOneTime(ipmi::Context::ptr& ctx, const bool& onetime) { using namespace chassis::internal; std::string service; boost::system::error_code ec = getService(ctx, bootOneTimeIntf, bootSettingsOneTimePath, service); if (!ec) { ec = ipmi::setDbusProperty(ctx, service, bootSettingsOneTimePath, bootOneTimeIntf, "Enabled", onetime); if (!ec) { return ipmi::ccSuccess; } } log<level::ERR>("Error in Boot Source Override OneTime Set", entry("ERROR=%s", ec.message().c_str())); return ipmi::ccUnspecifiedError; } static constexpr uint8_t setComplete = 0x0; static constexpr uint8_t setInProgress = 0x1; static uint8_t transferStatus = setComplete; static uint8_t bootFlagValidBitClr = 0; /** @brief implements the Get Chassis system boot option * @param ctx - context pointer * @param bootOptionParameter - boot option parameter selector * @param reserved1 - reserved bit * @param setSelector - selects a particular block or set of parameters * under the given parameter selector * write as 00h if parameter doesn't use a setSelector * @param blockSelector- selects a particular block within a set of * parameters write as 00h if parameter doesn't use a * blockSelector * * @return IPMI completion code plus response data * @return Payload contains below parameters: * version - parameter version * bootOptionParameter - boot option parameter selector * parmIndicator - parameter valid/invalid indicator * data - configuration parameter data */ ipmi::RspType<ipmi::message::Payload> ipmiChassisGetSysBootOptions(ipmi::Context::ptr ctx, uint7_t bootOptionParameter, bool reserved1, uint8_t setSelector, uint8_t blockSelector) { ipmi::Cc rc; if (reserved1) { return ipmi::responseInvalidFieldRequest(); } constexpr uint4_t version = 0x01; ipmi::message::Payload response; response.pack(version, uint4_t{}); using namespace boot_options; IpmiValue bootOption = ipmiDefault; if (types::enum_cast<BootOptionParameter>(bootOptionParameter) == BootOptionParameter::setInProgress) { response.pack(bootOptionParameter, reserved1, transferStatus); return ipmi::responseSuccess(std::move(response)); } if (types::enum_cast<BootOptionParameter>(bootOptionParameter) == BootOptionParameter::bootInfo) { constexpr uint8_t writeMask = 0; constexpr uint8_t bootInfoAck = 0; response.pack(bootOptionParameter, writeMask, bootInfoAck); return ipmi::responseSuccess(std::move(response)); } if (types::enum_cast<BootOptionParameter>(bootOptionParameter) == BootOptionParameter::bootFlagValidClr) { response.pack(bootOptionParameter, reserved1, uint5_t{bootFlagValidBitClr}, uint3_t{}); return ipmi::responseSuccess(std::move(response)); } /* * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc. * This is the only parameter used by petitboot. */ if (types::enum_cast<BootOptionParameter>(bootOptionParameter) == BootOptionParameter::bootFlags) { using namespace chassis::internal; using namespace chassis::internal::cache; try { Source::Sources bootSource; rc = getBootSource(ctx, bootSource); if (rc != ipmi::ccSuccess) { return ipmi::response(rc); } Type::Types bootType; rc = getBootType(ctx, bootType); if (rc != ipmi::ccSuccess) { return ipmi::response(rc); } Mode::Modes bootMode; rc = getBootMode(ctx, bootMode); if (rc != ipmi::ccSuccess) { return ipmi::response(rc); } bootOption = sourceDbusToIpmi.at(bootSource); if ((Mode::Modes::Regular == bootMode) && (Source::Sources::Default == bootSource)) { bootOption = ipmiDefault; } else if (Source::Sources::Default == bootSource) { bootOption = modeDbusToIpmi.at(bootMode); } IpmiValue biosBootType = typeDbusToIpmi.at(bootType); bool oneTimeEnabled; rc = getBootOneTime(ctx, oneTimeEnabled); if (rc != ipmi::ccSuccess) { return ipmi::response(rc); } uint1_t permanent = oneTimeEnabled ? 0 : 1; bool valid; rc = getBootEnable(ctx, valid); if (rc != ipmi::ccSuccess) { return ipmi::response(rc); } uint1_t validFlag = valid ? 1 : 0; response.pack(bootOptionParameter, reserved1, uint5_t{}, uint1_t{biosBootType}, uint1_t{permanent}, uint1_t{validFlag}, uint2_t{}, uint4_t{bootOption}, uint2_t{}, uint8_t{}, uint8_t{}, uint8_t{}); return ipmi::responseSuccess(std::move(response)); } catch (InternalFailure& e) { cache::objectsPtr.reset(); report<InternalFailure>(); return ipmi::responseUnspecifiedError(); } } else { if ((bootOptionParameter >= oemParmStart) && (bootOptionParameter <= oemParmEnd)) { if (types::enum_cast<BootOptionParameter>(bootOptionParameter) == BootOptionParameter::opalNetworkSettings) { response.pack(bootOptionParameter, reserved1); int ret = getHostNetworkData(response); if (ret < 0) { response.trailingOk = true; log<level::ERR>( "getHostNetworkData failed for GetSysBootOptions."); return ipmi::responseUnspecifiedError(); } else { return ipmi::responseSuccess(std::move(response)); } } else { log<level::ERR>( "ipmiChassisGetSysBootOptions: Unsupported parameter", entry("PARAM=0x%x", static_cast<uint8_t>(bootOptionParameter))); return ipmi::responseParmNotSupported(); } } else { log<level::ERR>( "ipmiChassisGetSysBootOptions: Unsupported parameter", entry("PARAM=0x%x", static_cast<uint8_t>(bootOptionParameter))); return ipmi::responseParmNotSupported(); } } return ipmi::responseUnspecifiedError(); } ipmi::RspType<> ipmiChassisSetSysBootOptions(ipmi::Context::ptr ctx, uint7_t parameterSelector, bool parameterIsValid, ipmi::message::Payload& data) { using namespace boot_options; ipmi::Cc rc; if (types::enum_cast<BootOptionParameter>(parameterSelector) == BootOptionParameter::setInProgress) { uint2_t setInProgressFlag; uint6_t rsvd; if (data.unpack(setInProgressFlag, rsvd) != 0 || !data.fullyUnpacked()) { return ipmi::responseReqDataLenInvalid(); } if (rsvd) { return ipmi::responseInvalidFieldRequest(); } if ((transferStatus == setInProgress) && (static_cast<uint8_t>(setInProgressFlag) != setComplete)) { return ipmi::response(IPMI_CC_FAIL_SET_IN_PROGRESS); } transferStatus = static_cast<uint8_t>(setInProgressFlag); return ipmi::responseSuccess(); } /* 000101 * Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc. * This is the only parameter used by petitboot. */ if (types::enum_cast<BootOptionParameter>(parameterSelector) == BootOptionParameter::bootFlags) { uint5_t rsvd; bool validFlag; bool permanent; bool biosBootType; bool lockOutResetButton; bool screenBlank; uint4_t bootDeviceSelector; bool lockKeyboard; bool cmosClear; uint8_t data3; uint4_t biosInfo; uint4_t rsvd1; uint5_t deviceInstance; uint3_t rsvd2; if (data.unpack(rsvd, biosBootType, permanent, validFlag, lockOutResetButton, screenBlank, bootDeviceSelector, lockKeyboard, cmosClear, data3, biosInfo, rsvd1, deviceInstance, rsvd2) != 0 || !data.fullyUnpacked()) { return ipmi::responseReqDataLenInvalid(); } if (rsvd || rsvd1 || rsvd2) { return ipmi::responseInvalidFieldRequest(); } using namespace chassis::internal; using namespace chassis::internal::cache; try { rc = setBootOneTime(ctx, !permanent); if (rc != ipmi::ccSuccess) { return ipmi::response(rc); } rc = setBootEnable(ctx, validFlag); if (rc != ipmi::ccSuccess) { return ipmi::response(rc); } auto modeItr = modeIpmiToDbus.find(static_cast<uint8_t>(bootDeviceSelector)); auto typeItr = typeIpmiToDbus.find(static_cast<uint8_t>(biosBootType)); auto sourceItr = sourceIpmiToDbus.find(static_cast<uint8_t>(bootDeviceSelector)); if (sourceIpmiToDbus.end() != sourceItr) { rc = setBootSource(ctx, sourceItr->second); if (rc != ipmi::ccSuccess) { return ipmi::response(rc); } // If a set boot device is mapping to a boot source, then reset // the boot mode D-Bus property to default. // This way the ipmid code can determine which property is not // at the default value if (sourceItr->second != Source::Sources::Default) { rc = setBootMode(ctx, Mode::Modes::Regular); if (rc != ipmi::ccSuccess) { return ipmi::response(rc); } } } if (typeIpmiToDbus.end() != typeItr) { rc = setBootType(ctx, typeItr->second); if (rc != ipmi::ccSuccess) { return ipmi::response(rc); } } if (modeIpmiToDbus.end() != modeItr) { rc = setBootMode(ctx, modeItr->second); if (rc != ipmi::ccSuccess) { return ipmi::response(rc); } // If a set boot device is mapping to a boot mode, then reset // the boot source D-Bus property to default. // This way the ipmid code can determine which property is not // at the default value if (modeItr->second != Mode::Modes::Regular) { rc = setBootSource(ctx, Source::Sources::Default); if (rc != ipmi::ccSuccess) { return ipmi::response(rc); } } } if ((modeIpmiToDbus.end() == modeItr) && (typeIpmiToDbus.end() == typeItr) && (sourceIpmiToDbus.end() == sourceItr)) { // return error if boot option is not supported log<level::ERR>( "ipmiChassisSetSysBootOptions: Boot option not supported"); return ipmi::responseInvalidFieldRequest(); } } catch (sdbusplus::exception_t& e) { objectsPtr.reset(); report<InternalFailure>(); log<level::ERR>( "ipmiChassisSetSysBootOptions: Error in setting Boot " "flag parameters"); return ipmi::responseUnspecifiedError(); } } else if (types::enum_cast<BootOptionParameter>(parameterSelector) == BootOptionParameter::bootInfo) { uint8_t writeMak; uint5_t bootInitiatorAckData; uint3_t rsvd; if (data.unpack(writeMak, bootInitiatorAckData, rsvd) != 0 || !data.fullyUnpacked()) { return ipmi::responseReqDataLenInvalid(); } if (rsvd) { return ipmi::responseInvalidFieldRequest(); } // (ccSuccess). There is no implementation in OpenBMC for this // parameter. This is added to support the ipmitool command `chassis // bootdev` which sends set on parameter #4, before setting the boot // flags. log<level::INFO>("ipmiChassisSetSysBootOptions: bootInfo parameter set " "successfully"); data.trailingOk = true; return ipmi::responseSuccess(); } else if (types::enum_cast<BootOptionParameter>(parameterSelector) == BootOptionParameter::bootFlagValidClr) { uint5_t bootFlagValidClr; uint3_t rsvd; if (data.unpack(bootFlagValidClr, rsvd) != 0 || !data.fullyUnpacked()) { return ipmi::responseReqDataLenInvalid(); } if (rsvd) { return ipmi::responseInvalidFieldRequest(); } // store boot flag valid bits clear value bootFlagValidBitClr = static_cast<uint8_t>(bootFlagValidClr); log<level::INFO>( "ipmiChassisSetSysBootOptions: bootFlagValidBits parameter set " "successfully", entry("value=0x%x", bootFlagValidBitClr)); return ipmi::responseSuccess(); } else { if ((parameterSelector >= static_cast<uint7_t>(oemParmStart)) && (parameterSelector <= static_cast<uint7_t>(oemParmEnd))) { if (types::enum_cast<BootOptionParameter>(parameterSelector) == BootOptionParameter::opalNetworkSettings) { ipmi::Cc ret = setHostNetworkData(data); if (ret != ipmi::ccSuccess) { log<level::ERR>("ipmiChassisSetSysBootOptions: Error in " "setHostNetworkData"); data.trailingOk = true; return ipmi::response(ret); } data.trailingOk = true; return ipmi::responseSuccess(); } else { log<level::ERR>( "ipmiChassisSetSysBootOptions: Unsupported parameters", entry("PARAM=0x%x", static_cast<uint8_t>(parameterSelector))); data.trailingOk = true; return ipmi::responseParmNotSupported(); } } data.trailingOk = true; return ipmi::responseParmNotSupported(); } return ipmi::responseSuccess(); } /** @brief implements Get POH counter command * @parameter * - none * @returns IPMI completion code plus response data * - minPerCount - Minutes per count * - counterReading - counter reading */ ipmi::RspType<uint8_t, // Minutes per count uint32_t // Counter reading > ipmiGetPOHCounter() { // sd_bus error try { return ipmi::responseSuccess(static_cast<uint8_t>(poh::minutesPerCount), getPOHCounter()); } catch (std::exception& e) { log<level::ERR>(e.what()); return ipmi::responseUnspecifiedError(); } } ipmi::RspType<uint3_t, // policy support uint5_t // reserved > ipmiChassisSetPowerRestorePolicy(boost::asio::yield_context yield, uint3_t policy, uint5_t reserved) { power_policy::DbusValue value = power_policy::RestorePolicy::Policy::AlwaysOff; if (reserved || (policy > power_policy::noChange)) { phosphor::logging::log<level::ERR>( "Reserved request parameter", entry("REQ=0x%x", static_cast<int>(policy))); return ipmi::responseInvalidFieldRequest(); } if (policy == power_policy::noChange) { // just return the supported policy return ipmi::responseSuccess(power_policy::allSupport, reserved); } for (auto const& it : power_policy::dbusToIpmi) { if (it.second == policy) { value = it.first; break; } } try { settings::Objects& objects = chassis::internal::cache::getObjects(); const settings::Path& powerRestoreSetting = objects.map.at(chassis::internal::powerRestoreIntf).front(); std::variant<std::string> property = convertForMessage(value); auto sdbusp = getSdBus(); boost::system::error_code ec; sdbusp->yield_method_call<void>( yield, ec, objects .service(powerRestoreSetting, chassis::internal::powerRestoreIntf) .c_str(), powerRestoreSetting, ipmi::PROP_INTF, "Set", chassis::internal::powerRestoreIntf, "PowerRestorePolicy", property); if (ec) { phosphor::logging::log<level::ERR>("Unspecified Error"); return ipmi::responseUnspecifiedError(); } } catch (InternalFailure& e) { chassis::internal::cache::objectsPtr.reset(); report<InternalFailure>(); return ipmi::responseUnspecifiedError(); } return ipmi::responseSuccess(power_policy::allSupport, reserved); } ipmi::RspType<> ipmiSetFrontPanelButtonEnables( ipmi::Context::ptr ctx, bool disablePowerButton, bool disableResetButton, bool disableDiagButton, bool disableSleepButton, uint4_t reserved) { using namespace chassis::internal; // set power button Enabled property bool success = setButtonEnabled(ctx, powerButtonPath, powerButtonIntf, !disablePowerButton); // set reset button Enabled property success &= setButtonEnabled(ctx, resetButtonPath, resetButtonIntf, !disableResetButton); if (!success) { // not all buttons were successfully set return ipmi::responseUnspecifiedError(); } return ipmi::responseSuccess(); } void register_netfn_chassis_functions() { createIdentifyTimer(); // Get Chassis Capabilities ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, ipmi::chassis::cmdGetChassisCapabilities, ipmi::Privilege::User, ipmiGetChassisCap); // Set Front Panel Button Enables ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, ipmi::chassis::cmdSetFrontPanelButtonEnables, ipmi::Privilege::Admin, ipmiSetFrontPanelButtonEnables); // Set Chassis Capabilities ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, ipmi::chassis::cmdSetChassisCapabilities, ipmi::Privilege::User, ipmiSetChassisCap); // <Get System Boot Options> ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, ipmi::chassis::cmdGetSystemBootOptions, ipmi::Privilege::Operator, ipmiChassisGetSysBootOptions); // <Get Chassis Status> ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, ipmi::chassis::cmdGetChassisStatus, ipmi::Privilege::User, ipmiGetChassisStatus); // <Chassis Get System Restart Cause> ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, ipmi::chassis::cmdGetSystemRestartCause, ipmi::Privilege::User, ipmiGetSystemRestartCause); // <Chassis Control> ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, ipmi::chassis::cmdChassisControl, ipmi::Privilege::Operator, ipmiChassisControl); // <Chassis Identify> ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, ipmi::chassis::cmdChassisIdentify, ipmi::Privilege::Operator, ipmiChassisIdentify); // <Set System Boot Options> ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, ipmi::chassis::cmdSetSystemBootOptions, ipmi::Privilege::Operator, ipmiChassisSetSysBootOptions); // <Get POH Counter> ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, ipmi::chassis::cmdGetPohCounter, ipmi::Privilege::User, ipmiGetPOHCounter); // <Set Power Restore Policy> ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, ipmi::chassis::cmdSetPowerRestorePolicy, ipmi::Privilege::Operator, ipmiChassisSetPowerRestorePolicy); }