/* * Copyright (c) 2018 Intel Corporation. * Copyright (c) 2018-present Facebook. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include namespace ipmi { int sendBicCmd(uint8_t, uint8_t, uint8_t, std::vector&, std::vector&); using namespace phosphor::logging; #ifdef BIC_ENABLED static void registerBICFunctions() __attribute__((constructor)); #endif extern message::Response::ptr executeIpmiCommand(message::Request::ptr); int sendBicCmd(uint8_t, uint8_t, uint8_t, std::vector&, std::vector&); constexpr std::array amdDimmLoopPrefix = {0xDD, 0xEE}; namespace dimm { std::unordered_map dimmLoops; } // namespace dimm //---------------------------------------------------------------------- // ipmiOemBicHandler (IPMI/Section - ) (CMD_OEM_BIC_INFO) // This Function will handle BIC request for netfn=0x38 and cmd=1 // send the response back to the sender. //---------------------------------------------------------------------- ipmi::RspType ipmiOemBicHandler(ipmi::Context::ptr ctx, IanaType reqIana, uint8_t interface, uint2_t lun, uint6_t netFnReq, uint8_t cmdReq, SecureBuffer data) { ipmi::message::Response::ptr res; // Updating the correct netfn and cmd in the ipmi Context ctx->netFn = ((uint8_t)netFnReq); ctx->cmd = cmdReq; // creating ipmi message request for calling executeIpmiCommand function auto req = std::make_shared(ctx, std::move(data)); // Calling executeIpmiCommand request function res = ipmi::executeIpmiCommand(req); // sending the response with headers and payload return ipmi::responseSuccess(reqIana, interface, lun, ++netFnReq, cmdReq, res->cc, res->payload); } void dimmLoopPatternDetection(size_t hostId, std::vector data) { if constexpr (postCodeSize != amdFourBytesPostCode) { return; } if (data.size() != amdFourBytesPostCode) { return; } /* Reference from Meta_BIOS_Requirement_Spec_v0.80 For AMD platform, the POST code looping pattern format should be: (each group has 4 bytes) ●Group #0: [DDEE0000] ●Group #1: [DDEE] + Total Error Count ●Group #2: [DDEE] + Number of Error DIMM ●Group #3: [DDEE] + Dimm location ●Group #4: [DDEE] + major code ●Group #5: [DDEE] + minor code */ std::array prefix = {data[3], data[2]}; if (prefix != amdDimmLoopPrefix) { // Clear all the post code stored before. if (dimm::dimmLoops[hostId].startDetect) { dimm::dimmLoops[hostId].totalErrorCount = 0; dimm::dimmLoops[hostId].postCode.clear(); dimm::dimmLoops[hostId].startDetect = false; } return; } // Which means it already got the dimm loop, stop checking again. if (dimm::dimmLoops[hostId].gotPattern) { return; } constexpr std::array anchorTag = {0x0, 0x0, 0xEE, 0xDD}; if (std::ranges::equal(anchorTag, data)) { dimm::dimmLoops[hostId].startDetect = true; } if (dimm::dimmLoops[hostId].startDetect) { // The second one is error count if (dimm::dimmLoops[hostId].postCode.size() % 6 == 1) { dimm::dimmLoops[hostId].totalErrorCount = (data[1] << 8) | data[0]; } dimm::dimmLoops[hostId].postCode.push_back(data); // Is the last element of dimmloop then stop to detect if (dimm::dimmLoops[hostId].postCode.size() == (dimm::dimmLoops[hostId].totalErrorCount * 6)) { // Gets whole pattern success dimm::dimmLoops[hostId].gotPattern = true; } } } //---------------------------------------------------------------------- // ipmiOemPostCodeHandler (CMD_OEM_BIC_POST_BUFFER_INFO) // This Function will handle BIC incoming postcode from multi-host for // netfn=0x38 and cmd=0x08 or 0x33 send the response back to the sender. //---------------------------------------------------------------------- ipmi::RspType ipmiOemPostCodeHandler( ipmi::Context::ptr ctx, IanaType reqIana, uint8_t dataLen, std::vector data) { // creating bus connection auto conn = getSdBus(); auto hostId = findHost(ctx->hostIdx); if (!hostId) { lg2::error("Invalid Host Id received"); return ipmi::responseInvalidCommand(); } dimmLoopPatternDetection(*hostId, data); using postcode_t = std::tuple>; std::string dbusObjStr = dbusObj + std::to_string((ctx->hostIdx + 1)); for (unsigned int index = 0; index < dataLen; index++) { uint64_t primaryPostCode = static_cast(data[index]); auto postCode = postcode_t(primaryPostCode, {}); try { auto method = conn->new_method_call( "xyz.openbmc_project.State.Boot.Raw", dbusObjStr.c_str(), "org.freedesktop.DBus.Properties", "Set"); // Adding parameters to method call method.append(dbusService, "Value", std::variant(postCode)); // Invoke method call function auto reply = conn->call(method); } catch (std::exception& e) { phosphor::logging::log( "post code handler error\n"); // sending the Error response return ipmi::responseResponseError(); } } return ipmi::responseSuccess(reqIana); } //---------------------------------------------------------------------- // ipmiOemGetBicGpioState (CMD_OEM_GET_BIC_GPIO_STATE) // This Function will handle BIC GPIO stats for // netfn=0x38 and cmd=0x03 send the response back to the sender. //---------------------------------------------------------------------- ipmi::RspType> ipmiOemGetBicGpioState( ipmi::Context::ptr ctx, std::vector reqIana) { std::vector respData; if (std::equal(reqIana.begin(), reqIana.end(), iana.begin()) == false) { phosphor::logging::log( "Invalid IANA number"); return ipmi::responseInvalidFieldRequest(); } uint8_t bicAddr = (uint8_t)ctx->hostIdx << 2; if (sendBicCmd(ctx->netFn, ctx->cmd, bicAddr, reqIana, respData)) { return ipmi::responseUnspecifiedError(); } std::vector gpioState; IanaType respIana; auto r = std::ranges::copy_n(respData.begin(), iana.size(), respIana.begin()).in; std::copy(r, respData.end(), std::back_inserter(gpioState)); return ipmi::responseSuccess(respIana, gpioState); } //---------------------------------------------------------------------- // ipmiOemSetHostPowerState (CMD_OEM_SET_HOST_POWER_STATE) // This Function will handle BIC incoming IPMI request for // setting host current state for netfn=0x38 and cmd=0x0C // send the response back to the sender. //---------------------------------------------------------------------- ipmi::RspType ipmiOemSetHostPowerState( ipmi::Context::ptr ctx, IanaType reqIana, uint8_t status) { std::string targetUnit; switch (static_cast(status)) { case HostPowerState::HOST_POWER_ON: targetUnit = "obmc-host-startmin@.target"; break; case HostPowerState::HOST_POWER_OFF: targetUnit = "obmc-host-stop@.target"; break; default: phosphor::logging::log( "IPMI ipmiOemHostPowerStatus power status error"); return ipmi::responseUnspecifiedError(); } int mousePos = targetUnit.find('@'); targetUnit.insert(mousePos + 1, std::to_string(ctx->hostIdx + 1)); auto conn = getSdBus(); auto method = conn->new_method_call(systemdService, systemdObjPath, systemdInterface, "StartUnit"); method.append(targetUnit); method.append("replace"); try { conn->call_noreply(method); } catch (const sdbusplus::exception::SdBusError& e) { phosphor::logging::log( "IPMI ipmiOemHostPowerStatus Failed in call method", phosphor::logging::entry("ERROR=%s", e.what())); return ipmi::responseUnspecifiedError(); } return ipmi::responseSuccess(reqIana); } //---------------------------------------------------------------------- // ipmiOemGetBiosFlashSize (CMD_OEM_GET_FLASH_SIZE) // This Function will return the bios flash size // netfn=0x38 and cmd=0x19 send the response back to the sender. //---------------------------------------------------------------------- ipmi::RspType ipmiOemGetBiosFlashSize( ipmi::Context::ptr ctx, IanaType ianaReq, uint8_t target) { if (iana != ianaReq) { phosphor::logging::log( "Invalid IANA ID length received"); return ipmi::responseReqDataLenInvalid(); } std::vector respData; uint8_t bicAddr = (uint8_t)ctx->hostIdx << 2; std::vector reqData(ianaReq.begin(), ianaReq.end()); reqData.emplace_back(target); if (sendBicCmd(ctx->netFn, ctx->cmd, bicAddr, reqData, respData)) { return ipmi::responseUnspecifiedError(); } if (respData.size() != flashSizeRespLen) { phosphor::logging::log( "Invalid Response Data length received"); return ipmi::responseReqDataLenInvalid(); } IanaType ianaResp; std::copy_n(respData.begin(), ianaResp.size(), ianaResp.begin()); if (iana != ianaResp) { phosphor::logging::log( "Invalid IANA ID received"); return ipmi::responseInvalidCommand(); } flashSize flashResp; std::vector::iterator respDataIter = respData.begin(); std::advance(respDataIter, ianaResp.size()); std::copy_n(respDataIter, flashResp.size(), flashResp.begin()); // sending the success response. return ipmi::responseSuccess(ianaResp, flashResp); } //---------------------------------------------------------------------- // ipmiOemClearCmos (CMD_OEM_CLEAR_CMOS) // This Function will clear the CMOS. // netfn=0x38 and cmd=0x25 //---------------------------------------------------------------------- ipmi::RspType ipmiOemClearCmos(ipmi::Context::ptr ctx, IanaType ianaReq) { if (iana != ianaReq) { phosphor::logging::log( "Invalid request of IANA ID length received"); return ipmi::responseReqDataLenInvalid(); } uint8_t bicAddr = (uint8_t)ctx->hostIdx << 2; std::vector respData; std::vector reqData(ianaReq.begin(), ianaReq.end()); if (sendBicCmd(ctx->netFn, ctx->cmd, bicAddr, reqData, respData)) { return ipmi::responseUnspecifiedError(); } if (respData.size() != iana.size()) { return ipmi::responseReqDataLenInvalid(); } IanaType resp; std::copy_n(respData.begin(), resp.size(), resp.begin()); if (iana != resp) { phosphor::logging::log( "Invalid response of IANA ID received"); return ipmi::responseUnspecifiedError(); } // sending the success response. return ipmi::responseSuccess(resp); } [[maybe_unused]] static void registerBICFunctions(void) { phosphor::logging::log( "Registering BIC commands"); ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemFive, static_cast(fb_bic_cmds::CMD_OEM_BIC_INFO), ipmi::Privilege::User, ipmiOemBicHandler); ipmi::registerHandler( ipmi::prioOpenBmcBase, ipmi::netFnOemFive, static_cast(fb_bic_cmds::CMD_OEM_SEND_POST_BUFFER_TO_BMC), ipmi::Privilege::User, ipmiOemPostCodeHandler); ipmi::registerHandler( ipmi::prioOpenBmcBase, ipmi::netFnOemFive, static_cast(fb_bic_cmds::CMD_OEM_1S_4BYTE_POST_BUF), ipmi::Privilege::User, ipmiOemPostCodeHandler); ipmi::registerHandler( ipmi::prioOemBase, ipmi::netFnOemFive, static_cast(fb_bic_cmds::CMD_OEM_GET_BIC_GPIO_STATE), ipmi::Privilege::User, ipmiOemGetBicGpioState); ipmi::registerHandler( ipmi::prioOpenBmcBase, ipmi::netFnOemFive, static_cast(fb_bic_cmds::CMD_OEM_SET_HOST_POWER_STATE), ipmi::Privilege::User, ipmiOemSetHostPowerState); ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemFive, static_cast(fb_bic_cmds::CMD_OEM_GET_FLASH_SIZE), ipmi::Privilege::User, ipmiOemGetBiosFlashSize); ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemFive, static_cast(fb_bic_cmds::CMD_OEM_CLEAR_CMOS), ipmi::Privilege::User, ipmiOemClearCmos); return; } } // namespace ipmi