Add MTM BMC Feature Control IPMI command
Certain features having security concerns needs to be enabled/disabled
only in manufacturing mode. Add a command to support the same for MCTP
feature.
In addition to starting or stopping a service, add an option to enable
or disable a service which will persist across AC cycles.
Request bytes:
Byte 1 – Supported features
00h - MCTP
01h to FFh - reserved (will be adding support for more features
as needed)
Byte 2 – Enable/Disable feature
00h – Stops the given feature
01h – Starts the given feature
02h - Disables the given feature
03h - Enables the given feature
04h to FFh – reserved
Byte 3 - Custom feature arguments
When byte 2 value is 00h:
00h - MCTP over PCIe
01h - MCTP over SMBus HSBP
02h - MCTP over SMBus PCIe slot
03h to FFh - reserved
Byte 4:5 – reserved
Tested:
Verified MCTP feature can be enabled/disabled as expected in MTM mode
the change will persist across AC cycles.
Signed-off-by: Arun P. Mohanan <arun.p.m@linux.intel.com>
Signed-off-by: Sumanth Bhat <sumanth.bhat@linux.intel.com>
Change-Id: I8f8be36ee3e0fd85cfb1e44599cd0db0a6a8d34a
diff --git a/src/manufacturingcommands.cpp b/src/manufacturingcommands.cpp
index 7d5a058..d502d6a 100644
--- a/src/manufacturingcommands.cpp
+++ b/src/manufacturingcommands.cpp
@@ -16,6 +16,7 @@
#include <linux/input.h>
+#include <boost/algorithm/string.hpp>
#include <boost/container/flat_map.hpp>
#include <ipmid/api.hpp>
#include <manufacturingcommands.hpp>
@@ -795,6 +796,8 @@
ipmi::intel::general::cmdGetManufacturingData):
case makeCmdKey(ipmi::intel::netFnGeneral,
ipmi::intel::general::cmdSetFITcLayout):
+ case makeCmdKey(ipmi::netFnOemOne,
+ ipmi::intel::general::cmdMTMBMCFeatureControl):
case makeCmdKey(ipmi::netFnStorage, ipmi::storage::cmdWriteFruData):
case makeCmdKey(ipmi::netFnOemTwo, ipmi::intel::platform::cmdClearCMOS):
@@ -1009,6 +1012,164 @@
return ipmi::responseSuccess();
}
+static std::vector<std::string>
+ getMCTPServiceConfigPaths(ipmi::Context::ptr& ctx)
+{
+ boost::system::error_code ec;
+ auto configPaths = ctx->bus->yield_method_call<std::vector<std::string>>(
+ ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
+ "/xyz/openbmc_project/inventory/system/board", 2,
+ std::array<const char*, 1>{
+ "xyz.openbmc_project.Configuration.MctpConfiguration"});
+ if (ec)
+ {
+ throw std::runtime_error(
+ "Failed to query configuration sub tree objects");
+ }
+ return configPaths;
+}
+
+static ipmi::RspType<> startOrStopService(ipmi::Context::ptr& ctx,
+ const uint8_t enable,
+ const std::string& serviceName)
+{
+ constexpr bool runtimeOnly = false;
+ constexpr bool force = false;
+
+ boost::system::error_code ec;
+ switch (enable)
+ {
+ case ipmi::SupportedFeatureActions::stop:
+ ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
+ systemDObjPath, systemDMgrIntf,
+ "StopUnit", serviceName, "replace");
+ break;
+ case ipmi::SupportedFeatureActions::start:
+ ctx->bus->yield_method_call(ctx->yield, ec, systemDService,
+ systemDObjPath, systemDMgrIntf,
+ "StartUnit", serviceName, "replace");
+ break;
+ case ipmi::SupportedFeatureActions::disable:
+ ctx->bus->yield_method_call(
+ ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
+ "MaskUnitFiles",
+ std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly,
+ force);
+ ctx->bus->yield_method_call(
+ ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
+ "DisableUnitFiles",
+ std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly);
+ break;
+ case ipmi::SupportedFeatureActions::enable:
+ ctx->bus->yield_method_call(
+ ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
+ "UnmaskUnitFiles",
+ std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly);
+ ctx->bus->yield_method_call(
+ ctx->yield, ec, systemDService, systemDObjPath, systemDMgrIntf,
+ "EnableUnitFiles",
+ std::array<const char*, 1>{serviceName.c_str()}, runtimeOnly,
+ force);
+ break;
+ default:
+ phosphor::logging::log<phosphor::logging::level::WARNING>(
+ "ERROR: Invalid feature action selected",
+ phosphor::logging::entry("ACTION=%d", enable));
+ return ipmi::responseInvalidFieldRequest();
+ }
+ if (ec)
+ {
+ phosphor::logging::log<phosphor::logging::level::WARNING>(
+ "ERROR: Service start or stop failed",
+ phosphor::logging::entry("SERVICE=%s", serviceName.c_str()));
+ return ipmi::responseUnspecifiedError();
+ }
+ return ipmi::responseSuccess();
+}
+
+static std::string getMCTPServiceName(const std::string& objectPath)
+{
+ const auto serviceArgument = boost::algorithm::replace_all_copy(
+ boost::algorithm::replace_first_copy(
+ objectPath, "/xyz/openbmc_project/inventory/system/board/", ""),
+ "/", "_2f");
+ std::string unitName =
+ "xyz.openbmc_project.mctpd@" + serviceArgument + ".service";
+ return unitName;
+}
+
+static ipmi::RspType<> handleMCTPFeature(ipmi::Context::ptr& ctx,
+ const uint8_t enable,
+ const std::string& binding)
+{
+ std::vector<std::string> configPaths;
+ try
+ {
+ configPaths = getMCTPServiceConfigPaths(ctx);
+ }
+ catch (const std::exception& e)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
+ return ipmi::responseUnspecifiedError();
+ }
+
+ for (const auto& objectPath : configPaths)
+ {
+ auto const pos = objectPath.find_last_of('/');
+ if (binding == objectPath.substr(pos + 1))
+ {
+ return startOrStopService(ctx, enable,
+ getMCTPServiceName(objectPath));
+ }
+ }
+ return ipmi::responseSuccess();
+}
+
+/** @brief implements MTM BMC Feature Control IPMI command which can be
+ * used to enable or disable the supported BMC features.
+ * @param yield - context object that represents the currently executing
+ * coroutine
+ * @param feature - feature enum to enable or disable
+ * @param enable - enable or disable the feature
+ * @param featureArg - custom arguments for that feature
+ * @param reserved - reserved for future use
+ *
+ * @returns IPMI completion code
+ */
+ipmi::RspType<> mtmBMCFeatureControl(ipmi::Context::ptr ctx,
+ const uint8_t feature,
+ const uint8_t enable,
+ const uint8_t featureArg,
+ const uint16_t reserved)
+{
+ if (reserved != 0)
+ {
+ return ipmi::responseInvalidFieldRequest();
+ }
+
+ switch (feature)
+ {
+ case ipmi::SupportedFeatureControls::mctp:
+ switch (featureArg)
+ {
+ case ipmi::SupportedMCTPBindings::mctpPCIe:
+ return handleMCTPFeature(ctx, enable, "MCTP_PCIe");
+ case ipmi::SupportedMCTPBindings::mctpSMBusHSBP:
+ return handleMCTPFeature(ctx, enable, "MCTP_SMBus_HSBP");
+ case ipmi::SupportedMCTPBindings::mctpSMBusPCIeSlot:
+ return handleMCTPFeature(ctx, enable,
+ "MCTP_SMBus_PCIe_slot");
+ default:
+ return ipmi::responseInvalidFieldRequest();
+ }
+ break;
+ default:
+ return ipmi::responseInvalidFieldRequest();
+ }
+ return ipmi::responseSuccess();
+}
} // namespace ipmi
void register_mtm_commands() __attribute__((constructor));
@@ -1039,6 +1200,10 @@
ipmi::intel::general::cmdSetFITcLayout,
ipmi::Privilege::Admin, ipmi::setFITcLayout);
+ ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnGeneral,
+ ipmi::intel::general::cmdMTMBMCFeatureControl,
+ ipmi::Privilege::Admin, ipmi::mtmBMCFeatureControl);
+
ipmi::registerHandler(ipmi::prioOemBase, ipmi::intel::netFnApp,
ipmi::intel::general::cmdSlotI2CMasterWriteRead,
ipmi::Privilege::Admin,