blob: 328d503c74667ba868344bcbbe125497f3127f52 [file] [log] [blame]
#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);
});
}