Firmware update commmands re-write
Firmware update commands re-write
* Updated to new API style
* Coding guidelines for all names
* Restricted few commands for PFR
* Few updates as per new OpenBMC EPS
* Changes for fw update status
* Removed legacy API's used for FW update
Tested:
- Tested firmware update using fwpiaupd.efi tool
in usb and kcs trasfer mode.
- Tested all firmware update commands over LAN.
Change-Id: I666cb139b2ca61d352e1cb6b1c18d045b7b1f197
Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
diff --git a/include/oemcommands.hpp b/include/oemcommands.hpp
index a382716..a6ece9c 100644
--- a/include/oemcommands.hpp
+++ b/include/oemcommands.hpp
@@ -52,6 +52,7 @@
static constexpr Cmd cmdSetShutdownPolicy = 0x60;
static constexpr Cmd cmdGetShutdownPolicy = 0x62;
static constexpr Cmd cmdGetMultiNodePresence = 0x63;
+static constexpr Cmd cmdGetBufferSize = 0x66;
static constexpr Cmd cmdSetFanConfig = 0x89;
static constexpr Cmd cmdGetFanConfig = 0x8a;
static constexpr Cmd cmdSetFanSpeedOffset = 0x8c;
diff --git a/src/firmware-update.cpp b/src/firmware-update.cpp
index d949a33..d3236d5 100644
--- a/src/firmware-update.cpp
+++ b/src/firmware-update.cpp
@@ -7,7 +7,7 @@
#include <unistd.h>
#include <boost/algorithm/string.hpp>
-#include <boost/asio.hpp>
+#include <boost/container/flat_map.hpp>
#include <boost/process/child.hpp>
#include <boost/uuid/random_generator.hpp>
#include <boost/uuid/uuid_io.hpp>
@@ -20,35 +20,98 @@
#include <ipmid/api.hpp>
#include <ipmid/utils.hpp>
#include <map>
+#include <phosphor-logging/log.hpp>
#include <random>
#include <sdbusplus/bus.hpp>
#include <sdbusplus/bus/match.hpp>
#include <sdbusplus/server/object.hpp>
#include <sdbusplus/timer.hpp>
-#include <sstream>
#ifdef INTEL_PFR_ENABLED
#include <spiDev.hpp>
#endif
+static constexpr bool DEBUG = true;
+static void registerFirmwareFunctions() __attribute__((constructor));
+
namespace ipmi
{
namespace firmware
{
constexpr Cmd cmdGetFwVersionInfo = 0x20;
+constexpr Cmd cmdGetFwSecurityVersionInfo = 0x21;
+constexpr Cmd cmdGetFwUpdateChannelInfo = 0x22;
+constexpr Cmd cmdGetBmcExecutionContext = 0x23;
constexpr Cmd cmdFwGetRootCertData = 0x25;
+constexpr Cmd cmdGetFwUpdateRandomNumber = 0x26;
+constexpr Cmd cmdSetFirmwareUpdateMode = 0x27;
+constexpr Cmd cmdExitFirmwareUpdateMode = 0x28;
+constexpr Cmd cmdGetSetFwUpdateControl = 0x29;
+constexpr Cmd cmdGetFirmwareUpdateStatus = 0x2A;
+constexpr Cmd cmdSetFirmwareUpdateOptions = 0x2B;
constexpr Cmd cmdFwImageWriteData = 0x2c;
} // namespace firmware
} // namespace ipmi
+namespace ipmi
+{
+// Custom completion codes
+constexpr Cc ccUsbAttachOrDetachFailed = 0x80;
+constexpr Cc ccNotSupportedInPresentState = 0xD5;
+
+static inline auto responseUsbAttachOrDetachFailed()
+{
+ return response(ccUsbAttachOrDetachFailed);
+}
+static inline auto responseNotSupportedInPresentState()
+{
+ return response(ccNotSupportedInPresentState);
+}
+} // namespace ipmi
+
+static constexpr const char *bmcStateIntf = "xyz.openbmc_project.State.BMC";
+static constexpr const char *bmcStatePath = "/xyz/openbmc_project/state/bmc0";
+static constexpr const char *bmcStateReady =
+ "xyz.openbmc_project.State.BMC.BMCState.Ready";
+static constexpr const char *bmcStateUpdateInProgress =
+ "xyz.openbmc_project.State.BMC.BMCState.UpdateInProgress";
+
+static constexpr char firmwareBufferFile[] = "/tmp/fw-download.bin";
+static std::chrono::steady_clock::time_point fwRandomNumGenTs;
+static constexpr auto fwRandomNumExpirySeconds = std::chrono::seconds(30);
+static constexpr size_t fwRandomNumLength = 8;
+static std::array<uint8_t, fwRandomNumLength> fwRandomNum;
+constexpr char usbCtrlPath[] = "/usr/bin/usb-ctrl";
+constexpr char fwUpdateMountPoint[] = "/tmp/usb-fwupd.mnt";
+constexpr char fwUpdateUsbVolImage[] = "/tmp/usb-fwupd.img";
+constexpr char fwUpdateUSBDevName[] = "fw-usb-mass-storage-dev";
+constexpr size_t fwPathMaxLength = 255;
+
#ifdef INTEL_PFR_ENABLED
uint32_t imgLength = 0;
uint32_t imgType = 0;
bool block0Mapped = false;
static constexpr uint32_t perBlock0MagicNum = 0xB6EAFD19;
+static constexpr const char *bmcActivePfmMTDDev = "/dev/mtd/pfm";
+static constexpr const char *bmcRecoveryImgMTDDev = "/dev/mtd/rc-image";
+static constexpr size_t pfmBaseOffsetInImage = 0x400;
+static constexpr size_t rootkeyOffsetInPfm = 0xA0;
+static constexpr size_t cskKeyOffsetInPfm = 0x124;
+static constexpr size_t cskSignatureOffsetInPfm = 0x19c;
+static constexpr size_t certKeyLen = 96;
+static constexpr size_t cskSignatureLen = 96;
+
static constexpr const char *versionIntf =
"xyz.openbmc_project.Software.Version";
+enum class FwGetRootCertDataTag : uint8_t
+{
+ activeRootKey = 1,
+ recoveryRootKey,
+ activeCSK,
+ recoveryCSK,
+};
+
enum class FWDeviceIDTag : uint8_t
{
bmcActiveImage = 1,
@@ -60,269 +123,349 @@
"/xyz/openbmc_project/software/bmc_active"},
{FWDeviceIDTag::bmcRecoveryImage,
"/xyz/openbmc_project/software/bmc_recovery"}};
+#endif // INTEL_PFR_ENABLED
-#endif
+enum class ChannelIdTag : uint8_t
+{
+ reserved = 0,
+ kcs = 1,
+ ipmb = 2,
+ rmcpPlus = 3
+};
-static constexpr const char *bmcStateIntf = "xyz.openbmc_project.State.BMC";
-static constexpr const char *bmcStatePath = "/xyz/openbmc_project/state/bmc0";
-static constexpr const char *bmcStateReady =
- "xyz.openbmc_project.State.BMC.BMCState.Ready";
-static constexpr const char *bmcStateUpdateInProgress =
- "xyz.openbmc_project.State.BMC.BMCState.UpdateInProgress";
+enum class BmcExecutionContext : uint8_t
+{
+ reserved = 0,
+ linuxOs = 0x10,
+ bootLoader = 0x11,
+};
-static constexpr const char *secondaryFitImageStartAddr = "22480000";
-static uint8_t getActiveBootImage(void);
-static void register_netfn_firmware_functions() __attribute__((constructor));
-
-// oem return code for firmware update control
-constexpr ipmi_ret_t IPMI_CC_REQ_INVALID_PHASE = 0xd5;
-constexpr ipmi_ret_t IPMI_CC_USB_ATTACH_FAIL = 0x80;
-
-static constexpr bool DEBUG = false;
-
-static constexpr char FW_UPDATE_SERVER_DBUS_NAME[] =
- "xyz.openbmc_project.fwupdate1.server";
-
-static constexpr char FW_UPDATE_SERVER_PATH[] =
- "/xyz/openbmc_project/fwupdate1";
-static constexpr char FW_UPDATE_SERVER_INFO_PATH[] =
- "/xyz/openbmc_project/fwupdate1/info";
-static constexpr char FW_UPDATE_ACTIVE_INFO_PATH[] =
- "/xyz/openbmc_project/fwupdate1/info/bmc_active";
-static constexpr char FW_UPDATE_BACKUP_INFO_PATH[] =
- "/xyz/openbmc_project/fwupdate1/info/bmc_backup";
-
-static constexpr char FW_UPDATE_INTERFACE[] = "xyz.openbmc_project.fwupdate1";
-static constexpr char FW_UPDATE_INFO_INTERFACE[] =
- "xyz.openbmc_project.fwupdate1.fwinfo";
-static constexpr char FW_UPDATE_SECURITY_INTERFACE[] =
- "xyz.openbmc_project.fwupdate1.security";
+enum class FwUpdateCtrlReq : uint8_t
+{
+ getCurrentControlStatus = 0x00,
+ imageTransferStart = 0x01,
+ imageTransferComplete = 0x02,
+ imageTransferAbort = 0x03,
+ setFirmwareFilename = 0x04,
+ attachUsbDevice = 0x05,
+ detachUsbDevice = 0x06
+};
constexpr std::size_t operator""_MB(unsigned long long v)
{
return 1024u * 1024u * v;
}
-static constexpr int FIRMWARE_BUFFER_MAX_SIZE = 32_MB;
+static constexpr size_t maxFirmwareImageSize = 32_MB;
-static constexpr char FIRMWARE_BUFFER_FILE[] = "/tmp/fw-download.bin";
-static bool local_download_is_active(void)
+static bool localDownloadInProgress(void)
{
struct stat sb;
- if (stat(FIRMWARE_BUFFER_FILE, &sb) < 0)
+ if (stat(firmwareBufferFile, &sb) < 0)
+ {
return false;
+ }
return true;
}
-class fw_update_status_cache
+class TransferHashCheck
+{
+ public:
+ enum class HashCheck : uint8_t
+ {
+ notRequested = 0,
+ requested,
+ sha2Success,
+ sha2Failed = 0xe2,
+ };
+
+ protected:
+ EVP_MD_CTX *ctx;
+ std::vector<uint8_t> expectedHash;
+ enum HashCheck check;
+ bool started;
+
+ public:
+ TransferHashCheck() : check(HashCheck::notRequested), started(false)
+ {
+ }
+ ~TransferHashCheck()
+ {
+ if (ctx)
+ {
+ EVP_MD_CTX_destroy(ctx);
+ ctx = NULL;
+ }
+ }
+ void init(const std::vector<uint8_t> &expected)
+ {
+ expectedHash = expected;
+ check = HashCheck::requested;
+ ctx = EVP_MD_CTX_create();
+ EVP_DigestInit(ctx, EVP_sha256());
+ }
+ void hash(const std::vector<uint8_t> &data)
+ {
+ if (!started)
+ {
+ started = true;
+ }
+ EVP_DigestUpdate(ctx, data.data(), data.size());
+ }
+ void clear()
+ {
+ // if not started, nothing to clear
+ if (started)
+ {
+ if (ctx)
+ {
+ EVP_MD_CTX_destroy(ctx);
+ }
+ if (check != HashCheck::notRequested)
+ {
+ check = HashCheck::requested;
+ }
+ ctx = EVP_MD_CTX_create();
+ EVP_DigestInit(ctx, EVP_sha256());
+ }
+ }
+ enum HashCheck verify()
+ {
+ if (check == HashCheck::requested)
+ {
+ unsigned int len = 0;
+ std::vector<uint8_t> digest(EVP_MD_size(EVP_sha256()));
+ EVP_DigestFinal(ctx, digest.data(), &len);
+ if (digest == expectedHash)
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "Transfer sha2 verify passed.");
+ check = HashCheck::sha2Success;
+ }
+ else
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Transfer sha2 verify failed.");
+ check = HashCheck::sha2Failed;
+ }
+ }
+ return check;
+ }
+ uint8_t status() const
+ {
+ return static_cast<uint8_t>(check);
+ }
+};
+
+class MappedFile
+{
+ public:
+ MappedFile(const std::string &fname) : addr(nullptr), fsize(0)
+ {
+ std::error_code ec;
+ size_t sz = std::filesystem::file_size(fname, ec);
+ int fd = open(fname.c_str(), O_RDONLY);
+ if (!ec || fd < 0)
+ {
+ return;
+ }
+ void *tmp = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0);
+ close(fd);
+ if (tmp == MAP_FAILED)
+ {
+ return;
+ }
+ addr = tmp;
+ fsize = sz;
+ }
+
+ ~MappedFile()
+ {
+ if (addr)
+ {
+ munmap(addr, fsize);
+ }
+ }
+ const uint8_t *data() const
+ {
+ return static_cast<const uint8_t *>(addr);
+ }
+ size_t size() const
+ {
+ return fsize;
+ }
+
+ private:
+ size_t fsize;
+ void *addr;
+};
+
+class FwUpdateStatusCache
{
public:
enum
{
- FW_STATE_INIT = 0,
- FW_STATE_IDLE,
- FW_STATE_DOWNLOAD,
- FW_STATE_VERIFY,
- FW_STATE_WRITE,
- FW_STATE_READY,
- FW_STATE_ERROR = 0x0f,
- FW_STATE_AC_CYCLE_REQUIRED = 0x83,
+ fwStateInit = 0,
+ fwStateIdle,
+ fwStateDownload,
+ fwStateVerify,
+ fwStateProgram,
+ fwStateUpdateSuccess,
+ fwStateError = 0x0f,
+ fwStateAcCycleRequired = 0x83,
};
- uint8_t state()
+ uint8_t getState()
{
- if (DEBUG)
- std::cerr << "fw-state: 0x" << std::hex << (int)_state << '\n';
- if ((_state == FW_STATE_IDLE || _state == FW_STATE_INIT) &&
- local_download_is_active())
+ if ((fwUpdateState == fwStateIdle || fwUpdateState == fwStateInit) &&
+ localDownloadInProgress())
{
- _state = FW_STATE_DOWNLOAD;
- _percent = 0;
+ fwUpdateState = fwStateDownload;
+ progressPercent = 0;
}
- return _state;
+ return fwUpdateState;
+ }
+ void resetStatusCache()
+ {
+ unlink(firmwareBufferFile);
+ }
+ void setState(const uint8_t state)
+ {
+ switch (state)
+ {
+ case fwStateInit:
+ case fwStateIdle:
+ case fwStateError:
+ resetStatusCache();
+ break;
+ case fwStateDownload:
+ case fwStateVerify:
+ case fwStateProgram:
+ case fwStateUpdateSuccess:
+ break;
+ default:
+ // Error
+ break;
+ }
+ fwUpdateState = state;
}
uint8_t percent()
{
- return _percent;
+ return progressPercent;
}
- std::string msg()
+ void updateActivationPercent(const std::string &objPath)
{
- return _msg;
- }
- std::string get_software_obj_path()
- {
- return _software_obj_path;
- }
- void set_software_obj_path(std::string &obj_path)
- {
- _software_obj_path = obj_path;
- _state = FW_STATE_WRITE;
- _percent = 0;
- _match = std::make_shared<sdbusplus::bus::match::match>(
- *_bus,
+ std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
+ fwUpdateState = fwStateProgram;
+ progressPercent = 0;
+ match = std::make_shared<sdbusplus::bus::match::match>(
+ *busp,
sdbusplus::bus::match::rules::propertiesChanged(
- _software_obj_path,
- "xyz.openbmc_project.Software.ActivationProgress"),
+ objPath, "xyz.openbmc_project.Software.ActivationProgress"),
[&](sdbusplus::message::message &msg) {
- if (DEBUG)
- std::cerr << "propertiesChanged lambda\n";
std::map<std::string, ipmi::DbusVariant> props;
- std::vector<std::string> inval;
+ std::vector<std::string> inVal;
std::string iface;
- msg.read(iface, props, inval);
- _parse_props(props);
+ try
+ {
+ msg.read(iface, props, inVal);
+ }
+ catch (const std::exception &e)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Exception caught in get ActivationProgress");
+ return;
+ }
+
+ auto it = props.find("Progress");
+ if (it != props.end())
+ {
+ progressPercent = std::get<uint8_t>(it->second);
+ if (progressPercent == 100)
+ {
+ fwUpdateState = fwStateUpdateSuccess;
+ }
+ }
});
}
- uint8_t activation_timer_timeout()
+ uint8_t activationTimerTimeout()
{
- std::cerr << "activation_timer_timout(): increase percentage...\n";
- _percent = _percent + 5;
- if (_percent >= 95)
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "activationTimerTimeout: Increase percentage...",
+ phosphor::logging::entry("PERCENT:%d", progressPercent));
+ progressPercent = progressPercent + 5;
+ if (progressPercent >= 95)
{
/*changing the state to ready to update firmware utility */
- _state = FW_STATE_READY;
+ fwUpdateState = fwStateUpdateSuccess;
}
- std::cerr << " _percent = " << (int)_percent << "\n";
- return _percent;
+ return progressPercent;
}
/* API for changing state to ERROR */
void firmwareUpdateAbortState()
{
- unlink(FIRMWARE_BUFFER_FILE);
+ unlink(firmwareBufferFile);
// changing the state to error
- _state = FW_STATE_ERROR;
+ fwUpdateState = fwStateError;
}
void setDeferRestart(bool deferRestart)
{
- _deferRestart = deferRestart;
+ deferRestartState = deferRestart;
}
void setInhibitDowngrade(bool inhibitDowngrade)
{
- _inhibitDowngrade = inhibitDowngrade;
+ inhibitDowngradeState = inhibitDowngrade;
}
bool getDeferRestart()
{
- return _deferRestart;
+ return deferRestartState;
}
bool getInhibitDowngrade()
{
- return _inhibitDowngrade;
+ return inhibitDowngradeState;
}
protected:
- void _parse_props(std::map<std::string, ipmi::DbusVariant> &properties)
- {
- if (DEBUG)
- std::cerr << "propertiesChanged (" << properties.size()
- << " elements)";
- for (const auto &t : properties)
- {
- auto key = t.first;
- auto value = t.second;
- if (key == "state")
- {
- auto state = std::get<std::string>(value);
- if (DEBUG)
- std::cerr << ", state=" << state;
- if (state == "INIT")
- _state = FW_STATE_INIT;
- else if (state == "IDLE")
- _state = FW_STATE_IDLE;
- else if (state == "DOWNLOAD")
- _state = FW_STATE_DOWNLOAD;
- else if (state == "VERIFY")
- _state = FW_STATE_VERIFY;
- else if (state == "WRITE")
- _state = FW_STATE_WRITE;
- else if (state == "READY")
- _state = FW_STATE_READY;
- else if (state == "ERROR")
- _state = FW_STATE_ERROR;
- else if (state == "AC_CYCLE_REQUIRED")
- _state = FW_STATE_AC_CYCLE_REQUIRED;
- else
- {
- _state = FW_STATE_ERROR;
- _msg = "internal error";
- }
- }
- else if (key == "percent")
- {
- _percent = std::get<int32_t>(value);
- if (DEBUG)
- std::cerr << ", pct=" << (int)_percent;
- }
- else if (key == "msg")
- {
- _msg = std::get<std::string>(value);
- if (DEBUG)
- std::cerr << ", msg='" << _msg << '\'';
- }
- else if (key == "Progress")
- {
- _percent = std::get<uint8_t>(value);
- ;
- if (_percent == 100)
- _state = FW_STATE_READY;
- }
- }
- if ((_state == FW_STATE_IDLE || _state == FW_STATE_INIT) &&
- local_download_is_active())
- {
- _state = FW_STATE_DOWNLOAD;
- _percent = 0;
- }
- if (DEBUG)
- std::cerr << '\n';
- }
-
- std::shared_ptr<sdbusplus::asio::connection> _bus;
- std::shared_ptr<sdbusplus::bus::match::match> _match;
- uint8_t _state = 0;
- uint8_t _percent = 0;
- bool _deferRestart = false;
- bool _inhibitDowngrade = false;
- std::string _msg;
-
- private:
- std::string _software_obj_path;
+ std::shared_ptr<sdbusplus::asio::connection> busp;
+ std::shared_ptr<sdbusplus::bus::match::match> match;
+ uint8_t fwUpdateState = 0;
+ uint8_t progressPercent = 0;
+ bool deferRestartState = false;
+ bool inhibitDowngradeState = false;
};
-static fw_update_status_cache fw_update_status;
+static FwUpdateStatusCache fwUpdateStatus;
+std::shared_ptr<TransferHashCheck> xferHashCheck;
-static std::chrono::steady_clock::time_point fw_random_number_timestamp;
-static constexpr int FW_RANDOM_NUMBER_LENGTH = 8;
-static constexpr auto FW_RANDOM_NUMBER_TTL = std::chrono::seconds(30);
-static uint8_t fw_random_number[FW_RANDOM_NUMBER_LENGTH];
-
-static ipmi_ret_t ipmi_firmware_get_fw_random_number(
- ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
- ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
+static void activateImage(const std::string &objPath)
{
- std::random_device rd;
- std::default_random_engine gen(rd());
- std::uniform_int_distribution<> dist{0, 255};
-
- if (*data_len != 0)
+ // If flag is false means to reboot
+ if (fwUpdateStatus.getDeferRestart() == false)
{
- *data_len = 0;
- return IPMI_CC_REQ_DATA_LEN_INVALID;
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "activating Image: ",
+ phosphor::logging::entry("OBJPATH =%s", objPath.c_str()));
+ std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
+ bus->async_method_call(
+ [](const boost::system::error_code ec) {
+ if (ec)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "async_method_call error: activateImage failed");
+ return;
+ }
+ },
+ "xyz.openbmc_project.Software.BMC.Updater", objPath,
+ "org.freedesktop.DBus.Properties", "Set",
+ "xyz.openbmc_project.Software.Activation", "RequestedActivation",
+ std::variant<std::string>("xyz.openbmc_project.Software.Activation."
+ "RequestedActivations.Active"));
}
-
- fw_random_number_timestamp = std::chrono::steady_clock::now();
-
- uint8_t *msg_reply = static_cast<uint8_t *>(response);
- for (int i = 0; i < FW_RANDOM_NUMBER_LENGTH; i++)
- fw_random_number[i] = msg_reply[i] = dist(gen);
-
- if (DEBUG)
- std::cerr << "FW Rand Num: 0x" << std::hex << (int)msg_reply[0] << " 0x"
- << (int)msg_reply[1] << " 0x" << (int)msg_reply[2] << " 0x"
- << (int)msg_reply[3] << " 0x" << (int)msg_reply[4] << " 0x"
- << (int)msg_reply[5] << " 0x" << (int)msg_reply[6] << " 0x"
- << (int)msg_reply[7] << '\n';
-
- *data_len = FW_RANDOM_NUMBER_LENGTH;
-
- return IPMI_CC_OK;
+ else
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "Firmware image activation is deferred.");
+ }
+ fwUpdateStatus.setState(
+ static_cast<uint8_t>(FwUpdateStatusCache::fwStateUpdateSuccess));
}
static bool getFirmwareUpdateMode()
@@ -370,422 +513,130 @@
}
}
-/** @brief Set Firmware Update Mode
- *
- * This function sets BMC into firmware update mode
- * after validating Random number obtained from the Get
- * Firmware Update Random Number command
- *
- * @parameter
- * - randNum - Random number(token)
- * @returns IPMI completion code
- **/
-ipmi::RspType<> ipmiSetFirmwareUpdateMode(
- std::array<uint8_t, FW_RANDOM_NUMBER_LENGTH> &randNum)
-{
- /* Firmware Update Random number is valid for 30 seconds only */
- auto timeElapsed =
- (std::chrono::steady_clock::now() - fw_random_number_timestamp);
- if (std::chrono::duration_cast<std::chrono::microseconds>(timeElapsed)
- .count() > std::chrono::duration_cast<std::chrono::microseconds>(
- FW_RANDOM_NUMBER_TTL)
- .count())
- {
- phosphor::logging::log<phosphor::logging::level::INFO>(
- "Firmware update random number expired.");
- return ipmi::responseInvalidFieldRequest();
- }
-
- /* Validate random number */
- for (int i = 0; i < FW_RANDOM_NUMBER_LENGTH; i++)
- {
- if (fw_random_number[i] != randNum[i])
- {
- phosphor::logging::log<phosphor::logging::level::INFO>(
- "Invalid random number specified.");
- return ipmi::responseInvalidFieldRequest();
- }
- }
-
- try
- {
- if (getFirmwareUpdateMode())
- {
- phosphor::logging::log<phosphor::logging::level::INFO>(
- "Already firmware update is in progress.");
- return ipmi::responseBusy();
- }
- }
- catch (const std::exception &e)
- {
- return ipmi::responseUnspecifiedError();
- }
-
- // FIXME? c++ doesn't off an option for exclusive file creation
- FILE *fp = fopen(FIRMWARE_BUFFER_FILE, "wx");
- if (!fp)
- {
- phosphor::logging::log<phosphor::logging::level::INFO>(
- "Unable to open file.");
- return ipmi::responseUnspecifiedError();
- }
- fclose(fp);
-
- try
- {
- setFirmwareUpdateMode(true);
- }
- catch (const std::exception &e)
- {
- unlink(FIRMWARE_BUFFER_FILE);
- return ipmi::responseUnspecifiedError();
- }
-
- return ipmi::responseSuccess();
-}
-
-/** @brief implements exit firmware update mode command
- * @param None
- *
- * @returns IPMI completion code
- */
-ipmi::RspType<> ipmiFirmwareExitFwUpdateMode()
-{
-
- if (DEBUG)
- {
- std::cerr << "Exit FW update mode \n";
- }
- switch (fw_update_status.state())
- {
- case fw_update_status_cache::FW_STATE_INIT:
- case fw_update_status_cache::FW_STATE_IDLE:
- return ipmi::responseInvalidFieldRequest();
- break;
- case fw_update_status_cache::FW_STATE_DOWNLOAD:
- case fw_update_status_cache::FW_STATE_VERIFY:
- break;
- case fw_update_status_cache::FW_STATE_WRITE:
- break;
- case fw_update_status_cache::FW_STATE_READY:
- case fw_update_status_cache::FW_STATE_ERROR:
- break;
- case fw_update_status_cache::FW_STATE_AC_CYCLE_REQUIRED:
- return ipmi::responseInvalidFieldRequest();
- break;
- }
- fw_update_status.firmwareUpdateAbortState();
-
- try
- {
- setFirmwareUpdateMode(false);
- }
- catch (const std::exception &e)
- {
- return ipmi::responseUnspecifiedError();
- }
-
- return ipmi::responseSuccess();
-}
-
-static void post_transfer_complete_handler(
- std::unique_ptr<sdbusplus::bus::match::match> &fw_update_matcher);
-static bool request_start_firmware_update(const std::string &uri)
-{
- if (DEBUG)
- std::cerr << "request start firmware update()\n";
-
- // fwupdate URIs start with file:// or usb:// or tftp:// etc. By the time
- // the code gets to this point, the file should be transferred start the
- // request (creating a new file in /tmp/images causes the update manager to
- // check if it is ready for activation)
- static std::unique_ptr<sdbusplus::bus::match::match> fw_update_matcher;
- post_transfer_complete_handler(fw_update_matcher);
- std::filesystem::rename(
- uri, "/tmp/images/" +
- boost::uuids::to_string(boost::uuids::random_generator()()));
- return true;
-}
-
-class transfer_hash_check
-{
- public:
- enum hash_check
- {
- CHECK_NOT_REQUESTED = 0,
- CHECK_REQUESTED,
- CHECK_PASSED_SHA2,
- CHECK_RESVD1,
- CHECK_FAILED_SHA2 = 0xe2,
- CHECK_RESVD2 = 0xe3,
- };
-
- protected:
- EVP_MD_CTX *_ctx;
- std::vector<uint8_t> _expected;
- enum hash_check _check;
- bool _started;
-
- public:
- transfer_hash_check() : _check(CHECK_NOT_REQUESTED), _started(false)
- {
- }
- ~transfer_hash_check()
- {
- if (_ctx)
- {
- EVP_MD_CTX_destroy(_ctx);
- _ctx = NULL;
- }
- }
- void init(const std::vector<uint8_t> &expected)
- {
- _expected = expected;
- _check = CHECK_REQUESTED;
- _ctx = EVP_MD_CTX_create();
- EVP_DigestInit(_ctx, EVP_sha256());
- }
- void hash(const std::vector<uint8_t> &data)
- {
- if (!_started)
- _started = true;
- EVP_DigestUpdate(_ctx, data.data(), data.size());
- }
- void clear()
- {
- // if not started, nothing to clear
- if (_started)
- {
- if (_ctx)
- EVP_MD_CTX_destroy(_ctx);
- if (_check != CHECK_NOT_REQUESTED)
- _check = CHECK_REQUESTED;
- _ctx = EVP_MD_CTX_create();
- EVP_DigestInit(_ctx, EVP_sha256());
- }
- }
- enum hash_check check()
- {
- if (_check == CHECK_REQUESTED)
- {
- unsigned int len;
- std::vector<uint8_t> digest(EVP_MD_size(EVP_sha256()));
- EVP_DigestFinal(_ctx, digest.data(), &len);
- if (digest == _expected)
- {
- if (DEBUG)
- std::cerr << "transfer sha2 check passed\n";
- _check = CHECK_PASSED_SHA2;
- }
- else
- {
- if (DEBUG)
- std::cerr << "transfer sha2 check failed\n";
- _check = CHECK_FAILED_SHA2;
- }
- }
- return _check;
- }
- uint8_t status() const
- {
- return static_cast<uint8_t>(_check);
- }
-};
-
-std::shared_ptr<transfer_hash_check> xfer_hash_check;
-
-static void activate_image(const char *obj_path)
-{
- // If flag is false means to reboot
- if (fw_update_status.getDeferRestart() == false)
- {
-
- if (DEBUG)
- {
- std::cerr << "activateImage()...\n";
- std::cerr << "obj_path = " << obj_path << "\n";
- }
- phosphor::logging::log<phosphor::logging::level::INFO>(
- "activating Image: ",
- phosphor::logging::entry("OBJPATH =%s", obj_path));
- std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
- bus->async_method_call(
- [](const boost::system::error_code ec) {
- if (ec)
- {
- phosphor::logging::log<phosphor::logging::level::ERR>(
- "async_method_call error: activate_image failed");
- return;
- }
- },
- "xyz.openbmc_project.Software.BMC.Updater", obj_path,
- "org.freedesktop.DBus.Properties", "Set",
- "xyz.openbmc_project.Software.Activation", "RequestedActivation",
- std::variant<std::string>("xyz.openbmc_project.Software.Activation."
- "RequestedActivations.Active"));
- }
- else
- {
- phosphor::logging::log<phosphor::logging::level::INFO>(
- "Firmware image activation is deferred.");
- }
-}
-
-static void post_transfer_complete_handler(
- std::unique_ptr<sdbusplus::bus::match::match> &fw_update_matcher)
+static void postTransferCompleteHandler(
+ std::unique_ptr<sdbusplus::bus::match::match> &fwUpdateMatchSignal)
{
// Setup timer for watching signal
static phosphor::Timer timer(
- [&fw_update_matcher]() { fw_update_matcher = nullptr; });
+ [&fwUpdateMatchSignal]() { fwUpdateMatchSignal = nullptr; });
- static phosphor::Timer activation_status_timer([]() {
- if (fw_update_status.activation_timer_timeout() >= 95)
+ static phosphor::Timer activationStatusTimer([]() {
+ if (fwUpdateStatus.activationTimerTimeout() >= 95)
{
- activation_status_timer.stop();
+ activationStatusTimer.stop();
+ fwUpdateStatus.setState(
+ static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify));
}
});
timer.start(std::chrono::microseconds(5000000), false);
// callback function for capturing signal
- auto callback = [&fw_update_matcher](sdbusplus::message::message &m) {
- if (DEBUG)
- std::cerr << "[complete] Match fired\n";
+ auto callback = [&](sdbusplus::message::message &m) {
bool flag = false;
std::vector<std::pair<
std::string,
std::vector<std::pair<std::string, std::variant<std::string>>>>>
- interfaces_properties;
-
- sdbusplus::message::object_path obj_path;
+ intfPropsPair;
+ sdbusplus::message::object_path objPath;
try
{
- m.read(obj_path, interfaces_properties); // Read in the object path
- // that was just created
+ m.read(objPath, intfPropsPair); // Read in the object path
+ // that was just created
}
- catch (std::exception &e)
+ catch (const std::exception &e)
{
- std::cerr
- << "[complete] Failed at post_transfer_complete-handler : "
- << e.what() << "\n";
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Exception caught in reading created object path.");
+ return;
}
// constructing response message
- if (DEBUG)
- std::cerr << "[complete] obj path = " << obj_path.str << "\n";
- for (auto &interface : interfaces_properties)
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "New Interface Added.",
+ phosphor::logging::entry("OBJPATH=%s", objPath.str.c_str()));
+ for (auto &interface : intfPropsPair)
{
- if (DEBUG)
- std::cerr << "[complete] interface = " << interface.first
- << "\n";
-
if (interface.first == "xyz.openbmc_project.Software.Activation")
{
- // cancel timer only when
- // xyz.openbmc_project.Software.Activation interface is
- // added
+ // There are chances of getting two signals for
+ // InterfacesAdded. So cross check and discrad second instance.
+ if (fwUpdateMatchSignal == nullptr)
+ {
+ return;
+ }
+ // Found our interface, disable callbacks
+ fwUpdateMatchSignal = nullptr;
- if (DEBUG)
- std::cerr << "[complete] Attempt to cancel timer...\n";
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "Start activationStatusTimer for status.");
try
{
timer.stop();
- activation_status_timer.start(
+ activationStatusTimer.start(
std::chrono::microseconds(3000000), true);
}
- catch (std::exception &e)
+ catch (const std::exception &e)
{
- std::cerr << "[complete] cancel timer error: " << e.what()
- << "\n";
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Exception caught in start activationStatusTimer.",
+ phosphor::logging::entry("ERROR=%s", e.what()));
}
- fw_update_status.set_software_obj_path(obj_path.str);
- activate_image(obj_path.str.c_str());
- if (DEBUG)
- std::cerr << "[complete] returned from activeImage()\n";
-
- fw_update_matcher = nullptr;
+ fwUpdateStatus.updateActivationPercent(objPath.str);
+ activateImage(objPath.str);
}
}
};
// Adding matcher
- fw_update_matcher = std::make_unique<sdbusplus::bus::match::match>(
+ fwUpdateMatchSignal = std::make_unique<sdbusplus::bus::match::match>(
*getSdBus(),
"interface='org.freedesktop.DBus.ObjectManager',type='signal',"
"member='InterfacesAdded',path='/xyz/openbmc_project/software'",
callback);
}
-
-class MappedFile
+static bool startFirmwareUpdate(const std::string &uri)
{
- public:
- MappedFile(const std::string &fname) : addr(nullptr), fsize(0)
- {
- std::error_code ec;
- size_t sz = std::filesystem::file_size(fname, ec);
- int fd = open(fname.c_str(), O_RDONLY);
- if (!ec || fd < 0)
- {
- return;
- }
- void *tmp = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0);
- close(fd);
- if (tmp == MAP_FAILED)
- {
- return;
- }
- addr = tmp;
- fsize = sz;
- }
+ // fwupdate URIs start with file:// or usb:// or tftp:// etc. By the time
+ // the code gets to this point, the file should be transferred start the
+ // request (creating a new file in /tmp/images causes the update manager to
+ // check if it is ready for activation)
+ static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatchSignal;
+ postTransferCompleteHandler(fwUpdateMatchSignal);
+ std::filesystem::rename(
+ uri, "/tmp/images/" +
+ boost::uuids::to_string(boost::uuids::random_generator()()));
+ return true;
+}
- ~MappedFile()
- {
- if (addr)
- {
- munmap(addr, fsize);
- }
- }
- const uint8_t *data() const
- {
- return static_cast<const uint8_t *>(addr);
- }
- size_t size() const
- {
- return fsize;
- }
-
- private:
- size_t fsize;
- void *addr;
-};
-
-static int transfer_from_file(const std::string &uri, bool move = true)
+static int transferImageFromFile(const std::string &uri, bool move = true)
{
std::error_code ec;
- if (DEBUG)
- std::cerr << "transfer_from_file(" << uri << ")\n";
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "Transfer Image From File.",
+ phosphor::logging::entry("URI=%s", uri.c_str()));
if (move)
{
- std::filesystem::rename(uri, FIRMWARE_BUFFER_FILE, ec);
+ std::filesystem::rename(uri, firmwareBufferFile, ec);
}
else
{
- std::filesystem::copy(uri, FIRMWARE_BUFFER_FILE,
+ std::filesystem::copy(uri, firmwareBufferFile,
std::filesystem::copy_options::overwrite_existing,
ec);
}
- if (xfer_hash_check)
+ if (xferHashCheck)
{
MappedFile mappedfw(uri);
- xfer_hash_check->hash(
+ xferHashCheck->hash(
{mappedfw.data(), mappedfw.data() + mappedfw.size()});
}
if (ec.value())
{
- std::cerr << "cp/mv returns: " << ec.message() << "(" << ec.value()
- << ")\n";
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Image copy failed.");
}
return ec.value();
}
@@ -798,265 +649,116 @@
return execProg.exit_code();
}
-constexpr char USB_CTRL_PATH[] = "/usr/bin/usb-ctrl";
-constexpr char FWUPDATE_MOUNT_POINT[] = "/tmp/usb-fwupd.mnt";
-constexpr char FWUPDATE_USB_VOL_IMG[] = "/tmp/usb-fwupd.img";
-constexpr char FWUPDATE_USB_DEV_NAME[] = "fw-usb-mass-storage-dev";
-constexpr size_t fwPathMaxLength = 255;
-static int transfer_from_usb(const std::string &uri)
+static int transferImageFromUsb(const std::string &uri)
{
int ret, sysret;
char fwpath[fwPathMaxLength];
- if (DEBUG)
- std::cerr << "transfer_from_usb(" << uri << ")\n";
- ret = executeCmd(USB_CTRL_PATH, "mount", FWUPDATE_USB_VOL_IMG,
- FWUPDATE_MOUNT_POINT);
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "Transfer Image From USB.",
+ phosphor::logging::entry("URI=%s", uri.c_str()));
+ ret = executeCmd(usbCtrlPath, "mount", fwUpdateUsbVolImage,
+ fwUpdateMountPoint);
if (ret)
{
return ret;
}
- std::string usb_path = std::string(FWUPDATE_MOUNT_POINT) + "/" + uri;
- ret = transfer_from_file(usb_path, false);
+ std::string usb_path = std::string(fwUpdateMountPoint) + "/" + uri;
+ ret = transferImageFromFile(usb_path, false);
- executeCmd(USB_CTRL_PATH, "cleanup", FWUPDATE_USB_VOL_IMG,
- FWUPDATE_MOUNT_POINT);
+ executeCmd(usbCtrlPath, "cleanup", fwUpdateUsbVolImage, fwUpdateMountPoint);
return ret;
}
-static bool transfer_firmware_from_uri(const std::string &uri)
+static bool transferFirmwareFromUri(const std::string &uri)
{
- static constexpr char FW_URI_FILE[] = "file://";
- static constexpr char FW_URI_USB[] = "usb://";
- if (DEBUG)
- std::cerr << "transfer_firmware_from_uri(" << uri << ")\n";
- if (boost::algorithm::starts_with(uri, FW_URI_FILE))
+ static constexpr char fwUriFile[] = "file://";
+ static constexpr char fwUriUsb[] = "usb://";
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "Transfer Image From URI.",
+ phosphor::logging::entry("URI=%s", uri.c_str()));
+ if (boost::algorithm::starts_with(uri, fwUriFile))
{
- std::string fname = uri.substr(sizeof(FW_URI_FILE) - 1);
- if (fname != FIRMWARE_BUFFER_FILE)
+ std::string fname = uri.substr(sizeof(fwUriFile) - 1);
+ if (fname != firmwareBufferFile)
{
- return 0 == transfer_from_file(fname);
+ return 0 == transferImageFromFile(fname);
}
return true;
}
- if (boost::algorithm::starts_with(uri, FW_URI_USB))
+ if (boost::algorithm::starts_with(uri, fwUriUsb))
{
- std::string fname = uri.substr(sizeof(FW_URI_USB) - 1);
- return 0 == transfer_from_usb(fname);
+ std::string fname = uri.substr(sizeof(fwUriUsb) - 1);
+ return 0 == transferImageFromUsb(fname);
}
return false;
}
/* Get USB-mass-storage device status: inserted => true, ejected => false */
-static int usb_get_status()
+static bool getUsbStatus()
{
- static constexpr char usb_gadget_base[] = "/sys/kernel/config/usb_gadget/";
- auto usb_device =
- std::filesystem::path(usb_gadget_base) / FWUPDATE_USB_DEV_NAME;
- std::error_code ec;
- return std::filesystem::exists(usb_device, ec) && !ec;
+ std::filesystem::path usbDevPath =
+ std::filesystem::path("/sys/kernel/config/usb_gadget") /
+ fwUpdateUSBDevName;
+ return (std::filesystem::exists(usbDevPath) ? true : false);
}
/* Insert the USB-mass-storage device status: success => 0, failure => non-0 */
-static int usb_attach_device()
+static int attachUsbDevice()
{
- if (usb_get_status())
+ if (getUsbStatus())
{
return 1;
}
- int ret =
- executeCmd(USB_CTRL_PATH, "setup", FWUPDATE_USB_VOL_IMG,
- std::to_string(FIRMWARE_BUFFER_MAX_SIZE / 1_MB).c_str());
+ int ret = executeCmd(usbCtrlPath, "setup", fwUpdateUsbVolImage,
+ std::to_string(maxFirmwareImageSize / 1_MB).c_str());
if (!ret)
{
- ret = executeCmd(USB_CTRL_PATH, "insert", FWUPDATE_USB_DEV_NAME,
- FWUPDATE_USB_VOL_IMG);
+ ret = executeCmd(usbCtrlPath, "insert", fwUpdateUSBDevName,
+ fwUpdateUsbVolImage);
}
return ret;
}
/* Eject the USB-mass-storage device status: success => 0, failure => non-0 */
-static int usb_detach_device()
+static int detachUsbDevice()
{
- if (!usb_get_status())
+ if (!getUsbStatus())
{
return 1;
}
- return executeCmd(USB_CTRL_PATH, "eject", FWUPDATE_USB_DEV_NAME);
+ return executeCmd(usbCtrlPath, "eject", fwUpdateUSBDevName);
}
-
-constexpr uint8_t controls_init = 0x00;
-constexpr uint8_t controls_transfer_started = 0x01;
-constexpr uint8_t controls_transfer_completed = 0x02;
-constexpr uint8_t controls_transfer_aborted = 0x04;
-constexpr uint8_t controls_usb_attached = 0x08;
-
-struct fw_update_control_request
+static uint8_t getActiveBootImage(ipmi::Context::ptr ctx)
{
- enum knob
+ constexpr uint8_t primaryImage = 0x01;
+ constexpr uint8_t secondaryImage = 0x02;
+ constexpr const char *secondaryFitImageStartAddr = "22480000";
+
+ uint8_t bootImage = primaryImage;
+ boost::system::error_code ec;
+ std::string value = ctx->bus->yield_method_call<std::string>(
+ ctx->yield, ec, "xyz.openbmc_project.U_Boot.Environment.Manager",
+ "/xyz/openbmc_project/u_boot/environment/mgr",
+ "xyz.openbmc_project.U_Boot.Environment.Manager", "Read", "bootcmd");
+ if (ec)
{
- CTRL_GET = 0,
- CTRL_XFER_START,
- CTRL_XFER_COMPLETE,
- CTRL_XFER_ABORT,
- CTRL_SET_FILENAME,
- CTRL_USB_ATTACH,
- CTRL_USB_DETACH,
- } __attribute__((packed));
- enum knob control;
- uint8_t nlen;
- char filename[fwPathMaxLength];
-} __attribute__((packed));
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Failed to read the bootcmd value");
+ return ipmi::ccUnspecifiedError;
+ }
-static ipmi_ret_t ipmi_firmware_control(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
- ipmi_request_t request,
- ipmi_response_t response,
- ipmi_data_len_t data_len,
- ipmi_context_t context)
-{
- static std::string fw_xfer_uri;
-
- if (DEBUG)
- std::cerr << "FW update control\n";
- *data_len = 0;
-
- static uint8_t controls = controls_init;
- ipmi_ret_t rc = IPMI_CC_OK;
- auto ctrl_req = reinterpret_cast<fw_update_control_request *>(request);
- auto ctrl_resp = reinterpret_cast<uint8_t *>(response);
-
- if (usb_get_status())
+ /* cheking for secondary FitImage Address 22480000 */
+ if (value.find(secondaryFitImageStartAddr) != std::string::npos)
{
- controls |= controls_usb_attached;
+ bootImage = secondaryImage;
}
else
{
- controls &= ~controls_usb_attached;
+ bootImage = primaryImage;
}
- switch (ctrl_req->control)
- {
- case fw_update_control_request::CTRL_GET:
- break;
- case fw_update_control_request::CTRL_XFER_START:
- {
- controls |= controls_transfer_started;
- // reset buffer to empty (truncate file)
- std::ofstream out(FIRMWARE_BUFFER_FILE,
- std::ofstream::binary | std::ofstream::trunc);
- fw_xfer_uri = std::string("file://") + FIRMWARE_BUFFER_FILE;
- if (xfer_hash_check)
- {
- xfer_hash_check->clear();
- }
-#ifdef INTEL_PFR_ENABLED
- imgLength = 0;
- imgType = 0;
- block0Mapped = false;
-#endif
- if (DEBUG)
- std::cerr << "transfer start\n";
- }
- break;
- case fw_update_control_request::CTRL_XFER_COMPLETE:
- {
- if (usb_get_status())
- {
- rc = IPMI_CC_REQ_INVALID_PHASE;
- }
- // finish transfer based on URI
- if (!transfer_firmware_from_uri(fw_xfer_uri))
- {
- rc = IPMI_CC_UNSPECIFIED_ERROR;
- break;
- }
- // transfer complete
- if (xfer_hash_check)
- {
- if (transfer_hash_check::CHECK_PASSED_SHA2 !=
- xfer_hash_check->check())
- {
- if (DEBUG)
- std::cerr << "xfer_hash_check returns not "
- "CHECK_PASSED_SHA2\n";
- rc = IPMI_CC_UNSPECIFIED_ERROR;
- break;
- }
- }
- // start the request
- if (!request_start_firmware_update(FIRMWARE_BUFFER_FILE))
- {
- if (DEBUG)
- std::cerr
- << "request_start_firmware_update returns failure\n";
- rc = IPMI_CC_UNSPECIFIED_ERROR;
- }
- if (rc == IPMI_CC_OK)
- {
- controls |= controls_transfer_completed;
- }
- }
- break;
- case fw_update_control_request::CTRL_XFER_ABORT:
- if (DEBUG)
- std::cerr << "send abort request\n";
- if (usb_get_status())
- {
- if (0 != usb_detach_device())
- {
- rc = IPMI_CC_USB_ATTACH_FAIL;
- }
- }
- fw_update_status.firmwareUpdateAbortState();
- controls |= controls_transfer_aborted;
- break;
- case fw_update_control_request::CTRL_SET_FILENAME:
- fw_xfer_uri.clear();
- fw_xfer_uri.insert(0, ctrl_req->filename, ctrl_req->nlen);
- break;
- case fw_update_control_request::CTRL_USB_ATTACH:
- if (usb_get_status())
- {
- rc = IPMI_CC_INVALID_FIELD_REQUEST;
- }
- else if (0 != usb_attach_device())
- {
- rc = IPMI_CC_USB_ATTACH_FAIL;
- }
- else
- {
- rc = IPMI_CC_OK;
- }
- break;
- case fw_update_control_request::CTRL_USB_DETACH:
- if (!usb_get_status())
- {
- rc = IPMI_CC_INVALID_FIELD_REQUEST;
- }
- if (0 != usb_detach_device())
- {
- rc = IPMI_CC_USB_ATTACH_FAIL;
- }
- else
- {
- rc = IPMI_CC_OK;
- }
- break;
- default:
- if (DEBUG)
- std::cerr << "control byte " << std::hex << ctrl_req->control
- << " unknown\n";
- rc = IPMI_CC_INVALID_FIELD_REQUEST;
- break;
- }
-
- if (rc == IPMI_CC_OK)
- {
- *ctrl_resp = controls;
- *data_len = sizeof(*ctrl_resp);
- }
-
- return rc;
+ return bootImage;
}
#ifdef INTEL_PFR_ENABLED
@@ -1142,434 +844,16 @@
return ipmi::responseSuccess(fwVerInfoList.size(), fwVerInfoList);
}
-#endif
-
-struct fw_security_revision_info
+using fwSecurityVersionInfoType = std::tuple<uint8_t, // ID Tag
+ uint8_t, // BKC Version
+ uint8_t>; // SVN Version
+ipmi::RspType<uint8_t, std::vector<fwSecurityVersionInfoType>>
+ ipmiGetFwSecurityVersionInfo()
{
- uint8_t id_tag;
- uint16_t sec_rev;
-} __attribute__((packed));
-
-static ipmi_ret_t ipmi_firmware_get_fw_security_revision(
- ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
- ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
-{
- if (DEBUG)
- std::cerr << "Get FW security revision info\n";
-
- // Byte 1 - Count (N) Number of devices data is being returned for.
- // Byte 2 - ID Tag 00 – reserved 01 – BMC Active Image 02 – BBU Active Image
- // 03 – BMC Backup Image 04 – BBU Backup Image 05 – BBR
- // Image
- // Byte 3 - Major Version Number
- // Byte 4 - Minor Version Number
- // Bytes 5:8 - Build Number
- // Bytes 9:12 - Build Timestamp Format: LSB first, same format as SEL
- // timestamp
- // Bytes 13:16 - Update Timestamp
- // Bytes - 17:(15xN) - Repeat of 2 through 16
-
- uint8_t count = 0;
- auto ret_count = reinterpret_cast<uint8_t *>(response);
- auto info =
- reinterpret_cast<struct fw_security_revision_info *>(ret_count + 1);
-
- std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
- for (uint8_t id_tag = 1; id_tag < 6; id_tag++)
- {
- const char *fw_path;
- switch (id_tag)
- {
- case 1:
- fw_path = FW_UPDATE_ACTIVE_INFO_PATH;
- break;
- case 2:
- fw_path = FW_UPDATE_BACKUP_INFO_PATH;
- break;
- case 3:
- case 4:
- case 5:
- continue; // skip for now
- break;
- }
- auto method =
- bus->new_method_call(FW_UPDATE_SERVER_DBUS_NAME, fw_path,
- "org.freedesktop.DBus.Properties", "GetAll");
- method.append(FW_UPDATE_INFO_INTERFACE, "security_version");
- ipmi::DbusVariant sec_rev;
- try
- {
- auto reply = bus->call(method);
-
- if (reply.is_method_error())
- continue;
-
- reply.read(sec_rev);
- }
- catch (sdbusplus::exception::SdBusError &e)
- {
- std::cerr << "SDBus Error: " << e.what();
- return IPMI_CC_UNSPECIFIED_ERROR;
- }
-
- info->id_tag = id_tag;
- info->sec_rev = std::get<int>(sec_rev);
- count++;
- info++;
- }
- *ret_count = count;
-
- // Status code.
- ipmi_ret_t rc = IPMI_CC_OK;
- *data_len = sizeof(count) + count * sizeof(*info);
-
- return rc;
+ // TODO: Need to add support.
+ return ipmi::responseInvalidCommand();
}
-struct fw_channel_size
-{
- uint8_t channel_id;
- uint32_t channel_size;
-} __attribute__((packed));
-
-enum
-{
- CHANNEL_RESVD = 0,
- CHANNEL_KCS,
- CHANNEL_RMCP_PLUS,
- CHANNEL_USB_DATA,
- CHANNEL_USB_MASS_STORAGE,
-} channel_transfer_type;
-
-static constexpr uint8_t channelListSize = 2;
-/** @brief implements Maximum Firmware Transfer size command
- * @parameter
- * - none
- * @returns IPMI completion code plus response data
- * - count - channel count
- * - channelList - channel list information
- */
-ipmi::RspType<uint8_t, // channel count
- std::array<std::tuple<uint8_t, uint32_t>,
- channelListSize> // channel
- // list
- >
- ipmiFirmwareMaxTransferSize()
-{
- constexpr uint8_t KCSMaxBufSize = 128;
- constexpr uint32_t RMCPPLUSMaxBufSize = 50 * 1024;
- if (DEBUG)
- std::cerr << "Get FW max transfer size\n";
- // Byte 1 - Count (N) Number of devices data is being returned for.
- // Byte 2 - ID Tag 00 – reserved 01 – kcs 02 – rmcp+,
- // 03 – usb data, 04 – usb mass storage
- // Byte 3-6 - transfer size (little endian)
- // Bytes - 7:(5xN) - Repeat of 2 through 6
- constexpr std::array<std::tuple<uint8_t, uint32_t>, channelListSize>
- channelList = {{{CHANNEL_KCS, KCSMaxBufSize},
- {CHANNEL_RMCP_PLUS, RMCPPLUSMaxBufSize}}};
- return ipmi::responseSuccess(channelListSize, channelList);
-}
-
-enum
-{
- EXEC_CTX_RESVD = 0,
- EXEC_CTX_FULL_LINUX = 0x10,
- EXEC_CTX_SAFE_MODE_LINUX = 0x11,
-} bmc_execution_context;
-
-struct fw_execution_context
-{
- uint8_t context;
- uint8_t image_selection;
-} __attribute__((packed));
-
-static ipmi_ret_t ipmi_firmware_get_fw_execution_context(
- ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
- ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
-{
- if (DEBUG)
- std::cerr << "Get FW execution context\n";
-
- // Byte 1 - execution context
- // 0x10 - full linux stack, 0x11 - safe-mode linux stack
- // Byte 2 - current image selection
- // 1 - primary, 2 - secondary
-
- auto info = reinterpret_cast<struct fw_execution_context *>(response);
- info->context = EXEC_CTX_FULL_LINUX;
-
- info->image_selection = getActiveBootImage();
-
- // Status code.
- ipmi_ret_t rc = IPMI_CC_OK;
- *data_len = sizeof(*info);
-
- return rc;
-}
-
-uint8_t getActiveBootImage(void)
-{
- // 0x01 - primaryImage
- constexpr uint8_t primaryImage = 0x01;
- // 0x02 - secondaryImage
- constexpr uint8_t secondaryImage = 0x02;
- uint8_t bootImage = primaryImage;
-
- std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
- auto method = bus->new_method_call(
- "xyz.openbmc_project.U_Boot.Environment.Manager",
- "/xyz/openbmc_project/u_boot/environment/mgr",
- "xyz.openbmc_project.U_Boot.Environment.Manager", "Read");
- method.append("bootcmd");
- std::string value;
- try
- {
- auto reply = bus->call(method);
- reply.read(value);
- }
- catch (sdbusplus::exception::SdBusError &e)
- {
- std::cerr << "SDBus Error: " << e.what();
- return IPMI_CC_UNSPECIFIED_ERROR;
- }
- /* cheking for secondary FitImage Address 22480000 */
- if (value.find(secondaryFitImageStartAddr) != std::string::npos)
- {
- bootImage = secondaryImage;
- }
- else
- {
- bootImage = primaryImage;
- }
-
- return bootImage;
-}
-/** @brief implements firmware get status command
- * @parameter
- * - none
- * @returns IPMI completion code plus response data
- * - status - processing status
- * - percentage - percentage completion
- * - check - channel integrity check status
- **/
-ipmi::RspType<uint8_t, // status
- uint8_t, // percentage
- uint8_t // check
- >
- ipmiFrmwareGetStatus()
-
-{
- if (DEBUG)
- std::cerr << "Get FW update status\n";
- // Byte 1 - status (0=init, 1=idle, 2=download, 3=validate, 4=write,
- // 5=ready, f=error, 83=ac cycle required)
- // Byte 2 - percent
- // Byte 3 - integrity check status (0=none, 1=req, 2=sha2ok, e2=sha2fail)
- uint8_t status = fw_update_status.state();
- uint8_t percent = fw_update_status.percent();
- uint8_t check = xfer_hash_check ? xfer_hash_check->status() : 0;
-
- // Status code.
- return ipmi::responseSuccess(status, percent, check);
-}
-
-static constexpr uint8_t FW_UPDATE_OPTIONS_NO_DOWNREV = (1 << 0);
-static constexpr uint8_t FW_UPDATE_OPTIONS_DEFER_RESTART = (1 << 1);
-static constexpr uint8_t FW_UPDATE_OPTIONS_SHA2_CHECK = (1 << 2);
-static constexpr uint8_t FW_UPDATE_OPTIONS_RESVD1 = (1 << 3);
-struct fw_update_options_request
-{
- uint8_t mask;
- uint8_t options;
-} __attribute__((packed));
-
-uint32_t fw_update_options = 0;
-static ipmi_ret_t ipmi_firmware_update_options(
- ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
- ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
-{
- if (DEBUG)
- std::cerr << "Get/set FW update options\n";
-
- // request:
- // Byte 1 - mask
- // Byte 2 - options
- // Byte 3-34 - optional integrity check expected value
- // response:
- // Byte 1 - set options
-
- auto fw_options =
- reinterpret_cast<struct fw_update_options_request *>(request);
-
- const char *path = FW_UPDATE_SERVER_INFO_PATH;
- const char *iface = FW_UPDATE_SECURITY_INTERFACE;
- if ((fw_options->mask & FW_UPDATE_OPTIONS_NO_DOWNREV) &&
- (fw_options->options & FW_UPDATE_OPTIONS_NO_DOWNREV) !=
- (fw_update_options & FW_UPDATE_OPTIONS_NO_DOWNREV))
- {
- if (fw_options->options & FW_UPDATE_OPTIONS_NO_DOWNREV)
- {
- fw_update_options |= FW_UPDATE_OPTIONS_NO_DOWNREV;
- /*setting flag to flase for deferring downgrade support*/
- fw_update_status.setInhibitDowngrade(true);
- }
- else
- {
- fw_update_options &= ~FW_UPDATE_OPTIONS_NO_DOWNREV;
- /*setting flag to true for downgrade support*/
- fw_update_status.setInhibitDowngrade(false);
- }
- }
- if ((fw_options->mask & FW_UPDATE_OPTIONS_DEFER_RESTART) &&
- (fw_options->options & FW_UPDATE_OPTIONS_DEFER_RESTART) !=
- (fw_update_options & FW_UPDATE_OPTIONS_DEFER_RESTART))
- {
- if (fw_options->options & FW_UPDATE_OPTIONS_DEFER_RESTART)
- {
- fw_update_options |= FW_UPDATE_OPTIONS_DEFER_RESTART;
- /* setting flag to true to stop image activation */
- fw_update_status.setDeferRestart(true);
- }
- else
- {
- /* setting flag to false for image activation */
- fw_update_options &= ~FW_UPDATE_OPTIONS_DEFER_RESTART;
- fw_update_status.setDeferRestart(false);
- }
- }
- if (fw_options->mask & FW_UPDATE_OPTIONS_SHA2_CHECK)
- {
- auto hash_size = EVP_MD_size(EVP_sha256());
- if (fw_options->options & FW_UPDATE_OPTIONS_SHA2_CHECK)
- {
- if (*data_len != (sizeof(*fw_options) + hash_size))
- {
- *data_len = 0;
- return IPMI_CC_REQ_DATA_LEN_INVALID;
- }
- xfer_hash_check = std::make_shared<transfer_hash_check>();
- auto exp_hash = reinterpret_cast<uint8_t *>(fw_options + 1);
- xfer_hash_check->init({exp_hash, exp_hash + hash_size});
- fw_update_options |= FW_UPDATE_OPTIONS_SHA2_CHECK;
- }
- else
- {
- fw_update_options &= ~FW_UPDATE_OPTIONS_SHA2_CHECK;
- // delete the xfer_hash_check object
- xfer_hash_check.reset();
- }
- }
- auto options_rsp = reinterpret_cast<uint8_t *>(response);
- *options_rsp = fw_update_options;
-
- if (DEBUG)
- std::cerr << "current fw_update_options = " << std::hex
- << fw_update_options << '\n';
- // Status code.
- *data_len = sizeof(*options_rsp);
- return IPMI_CC_OK;
-}
-
-struct fw_cert_info
-{
- uint16_t cert_len;
- uint64_t serial;
- uint8_t subject_len;
- char subject[255];
-} __attribute__((packed));
-
-static ipmi_ret_t ipmi_firmware_get_root_cert_info(
- ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
- ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
-{
- if (DEBUG)
- std::cerr << "Get FW root cert info\n";
-
- // request:
- // Byte 1 - certificate ID: request which certificate (ignored)
-
- // response:
- // Byte 1-2 - certificate length (little endian)
- // Byte 3-10 - serial number (little endian)
- // Byte 11 - subject length
- // Byte 12-N - subject data
-
- auto cert_info = reinterpret_cast<struct fw_cert_info *>(response);
- std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
- auto method = bus->new_method_call(
- FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_INFO_PATH,
- "org.freedesktop.DBus.Properties", "GetAll");
- method.append(FW_UPDATE_SECURITY_INTERFACE);
- std::string subject;
- uint64_t serial;
- std::string cert;
- try
- {
- auto reply = bus->call(method);
-
- std::vector<std::pair<std::string, ipmi::DbusVariant>> properties;
- reply.read(properties);
-
- for (const auto &t : properties)
- {
- auto key = t.first;
- auto value = t.second;
- if (key == "certificate_subject")
- {
- subject = std::get<std::string>(value);
- }
- else if (key == "cetificate_serial")
- {
- serial = std::get<uint64_t>(value);
- }
- else if (key == "certificate")
- {
- cert = std::get<std::string>(value);
- }
- }
- }
- catch (sdbusplus::exception::SdBusError &e)
- {
- std::cerr << "SDBus Error: " << e.what();
- return IPMI_CC_UNSPECIFIED_ERROR;
- }
-
- cert_info->cert_len = cert.size();
- cert_info->serial = serial;
- // truncate subject so it fits in the 255-byte array (if necessary)
- if (subject.size() > sizeof(cert_info->subject))
- subject.resize(sizeof(cert_info->subject));
- cert_info->subject_len = subject.size();
- std::copy(subject.begin(), subject.end(), cert_info->subject);
-
- // Status code.
- ipmi_ret_t rc = IPMI_CC_OK;
- // make sure to account for the *actual* size of the subject
- *data_len = sizeof(*cert_info) - sizeof(cert_info->subject) +
- cert_info->subject_len;
-
- return rc;
-}
-
-#ifdef INTEL_PFR_ENABLED
-enum class FwGetRootCertDataTag : uint8_t
-{
- activeRootKey = 1,
- recoveryRootKey,
- activeCSK,
- recoveryCSK,
-};
-
-static constexpr char *bmcActivePfmMTDDev = "/dev/mtd/pfm";
-static constexpr char *bmcRecoveryImgMTDDev = "/dev/mtd/rc-image";
-static constexpr size_t pfmBaseOffsetInImage = 0x400;
-static constexpr size_t rootkeyOffsetInPfm = 0xA0;
-static constexpr size_t cskKeyOffsetInPfm = 0x124;
-static constexpr size_t cskSignatureOffsetInPfm = 0x19c;
-static constexpr size_t certKeyLen = 96;
-static constexpr size_t cskSignatureLen = 96;
-
ipmi::RspType<std::array<uint8_t, certKeyLen>,
std::optional<std::array<uint8_t, cskSignatureLen>>>
ipmiGetFwRootCertData(uint8_t certId)
@@ -1637,7 +921,440 @@
return ipmi::responseSuccess(certKey, std::nullopt);
}
+#endif // INTEL_PFR_ENABLED
+
+static constexpr uint8_t channelListSize = 3;
+/** @brief implements Maximum Firmware Transfer size command
+ * @parameter
+ * - none
+ * @returns IPMI completion code plus response data
+ * - count - channel count
+ * - channelList - channel list information
+ */
+ipmi::RspType<uint8_t, // channel count
+ std::array<std::tuple<uint8_t, uint32_t>,
+ channelListSize> // Channel List
+ >
+ ipmiFirmwareMaxTransferSize()
+{
+ constexpr size_t kcsMaxBufSize = 128;
+ constexpr size_t rmcpPlusMaxBufSize = 50 * 1024;
+ constexpr size_t ipmbMaxBufSize = 4 * 1024;
+ // Byte 1 - Count (N) Number of devices data is being returned for.
+ // Byte 2 - ID Tag 00 – reserved 01 – kcs 02 – rmcp+, 03 - ipmb
+ // Byte 3-6 - transfer size (little endian)
+ // Bytes - 7:(5xN) - Repeat of 2 through 6
+ constexpr std::array<std::tuple<uint8_t, uint32_t>, channelListSize>
+ channelList = {
+ {{static_cast<uint8_t>(ChannelIdTag::kcs), kcsMaxBufSize},
+ {static_cast<uint8_t>(ChannelIdTag::ipmb), ipmbMaxBufSize},
+ {static_cast<uint8_t>(ChannelIdTag::rmcpPlus),
+ rmcpPlusMaxBufSize}}};
+
+ return ipmi::responseSuccess(channelListSize, channelList);
+}
+
+ipmi::RspType<uint8_t, uint8_t>
+ ipmiGetBmcExecutionContext(ipmi::Context::ptr ctx)
+{
+ // Byte 1 - Current execution context
+ // 0x10 - Linux OS, 0x11 - Bootloader, Forced-firmware updat mode
+ // Byte 2 - Partition pointer
+ // 0x01 - primary, 0x02 - secondary
+ uint8_t partitionPtr = getActiveBootImage(ctx);
+
+ return ipmi::responseSuccess(
+ static_cast<uint8_t>(BmcExecutionContext::linuxOs), partitionPtr);
+}
+
+/** @brief Get Firmware Update Random Number
+ *
+ * This function generate the random number used for
+ * setting the firmware update mode as authentication key.
+ *
+ * @parameter : None
+ * @returns IPMI completion code along with
+ * - random number
+ **/
+ipmi::RspType<std::array<uint8_t, fwRandomNumLength>>
+ ipmiGetFwUpdateRandomNumber()
+{
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "Generate FW update random number");
+ std::random_device rd;
+ std::default_random_engine gen(rd());
+ std::uniform_int_distribution<> dist{0, 255};
+
+ fwRandomNumGenTs = std::chrono::steady_clock::now();
+
+ for (int i = 0; i < fwRandomNumLength; i++)
+ {
+ fwRandomNum[i] = dist(gen);
+ }
+
+ return ipmi::responseSuccess(fwRandomNum);
+}
+
+/** @brief Set Firmware Update Mode
+ *
+ * This function sets BMC into firmware update mode
+ * after validating Random number obtained from the Get
+ * Firmware Update Random Number command
+ *
+ * @parameter
+ * - randNum - Random number(token)
+ * @returns IPMI completion code
+ **/
+ipmi::RspType<>
+ ipmiSetFirmwareUpdateMode(std::array<uint8_t, fwRandomNumLength> &randNum)
+{
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "Start FW update mode");
+ /* Firmware Update Random number is valid for 30 seconds only */
+ auto timeElapsed = (std::chrono::steady_clock::now() - fwRandomNumGenTs);
+ if (std::chrono::duration_cast<std::chrono::microseconds>(timeElapsed)
+ .count() > std::chrono::duration_cast<std::chrono::microseconds>(
+ fwRandomNumExpirySeconds)
+ .count())
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "Firmware update random number expired.");
+ return ipmi::responseInvalidFieldRequest();
+ }
+
+ /* Validate random number */
+ for (int i = 0; i < fwRandomNumLength; i++)
+ {
+ if (fwRandomNum[i] != randNum[i])
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "Invalid random number specified.");
+ return ipmi::responseInvalidFieldRequest();
+ }
+ }
+
+ try
+ {
+ if (getFirmwareUpdateMode())
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "Already firmware update is in progress.");
+ return ipmi::responseBusy();
+ }
+ }
+ catch (const std::exception &e)
+ {
+ return ipmi::responseUnspecifiedError();
+ }
+
+ // FIXME? c++ doesn't off an option for exclusive file creation
+ FILE *fp = fopen(firmwareBufferFile, "wx");
+ if (!fp)
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "Unable to open file.");
+ return ipmi::responseUnspecifiedError();
+ }
+ fclose(fp);
+
+ try
+ {
+ setFirmwareUpdateMode(true);
+ }
+ catch (const std::exception &e)
+ {
+ unlink(firmwareBufferFile);
+ return ipmi::responseUnspecifiedError();
+ }
+
+ return ipmi::responseSuccess();
+}
+
+/** @brief implements exit firmware update mode command
+ * @param None
+ *
+ * @returns IPMI completion code
+ */
+ipmi::RspType<> ipmiExitFirmwareUpdateMode()
+{
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "Exit FW update mode");
+ switch (fwUpdateStatus.getState())
+ {
+ case FwUpdateStatusCache::fwStateInit:
+ case FwUpdateStatusCache::fwStateIdle:
+ return ipmi::responseInvalidFieldRequest();
+ break;
+ case FwUpdateStatusCache::fwStateDownload:
+ case FwUpdateStatusCache::fwStateVerify:
+ break;
+ case FwUpdateStatusCache::fwStateProgram:
+ break;
+ case FwUpdateStatusCache::fwStateUpdateSuccess:
+ case FwUpdateStatusCache::fwStateError:
+ break;
+ case FwUpdateStatusCache::fwStateAcCycleRequired:
+ return ipmi::responseInvalidFieldRequest();
+ break;
+ }
+ fwUpdateStatus.firmwareUpdateAbortState();
+
+ try
+ {
+ setFirmwareUpdateMode(false);
+ }
+ catch (const std::exception &e)
+ {
+ return ipmi::responseUnspecifiedError();
+ }
+
+ return ipmi::responseSuccess();
+}
+
+/** @brief implements Get/Set Firmware Update Control
+ * @parameter
+ * - Byte 1: Control Byte
+ * - Byte 2: Firmware filename length (Optional)
+ * - Byte 3:N: Firmware filename data (Optional)
+ * @returns IPMI completion code plus response data
+ * - Byte 2: Current control status
+ **/
+ipmi::RspType<bool, bool, bool, bool, uint4_t>
+ ipmiGetSetFirmwareUpdateControl(const uint8_t controlReq,
+ const std::optional<std::string> &fileName)
+{
+ static std::string fwXferUriPath;
+ static bool imageTransferStarted = false;
+ static bool imageTransferCompleted = false;
+ static bool imageTransferAborted = false;
+
+ if ((controlReq !=
+ static_cast<uint8_t>(FwUpdateCtrlReq::setFirmwareFilename)) &&
+ (fileName))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Invalid request field (Filename).");
+ return ipmi::responseInvalidFieldRequest();
+ }
+
+ static bool usbAttached = getUsbStatus();
+
+ switch (static_cast<FwUpdateCtrlReq>(controlReq))
+ {
+ case FwUpdateCtrlReq::getCurrentControlStatus:
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "ipmiGetSetFirmwareUpdateControl: Get status");
+ break;
+ case FwUpdateCtrlReq::imageTransferStart:
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "ipmiGetSetFirmwareUpdateControl: Set transfer start");
+ imageTransferStarted = true;
+ // reset buffer to empty (truncate file)
+ std::ofstream out(firmwareBufferFile,
+ std::ofstream::binary | std::ofstream::trunc);
+ fwXferUriPath = std::string("file://") + firmwareBufferFile;
+ if (xferHashCheck)
+ {
+ xferHashCheck->clear();
+ }
+ // Setting state to download
+ fwUpdateStatus.setState(
+ static_cast<uint8_t>(FwUpdateStatusCache::fwStateDownload));
+#ifdef INTEL_PFR_ENABLED
+ imgLength = 0;
+ imgType = 0;
+ block0Mapped = false;
#endif
+ }
+ break;
+ case FwUpdateCtrlReq::imageTransferComplete:
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "ipmiGetSetFirmwareUpdateControl: Set transfer complete.");
+ if (usbAttached)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "USB should be detached to perform this operation.");
+ return ipmi::responseNotSupportedInPresentState();
+ }
+ // finish transfer based on URI
+ if (!transferFirmwareFromUri(fwXferUriPath))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "transferFirmwareFromUri failed.");
+ return ipmi::responseUnspecifiedError();
+ }
+ // transfer complete
+ if (xferHashCheck)
+ {
+ if (TransferHashCheck::HashCheck::sha2Success !=
+ xferHashCheck->verify())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "xferHashCheck failed.");
+ return ipmi::responseUnspecifiedError();
+ }
+ }
+ // Set state to verify and start the update
+ fwUpdateStatus.setState(
+ static_cast<uint8_t>(FwUpdateStatusCache::fwStateVerify));
+ // start the request
+ if (!startFirmwareUpdate(firmwareBufferFile))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "startFirmwareUpdate failed.");
+ return ipmi::responseUnspecifiedError();
+ }
+ imageTransferCompleted = true;
+ }
+ break;
+ case FwUpdateCtrlReq::imageTransferAbort:
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "ipmiGetSetFirmwareUpdateControl: Set transfer abort.");
+ if (usbAttached)
+ {
+ if (detachUsbDevice())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Detach USB device failed.");
+ return ipmi::responseUsbAttachOrDetachFailed();
+ }
+ usbAttached = false;
+ }
+ // During abort request reset the state to Init by cleaning update
+ // file.
+ fwUpdateStatus.firmwareUpdateAbortState();
+ imageTransferAborted = true;
+ break;
+ case FwUpdateCtrlReq::setFirmwareFilename:
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "ipmiGetSetFirmwareUpdateControl: Set filename.");
+ if (!fileName || ((*fileName).length() == 0))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Invalid Filename specified.");
+ return ipmi::responseInvalidFieldRequest();
+ }
+
+ fwXferUriPath = *fileName;
+ break;
+ case FwUpdateCtrlReq::attachUsbDevice:
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "ipmiGetSetFirmwareUpdateControl: Attach USB device.");
+ if (usbAttached)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "USB device is already attached.");
+ return ipmi::responseInvalidFieldRequest();
+ }
+ if (attachUsbDevice())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Attach USB device failed.");
+ return ipmi::responseUsbAttachOrDetachFailed();
+ }
+ usbAttached = true;
+ break;
+ case FwUpdateCtrlReq::detachUsbDevice:
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "ipmiGetSetFirmwareUpdateControl: Detach USB device.");
+ if (!usbAttached)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "USB device is not attached.");
+ return ipmi::responseInvalidFieldRequest();
+ }
+ if (detachUsbDevice())
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Detach USB device failed.");
+ return ipmi::responseUsbAttachOrDetachFailed();
+ }
+ usbAttached = false;
+ break;
+ default:
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Invalid control option specified.");
+ return ipmi::responseInvalidFieldRequest();
+ }
+
+ return ipmi::responseSuccess(imageTransferStarted, imageTransferCompleted,
+ imageTransferAborted, usbAttached, uint4_t(0));
+}
+
+/** @brief implements firmware get status command
+ * @parameter
+ * - none
+ * @returns IPMI completion code plus response data
+ * - status - processing status
+ * - percentage - percentage completion
+ * - check - channel integrity check status
+ **/
+ipmi::RspType<uint8_t, // status
+ uint8_t, // percentage
+ uint8_t // check
+ >
+ ipmiGetFirmwareUpdateStatus()
+
+{
+ // Byte 1 - status (0=init, 1=idle, 2=download, 3=validate, 4=write,
+ // 5=ready, f=error, 83=ac cycle required)
+ // Byte 2 - percent
+ // Byte 3 - integrity check status (0=none, 1=req, 2=sha2ok, e2=sha2fail)
+ uint8_t status = fwUpdateStatus.getState();
+ uint8_t percent = fwUpdateStatus.percent();
+ uint8_t check = xferHashCheck ? xferHashCheck->status() : 0;
+
+ // Status code.
+ return ipmi::responseSuccess(status, percent, check);
+}
+
+ipmi::RspType<bool, bool, bool, uint5_t> ipmiSetFirmwareUpdateOptions(
+ bool noDowngradeMask, bool deferRestartMask, bool sha2CheckMask,
+ uint5_t reserved1, bool noDowngrade, bool deferRestart, bool sha2Check,
+ uint5_t reserved2, std::optional<std::vector<uint8_t>> integrityCheckVal)
+{
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "Set firmware update options.");
+ bool noDowngradeState = fwUpdateStatus.getInhibitDowngrade();
+ bool deferRestartState = fwUpdateStatus.getDeferRestart();
+ bool sha2CheckState = xferHashCheck ? true : false;
+
+ if (noDowngradeMask && (noDowngradeState != noDowngrade))
+ {
+ fwUpdateStatus.setInhibitDowngrade(noDowngrade);
+ noDowngradeState = noDowngrade;
+ }
+ if (deferRestartMask && (deferRestartState != deferRestart))
+ {
+ fwUpdateStatus.setDeferRestart(deferRestart);
+ deferRestartState = deferRestart;
+ }
+ if (sha2CheckMask)
+ {
+ if (sha2Check)
+ {
+ auto hashSize = EVP_MD_size(EVP_sha256());
+ if ((*integrityCheckVal).size() != hashSize)
+ {
+ phosphor::logging::log<phosphor::logging::level::DEBUG>(
+ "Invalid size of Hash specified.");
+ return ipmi::responseInvalidFieldRequest();
+ }
+ xferHashCheck = std::make_shared<TransferHashCheck>();
+ xferHashCheck->init(*integrityCheckVal);
+ }
+ else
+ {
+ // delete the xferHashCheck object
+ xferHashCheck.reset();
+ }
+ sha2CheckState = sha2CheckMask;
+ }
+ return ipmi::responseSuccess(noDowngradeState, deferRestartState,
+ sha2CheckState, reserved1);
+}
ipmi::RspType<uint32_t>
ipmiFwImageWriteData(const std::vector<uint8_t> &writeData)
@@ -1650,14 +1367,14 @@
return ipmi::responseReqDataLenInvalid();
}
- if (fw_update_status.state() != fw_update_status_cache::FW_STATE_DOWNLOAD)
+ if (fwUpdateStatus.getState() != FwUpdateStatusCache::fwStateDownload)
{
- phosphor::logging::log<phosphor::logging::level::DEBUG>(
+ phosphor::logging::log<phosphor::logging::level::ERR>(
"Invalid firmware update state.");
return ipmi::response(ccCmdNotSupportedInPresentState);
}
- std::ofstream out(FIRMWARE_BUFFER_FILE,
+ std::ofstream out(firmwareBufferFile,
std::ofstream::binary | std::ofstream::app);
if (!out)
{
@@ -1668,7 +1385,7 @@
uint64_t fileDataLen = out.tellp();
- if ((fileDataLen + writeDataLen) > FIRMWARE_BUFFER_MAX_SIZE)
+ if ((fileDataLen + writeDataLen) > maxFirmwareImageSize)
{
phosphor::logging::log<phosphor::logging::level::DEBUG>(
"Firmware image size exceeds the limit");
@@ -1679,9 +1396,9 @@
out.write(data, writeDataLen);
out.close();
- if (xfer_hash_check)
+ if (xferHashCheck)
{
- xfer_hash_check->hash(writeData);
+ xferHashCheck->hash(writeData);
}
#ifdef INTEL_PFR_ENABLED
@@ -1704,7 +1421,7 @@
{
struct PFRImageBlock0 block0Data = {0};
- std::ifstream inFile(FIRMWARE_BUFFER_FILE,
+ std::ifstream inFile(firmwareBufferFile,
std::ios::binary | std::ios::in);
inFile.read(reinterpret_cast<char *>(&block0Data), sizeof(block0Data));
inFile.close();
@@ -1728,129 +1445,81 @@
return ipmi::responseSuccess(writeDataLen);
}
-struct intc_app_get_buffer_size_resp
-{
- uint8_t kcs_size;
- uint8_t ipmb_size;
-} __attribute__((packed));
-
-static constexpr int KCS_MAX_BUFFER_SIZE = 63;
-static constexpr int IPMB_MAX_BUFFER_SIZE = 128;
-static ipmi_ret_t ipmi_intel_app_get_buffer_size(
- ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
- ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
-{
- auto msg_reply =
- reinterpret_cast<intc_app_get_buffer_size_resp *>(response);
- // for now this is hard coded; really this number is dependent on
- // the BMC kcs driver as well as the host kcs driver....
- // we can't know the latter.
- msg_reply->kcs_size = KCS_MAX_BUFFER_SIZE / 4;
- msg_reply->ipmb_size = IPMB_MAX_BUFFER_SIZE / 4;
- *data_len = sizeof(*msg_reply);
-
- return IPMI_CC_OK;
-}
-
-static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_VERSION_INFO = 0x20;
-static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_SEC_VERSION_INFO = 0x21;
-static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_UPD_CHAN_INFO = 0x22;
-static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_BMC_EXEC_CTX = 0x23;
-static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_ROOT_CERT_INFO = 0x24;
-static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_UPDATE_RAND_NUM = 0x26;
-static constexpr ipmi_cmd_t IPMI_CMD_FW_SET_FW_UPDATE_MODE = 0x27;
-static constexpr ipmi_cmd_t cmdFirmwareExitFirmwareUpdateMode = 0x28;
-static constexpr ipmi_cmd_t IPMI_CMD_FW_UPDATE_CONTROL = 0x29;
-static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_STATUS = 0x2a;
-static constexpr ipmi_cmd_t IPMI_CMD_FW_SET_FW_UPDATE_OPTIONS = 0x2b;
-static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_TIMESTAMP = 0x2d;
-static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_UPDATE_ERR_MSG = 0xe0;
-static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_REMOTE_FW_INFO = 0xf0;
-
-static constexpr ipmi_netfn_t NETFUN_INTC_APP = 0x30;
-static constexpr ipmi_cmd_t IPMI_CMD_INTC_GET_BUFFER_SIZE = 0x66;
-
-static void register_netfn_firmware_functions()
+static void registerFirmwareFunctions()
{
// guarantee that we start with an already timed out timestamp
- fw_random_number_timestamp =
- std::chrono::steady_clock::now() - FW_RANDOM_NUMBER_TTL;
+ fwRandomNumGenTs =
+ std::chrono::steady_clock::now() - fwRandomNumExpirySeconds;
+ fwUpdateStatus.setState(
+ static_cast<uint8_t>(FwUpdateStatusCache::fwStateInit));
- unlink(FIRMWARE_BUFFER_FILE);
-
- // <Get BT Interface Capabilities>
- if (DEBUG)
- std::cerr << "Registering firmware update commands\n";
+ unlink(firmwareBufferFile);
#ifdef INTEL_PFR_ENABLED
// Following commands are supported only for PFR enabled platforms
// CMD:0x20 - Get Firmware Version Information
+ // CMD:0x21 - Get Firmware Security Version Information
+ // CMD:0x25 - Get Root Certificate Data
// get firmware version information
ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
ipmi::firmware::cmdGetFwVersionInfo,
ipmi::Privilege::Admin, ipmiGetFwVersionInfo);
-#endif
+
// get firmware security version information
- ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_FW_SEC_VERSION_INFO,
- NULL, ipmi_firmware_get_fw_security_revision,
- PRIVILEGE_ADMIN);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
+ ipmi::firmware::cmdGetFwSecurityVersionInfo,
+ ipmi::Privilege::Admin, ipmiGetFwSecurityVersionInfo);
- // get channel information (max transfer sizes)
- ipmi::registerHandler(ipmi::prioOemBase, NETFUN_FIRMWARE,
- IPMI_CMD_FW_GET_FW_UPD_CHAN_INFO,
- ipmi::Privilege::Admin, ipmiFirmwareMaxTransferSize);
-
- // get bmc execution context
- ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_BMC_EXEC_CTX, NULL,
- ipmi_firmware_get_fw_execution_context,
- PRIVILEGE_ADMIN);
-
- // get root certificate information
- ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_ROOT_CERT_INFO,
- NULL, ipmi_firmware_get_root_cert_info,
- PRIVILEGE_ADMIN);
-#ifdef INTEL_PFR_ENABLED
// get root certificate data
ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
ipmi::firmware::cmdFwGetRootCertData,
ipmi::Privilege::Admin, ipmiGetFwRootCertData);
#endif
- // generate bmc fw update random number (for enter fw tranfer mode)
- ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_FW_UPDATE_RAND_NUM,
- NULL, ipmi_firmware_get_fw_random_number,
- PRIVILEGE_ADMIN);
+ // get firmware update channel information (max transfer sizes)
+ ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
+ ipmi::firmware::cmdGetFwUpdateChannelInfo,
+ ipmi::Privilege::Admin, ipmiFirmwareMaxTransferSize);
- // Set Firmware Update Mode(0x27)
- ipmi::registerHandler(ipmi::prioOemBase, NETFUN_FIRMWARE,
- IPMI_CMD_FW_SET_FW_UPDATE_MODE,
+ // get bmc execution context
+ ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
+ ipmi::firmware::cmdGetBmcExecutionContext,
+ ipmi::Privilege::Admin, ipmiGetBmcExecutionContext);
+
+ // Get Firmware Update Random number
+ ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
+ ipmi::firmware::cmdGetFwUpdateRandomNumber,
+ ipmi::Privilege::Admin, ipmiGetFwUpdateRandomNumber);
+
+ // Set Firmware Update Mode
+ ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
+ ipmi::firmware::cmdSetFirmwareUpdateMode,
ipmi::Privilege::Admin, ipmiSetFirmwareUpdateMode);
- // exit firmware update mode
+ // Exit Firmware Update Mode
ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
- cmdFirmwareExitFirmwareUpdateMode,
- ipmi::Privilege::Admin, ipmiFirmwareExitFwUpdateMode);
+ ipmi::firmware::cmdExitFirmwareUpdateMode,
+ ipmi::Privilege::Admin, ipmiExitFirmwareUpdateMode);
- // firmware control mechanism (set filename, usb, etc.)
- ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_UPDATE_CONTROL, NULL,
- ipmi_firmware_control, PRIVILEGE_ADMIN);
+ // Get/Set Firmware Update Control
+ ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
+ ipmi::firmware::cmdGetSetFwUpdateControl,
+ ipmi::Privilege::Admin,
+ ipmiGetSetFirmwareUpdateControl);
- // get firmware update status
- ipmi::registerHandler(ipmi::prioOemBase, NETFUN_FIRMWARE,
- IPMI_CMD_FW_GET_STATUS, ipmi::Privilege::Admin,
- ipmiFrmwareGetStatus);
- // set firmware update options (no downgrade, etc.)
- ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_SET_FW_UPDATE_OPTIONS,
- NULL, ipmi_firmware_update_options, PRIVILEGE_ADMIN);
+ // Get Firmware Update Status
+ ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
+ ipmi::firmware::cmdGetFirmwareUpdateStatus,
+ ipmi::Privilege::Admin, ipmiGetFirmwareUpdateStatus);
+ // Set Firmware Update Options
+ ipmi::registerHandler(ipmi::prioOemBase, ipmi::netFnFirmware,
+ ipmi::firmware::cmdSetFirmwareUpdateOptions,
+ ipmi::Privilege::Admin, ipmiSetFirmwareUpdateOptions);
// write image data
ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
ipmi::firmware::cmdFwImageWriteData,
ipmi::Privilege::Admin, ipmiFwImageWriteData);
-
- // get buffer size is used by fw update (exclusively?)
- ipmi_register_callback(NETFUN_INTC_APP, IPMI_CMD_INTC_GET_BUFFER_SIZE, NULL,
- ipmi_intel_app_get_buffer_size, PRIVILEGE_USER);
return;
}
diff --git a/src/oemcommands.cpp b/src/oemcommands.cpp
index 209edd8..9980397 100644
--- a/src/oemcommands.cpp
+++ b/src/oemcommands.cpp
@@ -3445,6 +3445,25 @@
return ipmi::responseSuccess(result);
}
+/** @brief implements the maximum size of
+ * bridgeable messages used between KCS and
+ * IPMB interfacesget security mode command.
+ *
+ * @returns IPMI completion code with following data
+ * - KCS Buffer Size (In multiples of four bytes)
+ * - IPMB Buffer Size (In multiples of four bytes)
+ **/
+ipmi::RspType<uint8_t, uint8_t> ipmiOEMGetBufferSize()
+{
+ // for now this is hard coded; really this number is dependent on
+ // the BMC kcs driver as well as the host kcs driver....
+ // we can't know the latter.
+ uint8_t kcsMaxBufferSize = 63 / 4;
+ uint8_t ipmbMaxBufferSize = 128 / 4;
+
+ return ipmi::responseSuccess(kcsMaxBufferSize, ipmbMaxBufferSize);
+}
+
static void registerOEMFunctions(void)
{
phosphor::logging::log<phosphor::logging::level::INFO>(
@@ -3611,6 +3630,10 @@
registerHandler(prioOemBase, intel::netFnGeneral,
intel::general::cmdGetPSUVersion, Privilege::User,
ipmiOEMGetPSUVersion);
+
+ registerHandler(prioOemBase, intel::netFnGeneral,
+ intel::general::cmdGetBufferSize, Privilege::User,
+ ipmiOEMGetBufferSize);
}
} // namespace ipmi