| /* |
| // Copyright (c) 2018 Intel Corporation |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| */ |
| |
| #include "oemcommands.hpp" |
| |
| #include <boost/algorithm/string.hpp> |
| #include <ipmid/api.hpp> |
| #include <ipmid/utils.hpp> |
| #include <phosphor-logging/log.hpp> |
| |
| #include <variant> |
| |
| namespace ipmi |
| { |
| void register_netfn_bmc_control_functions() __attribute__((constructor)); |
| |
| static constexpr uint8_t rmcpServiceBitPos = 3; |
| static constexpr uint8_t webServiceBitPos = 5; |
| static constexpr uint8_t solServiceBitPos = 6; |
| static constexpr uint8_t kvmServiceBitPos = 15; |
| |
| static constexpr std::array<std::pair<uint8_t, const char*>, 4> bmcServices = {{ |
| // {bit position for service, service name} |
| {rmcpServiceBitPos, "phosphor-ipmi-net"}, |
| {webServiceBitPos, "bmcweb"}, |
| {solServiceBitPos, "obmc-console-ssh"}, |
| {kvmServiceBitPos, "start-ipkvm"}, |
| }}; |
| |
| static constexpr uint16_t maskBit15 = 0x8000; |
| |
| static constexpr const char* objectManagerIntf = |
| "org.freedesktop.DBus.ObjectManager"; |
| static constexpr const char* dBusPropIntf = "org.freedesktop.DBus.Properties"; |
| static constexpr const char* serviceConfigBasePath = |
| "/xyz/openbmc_project/control/service"; |
| static constexpr const char* serviceConfigAttrIntf = |
| "xyz.openbmc_project.Control.Service.Attributes"; |
| static constexpr const char* getMgdObjMethod = "GetManagedObjects"; |
| static constexpr const char* propMasked = "Masked"; |
| |
| std::string getServiceConfigMgrName() |
| { |
| static std::string serviceCfgMgr{}; |
| if (serviceCfgMgr.empty()) |
| { |
| try |
| { |
| auto sdbusp = getSdBus(); |
| serviceCfgMgr = ipmi::getService(*sdbusp, objectManagerIntf, |
| serviceConfigBasePath); |
| } |
| catch (const sdbusplus::exception::exception& e) |
| { |
| serviceCfgMgr.clear(); |
| phosphor::logging::log<phosphor::logging::level::ERR>( |
| "Error: In fetching disabling service manager name"); |
| return serviceCfgMgr; |
| } |
| } |
| return serviceCfgMgr; |
| } |
| |
| static inline void checkAndThrowError(boost::system::error_code& ec, |
| const std::string& msg) |
| { |
| if (ec) |
| { |
| std::string msgToLog = ec.message() + (msg.empty() ? "" : " - " + msg); |
| phosphor::logging::log<phosphor::logging::level::ERR>(msgToLog.c_str()); |
| throw sdbusplus::exception::SdBusError(-EIO, msgToLog.c_str()); |
| } |
| return; |
| } |
| |
| static inline bool getEnabledValue(const DbusInterfaceMap& intfMap) |
| { |
| for (const auto& intf : intfMap) |
| { |
| if (intf.first == serviceConfigAttrIntf) |
| { |
| auto it = intf.second.find(propMasked); |
| if (it == intf.second.end()) |
| { |
| phosphor::logging::log<phosphor::logging::level::ERR>( |
| "Error: in getting Masked property value"); |
| throw sdbusplus::exception::SdBusError( |
| -EIO, "ERROR in reading Masked property value"); |
| } |
| // return !Masked value |
| return !std::get<bool>(it->second); |
| } |
| } |
| return false; |
| } |
| |
| ipmi::RspType<> setBmcControlServices(boost::asio::yield_context yield, |
| uint8_t state, uint16_t serviceValue) |
| { |
| constexpr uint16_t servicesRsvdMask = 0x3F97; |
| constexpr uint8_t enableService = 0x1; |
| |
| if ((state > enableService) || (serviceValue & servicesRsvdMask) || |
| !serviceValue) |
| { |
| return ipmi::responseInvalidFieldRequest(); |
| } |
| try |
| { |
| auto sdbusp = getSdBus(); |
| boost::system::error_code ec; |
| auto objectMap = sdbusp->yield_method_call<ObjectValueTree>( |
| yield, ec, getServiceConfigMgrName().c_str(), serviceConfigBasePath, |
| objectManagerIntf, getMgdObjMethod); |
| checkAndThrowError(ec, "GetMangagedObjects for service cfg failed"); |
| |
| for (const auto& services : bmcServices) |
| { |
| // services.first holds the bit position of the service, check |
| // whether it has to be updated. |
| const uint16_t serviceMask = 1 << services.first; |
| if (!(serviceValue & serviceMask)) |
| { |
| continue; |
| } |
| for (const auto& obj : objectMap) |
| { |
| if (boost::algorithm::starts_with(obj.first.filename(), |
| services.second)) |
| { |
| if (state != getEnabledValue(obj.second)) |
| { |
| ec.clear(); |
| sdbusp->yield_method_call<>( |
| yield, ec, getServiceConfigMgrName().c_str(), |
| obj.first.str, dBusPropIntf, "Set", |
| serviceConfigAttrIntf, propMasked, |
| std::variant<bool>(!state)); |
| checkAndThrowError(ec, "Set Masked property failed"); |
| // Multiple instances may be present, so continue |
| } |
| } |
| } |
| } |
| } |
| catch (const sdbusplus::exception::exception& e) |
| { |
| return ipmi::responseUnspecifiedError(); |
| } |
| return ipmi::responseSuccess(); |
| } |
| |
| ipmi::RspType<uint16_t> getBmcControlServices(boost::asio::yield_context yield) |
| { |
| uint16_t serviceValue = 0; |
| try |
| { |
| auto sdbusp = getSdBus(); |
| boost::system::error_code ec; |
| auto objectMap = sdbusp->yield_method_call<ObjectValueTree>( |
| yield, ec, getServiceConfigMgrName().c_str(), serviceConfigBasePath, |
| objectManagerIntf, getMgdObjMethod); |
| checkAndThrowError(ec, "GetMangagedObjects for service cfg failed"); |
| |
| for (const auto& services : bmcServices) |
| { |
| for (const auto& obj : objectMap) |
| { |
| if (boost::algorithm::starts_with(obj.first.filename(), |
| services.second)) |
| { |
| serviceValue |= getEnabledValue(obj.second) |
| << services.first; |
| break; |
| } |
| } |
| } |
| } |
| catch (const sdbusplus::exception::exception& e) |
| { |
| return ipmi::responseUnspecifiedError(); |
| } |
| // Bit 14 should match bit 15 as single service maintains Video & USB |
| // redirection |
| serviceValue |= (serviceValue & maskBit15) >> 1; |
| return ipmi::responseSuccess(serviceValue); |
| } |
| |
| void register_netfn_bmc_control_functions() |
| { |
| registerHandler(prioOpenBmcBase, intel::netFnGeneral, |
| intel::general::cmdControlBmcServices, Privilege::Admin, |
| setBmcControlServices); |
| |
| registerHandler(prioOpenBmcBase, intel::netFnGeneral, |
| intel::general::cmdGetBmcServiceStatus, Privilege::User, |
| getBmcControlServices); |
| } |
| } // namespace ipmi |