SBMR Get/Send Boot Progress Code support
Implement IPMI commands for boot progress codes by following
DEN0069E_SBMR_2.1. Add configure option `arm-sbmr` to enable
SBMR IPMI commands. Boot progress code will update to Redfish
BootProgress property.
1. Send boot progress code (NetFn 0x2C, Command 0x2)
2. Get boot progress code (NetFn 0x2C, Command 0x3)
Test:
1. Send boot progress code
$> ipmitool raw 0x2C 0x02 0xAE 0x1 0x00 0x00 0x00 \
0x01 0x10 0x01 0x02 0x00
ae
2. Get boot progress code
$> ipmitool raw 0x2C 0x3 0xAE
ae 01 00 00 00 01 10 01 02 00
3. Redfish BootProgess LastState - /redfish/v1/Systems/system
{
...
"BootProgress": {
"LastState": "PCIResourceConfigStarted",
"LastStateTime": "2024-11-14T19:14:22.432272+00:00"
}
}
Signed-off-by: John Chung <john.chung@arm.com>
Change-Id: I58e6e322006039fceb8d4212c4f9f6a4b4f9e225
diff --git a/include/ipmid/api-types.hpp b/include/ipmid/api-types.hpp
index 92432e0..52c68ee 100644
--- a/include/ipmid/api-types.hpp
+++ b/include/ipmid/api-types.hpp
@@ -31,6 +31,9 @@
constexpr Group groupDMTG = 0x01;
constexpr Group groupSSI = 0x02;
constexpr Group groupVSO = 0x03;
+#ifdef ARM_SBMR_SUPPORT
+constexpr Group groupSBMR = 0xAE;
+#endif
constexpr Group groupDCMI = 0xDC;
/*
@@ -327,6 +330,14 @@
constexpr Cmd cmdGetDcmiConfigParameters = 0x13;
} // namespace dcmi
+#ifdef ARM_SBMR_SUPPORT
+namespace sbmr
+{
+constexpr Cmd cmdSendBootProgressCode = 0x02;
+constexpr Cmd cmdGetBootProgressCode = 0x03;
+} // namespace sbmr
+#endif
+
// These are the command network functions, the response
// network functions are the function + 1. So to determine
// the proper network function which issued the command
diff --git a/include/ipmid/message.hpp b/include/ipmid/message.hpp
index 7a709e4..6698581 100644
--- a/include/ipmid/message.hpp
+++ b/include/ipmid/message.hpp
@@ -48,7 +48,7 @@
Privilege priv, int rqSA, int hostIdx,
boost::asio::yield_context& yield) :
bus(bus), netFn(netFn), lun(lun), cmd(cmd), channel(channel),
- userId(userId), sessionId(sessionId), priv(priv), rqSA(rqSA),
+ userId(userId), sessionId(sessionId), priv(priv), group(0), rqSA(rqSA),
hostIdx(hostIdx), yield(yield)
{}
@@ -61,6 +61,8 @@
int userId;
uint32_t sessionId;
Privilege priv;
+ // defining body code for netFnGroup
+ Group group;
// srcAddr is only set on IPMB requests because
// Platform Event Message needs it to determine the incoming format
int rqSA;
diff --git a/include/ipmid/types.hpp b/include/ipmid/types.hpp
index f7306e2..91ff346 100644
--- a/include/ipmid/types.hpp
+++ b/include/ipmid/types.hpp
@@ -19,12 +19,13 @@
using DbusProperty = std::string;
using Association = std::tuple<std::string, std::string, std::string>;
+using BootProgressCode = std::tuple<std::vector<uint8_t>, std::vector<uint8_t>>;
-using Value =
- std::variant<bool, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t,
- uint64_t, double, std::string, std::vector<uint8_t>,
- std::vector<uint16_t>, std::vector<uint32_t>,
- std::vector<std::string>, std::vector<Association>>;
+using Value = std::variant<bool, uint8_t, int16_t, uint16_t, int32_t, uint32_t,
+ int64_t, uint64_t, double, std::string,
+ std::vector<uint8_t>, std::vector<uint16_t>,
+ std::vector<uint32_t>, std::vector<std::string>,
+ std::vector<Association>, BootProgressCode>;
using PropertyMap = std::map<DbusProperty, Value>;
diff --git a/ipmid-new.cpp b/ipmid-new.cpp
index f130780..7d3dd2d 100644
--- a/ipmid-new.cpp
+++ b/ipmid-new.cpp
@@ -300,6 +300,8 @@
return errorResponse(request, ccReqDataLenInvalid);
}
auto group = static_cast<Group>(bytes);
+ // Set defining body code
+ request->ctx->group = group;
message::Response::ptr response =
executeIpmiCommandCommon(groupHandlerMap, group, request);
ipmi::message::Payload prefix;
diff --git a/meson.build b/meson.build
index 7e8795a..a492697 100644
--- a/meson.build
+++ b/meson.build
@@ -103,6 +103,7 @@
'hybrid-sensors' : '-DFEATURE_HYBRID_SENSORS',
'sensors-cache' : '-DFEATURE_SENSORS_CACHE',
'dynamic-storages-only' : '-DFEATURE_DYNAMIC_STORAGES_ONLY',
+ 'arm-sbmr' : '-DARM_SBMR_SUPPORT',
}
foreach option_key, option_value : feature_map
@@ -272,6 +273,11 @@
openpower_cmds_src = ['storageaddsel.cpp']
endif
+arm_sbmr_cmds_src = []
+if get_option('arm-sbmr').allowed()
+ arm_sbmr_cmds_src = ['sbmrhandler.cpp']
+endif
+
libipmi20_src = [
'app/channel.cpp',
'app/watchdog.cpp',
@@ -295,6 +301,7 @@
transportoem_src,
storage_cmds_src,
openpower_cmds_src,
+ arm_sbmr_cmds_src,
conf_h,
]
diff --git a/meson.options b/meson.options
index e238c39..fcc8851 100644
--- a/meson.options
+++ b/meson.options
@@ -251,3 +251,10 @@
choices: ['null', 'serial'],
description: 'transport',
)
+
+# arm-sbmr specific functionality.
+option(
+ 'arm-sbmr',
+ type: 'feature',
+ description: 'Support Arm SBMR specific functions',
+)
diff --git a/sbmrhandler.cpp b/sbmrhandler.cpp
new file mode 100644
index 0000000..328d503
--- /dev/null
+++ b/sbmrhandler.cpp
@@ -0,0 +1,317 @@
+#include <ipmid/api.hpp>
+#include <ipmid/filter.hpp>
+#include <ipmid/utils.hpp>
+#include <phosphor-logging/lg2.hpp>
+
+constexpr auto sbmrBootStateIntf = "xyz.openbmc_project.State.Boot.Raw";
+constexpr auto sbmrHostStateIntf = "xyz.openbmc_project.State.Boot.Progress";
+constexpr auto sbmrBootProgressCodeSize = 9;
+
+constexpr auto bootProgressOem = "OEM";
+constexpr auto bootProgressOsRuning = "OSRunning";
+constexpr auto bootProgressOsStart = "OSStart";
+constexpr auto bootProgressPciInit = "PCIInit";
+constexpr auto bootProgressSystemInitComplete = "SystemInitComplete";
+constexpr auto bootProgressSystemSetup = "SystemSetup";
+
+// EFI_STATUS_CODE_TYPE
+constexpr auto efiProgressCode = 0x01;
+constexpr auto efiCodeSeverityNone = 0;
+
+// EFI_STATUS_CODE_CLASS
+constexpr auto efiIoBus = 0x02;
+constexpr auto efiSoftware = 0x03;
+
+// EFI_STATUS_CODE_SUBCLASS
+constexpr auto efiIoBusPci = 0x01;
+constexpr auto efiSoftwareDxeCore = 0x04;
+constexpr auto efiSoftwareDxeBsDriver = 0x05;
+constexpr auto efiSoftwareEfiBootService = 0x10;
+
+// EFI_STATUS_CODE_OPERATION
+constexpr auto efiIoBusPciResAlloc = 0x0110;
+constexpr auto efiSwDxeCorePcHandoffToNext = 0x0110;
+constexpr auto efiSwPcUserSetup = 0x0700;
+constexpr auto efiSwOsLoaderStart = 0x0180;
+constexpr auto efiSwBsPcExitBootServices = 0x1910;
+
+void registerNetfnSBMRFunctions() __attribute__((constructor));
+
+namespace ipmi
+{
+
+std::string getSbmrBootProgressStage(uint8_t codeType, uint8_t codeSeverity,
+ uint8_t codeClass, uint8_t codeSubClass,
+ uint16_t codeOperation)
+{
+ // Return OEM if code type or severity are unexpected
+ if (codeType != efiProgressCode || codeSeverity != efiCodeSeverityNone)
+ {
+ return bootProgressOem;
+ }
+
+ // Code Class Software
+ if (codeClass == efiSoftware)
+ {
+ if (codeSubClass == efiSoftwareDxeCore &&
+ codeOperation == efiSwDxeCorePcHandoffToNext)
+ {
+ return bootProgressSystemInitComplete;
+ }
+ else if (codeSubClass == efiSoftwareDxeBsDriver &&
+ codeOperation == efiSwPcUserSetup)
+ {
+ return bootProgressSystemSetup;
+ }
+ else if (codeSubClass == efiSoftwareDxeBsDriver &&
+ codeOperation == efiSwOsLoaderStart)
+ {
+ return bootProgressOsStart;
+ }
+ else if (codeSubClass == efiSoftwareEfiBootService &&
+ codeOperation == efiSwBsPcExitBootServices)
+ {
+ return bootProgressOsRuning;
+ }
+ }
+ // Code Class IO Bus
+ else if (codeClass == efiIoBus)
+ {
+ if (codeSubClass == efiIoBusPci && codeOperation == efiIoBusPciResAlloc)
+ {
+ return bootProgressPciInit;
+ }
+ }
+
+ // Fallback to OEM if no conditions met
+ return bootProgressOem;
+}
+
+bool updateBootProgressProperty(ipmi::Context::ptr& ctx,
+ const std::string& value)
+{
+ std::string bootProgress =
+ "xyz.openbmc_project.State.Boot.Progress.ProgressStages." + value;
+ ipmi::DbusObjectInfo sbmrHostStateObject{};
+
+ /* Get Host State Object */
+ boost::system::error_code ec =
+ ipmi::getDbusObject(ctx, sbmrHostStateIntf, sbmrHostStateObject);
+ if (ec.value())
+ {
+ lg2::error("Failed to get Host State object, Error={ERROR}", "ERROR",
+ ec.message());
+ return false;
+ }
+
+ /* Set Host State property */
+ ec = ipmi::setDbusProperty(ctx, sbmrHostStateObject.second,
+ sbmrHostStateObject.first, sbmrHostStateIntf,
+ "BootProgress", bootProgress);
+ if (ec.value())
+ {
+ lg2::error(
+ "updateBootProgressProperty, can't set progerty - Error={ERROR}",
+ "ERROR", ec.message());
+ return false;
+ }
+
+ return true;
+}
+
+bool updateBootProgressLastUpdateProperty(ipmi::Context::ptr& ctx,
+ uint64_t timeStamp)
+{
+ ipmi::DbusObjectInfo sbmrHostStateObject{};
+
+ /* Get Host State Object */
+ boost::system::error_code ec =
+ ipmi::getDbusObject(ctx, sbmrHostStateIntf, sbmrHostStateObject);
+ if (ec.value())
+ {
+ lg2::error("Failed to get Host State object, Error={ERROR}", "ERROR",
+ ec.message());
+ return false;
+ }
+
+ /* Set Host State property */
+ ec = ipmi::setDbusProperty(ctx, sbmrHostStateObject.second,
+ sbmrHostStateObject.first, sbmrHostStateIntf,
+ "BootProgressLastUpdate", timeStamp);
+ if (ec.value())
+ {
+ lg2::error(
+ "updateBootProgressLastUpdateProperty, can't set property - Error={ERROR}",
+ "ERROR", ec.message());
+ return false;
+ }
+
+ return true;
+}
+
+ipmi::RspType<> sendBootProgressCode(
+ ipmi::Context::ptr ctx, uint8_t codeType, uint8_t codeReserved1,
+ uint8_t codeReserved2, uint8_t codeSeverity, uint8_t codeOperation1,
+ uint8_t codeOperation2, uint8_t codeSubClass, uint8_t codeClass,
+ uint8_t instance)
+{
+ /* Update boot progress code to Dbus property */
+ ipmi::DbusObjectInfo sbmrBootStateObject{};
+
+ /* Get Boot State Object */
+ boost::system::error_code ec =
+ ipmi::getDbusObject(ctx, sbmrBootStateIntf, sbmrBootStateObject);
+ if (ec.value())
+ {
+ lg2::error("Failed to get Boot State object, Error={ERROR}", "ERROR",
+ ec.message());
+ return ipmi::responseUnspecifiedError();
+ }
+
+ /* Set Boot State property */
+ BootProgressCode bpCode(
+ {codeType, codeReserved1, codeReserved2, codeSeverity, codeOperation1,
+ codeOperation2, codeSubClass, codeClass, instance},
+ {});
+ ec = ipmi::setDbusProperty(ctx, sbmrBootStateObject.second,
+ sbmrBootStateObject.first, sbmrBootStateIntf,
+ "Value", bpCode);
+ if (ec.value())
+ {
+ lg2::error("Failed to set boot progress code, Error={ERROR}", "ERROR",
+ ec.message());
+ return ipmi::responseUnspecifiedError();
+ }
+
+ /* Update Redfish BootProgress object */
+ auto timeStamp = std::chrono::duration_cast<std::chrono::microseconds>(
+ std::chrono::system_clock::now().time_since_epoch())
+ .count();
+ if (!updateBootProgressLastUpdateProperty(ctx, timeStamp))
+ {
+ return ipmi::responseUnspecifiedError();
+ }
+
+ /* Chek for BootProgressTypes */
+ uint16_t codeOperation =
+ static_cast<uint16_t>(codeOperation1) << 8 | codeOperation2;
+
+ std::string stage = getSbmrBootProgressStage(
+ codeType, codeSeverity, codeClass, codeSubClass, codeOperation);
+
+ if (!updateBootProgressProperty(ctx, stage))
+ {
+ return ipmi::responseUnspecifiedError();
+ }
+
+ return ipmi::responseSuccess();
+}
+
+ipmi::RspType<uint8_t, // STATUS_CODE_TYPE
+ uint8_t, // STATUS_CODE_RESERVED1
+ uint8_t, // STATUS_CODE_RESERVED2
+ uint8_t, // STATUS_CODE_SEVERITY
+ uint8_t, // STATUS_CODE_OPERATION1
+ uint8_t, // STATUS_CODE_OPERATION2
+ uint8_t, // STATUS_CODE_SUBCLASS
+ uint8_t, // STATUS_CODE_CLASS
+ uint8_t> // Instance
+ getBootProgressCode(ipmi::Context::ptr ctx)
+{
+ ipmi::DbusObjectInfo sbmrBootStateObject{};
+
+ /* Get Boot State Object */
+ boost::system::error_code ec =
+ ipmi::getDbusObject(ctx, sbmrBootStateIntf, sbmrBootStateObject);
+ if (ec.value())
+ {
+ lg2::error("Failed to get Boot State object, Error={ERROR}", "ERROR",
+ ec.message());
+ return ipmi::responseUnspecifiedError();
+ }
+
+ /* Get Boot State property */
+ BootProgressCode value;
+ ec = ipmi::getDbusProperty(ctx, sbmrBootStateObject.second,
+ sbmrBootStateObject.first, sbmrBootStateIntf,
+ "Value", value);
+ if (ec.value())
+ {
+ lg2::error("Can't get property Value, Error={ERROR}", "ERROR",
+ ec.message());
+ return ipmi::responseUnspecifiedError();
+ }
+
+ auto respBootProgressCode = std::get<0>(std::move(value));
+ if (respBootProgressCode.size() != sbmrBootProgressCodeSize)
+ {
+ return ipmi::responseUnspecifiedError();
+ }
+
+ return ipmi::responseSuccess(
+ respBootProgressCode[0], respBootProgressCode[1],
+ respBootProgressCode[2], respBootProgressCode[3],
+ respBootProgressCode[4], respBootProgressCode[5],
+ respBootProgressCode[6], respBootProgressCode[7],
+ respBootProgressCode[8]);
+}
+
+bool checkAllowedMediumType(uint8_t mediumType)
+{
+ if (mediumType ==
+ static_cast<uint8_t>(ipmi::EChannelMediumType::smbusV20) ||
+ mediumType ==
+ static_cast<uint8_t>(ipmi::EChannelMediumType::systemInterface) ||
+ mediumType == static_cast<uint8_t>(ipmi::EChannelMediumType::oem))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+ipmi::Cc sbmrFilterCommands(ipmi::message::Request::ptr request)
+{
+ if (request->ctx->netFn != ipmi::netFnGroup ||
+ request->ctx->group != ipmi::groupSBMR)
+ {
+ // Skip if not group SBMR
+ return ipmi::ccSuccess;
+ }
+
+ ipmi::ChannelInfo chInfo;
+ if (ipmi::getChannelInfo(request->ctx->channel, chInfo) != ipmi::ccSuccess)
+ {
+ lg2::error("Failed to get Channel Info, channel={CHANNEL}", "CHANNEL",
+ request->ctx->channel);
+ return ipmi::ccUnspecifiedError;
+ }
+
+ if (request->ctx->cmd == ipmi::sbmr::cmdSendBootProgressCode &&
+ !checkAllowedMediumType(chInfo.mediumType))
+ {
+ lg2::error("Error - Medium interface not supported, medium={TYPE}",
+ "TYPE", chInfo.mediumType);
+ return ipmi::ccCommandNotAvailable;
+ }
+
+ return ipmi::ccSuccess;
+}
+
+} // namespace ipmi
+
+void registerNetfnSBMRFunctions()
+{
+ registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupSBMR,
+ ipmi::sbmr::cmdSendBootProgressCode,
+ ipmi::Privilege::Admin, ipmi::sendBootProgressCode);
+
+ registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupSBMR,
+ ipmi::sbmr::cmdGetBootProgressCode,
+ ipmi::Privilege::User, ipmi::getBootProgressCode);
+
+ ipmi::registerFilter(ipmi::prioOemBase,
+ [](ipmi::message::Request::ptr request) {
+ return ipmi::sbmrFilterCommands(request);
+ });
+}