/* // 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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>; 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 ipmi::DbusVariant& attributes, const std::string_view key, AttributesType& attrType) { payloadData += key; payloadData += '='; if (attrType == AttributesType::string || attrType == AttributesType::enumeration) { if (!std::holds_alternative(attributes)) { phosphor::logging::log( "fillPayloadData: No string data in attributes"); return false; } payloadData += std::get(attributes); } else if (attrType == AttributesType::integer) { if (!std::holds_alternative(attributes)) { phosphor::logging::log( "fillPayloadData: No int64_t data in attributes"); return false; } payloadData += std::to_string(std::get(attributes)); } else { phosphor::logging::log( "fillPayloadData: Unsupported attribute type"); return false; } payloadData += '\n'; return true; } bool getPendingList(ipmi::Context::ptr& ctx, std::string& payloadData) { std::variant pendingAttributesData; boost::system::error_code ec; payloadData.clear(); auto dbus = getSdBus(); if (!dbus) { phosphor::logging::log( "getPendingList: getSdBus() failed"); return false; } std::string service = getService(*dbus, biosConfigIntf, biosConfigBaseMgrPath); try { pendingAttributesData = dbus->yield_method_call>( 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(ex.what()); return false; } if (ec) { std::string err = "getPendingList: error while trying to get " "PendingAttributes, error = "; err += ec.message(); phosphor::logging::log(err.c_str()); return false; } const PendingAttributesType* pendingAttributes = std::get_if(&pendingAttributesData); if (!pendingAttributes) { phosphor::logging::log( "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( "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( "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( "computeCheckSum: length > maxGetPayloadDataSize"); return false; } std::unique_ptr 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(ipmi::PType::IntelXMLType1); auto& payloadInfo = gNVOOBdata.payloadInfo[payloadType]; if (!computeCheckSum(payloadFilePath, calcChecksum)) { phosphor::logging::log( "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(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( "updatePayloadInfo: Cannot get file status for Payload1 file"); return false; } if (!flushNVOOBdata()) { phosphor::logging::log( "updatePayloadInfo: flushNVOOBdata failed"); return false; } return true; } bool update(ipmi::Context::ptr& ctx) { std::string payloadFilePath = "/var/oob/Payload" + std::to_string(static_cast(ipmi::PType::IntelXMLType1)); std::string payloadData; if (!getPendingList(ctx, payloadData)) { phosphor::logging::log( "payload1::update : getPendingList() failed"); return false; } if (!updatePayloadFile(payloadFilePath, payloadData)) { phosphor::logging::log( "payload1::update : updatePayloadFile() failed"); return false; } if (!updatePayloadInfo(payloadFilePath)) { phosphor::logging::log( "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 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( "sendAllAttributes error: send all attributes - " "failed"); return; } phosphor::logging::log( "sendAllAttributes: send all attributes - done"); }, service, biosConfigBaseMgrPath, "org.freedesktop.DBus.Properties", "Set", biosConfigIntf, "BaseBIOSTable", std::variant(attributesData)); return true; } catch (const std::exception& ex) { phosphor::logging::log(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(&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 dbus = getSdBus(); Value variant = getDbusProperty( *dbus, "xyz.openbmc_project.State.Host0", "/xyz/openbmc_project/state/host0", "xyz.openbmc_project.State.OperatingSystem.Status", "OperatingSystemState"); auto& value = std::get(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( "'getDbusProperty' failed to read xyz.openbmc_project.State.Host0"); } return postCompleted; } /** @brief implement to get the Rest BIOS property * @returns status */ static int getResetBIOSSettings(uint8_t& ResetFlag) { try { std::shared_ptr dbus = getSdBus(); std::string service = getService(*dbus, biosConfigIntf, biosConfigBaseMgrPath); Value variant = getDbusProperty(*dbus, service, biosConfigBaseMgrPath, biosConfigIntf, resetBIOSSettingsProp); std::string_view ResetStr = std::get(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( "'depex' compute failed"); } if (!biosxml.getBaseTable(attributesData)) { phosphor::logging::log( "Failed to get bios base table"); } } catch (const std::exception& ex) { phosphor::logging::log(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( "generateAndSendAttributesData: generateAttributesData - failed"); gNVOOBdata.payloadInfo[payloadType].payloadStatus = static_cast(ipmi::PStatus::Corrupted); return; } phosphor::logging::log( "generateAndSendAttributesData : generateAttributesData is done"); if (!sendAllAttributes(service)) { phosphor::logging::log( "generateAndSendAttributesData: sendAllAttributes - failed"); gNVOOBdata.payloadInfo[payloadType].payloadStatus = static_cast(ipmi::PStatus::Corrupted); return; } phosphor::logging::log( "generateAndSendAttributesData : sendAllAttributes is done"); gNVOOBdata.payloadInfo[payloadType].payloadStatus = static_cast(ipmi::PStatus::Valid); } /** @brief implement executing the linux command to uncompress and generate the * xmlfile * @param[in] linux command * @returns status */ template static int generateBIOSXMLFile(const char* path, ArgTypes&&... tArgs) { boost::process::child execProg(path, const_cast(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(ipmi::PType::IntelXMLType0)) { unlink("/var/oob/Payload1"); gNVOOBdata.payloadInfo[static_cast(ipmi::PType::IntelXMLType1)] .payloadStatus = static_cast(ipmi::PStatus::Unknown); } } /** @brief implements to create the oob folders and nv space **/ static Cc InitNVOOBdata() { 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(&gNVOOBdata), sizeof(struct NVOOBdata)); ifs.close(); return ipmi::ccSuccess; } return ipmi::ccResponseError; } ipmi::RspType<> ipmiOEMSetBIOSCap(ipmi::Context::ptr&, 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 ipmiOEMGetBIOSCap(ipmi::Context::ptr&) { if (gNVOOBdata.mIsBIOSCapInitDone) { return ipmi::responseSuccess(gNVOOBdata.mBIOSCapabilities.OOBCapability, 0, 0, 0); } else { return ipmi::response(ipmiCCBIOSCapabilityInitNotDone); } } ipmi::RspType ipmiOEMSetPayload(ipmi::Context::ptr&, uint8_t paramSel, uint8_t payloadType, std::vector 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(ipmi::PType::IntelXMLType0)) { if (getPostCompleted()) { return ipmi::response(ipmiCCNotSupportedInCurrentState); } } switch (static_cast(paramSel)) { case ipmi::PTState::StartTransfer: { PayloadStartTransfer* pPayloadStartTransfer = reinterpret_cast(payload.data()); if (payload.size() < sizeof(PayloadStartTransfer)) { phosphor::logging::log( "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(ipmi::PStatus::Unknown); gNVOOBdata.payloadInfo[payloadType].payloadType = payloadType; return ipmi::responseSuccess( gNVOOBdata.payloadInfo[payloadType].payloadReservationID); } break; case ipmi::PTState::InProgress: { PayloadInProgress* pPayloadInProgress = reinterpret_cast(payload.data()); PayloadInfo payloadInfo = gNVOOBdata.payloadInfo[payloadType]; if (payload.size() < sizeof(PayloadInProgress)) { phosphor::logging::log( "BIOS Config Payload size is not correct"); return ipmi::responseReqDataLenInvalid(); } if (pPayloadInProgress->payloadReservationID != payloadInfo.payloadReservationID) { phosphor::logging::log( "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(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( "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(payload.data()); outFile.write(writedata + 16, payload.size() - 16); outFile.close(); gNVOOBdata.payloadInfo[payloadType].payloadStatus = static_cast(ipmi::PStatus::Unknown); gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten += payloadInfo.payloadCurrentSize; return ipmi::responseSuccess(payloadInfo.payloadCurrentSize); } break; case ipmi::PTState::EndTransfer: { PayloadEndTransfer* pPayloadEndTransfer = reinterpret_cast(payload.data()); PayloadInfo payloadInfo = gNVOOBdata.payloadInfo[payloadType]; if (pPayloadEndTransfer->payloadReservationID != payloadInfo.payloadReservationID) { return ipmi::responseInvalidReservationId(); } gNVOOBdata.payloadInfo[payloadType].payloadStatus = static_cast(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( "ipmiOEMSetPayload: Renaming Payload file - failed"); } if (payloadType == static_cast(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( "ipmiOEMSetPayload: generateBIOSXMLFile - failed"); gNVOOBdata.payloadInfo[payloadType].payloadStatus = static_cast(ipmi::PStatus::Corrupted); return ipmi::response(ipmiCCPayloadPayloadPacketMissed); } phosphor::logging::log( " 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( "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(ipmi::PStatus::Valid); } else { return ipmi::responseResponseError(); } flushNVOOBdata(); return ipmi::responseSuccess( gNVOOBdata.payloadInfo[payloadType].actualTotalPayloadWritten); } break; case ipmi::PTState::UserAbort: { PayloadEndTransfer* pPayloadEndTransfer = reinterpret_cast(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 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(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(ipmi::PType::IntelXMLType1)) { if (!payload1::update(ctx)) { phosphor::logging::log( "ipmiOEMGetPayload: unable to update NVOOBdata for payloadType " "= IntelXMLType1"); return ipmi::response(ipmi::ccUnspecifiedError); } } struct PayloadInfo res = gNVOOBdata.payloadInfo[payloadType]; switch (static_cast(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( "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(ipmi::PStatus::Valid))) { std::vector 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(maxGetPayloadDataSize)) { phosphor::logging::log( "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( "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(offset)) { phosphor::logging::log( "ipmiOEMGetPayload: filesize < offset"); return ipmi::responseInvalidFieldRequest(); } if ((static_cast(fileSize) - offset) < length) { phosphor::logging::log( "ipmiOEMGetPayload: (filesize - offset) < length "); return ipmi::responseInvalidFieldRequest(); } ifs.seekg(offset, std::ios::beg); std::array Buffer; ifs.read(reinterpret_cast(Buffer.data()), length); uint32_t readCount = ifs.gcount(); ifs.close(); boost::crc_32_type calcChecksum; calcChecksum.process_bytes( reinterpret_cast(Buffer.data()), readCount); uint32_t chkSum = calcChecksum.checksum(); retValue.pack(payloadType); retValue.pack(readCount); retValue.pack(chkSum); for (uint32_t 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&, std::array& pwdSeed, uint8_t algoInfo, std::array& 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 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, uint8_t, std::array> ipmiOEMGetBIOSHash(ipmi::Context::ptr&) { 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 newAdminHash; std::array 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( "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