John Chung | 0a3f40b | 2025-01-22 18:07:52 -0600 | [diff] [blame^] | 1 | #include <ipmid/api.hpp> |
| 2 | #include <ipmid/filter.hpp> |
| 3 | #include <ipmid/utils.hpp> |
| 4 | #include <phosphor-logging/lg2.hpp> |
| 5 | |
| 6 | constexpr auto sbmrBootStateIntf = "xyz.openbmc_project.State.Boot.Raw"; |
| 7 | constexpr auto sbmrHostStateIntf = "xyz.openbmc_project.State.Boot.Progress"; |
| 8 | constexpr auto sbmrBootProgressCodeSize = 9; |
| 9 | |
| 10 | constexpr auto bootProgressOem = "OEM"; |
| 11 | constexpr auto bootProgressOsRuning = "OSRunning"; |
| 12 | constexpr auto bootProgressOsStart = "OSStart"; |
| 13 | constexpr auto bootProgressPciInit = "PCIInit"; |
| 14 | constexpr auto bootProgressSystemInitComplete = "SystemInitComplete"; |
| 15 | constexpr auto bootProgressSystemSetup = "SystemSetup"; |
| 16 | |
| 17 | // EFI_STATUS_CODE_TYPE |
| 18 | constexpr auto efiProgressCode = 0x01; |
| 19 | constexpr auto efiCodeSeverityNone = 0; |
| 20 | |
| 21 | // EFI_STATUS_CODE_CLASS |
| 22 | constexpr auto efiIoBus = 0x02; |
| 23 | constexpr auto efiSoftware = 0x03; |
| 24 | |
| 25 | // EFI_STATUS_CODE_SUBCLASS |
| 26 | constexpr auto efiIoBusPci = 0x01; |
| 27 | constexpr auto efiSoftwareDxeCore = 0x04; |
| 28 | constexpr auto efiSoftwareDxeBsDriver = 0x05; |
| 29 | constexpr auto efiSoftwareEfiBootService = 0x10; |
| 30 | |
| 31 | // EFI_STATUS_CODE_OPERATION |
| 32 | constexpr auto efiIoBusPciResAlloc = 0x0110; |
| 33 | constexpr auto efiSwDxeCorePcHandoffToNext = 0x0110; |
| 34 | constexpr auto efiSwPcUserSetup = 0x0700; |
| 35 | constexpr auto efiSwOsLoaderStart = 0x0180; |
| 36 | constexpr auto efiSwBsPcExitBootServices = 0x1910; |
| 37 | |
| 38 | void registerNetfnSBMRFunctions() __attribute__((constructor)); |
| 39 | |
| 40 | namespace ipmi |
| 41 | { |
| 42 | |
| 43 | std::string getSbmrBootProgressStage(uint8_t codeType, uint8_t codeSeverity, |
| 44 | uint8_t codeClass, uint8_t codeSubClass, |
| 45 | uint16_t codeOperation) |
| 46 | { |
| 47 | // Return OEM if code type or severity are unexpected |
| 48 | if (codeType != efiProgressCode || codeSeverity != efiCodeSeverityNone) |
| 49 | { |
| 50 | return bootProgressOem; |
| 51 | } |
| 52 | |
| 53 | // Code Class Software |
| 54 | if (codeClass == efiSoftware) |
| 55 | { |
| 56 | if (codeSubClass == efiSoftwareDxeCore && |
| 57 | codeOperation == efiSwDxeCorePcHandoffToNext) |
| 58 | { |
| 59 | return bootProgressSystemInitComplete; |
| 60 | } |
| 61 | else if (codeSubClass == efiSoftwareDxeBsDriver && |
| 62 | codeOperation == efiSwPcUserSetup) |
| 63 | { |
| 64 | return bootProgressSystemSetup; |
| 65 | } |
| 66 | else if (codeSubClass == efiSoftwareDxeBsDriver && |
| 67 | codeOperation == efiSwOsLoaderStart) |
| 68 | { |
| 69 | return bootProgressOsStart; |
| 70 | } |
| 71 | else if (codeSubClass == efiSoftwareEfiBootService && |
| 72 | codeOperation == efiSwBsPcExitBootServices) |
| 73 | { |
| 74 | return bootProgressOsRuning; |
| 75 | } |
| 76 | } |
| 77 | // Code Class IO Bus |
| 78 | else if (codeClass == efiIoBus) |
| 79 | { |
| 80 | if (codeSubClass == efiIoBusPci && codeOperation == efiIoBusPciResAlloc) |
| 81 | { |
| 82 | return bootProgressPciInit; |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | // Fallback to OEM if no conditions met |
| 87 | return bootProgressOem; |
| 88 | } |
| 89 | |
| 90 | bool updateBootProgressProperty(ipmi::Context::ptr& ctx, |
| 91 | const std::string& value) |
| 92 | { |
| 93 | std::string bootProgress = |
| 94 | "xyz.openbmc_project.State.Boot.Progress.ProgressStages." + value; |
| 95 | ipmi::DbusObjectInfo sbmrHostStateObject{}; |
| 96 | |
| 97 | /* Get Host State Object */ |
| 98 | boost::system::error_code ec = |
| 99 | ipmi::getDbusObject(ctx, sbmrHostStateIntf, sbmrHostStateObject); |
| 100 | if (ec.value()) |
| 101 | { |
| 102 | lg2::error("Failed to get Host State object, Error={ERROR}", "ERROR", |
| 103 | ec.message()); |
| 104 | return false; |
| 105 | } |
| 106 | |
| 107 | /* Set Host State property */ |
| 108 | ec = ipmi::setDbusProperty(ctx, sbmrHostStateObject.second, |
| 109 | sbmrHostStateObject.first, sbmrHostStateIntf, |
| 110 | "BootProgress", bootProgress); |
| 111 | if (ec.value()) |
| 112 | { |
| 113 | lg2::error( |
| 114 | "updateBootProgressProperty, can't set progerty - Error={ERROR}", |
| 115 | "ERROR", ec.message()); |
| 116 | return false; |
| 117 | } |
| 118 | |
| 119 | return true; |
| 120 | } |
| 121 | |
| 122 | bool updateBootProgressLastUpdateProperty(ipmi::Context::ptr& ctx, |
| 123 | uint64_t timeStamp) |
| 124 | { |
| 125 | ipmi::DbusObjectInfo sbmrHostStateObject{}; |
| 126 | |
| 127 | /* Get Host State Object */ |
| 128 | boost::system::error_code ec = |
| 129 | ipmi::getDbusObject(ctx, sbmrHostStateIntf, sbmrHostStateObject); |
| 130 | if (ec.value()) |
| 131 | { |
| 132 | lg2::error("Failed to get Host State object, Error={ERROR}", "ERROR", |
| 133 | ec.message()); |
| 134 | return false; |
| 135 | } |
| 136 | |
| 137 | /* Set Host State property */ |
| 138 | ec = ipmi::setDbusProperty(ctx, sbmrHostStateObject.second, |
| 139 | sbmrHostStateObject.first, sbmrHostStateIntf, |
| 140 | "BootProgressLastUpdate", timeStamp); |
| 141 | if (ec.value()) |
| 142 | { |
| 143 | lg2::error( |
| 144 | "updateBootProgressLastUpdateProperty, can't set property - Error={ERROR}", |
| 145 | "ERROR", ec.message()); |
| 146 | return false; |
| 147 | } |
| 148 | |
| 149 | return true; |
| 150 | } |
| 151 | |
| 152 | ipmi::RspType<> sendBootProgressCode( |
| 153 | ipmi::Context::ptr ctx, uint8_t codeType, uint8_t codeReserved1, |
| 154 | uint8_t codeReserved2, uint8_t codeSeverity, uint8_t codeOperation1, |
| 155 | uint8_t codeOperation2, uint8_t codeSubClass, uint8_t codeClass, |
| 156 | uint8_t instance) |
| 157 | { |
| 158 | /* Update boot progress code to Dbus property */ |
| 159 | ipmi::DbusObjectInfo sbmrBootStateObject{}; |
| 160 | |
| 161 | /* Get Boot State Object */ |
| 162 | boost::system::error_code ec = |
| 163 | ipmi::getDbusObject(ctx, sbmrBootStateIntf, sbmrBootStateObject); |
| 164 | if (ec.value()) |
| 165 | { |
| 166 | lg2::error("Failed to get Boot State object, Error={ERROR}", "ERROR", |
| 167 | ec.message()); |
| 168 | return ipmi::responseUnspecifiedError(); |
| 169 | } |
| 170 | |
| 171 | /* Set Boot State property */ |
| 172 | BootProgressCode bpCode( |
| 173 | {codeType, codeReserved1, codeReserved2, codeSeverity, codeOperation1, |
| 174 | codeOperation2, codeSubClass, codeClass, instance}, |
| 175 | {}); |
| 176 | ec = ipmi::setDbusProperty(ctx, sbmrBootStateObject.second, |
| 177 | sbmrBootStateObject.first, sbmrBootStateIntf, |
| 178 | "Value", bpCode); |
| 179 | if (ec.value()) |
| 180 | { |
| 181 | lg2::error("Failed to set boot progress code, Error={ERROR}", "ERROR", |
| 182 | ec.message()); |
| 183 | return ipmi::responseUnspecifiedError(); |
| 184 | } |
| 185 | |
| 186 | /* Update Redfish BootProgress object */ |
| 187 | auto timeStamp = std::chrono::duration_cast<std::chrono::microseconds>( |
| 188 | std::chrono::system_clock::now().time_since_epoch()) |
| 189 | .count(); |
| 190 | if (!updateBootProgressLastUpdateProperty(ctx, timeStamp)) |
| 191 | { |
| 192 | return ipmi::responseUnspecifiedError(); |
| 193 | } |
| 194 | |
| 195 | /* Chek for BootProgressTypes */ |
| 196 | uint16_t codeOperation = |
| 197 | static_cast<uint16_t>(codeOperation1) << 8 | codeOperation2; |
| 198 | |
| 199 | std::string stage = getSbmrBootProgressStage( |
| 200 | codeType, codeSeverity, codeClass, codeSubClass, codeOperation); |
| 201 | |
| 202 | if (!updateBootProgressProperty(ctx, stage)) |
| 203 | { |
| 204 | return ipmi::responseUnspecifiedError(); |
| 205 | } |
| 206 | |
| 207 | return ipmi::responseSuccess(); |
| 208 | } |
| 209 | |
| 210 | ipmi::RspType<uint8_t, // STATUS_CODE_TYPE |
| 211 | uint8_t, // STATUS_CODE_RESERVED1 |
| 212 | uint8_t, // STATUS_CODE_RESERVED2 |
| 213 | uint8_t, // STATUS_CODE_SEVERITY |
| 214 | uint8_t, // STATUS_CODE_OPERATION1 |
| 215 | uint8_t, // STATUS_CODE_OPERATION2 |
| 216 | uint8_t, // STATUS_CODE_SUBCLASS |
| 217 | uint8_t, // STATUS_CODE_CLASS |
| 218 | uint8_t> // Instance |
| 219 | getBootProgressCode(ipmi::Context::ptr ctx) |
| 220 | { |
| 221 | ipmi::DbusObjectInfo sbmrBootStateObject{}; |
| 222 | |
| 223 | /* Get Boot State Object */ |
| 224 | boost::system::error_code ec = |
| 225 | ipmi::getDbusObject(ctx, sbmrBootStateIntf, sbmrBootStateObject); |
| 226 | if (ec.value()) |
| 227 | { |
| 228 | lg2::error("Failed to get Boot State object, Error={ERROR}", "ERROR", |
| 229 | ec.message()); |
| 230 | return ipmi::responseUnspecifiedError(); |
| 231 | } |
| 232 | |
| 233 | /* Get Boot State property */ |
| 234 | BootProgressCode value; |
| 235 | ec = ipmi::getDbusProperty(ctx, sbmrBootStateObject.second, |
| 236 | sbmrBootStateObject.first, sbmrBootStateIntf, |
| 237 | "Value", value); |
| 238 | if (ec.value()) |
| 239 | { |
| 240 | lg2::error("Can't get property Value, Error={ERROR}", "ERROR", |
| 241 | ec.message()); |
| 242 | return ipmi::responseUnspecifiedError(); |
| 243 | } |
| 244 | |
| 245 | auto respBootProgressCode = std::get<0>(std::move(value)); |
| 246 | if (respBootProgressCode.size() != sbmrBootProgressCodeSize) |
| 247 | { |
| 248 | return ipmi::responseUnspecifiedError(); |
| 249 | } |
| 250 | |
| 251 | return ipmi::responseSuccess( |
| 252 | respBootProgressCode[0], respBootProgressCode[1], |
| 253 | respBootProgressCode[2], respBootProgressCode[3], |
| 254 | respBootProgressCode[4], respBootProgressCode[5], |
| 255 | respBootProgressCode[6], respBootProgressCode[7], |
| 256 | respBootProgressCode[8]); |
| 257 | } |
| 258 | |
| 259 | bool checkAllowedMediumType(uint8_t mediumType) |
| 260 | { |
| 261 | if (mediumType == |
| 262 | static_cast<uint8_t>(ipmi::EChannelMediumType::smbusV20) || |
| 263 | mediumType == |
| 264 | static_cast<uint8_t>(ipmi::EChannelMediumType::systemInterface) || |
| 265 | mediumType == static_cast<uint8_t>(ipmi::EChannelMediumType::oem)) |
| 266 | { |
| 267 | return true; |
| 268 | } |
| 269 | |
| 270 | return false; |
| 271 | } |
| 272 | |
| 273 | ipmi::Cc sbmrFilterCommands(ipmi::message::Request::ptr request) |
| 274 | { |
| 275 | if (request->ctx->netFn != ipmi::netFnGroup || |
| 276 | request->ctx->group != ipmi::groupSBMR) |
| 277 | { |
| 278 | // Skip if not group SBMR |
| 279 | return ipmi::ccSuccess; |
| 280 | } |
| 281 | |
| 282 | ipmi::ChannelInfo chInfo; |
| 283 | if (ipmi::getChannelInfo(request->ctx->channel, chInfo) != ipmi::ccSuccess) |
| 284 | { |
| 285 | lg2::error("Failed to get Channel Info, channel={CHANNEL}", "CHANNEL", |
| 286 | request->ctx->channel); |
| 287 | return ipmi::ccUnspecifiedError; |
| 288 | } |
| 289 | |
| 290 | if (request->ctx->cmd == ipmi::sbmr::cmdSendBootProgressCode && |
| 291 | !checkAllowedMediumType(chInfo.mediumType)) |
| 292 | { |
| 293 | lg2::error("Error - Medium interface not supported, medium={TYPE}", |
| 294 | "TYPE", chInfo.mediumType); |
| 295 | return ipmi::ccCommandNotAvailable; |
| 296 | } |
| 297 | |
| 298 | return ipmi::ccSuccess; |
| 299 | } |
| 300 | |
| 301 | } // namespace ipmi |
| 302 | |
| 303 | void registerNetfnSBMRFunctions() |
| 304 | { |
| 305 | registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupSBMR, |
| 306 | ipmi::sbmr::cmdSendBootProgressCode, |
| 307 | ipmi::Privilege::Admin, ipmi::sendBootProgressCode); |
| 308 | |
| 309 | registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupSBMR, |
| 310 | ipmi::sbmr::cmdGetBootProgressCode, |
| 311 | ipmi::Privilege::User, ipmi::getBootProgressCode); |
| 312 | |
| 313 | ipmi::registerFilter(ipmi::prioOemBase, |
| 314 | [](ipmi::message::Request::ptr request) { |
| 315 | return ipmi::sbmrFilterCommands(request); |
| 316 | }); |
| 317 | } |