blob: ed3f6f83800216dead1eca5f40b04f2f4681e447 [file] [log] [blame]
/*
* 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 <biccommands.hpp>
#include <commandutils.hpp>
#include <ipmid/api-types.hpp>
#include <ipmid/api.hpp>
#include <phosphor-logging/log.hpp>
#include <types.hpp>
#include <iostream>
#include <variant>
#include <vector>
namespace ipmi
{
int sendBicCmd(uint8_t, uint8_t, uint8_t, std::vector<uint8_t>&,
std::vector<uint8_t>&);
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<uint8_t>&,
std::vector<uint8_t>&);
constexpr std::array<uint8_t, 2> amdDimmLoopPrefix = {0xDD, 0xEE};
namespace dimm
{
std::unordered_map<hostId, dimmLoop> 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<IanaType, uint8_t, uint2_t, uint6_t, uint8_t, uint8_t,
ipmi::message::Payload>
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<ipmi::message::Request>(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<uint8_t> 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<uint8_t, 2> 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<uint8_t, 4> 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 incomming postcode from multi-host for
// netfn=0x38 and cmd=0x08 or 0x33 send the response back to the sender.
//----------------------------------------------------------------------
ipmi::RspType<IanaType> ipmiOemPostCodeHandler(ipmi::Context::ptr ctx,
IanaType reqIana,
uint8_t dataLen,
std::vector<uint8_t> 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<uint64_t, std::vector<uint8_t>>;
std::string dbusObjStr = dbusObj + std::to_string((ctx->hostIdx + 1));
for (unsigned int index = 0; index < dataLen; index++)
{
uint64_t primaryPostCode = static_cast<uint64_t>(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 paramters to method call
method.append(dbusService, "Value",
std::variant<postcode_t>(postCode));
// Invoke method call function
auto reply = conn->call(method);
}
catch (std::exception& e)
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"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<IanaType, std::vector<uint8_t>>
ipmiOemGetBicGpioState(ipmi::Context::ptr ctx, std::vector<uint8_t> reqIana)
{
std::vector<uint8_t> respData;
if (std::equal(reqIana.begin(), reqIana.end(), iana.begin()) == false)
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"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<uint8_t> 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 incomming IPMI request for
// setting host current state for netfn=0x38 and cmd=0x0C
// send the response back to the sender.
//----------------------------------------------------------------------
ipmi::RspType<IanaType> ipmiOemSetHostPowerState(ipmi::Context::ptr ctx,
IanaType reqIana,
uint8_t status)
{
std::string targetUnit;
switch (static_cast<HostPowerState>(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<phosphor::logging::level::ERR>(
"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<phosphor::logging::level::ERR>(
"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<IanaType, flashSize>
ipmiOemGetBiosFlashSize(ipmi::Context::ptr ctx, IanaType ianaReq,
uint8_t target)
{
if (iana != ianaReq)
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"Invalid IANA ID length received");
return ipmi::responseReqDataLenInvalid();
}
std::vector<uint8_t> respData;
uint8_t bicAddr = (uint8_t)ctx->hostIdx << 2;
std::vector<uint8_t> 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<phosphor::logging::level::ERR>(
"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<phosphor::logging::level::ERR>(
"Invalid IANA ID received");
return ipmi::responseInvalidCommand();
}
flashSize flashResp;
std::vector<uint8_t>::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<IanaType> ipmiOemClearCmos(ipmi::Context::ptr ctx,
IanaType ianaReq)
{
if (iana != ianaReq)
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"Invalid request of IANA ID length received");
return ipmi::responseReqDataLenInvalid();
}
uint8_t bicAddr = (uint8_t)ctx->hostIdx << 2;
std::vector<uint8_t> respData;
std::vector<uint8_t> 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<phosphor::logging::level::ERR>(
"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<phosphor::logging::level::INFO>(
"Registering BIC commands");
ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemFive,
static_cast<Cmd>(fb_bic_cmds::CMD_OEM_BIC_INFO),
ipmi::Privilege::User, ipmiOemBicHandler);
ipmi::registerHandler(
ipmi::prioOpenBmcBase, ipmi::netFnOemFive,
static_cast<Cmd>(fb_bic_cmds::CMD_OEM_SEND_POST_BUFFER_TO_BMC),
ipmi::Privilege::User, ipmiOemPostCodeHandler);
ipmi::registerHandler(
ipmi::prioOpenBmcBase, ipmi::netFnOemFive,
static_cast<Cmd>(fb_bic_cmds::CMD_OEM_1S_4BYTE_POST_BUF),
ipmi::Privilege::User, ipmiOemPostCodeHandler);
ipmi::registerHandler(
ipmi::prioOemBase, ipmi::netFnOemFive,
static_cast<Cmd>(fb_bic_cmds::CMD_OEM_GET_BIC_GPIO_STATE),
ipmi::Privilege::User, ipmiOemGetBicGpioState);
ipmi::registerHandler(
ipmi::prioOpenBmcBase, ipmi::netFnOemFive,
static_cast<Cmd>(fb_bic_cmds::CMD_OEM_SET_HOST_POWER_STATE),
ipmi::Privilege::User, ipmiOemSetHostPowerState);
ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemFive,
static_cast<Cmd>(fb_bic_cmds::CMD_OEM_GET_FLASH_SIZE),
ipmi::Privilege::User, ipmiOemGetBiosFlashSize);
ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemFive,
static_cast<Cmd>(fb_bic_cmds::CMD_OEM_CLEAR_CMOS),
ipmi::Privilege::User, ipmiOemClearCmos);
return;
}
} // namespace ipmi