IPMI OEM Commands for OOB bios config
Four IPMI OEM commands are defined and implemented for the
communication beteween BIOS and BMC.
cmdSetBIOSCap = 0xD3;
cmdGetBIOSCap = 0xD4;
cmdSetPayload = 0xD5;
cmdGetPayload = 0xD6;
cmdSetPasswordHashInfo = 0xD7;
cmdGetStoredPasswordHash = 0xD8;
Added the OTA Payload Type
Tested:
1. ipmitool is working well for these 6 commands
2. passed unit test w/ bios and bios.xml is generated successfully
Change-Id: Ic318c18ca6d59c3ad6e10df9ffb2b22a38a55ddf
Signed-off-by: Kuiying Wang <kuiying.wang@intel.com>
Signed-off-by: Suryakanth Sekar <suryakanth.sekar@linux.intel.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 12ad5bc..4aa0f03 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -97,7 +97,7 @@
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
add_library (zinteloemcmds
- SHARED src/oemcommands.cpp src/sensorcommands.cpp
+ SHARED src/oemcommands.cpp src/sensorcommands.cpp src/biosconfigcommands.cpp
src/storagecommands.cpp src/multinodecommands.cpp
src/firmware-update.cpp src/appcommands.cpp src/smbioshandler.cpp
src/smbiosmdrv2handler.cpp src/manufacturingcommands.cpp
@@ -117,6 +117,9 @@
target_link_libraries (zinteloemcmds ${OPENSSL_CRYPTO_LIBRARY})
target_link_libraries (zinteloemcmds gpiodcxx)
+find_package (tinyxml2 REQUIRED)
+target_link_libraries (zinteloemcmds tinyxml2)
+
install (TARGETS zinteloemcmds DESTINATION lib/ipmid-providers)
option (INTEL_PFR_ENABLED "Intel PFR Enabled" OFF)
option (BMC_VALIDATION_UNSECURE_FEATURE
diff --git a/include/biosconfigcommands.hpp b/include/biosconfigcommands.hpp
new file mode 100644
index 0000000..253c357
--- /dev/null
+++ b/include/biosconfigcommands.hpp
@@ -0,0 +1,90 @@
+/*
+// 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.
+*/
+
+#pragma once
+
+#include <array>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <variant>
+#include <vector>
+
+constexpr uint8_t maxPayloadSupported = 0x6;
+constexpr uint8_t maxHashSize = 64;
+constexpr uint8_t maxSeedSize = 16;
+constexpr uint8_t maxPasswordSize = 64;
+
+#pragma pack(push, 1)
+struct PayloadStartTransfer
+{
+ uint16_t payloadVersion;
+ uint32_t payloadTotalSize;
+ uint32_t payloadTotalChecksum;
+ uint8_t payloadFlag;
+};
+#pragma pack(pop)
+
+struct PayloadInProgress
+{
+ uint32_t payloadReservationID;
+ uint32_t payloadOffset;
+ uint32_t payloadCurrentSize;
+ uint32_t payloadCurrentChecksum;
+};
+
+struct PayloadEndTransfer
+{
+ uint32_t payloadReservationID;
+};
+
+struct SetPayloadRetValue
+{
+ uint32_t reservationToken;
+ uint32_t actualPayloadWritten;
+ uint32_t actualTotalPayloadWritten;
+};
+
+struct setBIOSCapabilitiesReq
+{
+ uint8_t OOBCapability;
+ uint8_t reserved1;
+ uint8_t reserved2;
+ uint8_t reserved3;
+};
+
+struct PayloadInfo
+{
+ uint32_t payloadReservationID;
+ uint8_t payloadType;
+ uint16_t payloadVersion;
+ uint32_t payloadTotalSize;
+ uint32_t payloadTotalChecksum;
+ uint8_t payloadStatus;
+ uint8_t payloadflag;
+ uint32_t payloadTimeStamp;
+ uint32_t payloadCurrentSize;
+ uint32_t payloadCurrentChecksum;
+ uint32_t actualTotalPayloadWritten;
+ std::string payloadFilePath;
+};
+
+struct NVOOBdata
+{
+ setBIOSCapabilitiesReq mBIOSCapabilities;
+ uint8_t mIsBIOSCapInitDone;
+ PayloadInfo payloadInfo[maxPayloadSupported];
+};
diff --git a/include/oemcommands.hpp b/include/oemcommands.hpp
index 8cd8cf9..19d2bca 100644
--- a/include/oemcommands.hpp
+++ b/include/oemcommands.hpp
@@ -74,6 +74,12 @@
static constexpr Cmd cmdSetSecurityMode = 0xB4;
static constexpr Cmd cmdMtmKeepAlive = 0xB5;
static constexpr Cmd cmdOEMGetReading = 0xE2;
+static constexpr Cmd cmdSetBIOSCap = 0xD3;
+static constexpr Cmd cmdGetBIOSCap = 0xD4;
+static constexpr Cmd cmdSetPayload = 0xD5;
+static constexpr Cmd cmdGetPayload = 0xD6;
+static constexpr Cmd cmdSetBIOSPwdHashInfo = 0xD7;
+static constexpr Cmd cmdGetBIOSPwdHash = 0xD8;
static constexpr Cmd cmdGetNmiStatus = 0xE5;
static constexpr Cmd cmdSetEfiBootOptions = 0xEA;
static constexpr Cmd cmdGetEfiBootOptions = 0xEB;
diff --git a/src/biosconfigcommands.cpp b/src/biosconfigcommands.cpp
new file mode 100644
index 0000000..1c6d060
--- /dev/null
+++ b/src/biosconfigcommands.cpp
@@ -0,0 +1,902 @@
+/*
+// 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 <openssl/sha.h>
+#include <tinyxml2.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 <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/message/types.hpp>
+
+#include <filesystem>
+
+namespace ipmi
+{
+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 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 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}}}}
+*/
+std::map<std::string,
+ std::tuple<std::string, bool, std::string, std::string, std::string,
+ std::variant<int64_t, std::string>,
+ std::variant<int64_t, std::string>,
+ std::map<std::string, std::variant<int64_t, std::string>>>>
+ 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
+};
+
+/** @brief implement to set the BaseBIOSTable property
+ * @returns status
+ */
+static int sendAllAttributes(ipmi::Context::ptr ctx)
+{
+ boost::system::error_code ec;
+ std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
+ std::string service =
+ getService(*dbus, biosConfigIntf, biosConfigBaseMgrPath);
+ ec.clear();
+ ctx->bus->yield_method_call<>(
+ ctx->yield, ec, service, biosConfigBaseMgrPath,
+ "org.freedesktop.DBus.Properties", "Set", biosConfigIntf,
+ "BaseBIOSTable", AttributesData);
+ if (ec)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Failed to sendAllAttributes");
+ return -1;
+ }
+ return 0;
+}
+
+/** @brief implement to flush the updated data in nv space
+ * @returns status
+ */
+static uint8_t flushNVOOBdata()
+{
+ std::ofstream outFile(biosConfigNVPath, std::ios::binary | std::ios::app);
+ if (outFile.good())
+ {
+ outFile.seekp(std::ios_base::beg);
+ const char* writedata = reinterpret_cast<const char*>(&gNVOOBdata);
+ outFile.write(writedata, sizeof(struct NVOOBdata));
+ outFile.close();
+ }
+ return 0;
+}
+
+/** @brief implement to get the System State
+ * @returns status
+ */
+
+static int getSystemOSState(std::string& OsStatus)
+{
+
+ 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");
+ OsStatus = std::get<std::string>(variant);
+ return ipmi::ccSuccess;
+ }
+ catch (const std::exception& e)
+ {
+ return ipmi::ccUnspecifiedError;
+ }
+}
+
+/** @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);
+ ResetFlag = static_cast<uint8_t>(std::get<std::uint8_t>(variant));
+ return ipmi::ccSuccess;
+ }
+ catch (const std::exception& e)
+ {
+ return ipmi::ccUnspecifiedError;
+ }
+}
+
+/** @brief implement generate naive- dbus from XML file
+ * @returns status
+ */
+static int generateAttributesData()
+{
+ // Open the bios.xml and parse it
+ // Extract the needed data and store it in AttributesData variable
+ // Close the bios.xml
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "generateAttributesData");
+ tinyxml2::XMLDocument xmlDoc;
+
+ xmlDoc.LoadFile(biosXMLFilePath);
+ tinyxml2::XMLNode* pRoot = xmlDoc.FirstChild();
+ if (pRoot == nullptr)
+ {
+ return 0;
+ }
+ tinyxml2::XMLElement* pElement = pRoot->FirstChildElement("biosknobs");
+ if (pElement == nullptr)
+ {
+ return 0;
+ }
+ tinyxml2::XMLElement* pKnobsElement = pElement->FirstChildElement("knob");
+
+ while (pKnobsElement != nullptr)
+ {
+ bool readOnlyStatus = false;
+ std::string attrType =
+ "xyz.openbmc_project.BIOSConfig.Manager.AttributeType.String";
+ std::string name, curvalue, dname, menupath, defaultvalue, description;
+ name = pKnobsElement->Attribute("name")
+ ? pKnobsElement->Attribute("name")
+ : "";
+ curvalue = pKnobsElement->Attribute("CurrentVal")
+ ? pKnobsElement->Attribute("CurrentVal")
+ : "";
+ dname = pKnobsElement->Attribute("prompt")
+ ? pKnobsElement->Attribute("prompt")
+ : "";
+ menupath = pKnobsElement->Attribute("SetupPgPtr")
+ ? pKnobsElement->Attribute("SetupPgPtr")
+ : "";
+ defaultvalue = pKnobsElement->Attribute("default")
+ ? pKnobsElement->Attribute("default")
+ : "";
+ description = pKnobsElement->Attribute("description")
+ ? pKnobsElement->Attribute("description")
+ : "";
+ phosphor::logging::log<phosphor::logging::level::INFO>(name.c_str());
+ if (!name.empty() && !curvalue.empty() && !dname.empty() &&
+ !menupath.empty() && !defaultvalue.empty())
+ {
+ std::string rootPath = "./" + std::string(menupath);
+
+ std::map<std::string, std::variant<int64_t, std::string>> optionMap;
+ tinyxml2::XMLElement* pOptionsElement =
+ pKnobsElement->FirstChildElement("options");
+ nlohmann::json optionsArray = nlohmann::json::array();
+ if (pOptionsElement != nullptr)
+ {
+ tinyxml2::XMLElement* pOptionElement =
+ pOptionsElement->FirstChildElement("option");
+ while (pOptionElement != nullptr)
+ {
+ const std::string text = pOptionElement->Attribute("text");
+ const std::string attrValue =
+ pOptionElement->Attribute("value");
+ if (!text.empty() && !attrValue.empty())
+ {
+
+ optionMap.emplace(std::make_pair(std::move(text),
+ std::move(attrValue)));
+ }
+ pOptionElement =
+ pOptionElement->NextSiblingElement("option");
+ }
+ }
+
+ AttributesData.emplace(std::make_pair(
+ name,
+ std::make_tuple(attrType, readOnlyStatus, dname, description,
+ rootPath, curvalue, defaultvalue, optionMap)));
+ }
+ pKnobsElement = pKnobsElement->NextSiblingElement("knob");
+ }
+
+ return 0;
+}
+
+/** @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 | std::ios::ate);
+
+ if (ifs.good())
+ {
+
+ ifs.read(reinterpret_cast<char*>(&gNVOOBdata),
+ sizeof(struct NVOOBdata));
+ ifs.close();
+ return ipmi::ccSuccess;
+ }
+ return ipmi::ccResponseError;
+}
+
+/** @brief implements check the command interface is
+ ** system interface or not
+ ** true mean System interface and false mean LAN or IPMB
+ **/
+static bool IsSystemInterface(ipmi::Context::ptr ctx)
+{
+ ChannelInfo chInfo;
+ Cc status = false;
+
+ try
+ {
+ getChannelInfo(ctx->channel, chInfo);
+ }
+ catch (sdbusplus::exception_t& e)
+ {
+ return false;
+ }
+ if (chInfo.mediumType !=
+ static_cast<uint8_t>(EChannelMediumType::systemInterface))
+ {
+ return false;
+ }
+ return true;
+}
+
+ipmi::RspType<> ipmiOEMSetBIOSCap(ipmi::Context::ptr ctx,
+ uint8_t BIOSCapabilties, uint8_t reserved1,
+ uint8_t reserved2, uint8_t reserved3)
+{
+ std::string OSState;
+ getSystemOSState(OSState);
+
+ if (OSState != "OperatingState" && IsSystemInterface(ctx))
+ {
+ if (reserved1 != 0 || reserved2 != 0 || reserved3 != 0)
+ {
+ return ipmi::responseInvalidFieldRequest();
+ }
+
+ gNVOOBdata.mBIOSCapabilities.OOBCapability = BIOSCapabilties;
+ gNVOOBdata.mIsBIOSCapInitDone = true;
+
+ flushNVOOBdata();
+ return ipmi::responseSuccess();
+ }
+ else
+ {
+
+ return ipmi::response(ipmiCCNotSupportedInCurrentState);
+ }
+}
+
+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))
+ {
+ std::string OSState;
+
+ getSystemOSState(OSState);
+ if (!IsSystemInterface(ctx) || OSState == "OperatingState")
+ {
+ return ipmi::responseCommandNotAvailable();
+ }
+ }
+
+ 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);
+
+ 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)
+ {
+ 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");
+ }
+
+ gNVOOBdata.payloadInfo[payloadType].payloadFilePath =
+ PayloadFilePath;
+ 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);
+ }
+ }
+ gNVOOBdata.payloadInfo[payloadType].payloadStatus =
+ static_cast<uint8_t>(ipmi::PStatus::Valid);
+
+ 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].payloadFilePath =
+ PayloadFilePath;
+ }
+ else
+ {
+ return ipmi::responseResponseError();
+ }
+
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ " ipmiOEMSetPayload : Convert XML into native-dbus DONE");
+ generateAttributesData();
+
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ " ipmiOEMSetPayload : BaseBIOSTable Property is set");
+ sendAllAttributes(ctx);
+
+ 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 (!(gNVOOBdata.mBIOSCapabilities.OOBCapability & (biosCapOffsetBit)))
+ {
+ return ipmi::response(ipmiCCBIOSCapabilityInitNotDone);
+ }
+ // Validate the Payload Type
+ if (payloadType > maxPayloadSupported)
+ {
+ return ipmi::responseInvalidFieldRequest();
+ }
+
+ struct PayloadInfo res = gNVOOBdata.payloadInfo[payloadType];
+
+ switch (static_cast<GetPayloadParameter>(paramSel))
+ {
+ case ipmi::GetPayloadParameter::GetPayloadInfo:
+ {
+ retValue.pack(res.payloadVersion);
+ retValue.pack(res.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::ifstream ifs(res.payloadFilePath, std::ios::in |
+ std::ios::binary |
+ std::ios::ate);
+ std::ifstream::pos_type fileSize = ifs.tellg();
+ // Total file data within given offset
+ if (fileSize < static_cast<uint64_t>(offset))
+ {
+ ifs.close();
+ 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,
+ std::array<uint8_t, maxHashSize>& userPwdHash)
+{
+
+ std::string OSState;
+
+ // We should support this command only in KCS Interface
+ if (!IsSystemInterface(ctx))
+ {
+ return ipmi::responseCommandNotAvailable();
+ }
+ getSystemOSState(OSState);
+ // We should not support this command after System Booted - After Exit Boot
+ // service called
+
+ if (OSState != "OperatingState")
+ {
+
+ if (algoInfo != algoSHA384)
+ {
+ // Atpresent, we are supporting only SHA384- HASH algo in BIOS side
+ return ipmi::responseInvalidFieldRequest();
+ }
+ std::string HashFilePath = "/var/lib/bios-settings-manager/seedData";
+
+ nlohmann::json json;
+ json["Seed"] = pwdSeed;
+ json["HashAlgo"] = "SHA384";
+ json["IsAdminPwdChanged"] = false;
+ json["AdminPwdHash"] = adminPwdHash;
+ json["IsUserPwdChanged"] = false;
+ json["UserPwdHash"] = userPwdHash;
+ std::ofstream ofs(HashFilePath, std::ios::out);
+ const auto& writeData = json.dump();
+ ofs << writeData;
+ ofs.close();
+ return ipmi::responseSuccess();
+ }
+ else
+ {
+
+ return ipmi::response(ipmiCCNotSupportedInCurrentState);
+ }
+}
+
+ipmi::RspType<uint8_t, std::array<uint8_t, maxHashSize>,
+ std::array<uint8_t, maxHashSize>>
+ ipmiOEMGetBIOSHash(ipmi::Context::ptr ctx)
+{
+
+ std::string OSState;
+ nlohmann::json data = nullptr;
+
+ // We should support this command only in KCS Interface
+ if (!IsSystemInterface(ctx))
+ {
+ return ipmi::responseCommandNotAvailable();
+ }
+
+ getSystemOSState(OSState);
+ // We should not support this command after System Booted - After Exit Boot
+ // service called
+
+ if (OSState != "OperatingState")
+ {
+ std::string HashFilePath =
+ "/var/lib/bios-settings-manager/passwordData";
+
+ std::ifstream devIdFile(HashFilePath);
+ if (devIdFile.is_open())
+ {
+
+ 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, maxHashSize> newUserHash;
+ uint8_t flag = 0;
+ uint8_t adminPwdChangedFlag = 0;
+ uint8_t userPwdChangedFlag = 0;
+ if (!data.is_discarded())
+ {
+
+ adminPwdChangedFlag = data["IsAdminPwdChanged"];
+ newAdminHash = data["AdminPwdHash"];
+ newUserHash = data["UserPwdHash"];
+ userPwdChangedFlag = data["IsUserPwdChanged"];
+ }
+
+ // 0: BIT 4 - New Admin Password Not Present
+ // 1: BIT 4 - New Admin Password Hash Present
+ // 0: BIT 5 - New User Password Not Present
+ // 1: BIT 5 - New User Password Hash Present
+ // 0: BIT 0 - Default Setting flag is not set
+ // 1: BIT 0 - Default Setting flag is set
+ auto status = getResetBIOSSettings(flag);
+ if (status)
+ {
+ return ipmi::responseResponseError();
+ }
+ if (adminPwdChangedFlag)
+ {
+ flag |= adminPasswordChanged;
+ }
+ if (userPwdChangedFlag)
+ {
+ flag |= userPasswordChanged;
+ }
+
+ std::copy(std::begin(newAdminHash), std::end(newAdminHash),
+ std::begin(newAdminHash));
+ std::copy(std::begin(newUserHash), std::end(newUserHash),
+ std::begin(newUserHash));
+ return ipmi::responseSuccess(flag, newAdminHash, newUserHash);
+ }
+ else
+ {
+ return ipmi::responseResponseError();
+ }
+ }
+ else
+ {
+
+ return ipmi::response(ipmiCCNotSupportedInCurrentState);
+ }
+}
+
+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