#include "config.h" #include "chassishandler.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include std::unique_ptr 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::server::xyz::openbmc_project::state; namespace fs = std::filesystem; using namespace phosphor::logging; using namespace sdbusplus::error::xyz::openbmc_project::common; using namespace sdbusplus::server::xyz::openbmc_project::control::boot; 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_t dbus(ipmid_get_sd_bus_connection()); namespace cache { std::unique_ptr objectsPtr = nullptr; settings::Objects& getObjects() { if (objectsPtr == nullptr) { objectsPtr = std::make_unique( dbus, std::vector{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_t 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(properties["Address"]); auto gateway = std::get(properties["Gateway"]); auto prefix = std::get(properties["PrefixLength"]); uint8_t isStatic = (std::get(properties["Origin"]) == "xyz.openbmc_project.Network.IP.AddressOrigin.Static") ? 1 : 0; auto MACAddress = std::get(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 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(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 addrInBinary(addrSize); inet_pton(addressFamily, ipAddress.c_str(), reinterpret_cast(addrInBinary.data())); payload.pack(addrInBinary); payload.pack(prefix); std::vector gatewayDetails(addrSize); inet_pton(addressFamily, gateway.c_str(), reinterpret_cast(gatewayDetails.data())); payload.pack(gatewayDetails); } catch (const InternalFailure& e) { commit(); 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)) { lg2::error("Invalid net config"); rc = -1; return rc; } std::copy(netConfInitialBytes, netConfInitialBytes + sizeof(netConfInitialBytes), configDataStartingAddress); if (payloadSize < skipFirstTwoBytes + addrSizeOffset + sizeof(addrSize)) { lg2::error("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 msgPayloadBytes(msgLen); if (data.unpack(msgPayloadBytes) != 0 || !data.fullyUnpacked()) { lg2::error("Error in unpacking message of setHostNetworkData"); return ipmi::ccReqDataLenInvalid; } uint8_t* msgPayloadStartingPos = msgPayloadBytes.data(); constexpr size_t cookieSize = 4; if (msgLen < cookieOffset + cookieSize) { lg2::error("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; } lg2::error("Invalid Cookie"); elog(); } // vesion == 0x00 0x01 if (msgLen < versionOffset + sizeVersion) { lg2::error("Error in version getting of setHostNetworkData"); return ipmi::ccReqDataLenInvalid; } if (std::equal(msgPayloadStartingPos + versionOffset, msgPayloadStartingPos + versionOffset + sizeVersion, (netConfInitialBytes + versionOffset)) != 0) { lg2::error("Invalid Version"); elog(); } if (msgLen < macOffset + 6) { lg2::error( "Error in mac address getting of setHostNetworkData"); return ipmi::ccReqDataLenInvalid; } std::stringstream result; std::copy((msgPayloadStartingPos + macOffset), (msgPayloadStartingPos + macOffset + 5), std::ostream_iterator(result, ":")); mac = result.str(); if (msgLen < addrTypeOffset + sizeof(decltype(addrOrigin))) { lg2::error( "Error in original address getting of setHostNetworkData"); return ipmi::ccReqDataLenInvalid; } std::copy(msgPayloadStartingPos + addrTypeOffset, msgPayloadStartingPos + addrTypeOffset + sizeof(decltype(addrOrigin)), std::ostream_iterator(result, "")); addrOrigin = result.str(); if (!addrOrigin.empty()) { addressOrigin = "xyz.openbmc_project.Network.IP.AddressOrigin.Static"; } if (msgLen < addrSizeOffset + sizeof(decltype(addrSize))) { lg2::error( "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))) { lg2::error("Error in prefix getting of setHostNetworkData"); return ipmi::ccReqDataLenInvalid; } // std::copy(msgPayloadStartingPos + prefixOffset, // msgPayloadStartingPos + prefixOffset + // sizeof(decltype(prefix)), // &prefix); // Workaround compiler misdetecting out of bounds memcpy prefix = msgPayloadStartingPos[prefixOffset]; 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) { lg2::error("Error in IP address getting of setHostNetworkData"); return ipmi::ccReqDataLenInvalid; } ipAddress = getAddrStr(family, msgPayloadStartingPos, ipAddrOffset, addrSize); if (msgLen < gatewayOffset + addrSize) { lg2::error( "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_t 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)); lg2::debug("Network configuration changed: {NETWORKCONFIG}", "NETWORKCONFIG", hostNetworkConfig); } catch (const sdbusplus::exception_t& e) { commit(); lg2::error("Error in ipmiChassisSetSysBootOptions call"); return ipmi::ccUnspecifiedError; } return ipmi::ccSuccess; } uint32_t getPOHCounter() { sdbusplus::bus_t 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(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 ipmiGetChassisCap() { ipmi::PropertyMap properties; try { sdbusplus::bus_t 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 (const std::exception& e) { lg2::error("Failed to fetch Chassis Capability properties: {ERROR}", "ERROR", e); return ipmi::responseUnspecifiedError(); } bool* chassisIntrusionFlag = std::get_if(&properties[chassisIntrusionProp]); if (chassisIntrusionFlag == nullptr) { lg2::error("Error to get chassis Intrusion flags"); return ipmi::responseUnspecifiedError(); } bool* chassisFrontPanelFlag = std::get_if(&properties[chassisFrontPanelLockoutProp]); if (chassisFrontPanelFlag == nullptr) { lg2::error("Error to get chassis intrusion flags"); return ipmi::responseUnspecifiedError(); } bool* chassisNMIFlag = std::get_if(&properties[chassisNMIProp]); if (chassisNMIFlag == nullptr) { lg2::error("Error to get chassis NMI flags"); return ipmi::responseUnspecifiedError(); } bool* chassisPowerInterlockFlag = std::get_if(&properties[chassisPowerInterlockProp]); if (chassisPowerInterlockFlag == nullptr) { lg2::error("Error to get chassis power interlock flags"); return ipmi::responseUnspecifiedError(); } uint8_t* chassisFRUInfoDevAddr = std::get_if(&properties[chassisFRUDevAddrProp]); if (chassisFRUInfoDevAddr == nullptr) { lg2::error("Error to get chassis FRU info device address"); return ipmi::responseUnspecifiedError(); } uint8_t* chassisSDRDevAddr = std::get_if(&properties[chassisSDRDevAddrProp]); if (chassisSDRDevAddr == nullptr) { lg2::error("Error to get chassis SDR device address"); return ipmi::responseUnspecifiedError(); } uint8_t* chassisSELDevAddr = std::get_if(&properties[chassisSELDevAddrProp]); if (chassisSELDevAddr == nullptr) { lg2::error("Error to get chassis SEL device address"); return ipmi::responseUnspecifiedError(); } uint8_t* chassisSMDevAddr = std::get_if(&properties[chassisSMDevAddrProp]); if (chassisSMDevAddr == nullptr) { lg2::error("Error to get chassis SM device address"); return ipmi::responseUnspecifiedError(); } uint8_t* chassisBridgeDevAddr = std::get_if(&properties[chassisBridgeDevAddrProp]); if (chassisBridgeDevAddr == nullptr) { lg2::error("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) { lg2::error("Unsupported request parameter"); return ipmi::responseInvalidFieldRequest(); } if ((fruDeviceAddr & ~chassisCapAddrMask) != 0) { lg2::error("Unsupported request parameter(FRU Addr) for REQ={REQ}", "REQ", lg2::hex, fruDeviceAddr); return ipmi::responseInvalidFieldRequest(); } if ((sdrDeviceAddr & ~chassisCapAddrMask) != 0) { lg2::error("Unsupported request parameter(SDR Addr) for REQ={REQ}", "REQ", lg2::hex, sdrDeviceAddr); return ipmi::responseInvalidFieldRequest(); } if ((selDeviceAddr & ~chassisCapAddrMask) != 0) { lg2::error("Unsupported request parameter(SEL Addr) for REQ={REQ}", "REQ", lg2::hex, selDeviceAddr); return ipmi::responseInvalidFieldRequest(); } if ((smDeviceAddr & ~chassisCapAddrMask) != 0) { lg2::error("Unsupported request parameter(SM Addr) for REQ={REQ}", "REQ", lg2::hex, smDeviceAddr); return ipmi::responseInvalidFieldRequest(); } if ((bridgeDeviceAddr & ~chassisCapAddrMask) != 0) { lg2::error("Unsupported request parameter(Bridge Addr) for REQ={REQ}", "REQ", lg2::hex, bridgeDeviceAddr); return ipmi::responseInvalidFieldRequest(); } try { sdbusplus::bus_t 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 (const std::exception& e) { lg2::error("Failed to set chassis capability properties: {ERR}", "ERR", e); return ipmi::responseUnspecifiedError(); } return ipmi::responseSuccess(); } //------------------------------------------ // Calls into Host State Manager Dbus object //------------------------------------------ int initiateHostStateTransition(ipmi::Context::ptr& ctx, State::Host::Transition transition) { // OpenBMC Host State Manager dbus framework constexpr auto hostStatePath = "/xyz/openbmc_project/state/host0"; constexpr auto hostStateIntf = "xyz.openbmc_project.State.Host"; // Convert to string equivalent of the passed in transition enum. auto request = sdbusplus::common::xyz::openbmc_project::state::convertForMessage( transition); std::string service; boost::system::error_code ec = ipmi::getService(ctx, hostStateIntf, hostStatePath, service); if (!ec) { ec = ipmi::setDbusProperty(ctx, service, hostStatePath, hostStateIntf, "RequestedHostTransition", request); } if (ec) { lg2::error( "Failed to initiate transition for request {REQUEST}: {EXCEPTION}", "REQUEST", request, "EXCEPTION", ec.message()); return -1; } lg2::info( "Transition request {REQUEST} initiated successfully by user {USERID}", "REQUEST", request, "USERID", ctx->userId); return 0; } //------------------------------------------ // Calls into Chassis State Manager Dbus object //------------------------------------------ int initiateChassisStateTransition(ipmi::Context::ptr& ctx, State::Chassis::Transition transition) { // OpenBMC Chassis State Manager dbus framework constexpr auto chassisStatePath = "/xyz/openbmc_project/state/chassis0"; constexpr auto chassisStateIntf = "xyz.openbmc_project.State.Chassis"; std::string service; boost::system::error_code ec = ipmi::getService(ctx, chassisStateIntf, chassisStatePath, service); // Convert to string equivalent of the passed in transition enum. auto request = sdbusplus::common::xyz::openbmc_project::state::convertForMessage( transition); if (!ec) { ec = ipmi::setDbusProperty(ctx, service, chassisStatePath, chassisStateIntf, "RequestedPowerTransition", request); } if (ec) { lg2::error("Failed to initiate transition {REQUEST}: {EXCEPTION}", "REQUEST", request, "EXCEPTION", ec.message()); return -1; } return 0; } //------------------------------------------ // Trigger an NMI on the host via dbus //------------------------------------------ static int doNmi(ipmi::Context::ptr& ctx) { constexpr const char* nmiIntfName = "xyz.openbmc_project.Control.Host.NMI"; ipmi::DbusObjectInfo nmiObj{}; boost::system::error_code ec; ec = ipmi::getDbusObject(ctx, nmiIntfName, nmiObj); if (ec) { lg2::error("Failed to find NMI service: {ERROR}", "ERROR", ec.message()); return -1; } ctx->bus->yield_method_call(ctx->yield, ec, nmiObj.second, nmiObj.first, nmiIntfName, "NMI"); if (ec) { lg2::error("NMI call failed: {ERROR}", "ERROR", ec.message()); elog(); return -1; } return 0; } namespace power_policy { using namespace sdbusplus::server::xyz::openbmc_project::control::power; using IpmiValue = uint8_t; using DbusValue = RestorePolicy::Policy; const std::map dbusToIpmi = { {RestorePolicy::Policy::AlwaysOff, 0x00}, {RestorePolicy::Policy::Restore, 0x01}, {RestorePolicy::Policy::AlwaysOn, 0x02}, {RestorePolicy::Policy::None, 0x03}}; static constexpr uint8_t noChange = 0x03; static constexpr uint8_t allSupport = 0x01 | 0x02 | 0x04; /* helper function for Get Chassis Status Command */ std::optional 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(result)); restorePolicy = dbusToIpmi.at(powerRestore); } catch (const std::exception& e) { lg2::error( "Failed to fetch pgood property ({PATH}/{INTERFACE}): {ERROR}", "PATH", objects.map.at(powerRestoreIntf).front(), "INTERFACE", powerRestoreIntf, "ERROR", e); 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 getPowerStatus() { bool powerGood = false; std::shared_ptr 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(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(std::get(variant)); } catch (const std::exception& e) { lg2::error("Failed to fetch pgood property: {ERROR}", "ERROR", e); 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 bus = getSdBus(); try { auto service = ipmi::getService(*bus, powerControlIntf, powerControlObj); ipmi::Value variant = ipmi::getDbusProperty( *bus, service, powerControlObj, powerControlIntf, "PFail"); acFail = std::get(variant); } catch (const std::exception& e) { lg2::error( "Failed to fetch PFail property ({PATH}/{INTERFAC}): {ERROR}", "PATH", powerControlObj, "INTERFACE", powerControlIntf, "ERROR", e); } return acFail; } } // namespace power_policy static std::optional getButtonEnabled(const std::string& buttonPath, const std::string& buttonIntf) { std::shared_ptr 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(enabled); } catch (const sdbusplus::exception_t& e) { lg2::error("Fail to get button Enabled property ({PATH}): {ERROR}", "PATH", buttonPath, "ERROR", e); 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) { lg2::error( "Fail to set button Enabled property ({SERVICE}:{PATH}): {ERROR}", "SERVICE", service, "PATH", buttonPath, "ERROR", ec.message()); return false; } return true; } static std::optional getChassisIntrusionStatus(ipmi::Context::ptr& ctx) { constexpr const char* chassisIntrusionPath = "/xyz/openbmc_project/Chassis/Intrusion"; constexpr const char* chassisIntrusionInf = "xyz.openbmc_project.Chassis.Intrusion"; std::string service; boost::system::error_code ec = ipmi::getService( ctx, chassisIntrusionInf, chassisIntrusionPath, service); if (!ec) { std::string chassisIntrusionStr; ec = ipmi::getDbusProperty( ctx, service, chassisIntrusionPath, chassisIntrusionInf, "Status", chassisIntrusionStr); if (!ec) { bool ret = (chassisIntrusionStr == "HardwareIntrusion") ? true : false; return std::make_optional(ret); } } lg2::error("Fail to get Chassis Intrusion Status property " "({PATH}/{INTERFACE}): {ERROR}", "PATH", chassisIntrusionPath, "INTERFACE", chassisIntrusionInf, "ERROR", ec.message()); return std::nullopt; } //---------------------------------------------------------------------- // Get Chassis Status commands //---------------------------------------------------------------------- ipmi::RspType ipmiGetChassisStatus(ipmi::Context::ptr& ctx) { using namespace chassis::internal; std::optional restorePolicy = power_policy::getPowerRestorePolicy(); std::optional powerGood = power_policy::getPowerStatus(); if (!restorePolicy || !powerGood) { return ipmi::responseUnspecifiedError(); } // Front Panel Button Capabilities and disable/enable status(Optional) std::optional powerButtonReading = getButtonEnabled(powerButtonPath, powerButtonIntf); // allow disable if the interface is present bool powerButtonDisableAllow = static_cast(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 resetButtonReading = getButtonEnabled(resetButtonPath, resetButtonIntf); // allow disable if the interface is present bool resetButtonDisableAllow = static_cast(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(); bool chassisIntrusionActive = false; std::optional chassisIntrusionStatus = getChassisIntrusionStatus(ctx); if (chassisIntrusionStatus) { chassisIntrusionActive = chassisIntrusionStatus.value(); } // 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 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(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 getRestartCause(ipmi::Context::ptr ctx) { constexpr const char* restartCausePath = "/xyz/openbmc_project/state/host0"; constexpr const char* restartCauseIntf = "xyz.openbmc_project.State.Host"; std::string service; boost::system::error_code ec = ipmi::getService(ctx, restartCauseIntf, restartCausePath, service); if (!ec) { std::string restartCauseStr; ec = ipmi::getDbusProperty( ctx, service, restartCausePath, restartCauseIntf, "RestartCause", restartCauseStr); if (!ec) { auto cause = State::Host::convertRestartCauseFromString(restartCauseStr); return types::enum_cast( restartCauseToIpmiRestartCause(cause)); } } lg2::error( "Failed to fetch RestartCause property ({PATH}/{INTERFACE}): {ERROR}", "ERROR", ec.message(), "PATH", restartCausePath, "INTERFACE", restartCauseIntf); return std::nullopt; } ipmi::RspType ipmiGetSystemRestartCause(ipmi::Context::ptr ctx) { std::optional cause = getRestartCause(ctx); if (!cause) { return ipmi::responseUnspecifiedError(); } constexpr uint4_t reserved = 0; auto channel = static_cast(ctx->channel); return ipmi::responseSuccess(cause.value(), reserved, channel); } /** @brief Implementation of chassis control command * * @param - chassisControl command byte * * @return Success or InvalidFieldRequest. */ ipmi::RspType<> ipmiChassisControl(ipmi::Context::ptr& ctx, uint8_t chassisControl) { int rc = 0; switch (chassisControl) { case CMD_POWER_ON: rc = initiateHostStateTransition(ctx, State::Host::Transition::On); break; case CMD_POWER_OFF: rc = initiateChassisStateTransition( ctx, State::Chassis::Transition::Off); break; case CMD_HARD_RESET: rc = initiateHostStateTransition( ctx, State::Host::Transition::ForceWarmReboot); break; case CMD_POWER_CYCLE: rc = initiateHostStateTransition(ctx, State::Host::Transition::Reboot); break; case CMD_SOFT_OFF_VIA_OVER_TEMP: rc = initiateHostStateTransition(ctx, State::Host::Transition::Off); break; case CMD_PULSE_DIAGNOSTIC_INTR: rc = doNmi(ctx); break; default: { lg2::error("Invalid Chassis Control command: {CMD}", "CMD", lg2::hex, 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 try { return ipmi::getService(*getSdBus(), "xyz.openbmc_project.Led.Group", identify_led_object_name); } catch (const std::exception& e) { lg2::error("Chassis Identify: Error communicating to mapper: {ERROR}", "ERROR", e); elog(); } } /** @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; try { std::string connection = getEnclosureIdentifyConnection(); auto msg = std::string("enclosureIdentifyLed(") + boost::lexical_cast(flag) + ")"; lg2::debug(msg.c_str()); ipmi::setDbusProperty(*getSdBus(), connection, identify_led_object_name, "xyz.openbmc_project.Led.Group", "Asserted", flag); } catch (const std::exception& e) { lg2::error("Chassis Identify: Error Setting State {LED_STATE}: {ERROR}", "LED_STATE", flag, "ERROR", e); elog(); } } /** @brief Callback method to turn off LED */ void enclosureIdentifyLedOff() { try { chassisIDState = ChassisIDState::off; enclosureIdentifyLed(false); } catch (const InternalFailure& e) { report(); } } /** @brief Create timer to turn on and off the enclosure LED */ void createIdentifyTimer() { if (!identifyTimer) { identifyTimer = std::make_unique(enclosureIdentifyLedOff); } } ipmi::RspType<> ipmiChassisIdentify(std::optional interval, std::optional 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(); return ipmi::responseResponseError(); } if (forceIdentify) { chassisIDState = ChassisIDState::indefiniteOn; return ipmi::responseSuccess(); } // start the timer auto time = std::chrono::duration_cast( std::chrono::seconds(identifyInterval)); identifyTimer->start(time); } else if (!identifyInterval) { identifyTimer->stop(); enclosureIdentifyLedOff(); } return ipmi::responseSuccess(); } namespace boot_options { using namespace sdbusplus::server::xyz::openbmc_project::control::boot; using IpmiValue = uint8_t; constexpr auto ipmiDefault = 0; std::map typeIpmiToDbus = {{0x00, Type::Types::Legacy}, {0x01, Type::Types::EFI}}; std::map sourceIpmiToDbus = { {0x01, Source::Sources::Network}, {0x02, Source::Sources::Disk}, {0x05, Source::Sources::ExternalMedia}, {0x0f, Source::Sources::RemovableMedia}, {ipmiDefault, Source::Sources::Default}}; std::map 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 typeDbusToIpmi = {{Type::Types::Legacy, 0x00}, {Type::Types::EFI, 0x01}}; std::map sourceDbusToIpmi = { {Source::Sources::Network, 0x01}, {Source::Sources::Disk, 0x02}, {Source::Sources::ExternalMedia, 0x05}, {Source::Sources::RemovableMedia, 0x0f}, {Source::Sources::Default, ipmiDefault}}; std::map 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; } } lg2::error("Error in BootSource Get: {ERROR}", "ERROR", ec.message()); 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; } } lg2::error("Error in BootSource Set: {ERROR}", "ERROR", ec.message()); 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; } } lg2::error("Error in BootMode Get: {ERROR}", "ERROR", ec.message()); 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; } } lg2::error("Error in BootMode Set: {ERROR}", "ERROR", ec.message()); 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) { lg2::error("Error in BootType Get: {ERROR}", "ERROR", ec.message()); 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) { lg2::error("Error in BootType Set: {ERROR}", "ERROR", ec.message()); 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; } } lg2::error("Error in Boot Override Enable Get: {ERROR}", "ERROR", ec.message()); 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; } } lg2::error("Error in Boot Source Override Enable Set: {ERROR}", "ERROR", ec.message()); 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; } } lg2::error("Error in Boot Override OneTime Get: {ERROR}", "ERROR", ec.message()); 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; } } lg2::error("Error in Boot Source Override OneTime Set: {ERROR}", "ERROR", ec.message()); 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; static uint5_t bootInitiatorAckData = 0x0; static bool cmosClear = false; /** @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 ipmiChassisGetSysBootOptions(ipmi::Context::ptr ctx, uint7_t bootOptionParameter, bool reserved1, [[maybe_unused]] uint8_t setSelector, [[maybe_unused]] 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::setInProgress) { response.pack(bootOptionParameter, reserved1, transferStatus); return ipmi::responseSuccess(std::move(response)); } if (types::enum_cast(bootOptionParameter) == BootOptionParameter::bootInfo) { constexpr uint8_t writeMask = 0; response.pack(bootOptionParameter, reserved1, writeMask, bootInitiatorAckData); return ipmi::responseSuccess(std::move(response)); } if (types::enum_cast(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::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}, uint1_t{}, cmosClear, uint8_t{}, uint8_t{}, uint8_t{}); return ipmi::responseSuccess(std::move(response)); } catch (const InternalFailure& e) { cache::objectsPtr.reset(); report(); return ipmi::responseUnspecifiedError(); } } else { if ((bootOptionParameter >= oemParmStart) && (bootOptionParameter <= oemParmEnd)) { if (types::enum_cast(bootOptionParameter) == BootOptionParameter::opalNetworkSettings) { response.pack(bootOptionParameter, reserved1); int ret = getHostNetworkData(response); if (ret < 0) { response.trailingOk = true; lg2::error( "getHostNetworkData failed for GetSysBootOptions."); return ipmi::responseUnspecifiedError(); } else { return ipmi::responseSuccess(std::move(response)); } } else { lg2::error( "ipmiChassisGetSysBootOptions: Unsupported parameter {PARAM}", "PARAM", lg2::hex, static_cast(bootOptionParameter)); return ipmi::responseParmNotSupported(); } } else { lg2::error( "ipmiChassisGetSysBootOptions: Unsupported parameter {PARAM}", "PARAM", lg2::hex, static_cast(bootOptionParameter)); return ipmi::responseParmNotSupported(); } } return ipmi::responseUnspecifiedError(); } ipmi::RspType<> ipmiChassisSetSysBootOptions(ipmi::Context::ptr ctx, uint7_t parameterSelector, bool, ipmi::message::Payload& data) { using namespace boot_options; ipmi::Cc rc; if (types::enum_cast(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(setInProgressFlag) != setComplete)) { return ipmi::response(IPMI_CC_FAIL_SET_IN_PROGRESS); } transferStatus = static_cast(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(parameterSelector) == BootOptionParameter::bootFlags) { uint5_t rsvd; bool validFlag; bool permanent; bool biosBootType; bool lockOutResetButton; bool screenBlank; uint4_t bootDeviceSelector; bool lockKeyboard; 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(bootDeviceSelector)); auto typeItr = typeIpmiToDbus.find(static_cast(biosBootType)); auto sourceItr = sourceIpmiToDbus.find(static_cast(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 lg2::error( "ipmiChassisSetSysBootOptions: Boot option not supported"); return ipmi::responseInvalidFieldRequest(); } } catch (const sdbusplus::exception_t& e) { objectsPtr.reset(); report(); lg2::error("ipmiChassisSetSysBootOptions: Error in setting Boot " "flag parameters"); return ipmi::responseUnspecifiedError(); } } else if (types::enum_cast(parameterSelector) == BootOptionParameter::bootInfo) { uint8_t writeMak; uint5_t bootInfoAck; uint3_t rsvd; if (data.unpack(writeMak, bootInfoAck, rsvd) != 0 || !data.fullyUnpacked()) { return ipmi::responseReqDataLenInvalid(); } if (rsvd) { return ipmi::responseInvalidFieldRequest(); } bootInitiatorAckData &= ~writeMak; bootInitiatorAckData |= (writeMak & bootInfoAck); lg2::info("ipmiChassisSetSysBootOptions: bootInfo parameter set " "successfully"); data.trailingOk = true; return ipmi::responseSuccess(); } else if (types::enum_cast(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(bootFlagValidClr); lg2::info( "ipmiChassisSetSysBootOptions: bootFlagValidBits parameter set " "successfully to {VALUE}", "VALUE", lg2::hex, bootFlagValidBitClr); return ipmi::responseSuccess(); } else { if ((parameterSelector >= static_cast(oemParmStart)) && (parameterSelector <= static_cast(oemParmEnd))) { if (types::enum_cast(parameterSelector) == BootOptionParameter::opalNetworkSettings) { ipmi::Cc ret = setHostNetworkData(data); if (ret != ipmi::ccSuccess) { lg2::error("ipmiChassisSetSysBootOptions: Error in " "setHostNetworkData"); data.trailingOk = true; return ipmi::response(ret); } data.trailingOk = true; return ipmi::responseSuccess(); } else { lg2::error( "ipmiChassisSetSysBootOptions: Unsupported param: {PARAM}", "PARAM", lg2::hex, static_cast(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 ipmiGetPOHCounter() { // sd_bus error try { return ipmi::responseSuccess(static_cast(poh::minutesPerCount), getPOHCounter()); } catch (const std::exception& e) { lg2::error("getPOHCounter error: {ERROR}", "ERROR", e); return ipmi::responseUnspecifiedError(); } } ipmi::RspType 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)) { lg2::error("Reserved request parameter: {REQ}", "REQ", lg2::hex, static_cast(policy)); return ipmi::responseInvalidFieldRequest(); } if (policy == power_policy::noChange) { // just return the supported policy return ipmi::responseSuccess(power_policy::allSupport, reserved); } for (const auto& 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 property = convertForMessage(value); auto sdbusp = getSdBus(); boost::system::error_code ec; sdbusp->yield_method_call( yield, ec, objects .service(powerRestoreSetting, chassis::internal::powerRestoreIntf) .c_str(), powerRestoreSetting, ipmi::PROP_INTF, "Set", chassis::internal::powerRestoreIntf, "PowerRestorePolicy", property); if (ec) { lg2::error("Unspecified Error"); return ipmi::responseUnspecifiedError(); } } catch (const InternalFailure& e) { chassis::internal::cache::objectsPtr.reset(); report(); return ipmi::responseUnspecifiedError(); } return ipmi::responseSuccess(power_policy::allSupport, reserved); } ipmi::RspType<> ipmiSetFrontPanelButtonEnables(ipmi::Context::ptr ctx, bool disablePowerButton, bool disableResetButton, bool, bool, uint4_t) { 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); // ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, ipmi::chassis::cmdGetSystemBootOptions, ipmi::Privilege::Operator, ipmiChassisGetSysBootOptions); // ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, ipmi::chassis::cmdGetChassisStatus, ipmi::Privilege::User, ipmiGetChassisStatus); // ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, ipmi::chassis::cmdGetSystemRestartCause, ipmi::Privilege::User, ipmiGetSystemRestartCause); // ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, ipmi::chassis::cmdChassisControl, ipmi::Privilege::Operator, ipmiChassisControl); // ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, ipmi::chassis::cmdChassisIdentify, ipmi::Privilege::Operator, ipmiChassisIdentify); // ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, ipmi::chassis::cmdSetSystemBootOptions, ipmi::Privilege::Operator, ipmiChassisSetSysBootOptions); // ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, ipmi::chassis::cmdGetPohCounter, ipmi::Privilege::User, ipmiGetPOHCounter); // ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnChassis, ipmi::chassis::cmdSetPowerRestorePolicy, ipmi::Privilege::Operator, ipmiChassisSetPowerRestorePolicy); }