| #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); |
| }); |
| } |