blob: 17c850a262f6063887c72ea8c0cc5f7105f70ef3 [file] [log] [blame]
/*
// 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 const std::unordered_map<uint8_t, std::string> bmcServices = {
// {bit position for service, service object path}
{rmcpServiceBitPos,
"/xyz/openbmc_project/control/service/phosphor_2dipmi_2dnet"},
{webServiceBitPos, "/xyz/openbmc_project/control/service/bmcweb"},
{solServiceBitPos,
"/xyz/openbmc_project/control/service/obmc_2dconsole_2dssh"},
{kvmServiceBitPos, "/xyz/openbmc_project/control/service/start_2dipkvm"},
};
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.str,
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.str,
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