1 /* 2 * Copyright (c) 2018 Intel Corporation. 3 * Copyright (c) 2018-present Facebook. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 #include <biccommands.hpp> 19 #include <commandutils.hpp> 20 #include <ipmid/api-types.hpp> 21 #include <ipmid/api.hpp> 22 #include <phosphor-logging/log.hpp> 23 #include <types.hpp> 24 25 #include <iostream> 26 #include <variant> 27 #include <vector> 28 29 namespace ipmi 30 { 31 32 int sendBicCmd(uint8_t, uint8_t, uint8_t, std::vector<uint8_t>&, 33 std::vector<uint8_t>&); 34 35 using namespace phosphor::logging; 36 37 #ifdef BIC_ENABLED 38 static void registerBICFunctions() __attribute__((constructor)); 39 #endif 40 41 extern message::Response::ptr executeIpmiCommand(message::Request::ptr); 42 43 int sendBicCmd(uint8_t, uint8_t, uint8_t, std::vector<uint8_t>&, 44 std::vector<uint8_t>&); 45 46 constexpr std::array<uint8_t, 2> amdDimmLoopPrefix = {0xDD, 0xEE}; 47 48 namespace dimm 49 { 50 std::unordered_map<hostId, dimmLoop> dimmLoops; 51 } // namespace dimm 52 53 //---------------------------------------------------------------------- 54 // ipmiOemBicHandler (IPMI/Section - ) (CMD_OEM_BIC_INFO) 55 // This Function will handle BIC request for netfn=0x38 and cmd=1 56 // send the response back to the sender. 57 //---------------------------------------------------------------------- 58 59 ipmi::RspType<IanaType, uint8_t, uint2_t, uint6_t, uint8_t, uint8_t, 60 ipmi::message::Payload> ipmiOemBicHandler(ipmi::Context::ptr ctx,IanaType reqIana,uint8_t interface,uint2_t lun,uint6_t netFnReq,uint8_t cmdReq,SecureBuffer data)61 ipmiOemBicHandler(ipmi::Context::ptr ctx, IanaType reqIana, 62 uint8_t interface, uint2_t lun, uint6_t netFnReq, 63 uint8_t cmdReq, SecureBuffer data) 64 { 65 ipmi::message::Response::ptr res; 66 67 // Updating the correct netfn and cmd in the ipmi Context 68 ctx->netFn = ((uint8_t)netFnReq); 69 ctx->cmd = cmdReq; 70 71 // creating ipmi message request for calling executeIpmiCommand function 72 auto req = std::make_shared<ipmi::message::Request>(ctx, std::move(data)); 73 74 // Calling executeIpmiCommand request function 75 res = ipmi::executeIpmiCommand(req); 76 77 // sending the response with headers and payload 78 return ipmi::responseSuccess(reqIana, interface, lun, ++netFnReq, cmdReq, 79 res->cc, res->payload); 80 } 81 dimmLoopPatternDetection(size_t hostId,std::vector<uint8_t> data)82 void dimmLoopPatternDetection(size_t hostId, std::vector<uint8_t> data) 83 { 84 if constexpr (postCodeSize != amdFourBytesPostCode) 85 { 86 return; 87 } 88 89 if (data.size() != amdFourBytesPostCode) 90 { 91 return; 92 } 93 94 /* 95 Reference from Meta_BIOS_Requirement_Spec_v0.80 96 For AMD platform, the POST code looping pattern format should be: 97 (each group has 4 bytes) 98 ●Group #0: [DDEE0000] 99 ●Group #1: [DDEE] + Total Error Count 100 ●Group #2: [DDEE] + Number of Error DIMM 101 ●Group #3: [DDEE] + Dimm location 102 ●Group #4: [DDEE] + major code 103 ●Group #5: [DDEE] + minor code 104 */ 105 std::array<uint8_t, 2> prefix = {data[3], data[2]}; 106 107 if (prefix != amdDimmLoopPrefix) 108 { 109 // Clear all the post code stored before. 110 if (dimm::dimmLoops[hostId].startDetect) 111 { 112 dimm::dimmLoops[hostId].totalErrorCount = 0; 113 dimm::dimmLoops[hostId].postCode.clear(); 114 115 dimm::dimmLoops[hostId].startDetect = false; 116 } 117 return; 118 } 119 120 // Which means it already got the dimm loop, stop checking again. 121 if (dimm::dimmLoops[hostId].gotPattern) 122 { 123 return; 124 } 125 126 constexpr std::array<uint8_t, 4> anchorTag = {0x0, 0x0, 0xEE, 0xDD}; 127 if (std::ranges::equal(anchorTag, data)) 128 { 129 dimm::dimmLoops[hostId].startDetect = true; 130 } 131 if (dimm::dimmLoops[hostId].startDetect) 132 { 133 // The second one is error count 134 if (dimm::dimmLoops[hostId].postCode.size() % 6 == 1) 135 { 136 dimm::dimmLoops[hostId].totalErrorCount = (data[1] << 8) | data[0]; 137 } 138 139 dimm::dimmLoops[hostId].postCode.push_back(data); 140 141 // Is the last element of dimmloop then stop to detect 142 if (dimm::dimmLoops[hostId].postCode.size() == 143 (dimm::dimmLoops[hostId].totalErrorCount * 6)) 144 { 145 // Gets whole pattern success 146 dimm::dimmLoops[hostId].gotPattern = true; 147 } 148 } 149 } 150 151 //---------------------------------------------------------------------- 152 // ipmiOemPostCodeHandler (CMD_OEM_BIC_POST_BUFFER_INFO) 153 // This Function will handle BIC incoming postcode from multi-host for 154 // netfn=0x38 and cmd=0x08 or 0x33 send the response back to the sender. 155 //---------------------------------------------------------------------- 156 ipmiOemPostCodeHandler(ipmi::Context::ptr ctx,IanaType reqIana,uint8_t dataLen,std::vector<uint8_t> data)157 ipmi::RspType<IanaType> ipmiOemPostCodeHandler( 158 ipmi::Context::ptr ctx, IanaType reqIana, uint8_t dataLen, 159 std::vector<uint8_t> data) 160 { 161 // creating bus connection 162 auto conn = getSdBus(); 163 164 auto hostId = findHost(ctx->hostIdx); 165 if (!hostId) 166 { 167 lg2::error("Invalid Host Id received"); 168 return ipmi::responseInvalidCommand(); 169 } 170 dimmLoopPatternDetection(*hostId, data); 171 172 using postcode_t = std::tuple<uint64_t, std::vector<uint8_t>>; 173 174 std::string dbusObjStr = dbusObj + std::to_string((ctx->hostIdx + 1)); 175 176 for (unsigned int index = 0; index < dataLen; index++) 177 { 178 uint64_t primaryPostCode = static_cast<uint64_t>(data[index]); 179 auto postCode = postcode_t(primaryPostCode, {}); 180 181 try 182 { 183 auto method = conn->new_method_call( 184 "xyz.openbmc_project.State.Boot.Raw", dbusObjStr.c_str(), 185 "org.freedesktop.DBus.Properties", "Set"); 186 187 // Adding parameters to method call 188 method.append(dbusService, "Value", 189 std::variant<postcode_t>(postCode)); 190 191 // Invoke method call function 192 auto reply = conn->call(method); 193 } 194 195 catch (std::exception& e) 196 { 197 phosphor::logging::log<phosphor::logging::level::ERR>( 198 "post code handler error\n"); 199 200 // sending the Error response 201 return ipmi::responseResponseError(); 202 } 203 } 204 205 return ipmi::responseSuccess(reqIana); 206 } 207 208 //---------------------------------------------------------------------- 209 // ipmiOemGetBicGpioState (CMD_OEM_GET_BIC_GPIO_STATE) 210 // This Function will handle BIC GPIO stats for 211 // netfn=0x38 and cmd=0x03 send the response back to the sender. 212 //---------------------------------------------------------------------- 213 ipmiOemGetBicGpioState(ipmi::Context::ptr ctx,std::vector<uint8_t> reqIana)214 ipmi::RspType<IanaType, std::vector<uint8_t>> ipmiOemGetBicGpioState( 215 ipmi::Context::ptr ctx, std::vector<uint8_t> reqIana) 216 { 217 std::vector<uint8_t> respData; 218 219 if (std::equal(reqIana.begin(), reqIana.end(), iana.begin()) == false) 220 { 221 phosphor::logging::log<phosphor::logging::level::ERR>( 222 "Invalid IANA number"); 223 return ipmi::responseInvalidFieldRequest(); 224 } 225 226 uint8_t bicAddr = (uint8_t)ctx->hostIdx << 2; 227 228 if (sendBicCmd(ctx->netFn, ctx->cmd, bicAddr, reqIana, respData)) 229 { 230 return ipmi::responseUnspecifiedError(); 231 } 232 233 std::vector<uint8_t> gpioState; 234 IanaType respIana; 235 236 auto r = 237 std::ranges::copy_n(respData.begin(), iana.size(), respIana.begin()).in; 238 std::copy(r, respData.end(), std::back_inserter(gpioState)); 239 240 return ipmi::responseSuccess(respIana, gpioState); 241 } 242 243 //---------------------------------------------------------------------- 244 // ipmiOemSetHostPowerState (CMD_OEM_SET_HOST_POWER_STATE) 245 // This Function will handle BIC incoming IPMI request for 246 // setting host current state for netfn=0x38 and cmd=0x0C 247 // send the response back to the sender. 248 //---------------------------------------------------------------------- 249 ipmiOemSetHostPowerState(ipmi::Context::ptr ctx,IanaType reqIana,uint8_t status)250 ipmi::RspType<IanaType> ipmiOemSetHostPowerState( 251 ipmi::Context::ptr ctx, IanaType reqIana, uint8_t status) 252 { 253 std::string targetUnit; 254 255 switch (static_cast<HostPowerState>(status)) 256 { 257 case HostPowerState::HOST_POWER_ON: 258 targetUnit = "obmc-host-startmin@.target"; 259 break; 260 case HostPowerState::HOST_POWER_OFF: 261 targetUnit = "obmc-host-stop@.target"; 262 break; 263 default: 264 phosphor::logging::log<phosphor::logging::level::ERR>( 265 "IPMI ipmiOemHostPowerStatus power status error"); 266 return ipmi::responseUnspecifiedError(); 267 } 268 269 int mousePos = targetUnit.find('@'); 270 targetUnit.insert(mousePos + 1, std::to_string(ctx->hostIdx + 1)); 271 272 auto conn = getSdBus(); 273 auto method = conn->new_method_call(systemdService, systemdObjPath, 274 systemdInterface, "StartUnit"); 275 method.append(targetUnit); 276 method.append("replace"); 277 278 try 279 { 280 conn->call_noreply(method); 281 } 282 catch (const sdbusplus::exception::SdBusError& e) 283 { 284 phosphor::logging::log<phosphor::logging::level::ERR>( 285 "IPMI ipmiOemHostPowerStatus Failed in call method", 286 phosphor::logging::entry("ERROR=%s", e.what())); 287 return ipmi::responseUnspecifiedError(); 288 } 289 290 return ipmi::responseSuccess(reqIana); 291 } 292 293 //---------------------------------------------------------------------- 294 // ipmiOemGetBiosFlashSize (CMD_OEM_GET_FLASH_SIZE) 295 // This Function will return the bios flash size 296 // netfn=0x38 and cmd=0x19 send the response back to the sender. 297 //---------------------------------------------------------------------- 298 ipmiOemGetBiosFlashSize(ipmi::Context::ptr ctx,IanaType ianaReq,uint8_t target)299 ipmi::RspType<IanaType, flashSize> ipmiOemGetBiosFlashSize( 300 ipmi::Context::ptr ctx, IanaType ianaReq, uint8_t target) 301 { 302 if (iana != ianaReq) 303 { 304 phosphor::logging::log<phosphor::logging::level::ERR>( 305 "Invalid IANA ID length received"); 306 return ipmi::responseReqDataLenInvalid(); 307 } 308 309 std::vector<uint8_t> respData; 310 uint8_t bicAddr = (uint8_t)ctx->hostIdx << 2; 311 std::vector<uint8_t> reqData(ianaReq.begin(), ianaReq.end()); 312 reqData.emplace_back(target); 313 314 if (sendBicCmd(ctx->netFn, ctx->cmd, bicAddr, reqData, respData)) 315 { 316 return ipmi::responseUnspecifiedError(); 317 } 318 319 if (respData.size() != flashSizeRespLen) 320 { 321 phosphor::logging::log<phosphor::logging::level::ERR>( 322 "Invalid Response Data length received"); 323 return ipmi::responseReqDataLenInvalid(); 324 } 325 326 IanaType ianaResp; 327 std::copy_n(respData.begin(), ianaResp.size(), ianaResp.begin()); 328 329 if (iana != ianaResp) 330 { 331 phosphor::logging::log<phosphor::logging::level::ERR>( 332 "Invalid IANA ID received"); 333 return ipmi::responseInvalidCommand(); 334 } 335 336 flashSize flashResp; 337 std::vector<uint8_t>::iterator respDataIter = respData.begin(); 338 std::advance(respDataIter, ianaResp.size()); 339 std::copy_n(respDataIter, flashResp.size(), flashResp.begin()); 340 341 // sending the success response. 342 return ipmi::responseSuccess(ianaResp, flashResp); 343 } 344 345 //---------------------------------------------------------------------- 346 // ipmiOemClearCmos (CMD_OEM_CLEAR_CMOS) 347 // This Function will clear the CMOS. 348 // netfn=0x38 and cmd=0x25 349 //---------------------------------------------------------------------- ipmiOemClearCmos(ipmi::Context::ptr ctx,IanaType ianaReq)350 ipmi::RspType<IanaType> ipmiOemClearCmos(ipmi::Context::ptr ctx, 351 IanaType ianaReq) 352 { 353 if (iana != ianaReq) 354 { 355 phosphor::logging::log<phosphor::logging::level::ERR>( 356 "Invalid request of IANA ID length received"); 357 return ipmi::responseReqDataLenInvalid(); 358 } 359 360 uint8_t bicAddr = (uint8_t)ctx->hostIdx << 2; 361 362 std::vector<uint8_t> respData; 363 std::vector<uint8_t> reqData(ianaReq.begin(), ianaReq.end()); 364 365 if (sendBicCmd(ctx->netFn, ctx->cmd, bicAddr, reqData, respData)) 366 { 367 return ipmi::responseUnspecifiedError(); 368 } 369 370 if (respData.size() != iana.size()) 371 { 372 return ipmi::responseReqDataLenInvalid(); 373 } 374 375 IanaType resp; 376 std::copy_n(respData.begin(), resp.size(), resp.begin()); 377 378 if (iana != resp) 379 { 380 phosphor::logging::log<phosphor::logging::level::ERR>( 381 "Invalid response of IANA ID received"); 382 return ipmi::responseUnspecifiedError(); 383 } 384 385 // sending the success response. 386 return ipmi::responseSuccess(resp); 387 } 388 registerBICFunctions(void)389 [[maybe_unused]] static void registerBICFunctions(void) 390 { 391 phosphor::logging::log<phosphor::logging::level::INFO>( 392 "Registering BIC commands"); 393 394 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemFive, 395 static_cast<Cmd>(fb_bic_cmds::CMD_OEM_BIC_INFO), 396 ipmi::Privilege::User, ipmiOemBicHandler); 397 ipmi::registerHandler( 398 ipmi::prioOpenBmcBase, ipmi::netFnOemFive, 399 static_cast<Cmd>(fb_bic_cmds::CMD_OEM_SEND_POST_BUFFER_TO_BMC), 400 ipmi::Privilege::User, ipmiOemPostCodeHandler); 401 ipmi::registerHandler( 402 ipmi::prioOpenBmcBase, ipmi::netFnOemFive, 403 static_cast<Cmd>(fb_bic_cmds::CMD_OEM_1S_4BYTE_POST_BUF), 404 ipmi::Privilege::User, ipmiOemPostCodeHandler); 405 ipmi::registerHandler( 406 ipmi::prioOemBase, ipmi::netFnOemFive, 407 static_cast<Cmd>(fb_bic_cmds::CMD_OEM_GET_BIC_GPIO_STATE), 408 ipmi::Privilege::User, ipmiOemGetBicGpioState); 409 ipmi::registerHandler( 410 ipmi::prioOpenBmcBase, ipmi::netFnOemFive, 411 static_cast<Cmd>(fb_bic_cmds::CMD_OEM_SET_HOST_POWER_STATE), 412 ipmi::Privilege::User, ipmiOemSetHostPowerState); 413 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemFive, 414 static_cast<Cmd>(fb_bic_cmds::CMD_OEM_GET_FLASH_SIZE), 415 ipmi::Privilege::User, ipmiOemGetBiosFlashSize); 416 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemFive, 417 static_cast<Cmd>(fb_bic_cmds::CMD_OEM_CLEAR_CMOS), 418 ipmi::Privilege::User, ipmiOemClearCmos); 419 return; 420 } 421 422 } // namespace ipmi 423