blob: 83b895927834d926ed2850094af054d090ef5149 [file] [log] [blame]
/*
// Copyright (c) 2020 Intel Corporation
//
// 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 "biosxml.hpp"
#include <openssl/sha.h>
#include <biosconfigcommands.hpp>
#include <boost/crc.hpp>
#include <boost/process/child.hpp>
#include <boost/process/io.hpp>
#include <ipmid/api.hpp>
#include <ipmid/message.hpp>
#include <ipmid/message/types.hpp>
#include <ipmid/types.hpp>
#include <ipmid/utils.hpp>
#include <nlohmann/json.hpp>
#include <oemcommands.hpp>
#include <sdbusplus/bus.hpp>
#include <sdbusplus/message/types.hpp>
#include <filesystem>
#include <string_view>
namespace ipmi
{
static bool flushNVOOBdata();
static void registerBIOSConfigFunctions() __attribute__((constructor));
// Define BIOS config related Completion Code
using Cc = uint8_t;
static constexpr Cc ipmiCCPayloadPayloadPacketMissed = 0x80;
static constexpr Cc ipmiCCBIOSPasswordInitNotDone = 0x80;
static constexpr Cc ipmiCCPayloadChecksumFailed = 0x81;
static constexpr Cc ipmiCCNotSupportedInCurrentState = 0x82;
static constexpr Cc ipmiCCPayloadPayloadInComplete = 0x83;
static constexpr Cc ipmiCCBIOSCapabilityInitNotDone = 0x85;
static constexpr Cc ipmiCCPayloadLengthIllegal = 0x85;
static constexpr uint8_t userPasswordChanged = (1 << 5);
static constexpr uint8_t adminPasswordChanged = (1 << 4);
static constexpr uint8_t restoreDefaultValues = (1 << 7);
static constexpr const char* biosConfigFolder = "/var/oob";
static constexpr const char* biosConfigNVPath = "/var/oob/nvoobdata.dat";
static constexpr const uint8_t algoSHA384 = 2;
static constexpr const uint8_t algoSHA256 = 1;
static constexpr const uint8_t biosCapOffsetBit = 0x3;
static constexpr uint16_t maxGetPayloadDataSize = 4096;
static constexpr const char* biosXMLFilePath = "/var/oob/bios.xml";
static constexpr const char* biosXMLFilePath1 = "/var/oob/tempbios.xml";
static constexpr const char* biosConfigBaseMgrPath =
"/xyz/openbmc_project/bios_config/manager";
static constexpr const char* biosConfigIntf =
"xyz.openbmc_project.BIOSConfig.Manager";
static constexpr const char* resetBIOSSettingsProp = "ResetBIOSSettings";
/*baseBIOSTable
map{attributeName,struct{attributeType,readonlyStatus,displayname,
description,menuPath,current,default,
array{struct{optionstring,optionvalue}}}}
*/
bios::BiosBaseTableType attributesData;
NVOOBdata gNVOOBdata;
enum class PTState : uint8_t
{
StartTransfer = 0,
InProgress = 1,
EndTransfer = 2,
UserAbort = 3
};
enum class PStatus : uint8_t
{
Unknown = 0,
Valid = 1,
Corrupted = 2
};
enum class PType : uint8_t
{
IntelXMLType0 = 0,
IntelXMLType1 = 1,
OTAPayload = 5,
};
//
// GetPayload Payload status enumeration
//
enum class GetPayloadParameter : uint8_t
{
GetPayloadInfo = 0, // 0
GetPayloadData = 1, // 1
GetPayloadStatus = 2,
MaxPayloadParameters
};
namespace payload1
{
enum class AttributesType : uint8_t
{
unknown = 0,
string,
integer,
enumeration
};
using PendingAttributesType =
std::map<std::string,
std::tuple<std::string, std::variant<int64_t, std::string>>>;
AttributesType getAttrType(const std::string_view typeDbus)
{
if (typeDbus == "xyz.openbmc_project.BIOSConfig.Manager."
"AttributeType.String")
{
return AttributesType::string;
}
else if (typeDbus == "xyz.openbmc_project.BIOSConfig."
"Manager.AttributeType.Integer")
{
return AttributesType::integer;
}
else if (typeDbus == "xyz.openbmc_project.BIOSConfig."
"Manager.AttributeType.Enumeration")
{
return AttributesType::enumeration;
}
return AttributesType::unknown;
}
bool fillPayloadData(std::string& payloadData,
const std::variant<int64_t, std::string>& attributes,
const std::string_view key, AttributesType& attrType)
{
payloadData += key;
payloadData += '=';
if (attrType == AttributesType::string ||
attrType == AttributesType::enumeration)
{
if (!std::holds_alternative<std::string>(attributes))
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"fillPayloadData: No string data in attributes");
return false;
}
payloadData += std::get<std::string>(attributes);
}
else if (attrType == AttributesType::integer)
{
if (!std::holds_alternative<int64_t>(attributes))
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"fillPayloadData: No int64_t data in attributes");
return false;
}
payloadData += std::to_string(std::get<int64_t>(attributes));
}
else
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"fillPayloadData: Unsupported attribute type");
return false;
}
payloadData += '\n';
return true;
}
bool getPendingList(ipmi::Context::ptr ctx, std::string& payloadData)
{
std::variant<PendingAttributesType> pendingAttributesData;
boost::system::error_code ec;
payloadData.clear();
auto dbus = getSdBus();
if (!dbus)
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"getPendingList: getSdBus() failed");
return false;
}
std::string service =
getService(*dbus, biosConfigIntf, biosConfigBaseMgrPath);
try
{
pendingAttributesData =
dbus->yield_method_call<std::variant<PendingAttributesType>>(
ctx->yield, ec, service,
"/xyz/openbmc_project/bios_config/manager",
"org.freedesktop.DBus.Properties", "Get",
"xyz.openbmc_project.BIOSConfig.Manager", "PendingAttributes");
}
catch (const std::exception& ex)
{
phosphor::logging::log<phosphor::logging::level::ERR>(ex.what());
return false;
}
if (ec)
{
std::string err = "getPendingList: error while trying to get "
"PendingAttributes, error = ";
err += ec.message();
phosphor::logging::log<phosphor::logging::level::ERR>(err.c_str());
return false;
}
const PendingAttributesType* pendingAttributes =
std::get_if<PendingAttributesType>(&pendingAttributesData);
if (!pendingAttributes)
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"getPendingList: pendingAttributes is null");
return false;
}
for (const auto& [key, attributes] : *pendingAttributes)
{
const std::string& itemType = std::get<0>(attributes);
AttributesType attrType = getAttrType(itemType);
if (!fillPayloadData(payloadData, std::get<1>(attributes), key,
attrType))
{
return false;
}
}
if (payloadData.empty())
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"getPendingList: payloadData is empty");
return false;
}
return true;
}
bool updatePayloadFile(std::string& payloadFilePath, std::string payloadData)
{
std::ofstream payloadFile(payloadFilePath,
std::ios::out | std::ios::binary);
payloadFile << payloadData;
if (!payloadFile.good())
{
return false;
}
return true;
}
bool computeCheckSum(std::string& payloadFilePath,
boost::crc_32_type& calcChecksum)
{
std::ifstream payloadFile(payloadFilePath.c_str(),
std::ios::in | std::ios::binary | std::ios::ate);
if (!payloadFile.good())
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"computeCheckSum: Cannot open Payload1 file");
return false;
}
payloadFile.seekg(0, payloadFile.end);
int length = payloadFile.tellg();
payloadFile.seekg(0, payloadFile.beg);
if (maxGetPayloadDataSize < length)
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"computeCheckSum: length > maxGetPayloadDataSize");
return false;
}
std::unique_ptr<char[]> payloadBuffer(new char[length]);
payloadFile.read(payloadBuffer.get(), length);
uint32_t readCount = payloadFile.gcount();
calcChecksum.process_bytes(payloadBuffer.get(), readCount);
return true;
}
bool updatePayloadInfo(std::string& payloadFilePath)
{
boost::crc_32_type calcChecksum;
uint8_t payloadType = static_cast<uint8_t>(ipmi::PType::IntelXMLType1);
auto& payloadInfo = gNVOOBdata.payloadInfo[payloadType];
if (!computeCheckSum(payloadFilePath, calcChecksum))
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"updatePayloadInfo: Cannot compute checksum for Payload1 file");
return false;
}
payloadInfo.payloadVersion = 0;
payloadInfo.payloadflag = 0;
payloadInfo.payloadReservationID = rand();
payloadInfo.payloadType = payloadType;
payloadInfo.payloadTotalChecksum = calcChecksum.checksum();
payloadInfo.payloadCurrentChecksum = payloadInfo.payloadTotalChecksum;
payloadInfo.payloadStatus = (static_cast<uint8_t>(ipmi::PStatus::Valid));
struct stat filestat;
/* Get entry's information. */
if (!stat(payloadFilePath.c_str(), &filestat))
{
payloadInfo.payloadTimeStamp = filestat.st_mtime;
payloadInfo.payloadTotalSize = filestat.st_size;
payloadInfo.payloadCurrentSize = filestat.st_size;
payloadInfo.actualTotalPayloadWritten = filestat.st_size;
}
else
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"updatePayloadInfo: Cannot get file status for Payload1 file");
return false;
}
if (!flushNVOOBdata())
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"updatePayloadInfo: flushNVOOBdata failed");
return false;
}
return true;
}
bool update(ipmi::Context::ptr ctx)
{
std::string payloadFilePath =
"/var/oob/Payload" +
std::to_string(static_cast<uint8_t>(ipmi::PType::IntelXMLType1));
std::string payloadData;
if (!getPendingList(ctx, payloadData))
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"payload1::update : getPendingList() failed");
return false;
}
if (!updatePayloadFile(payloadFilePath, payloadData))
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"payload1::update : updatePayloadFile() failed");
return false;
}
if (!updatePayloadInfo(payloadFilePath))
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"payload1::update : updatePayloadInfo() failed");
return false;
}
return true;
}
} // namespace payload1
/** @brief implement to set the BaseBIOSTable property
* @returns status
*/
static bool sendAllAttributes(std::string service)
{
std::shared_ptr<sdbusplus::asio::connection> pSdBusPlus = getSdBus();
if (pSdBusPlus)
{
try
{
pSdBusPlus->async_method_call(
[](const boost::system::error_code ec) {
/* No more need to keep attributes data in memory */
attributesData.clear();
if (ec)
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"sendAllAttributes error: send all attributes - "
"failed");
return;
}
phosphor::logging::log<phosphor::logging::level::INFO>(
"sendAllAttributes: send all attributes - done");
},
service, biosConfigBaseMgrPath,
"org.freedesktop.DBus.Properties", "Set", biosConfigIntf,
"BaseBIOSTable",
std::variant<bios::BiosBaseTableType>(attributesData));
return true;
}
catch (const std::exception& ex)
{
phosphor::logging::log<phosphor::logging::level::ERR>(ex.what());
}
}
return false;
}
/** @brief implement to flush the updated data in nv space
* @returns status
*/
static bool flushNVOOBdata()
{
std::ofstream outFile(biosConfigNVPath, std::ios::binary);
outFile.seekp(std::ios_base::beg);
const char* writedata = reinterpret_cast<const char*>(&gNVOOBdata);
outFile.write(writedata, sizeof(struct NVOOBdata));
if (!outFile.good())
{
return false;
}
return true;
}
/** @brief implement to get the System State
* @returns status
*/
static bool getPostCompleted()
{
/*
* In case of failure we treat postCompleted as true.
* So that BIOS config commands is not accepted by BMC by mistake.
*/
bool postCompleted = true;
try
{
std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Value variant =
getDbusProperty(*dbus, "xyz.openbmc_project.State.OperatingSystem",
"/xyz/openbmc_project/state/os",
"xyz.openbmc_project.State.OperatingSystem.Status",
"OperatingSystemState");
auto& value = std::get<std::string>(variant);
// The short strings "Standby" is deprecated in favor of the
// full enum strings. Support for the short strings will be
// removed in the future.
postCompleted = (value == "Standby") ||
(value == "xyz.openbmc_project.State.OperatingSystem."
"Status.OSStatus.Standby");
}
catch (const std::exception& e)
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"'getDbusProperty' failed to read "
"xyz.openbmc_project.State.OperatingSystem");
}
return postCompleted;
}
/** @brief implement to get the Rest BIOS property
* @returns status
*/
static int getResetBIOSSettings(uint8_t& ResetFlag)
{
try
{
std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
std::string service =
getService(*dbus, biosConfigIntf, biosConfigBaseMgrPath);
Value variant = getDbusProperty(*dbus, service, biosConfigBaseMgrPath,
biosConfigIntf, resetBIOSSettingsProp);
std::string_view ResetStr = std::get<std::string>(variant);
if (ResetStr ==
"xyz.openbmc_project.BIOSConfig.Manager.ResetFlag.NoAction")
{
ResetFlag = 0;
}
else if (ResetStr == "xyz.openbmc_project.BIOSConfig.Manager.ResetFlag."
"FactoryDefaults")
{
ResetFlag = 1;
}
else if (ResetStr == "xyz.openbmc_project.BIOSConfig.Manager.ResetFlag."
"FailSafeDefaults")
{
ResetFlag = 2;
}
else
{
return ipmi::ccUnspecifiedError;
}
return ipmi::ccSuccess;
}
catch (const std::exception& e)
{
return ipmi::ccUnspecifiedError;
}
}
/** @brief Get attributes data (bios base table) from bios.xml
*/
static bool generateAttributesData()
{
try
{
bios::Xml biosxml(biosXMLFilePath);
if (!biosxml.doDepexCompute())
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"'depex' compute failed");
}
if (!biosxml.getBaseTable(attributesData))
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"Failed to get bios base table");
}
}
catch (const std::exception& ex)
{
phosphor::logging::log<phosphor::logging::level::ERR>(ex.what());
return false;
}
return true;
}
/** @brief Generate attributes data from bios.xml
* and send attributes data (bios base table) to dbus using set method.
*/
static void generateAndSendAttributesData(std::string service,
uint8_t payloadType)
{
if (!generateAttributesData())
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"generateAndSendAttributesData: generateAttributesData - failed");
gNVOOBdata.payloadInfo[payloadType].payloadStatus =
static_cast<uint8_t>(ipmi::PStatus::Corrupted);
return;
}
phosphor::logging::log<phosphor::logging::level::INFO>(
"generateAndSendAttributesData : generateAttributesData is done");
if (!sendAllAttributes(service))
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"generateAndSendAttributesData: sendAllAttributes - failed");
gNVOOBdata.payloadInfo[payloadType].payloadStatus =
static_cast<uint8_t>(ipmi::PStatus::Corrupted);
return;
}
phosphor::logging::log<phosphor::logging::level::INFO>(
"generateAndSendAttributesData : sendAllAttributes is done");
gNVOOBdata.payloadInfo[payloadType].payloadStatus =
static_cast<uint8_t>(ipmi::PStatus::Valid);
}
/** @brief implement executing the linux command to uncompress and generate the
* xmlfile
* @param[in] linux command
* @returns status
*/
template <typename... ArgTypes>
static int generateBIOSXMLFile(const char* path, ArgTypes&&... tArgs)
{
boost::process::child execProg(path, const_cast<char*>(tArgs)...,
boost::process::std_out > biosXMLFilePath);
execProg.wait();
return execProg.exit_code();
}
/** @brief implements to clean up the temp/ existing payload file
**/
static void cleanUpPayloadFile(uint8_t& payloadType)
{
// Clear the payload Information
std::string FilePath = "/var/oob/temp" + std::to_string(payloadType);
unlink(FilePath.c_str());
FilePath = "/var/oob/Payload" + std::to_string(payloadType);
unlink(FilePath.c_str());
if (payloadType == static_cast<uint8_t>(ipmi::PType::IntelXMLType0))
{
unlink("/var/oob/Payload1");
gNVOOBdata.payloadInfo[static_cast<uint8_t>(ipmi::PType::IntelXMLType1)]
.payloadStatus = static_cast<uint8_t>(ipmi::PStatus::Unknown);
}
}
/** @brief implements to create the oob folders and nv space
**/
static Cc InitNVOOBdata()
{
FILE* fptr;
uint16_t size;
if (!(std::filesystem::exists(biosConfigFolder)))
{
std::filesystem::create_directory(biosConfigFolder);
}
std::ifstream ifs(biosConfigNVPath, std::ios::in | std::ios::binary);
if (ifs.good())
{
ifs.seekg(std::ios_base::beg);
ifs.read(reinterpret_cast<char*>(&gNVOOBdata),
sizeof(struct NVOOBdata));
ifs.close();
return ipmi::ccSuccess;
}
return ipmi::ccResponseError;
}
ipmi::RspType<> ipmiOEMSetBIOSCap(ipmi::Context::ptr ctx,
uint8_t BIOSCapabilties, uint8_t reserved1,
uint8_t reserved2, uint8_t reserved3)
{
if (getPostCompleted())
{
return ipmi::response(ipmiCCNotSupportedInCurrentState);
}
if (reserved1 != 0 || reserved2 != 0 || reserved3 != 0)
{
return ipmi::responseInvalidFieldRequest();
}
gNVOOBdata.mBIOSCapabilities.OOBCapability = BIOSCapabilties;
gNVOOBdata.mIsBIOSCapInitDone = true;
flushNVOOBdata();
return ipmi::responseSuccess();
}
ipmi::RspType<uint8_t, uint8_t, uint8_t, uint8_t>
ipmiOEMGetBIOSCap(ipmi::Context::ptr ctx)
{
if (gNVOOBdata.mIsBIOSCapInitDone)
{
return ipmi::responseSuccess(gNVOOBdata.mBIOSCapabilities.OOBCapability,
0, 0, 0);
}
else
{
return ipmi::response(ipmiCCBIOSCapabilityInitNotDone);
}
}
ipmi::RspType<uint32_t> ipmiOEMSetPayload(ipmi::Context::ptr ctx,
uint8_t paramSel, uint8_t payloadType,
std::vector<uint8_t> payload)
{
uint8_t biosCapOffsetBit = 2; // BIT:1 0-OOB BIOS config not supported
// 1-OOB BIOS config is supported
if (!(gNVOOBdata.mBIOSCapabilities.OOBCapability & (biosCapOffsetBit)))
{
return ipmi::response(ipmiCCBIOSCapabilityInitNotDone);
}
// Validate the Payload Type
if (payloadType > maxPayloadSupported)
{
return ipmi::responseInvalidFieldRequest();
}
// We should support this Payload type 0 command only in KCS Interface
if (payloadType == static_cast<uint8_t>(ipmi::PType::IntelXMLType0))
{
if (getPostCompleted())
{
return ipmi::response(ipmiCCNotSupportedInCurrentState);
}
}
switch (static_cast<PTState>(paramSel))
{
case ipmi::PTState::StartTransfer:
{
PayloadStartTransfer* pPayloadStartTransfer =
reinterpret_cast<PayloadStartTransfer*>(payload.data());
if (payload.size() < sizeof(PayloadStartTransfer))
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"ipmiOEMSetPayload: BIOS Config Payload size is not "
"correct");
return ipmi::responseReqDataLenInvalid();
}
cleanUpPayloadFile(payloadType);
gNVOOBdata.payloadInfo[payloadType].payloadReservationID = rand();
gNVOOBdata.payloadInfo[payloadType].payloadTotalChecksum =
pPayloadStartTransfer->payloadTotalChecksum;
gNVOOBdata.payloadInfo[payloadType].payloadTotalSize =
pPayloadStartTransfer->payloadTotalSize;
gNVOOBdata.payloadInfo[payloadType].payloadVersion =
pPayloadStartTransfer->payloadVersion;
gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten = 0;
gNVOOBdata.payloadInfo[payloadType].payloadStatus =
static_cast<uint8_t>(ipmi::PStatus::Unknown);
gNVOOBdata.payloadInfo[payloadType].payloadType = payloadType;
return ipmi::responseSuccess(
gNVOOBdata.payloadInfo[payloadType].payloadReservationID);
}
break;
case ipmi::PTState::InProgress:
{
PayloadInProgress* pPayloadInProgress =
reinterpret_cast<PayloadInProgress*>(payload.data());
PayloadInfo payloadInfo = gNVOOBdata.payloadInfo[payloadType];
if (payload.size() < sizeof(PayloadInProgress))
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"BIOS Config Payload size is not correct");
return ipmi::responseReqDataLenInvalid();
}
if (pPayloadInProgress->payloadReservationID !=
payloadInfo.payloadReservationID)
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"BIOS Config Payload reservation ID is not correct");
return ipmi::responseInvalidReservationId();
}
payloadInfo.payloadCurrentSize =
pPayloadInProgress->payloadCurrentSize;
// Need to verify the current Payload Checksum
const uint8_t* data =
reinterpret_cast<const uint8_t*>(payload.data());
// we have to remove the current size, current offset, current
// length,checksum bytes , reservation bytes
boost::crc_32_type calcChecksum;
calcChecksum.process_bytes(data + 16, payload.size() - 16);
if (calcChecksum.checksum() !=
pPayloadInProgress->payloadCurrentChecksum)
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"ipmiOEMSetPayload: Payload Checksum Failed");
return ipmi::response(ipmiCCPayloadChecksumFailed);
}
// store the data in temp file
std::string FilePath =
"/var/oob/temp" + std::to_string(payloadType);
std::ofstream outFile(FilePath, std::ios::binary | std::ios::app);
outFile.seekp(pPayloadInProgress->payloadOffset);
// we have to remove the current size, current offset, current
// length,checksum bytes , reservation bytes
const char* writedata =
reinterpret_cast<const char*>(payload.data());
outFile.write(writedata + 16, payload.size() - 16);
outFile.close();
gNVOOBdata.payloadInfo[payloadType].payloadStatus =
static_cast<uint8_t>(ipmi::PStatus::Unknown);
gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten +=
payloadInfo.payloadCurrentSize;
return ipmi::responseSuccess(payloadInfo.payloadCurrentSize);
}
break;
case ipmi::PTState::EndTransfer:
{
PayloadEndTransfer* pPayloadEndTransfer =
reinterpret_cast<PayloadEndTransfer*>(payload.data());
PayloadInfo payloadInfo = gNVOOBdata.payloadInfo[payloadType];
if (pPayloadEndTransfer->payloadReservationID !=
payloadInfo.payloadReservationID)
{
return ipmi::responseInvalidReservationId();
}
gNVOOBdata.payloadInfo[payloadType].payloadStatus =
static_cast<uint8_t>(ipmi::PStatus::Unknown);
if (gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten !=
gNVOOBdata.payloadInfo[payloadType].payloadTotalSize)
{
return ipmi::response(ipmiCCPayloadPayloadInComplete);
}
std::string tempFilePath =
"/var/oob/temp" + std::to_string(payloadType);
std::string payloadFilePath =
"/var/oob/Payload" + std::to_string(payloadType);
auto renamestatus =
std::rename(tempFilePath.c_str(), payloadFilePath.c_str());
if (renamestatus)
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"ipmiOEMSetPayload: Renaming Payload file - failed");
}
if (payloadType == static_cast<uint8_t>(ipmi::PType::IntelXMLType0))
{
// Unzip the Intel format XML file type 0
auto response = generateBIOSXMLFile("/usr/bin/lzcat", "-d",
payloadFilePath.c_str());
if (response)
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"ipmiOEMSetPayload: generateBIOSXMLFile - failed");
gNVOOBdata.payloadInfo[payloadType].payloadStatus =
static_cast<uint8_t>(ipmi::PStatus::Corrupted);
return ipmi::response(ipmiCCPayloadPayloadPacketMissed);
}
phosphor::logging::log<phosphor::logging::level::INFO>(
" ipmiOEMSetPayload : Convert XML into native-dbus DONE");
/* So that we don't block the call */
auto io = getIoContext();
auto dbus = getSdBus();
if (io && dbus)
{
std::string service = getService(*dbus, biosConfigIntf,
biosConfigBaseMgrPath);
boost::asio::post(*io, [service, payloadType] {
generateAndSendAttributesData(service, payloadType);
});
}
else
{
phosphor::logging::log<phosphor::logging::level::INFO>(
"ipmiOEMSetPayload: Unable to get io context or sdbus");
return ipmi::responseResponseError();
}
}
struct stat filestat;
/* Get entry's information. */
if (!stat(payloadFilePath.c_str(), &filestat))
{
gNVOOBdata.payloadInfo[payloadType].payloadTimeStamp =
filestat.st_mtime;
gNVOOBdata.payloadInfo[payloadType].payloadTotalSize =
filestat.st_size;
gNVOOBdata.payloadInfo[payloadType].payloadStatus =
static_cast<uint8_t>(ipmi::PStatus::Valid);
}
else
{
return ipmi::responseResponseError();
}
flushNVOOBdata();
return ipmi::responseSuccess(
gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten);
}
break;
case ipmi::PTState::UserAbort:
{
PayloadEndTransfer* pPayloadEndTransfer =
reinterpret_cast<PayloadEndTransfer*>(payload.data());
PayloadInfo payloadInfo = gNVOOBdata.payloadInfo[payloadType];
if (pPayloadEndTransfer->payloadReservationID !=
payloadInfo.payloadReservationID)
{
return ipmi::responseInvalidReservationId();
}
gNVOOBdata.payloadInfo[payloadType].payloadReservationID = 0;
gNVOOBdata.payloadInfo[payloadType].payloadType = 0;
gNVOOBdata.payloadInfo[payloadType].payloadTotalSize = 0;
// Delete the temp file
std::string tempFilePath =
"/var/oob/temp" + std::to_string(payloadType);
unlink(tempFilePath.c_str());
flushNVOOBdata();
return ipmi::responseSuccess();
}
break;
default:
return ipmi::responseInvalidFieldRequest();
}
return ipmi::responseResponseError();
}
ipmi::RspType<message::Payload>
ipmiOEMGetPayload(ipmi::Context::ptr ctx, uint8_t paramSel,
uint8_t payloadType, ipmi::message::Payload& payload)
{
// 1-OOB BIOS config is supported
message::Payload retValue;
if (static_cast<GetPayloadParameter>(paramSel) >=
ipmi::GetPayloadParameter::MaxPayloadParameters)
{
return ipmi::responseInvalidFieldRequest();
}
if (!(gNVOOBdata.mBIOSCapabilities.OOBCapability & (biosCapOffsetBit)))
{
return ipmi::response(ipmiCCBIOSCapabilityInitNotDone);
}
// Validate the Payload Type
if (payloadType > maxPayloadSupported)
{
return ipmi::responseInvalidFieldRequest();
}
if (payloadType == static_cast<uint8_t>(ipmi::PType::IntelXMLType1))
{
if (!payload1::update(ctx))
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"ipmiOEMGetPayload: unable to update NVOOBdata for payloadType "
"= IntelXMLType1");
return ipmi::response(ipmi::ccUnspecifiedError);
}
}
struct PayloadInfo res = gNVOOBdata.payloadInfo[payloadType];
switch (static_cast<GetPayloadParameter>(paramSel))
{
case ipmi::GetPayloadParameter::GetPayloadInfo:
{
std::string payloadFilePath =
"/var/oob/Payload" + std::to_string(payloadType);
std::ifstream ifs(payloadFilePath,
std::ios::in | std::ios::binary | std::ios::ate);
if (!ifs.good())
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"ipmiOEMGetPayload: Payload File Error");
// File does not exist code here
return ipmi::response(ipmi::ccUnspecifiedError);
}
ifs.close();
retValue.pack(res.payloadVersion);
retValue.pack(payloadType);
retValue.pack(res.payloadTotalSize);
retValue.pack(res.payloadTotalChecksum);
retValue.pack(res.payloadflag);
retValue.pack(res.payloadStatus);
retValue.pack(res.payloadTimeStamp);
return ipmi::responseSuccess(std::move(retValue));
}
break;
case ipmi::GetPayloadParameter::GetPayloadData:
{
if (res.payloadStatus ==
(static_cast<uint8_t>(ipmi::PStatus::Valid)))
{
std::vector<uint32_t> reqData;
if (payload.unpack(reqData) || !payload.fullyUnpacked())
{
return ipmi::responseReqDataLenInvalid();
}
uint32_t offset = reqData.at(0);
uint32_t length = reqData.at(1);
std::string payloadFilePath =
"/var/oob/Payload" + std::to_string(payloadType);
if (length > static_cast<uint32_t>(maxGetPayloadDataSize))
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"ipmiOEMGetPayload: length > maxGetPayloadDataSize",
phosphor::logging::entry("LENGTH=%d", length),
phosphor::logging::entry("maxGetPayloadDataSize=%d",
maxGetPayloadDataSize));
return ipmi::responseInvalidFieldRequest();
}
std::ifstream ifs(payloadFilePath, std::ios::in |
std::ios::binary |
std::ios::ate);
if (!ifs.good())
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"ipmiOEMGetPayload: Payload File Error");
// File does not exist code here
return ipmi::response(ipmi::ccUnspecifiedError);
}
std::ifstream::pos_type fileSize = ifs.tellg();
// Total file data within given offset
if (fileSize < static_cast<uint64_t>(offset))
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"ipmiOEMGetPayload: filesize < offset");
return ipmi::responseInvalidFieldRequest();
}
if ((static_cast<uint64_t>(fileSize) - offset) < length)
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"ipmiOEMGetPayload: (filesize - offset) < length ");
return ipmi::responseInvalidFieldRequest();
}
ifs.seekg(offset, std::ios::beg);
std::array<uint8_t, maxGetPayloadDataSize> Buffer;
ifs.read(reinterpret_cast<char*>(Buffer.data()), length);
uint32_t readCount = ifs.gcount();
ifs.close();
boost::crc_32_type calcChecksum;
calcChecksum.process_bytes(
reinterpret_cast<char*>(Buffer.data()), readCount);
uint32_t chkSum = calcChecksum.checksum();
retValue.pack(payloadType);
retValue.pack(readCount);
retValue.pack(chkSum);
for (int i = 0; i < readCount; i++)
{
retValue.pack(Buffer.at(i));
}
return ipmi::responseSuccess(std::move(retValue));
}
else
{
return ipmi::responseResponseError();
}
}
break;
case ipmi::GetPayloadParameter::GetPayloadStatus:
{
retValue.pack(gNVOOBdata.payloadInfo[payloadType].payloadStatus);
return ipmi::responseSuccess(std::move(retValue));
}
break;
default:
return ipmi::responseInvalidFieldRequest();
}
return ipmi::responseInvalidFieldRequest();
}
ipmi::RspType<> ipmiOEMSetBIOSHashInfo(
ipmi::Context::ptr ctx, std::array<uint8_t, maxSeedSize>& pwdSeed,
uint8_t algoInfo, std::array<uint8_t, maxHashSize>& adminPwdHash)
{
// We should not support this command after System Booted - After Exit Boot
// service called
if (getPostCompleted())
{
return ipmi::response(ipmiCCNotSupportedInCurrentState);
}
nlohmann::json json;
if ((algoInfo & 0xF) == algoSHA384)
{
json["HashAlgo"] = "SHA384";
}
else if ((algoInfo & 0xF) == algoSHA256)
{
json["HashAlgo"] = "SHA256";
}
else
{
return ipmi::responseInvalidFieldRequest();
}
json["Seed"] = pwdSeed;
json["IsAdminPwdChanged"] = false;
json["AdminPwdHash"] = adminPwdHash;
json["IsUserPwdChanged"] = false;
std::array<uint8_t, maxHashSize> userPwdHash;
userPwdHash.fill({}); // initializing with 0 as user password hash field
// is not used presently
json["UserPwdHash"] = userPwdHash;
json["StatusFlag"] = algoInfo;
std::string hashFilePath = "/var/lib/bios-settings-manager/seedData";
std::ofstream ofs(hashFilePath, std::ios::out);
const auto& writeData = json.dump();
ofs << writeData;
ofs.close();
return ipmi::responseSuccess();
}
ipmi::RspType<std::array<uint8_t, maxSeedSize>, uint8_t,
std::array<uint8_t, maxHashSize>>
ipmiOEMGetBIOSHash(ipmi::Context::ptr ctx)
{
nlohmann::json data = nullptr;
// We should not support this command after System Booted - After Exit Boot
// service called
if (getPostCompleted())
{
return ipmi::response(ipmiCCNotSupportedInCurrentState);
}
std::string HashFilePath = "/var/lib/bios-settings-manager/seedData";
std::ifstream devIdFile(HashFilePath);
if (!devIdFile.is_open())
{
return ipmi::responseResponseError();
}
try
{
data = nlohmann::json::parse(devIdFile, nullptr, false);
}
catch (const nlohmann::json::parse_error& e)
{
return ipmi::responseResponseError();
}
if (data.is_discarded())
{
return ipmi::responseResponseError();
}
std::array<uint8_t, maxHashSize> newAdminHash;
std::array<uint8_t, maxSeedSize> seed;
uint8_t flag = 0;
uint8_t adminPwdChangedFlag = 0;
if (!data.is_discarded())
{
adminPwdChangedFlag = data["IsAdminPwdChanged"];
newAdminHash = data["AdminPwdHash"];
seed = data["Seed"];
}
auto status = getResetBIOSSettings(flag);
if (status)
{
return ipmi::responseResponseError();
}
if (flag)
{
flag |= restoreDefaultValues;
}
if (adminPwdChangedFlag)
{
flag |= adminPasswordChanged;
}
std::copy(std::begin(newAdminHash), std::end(newAdminHash),
std::begin(newAdminHash));
return ipmi::responseSuccess(seed, flag, newAdminHash);
}
static void registerBIOSConfigFunctions(void)
{
phosphor::logging::log<phosphor::logging::level::INFO>(
"BIOSConfig module initialization");
InitNVOOBdata();
registerHandler(prioOemBase, intel::netFnGeneral,
intel::general::cmdSetBIOSCap, Privilege::Admin,
ipmiOEMSetBIOSCap);
registerHandler(prioOemBase, intel::netFnGeneral,
intel::general::cmdGetBIOSCap, Privilege::User,
ipmiOEMGetBIOSCap);
registerHandler(prioOemBase, intel::netFnGeneral,
intel::general::cmdSetBIOSPwdHashInfo, Privilege::Admin,
ipmiOEMSetBIOSHashInfo);
registerHandler(prioOemBase, intel::netFnGeneral,
intel::general::cmdGetBIOSPwdHash, Privilege::User,
ipmiOEMGetBIOSHash);
registerHandler(prioOemBase, intel::netFnGeneral,
intel::general::cmdGetPayload, Privilege::User,
ipmiOEMGetPayload);
registerHandler(prioOemBase, intel::netFnGeneral,
intel::general::cmdSetPayload, Privilege::Admin,
ipmiOEMSetPayload);
return;
}
} // namespace ipmi