Added support for Get Firmware Version Information
Added support for Get Firmware Version Information.
This command works only for PFR enabled platform.
Tested:
- On PFR enabled platforms:
Req: ipmitool raw 0x08 0x20
Res: 02 01 00 14 02 00 00 00 00 00 00 00 00 00 00 00
02 00 14 02 00 00 00 00 00 00 00 00 00 00 00
- Non-PFR platforms
Req: ipmitool raw 0x08 0x20
Res: Invalid command(0xC1)
Change-Id: I67a0674fbde053dc9088e76bbec871ddbab32e04
Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
diff --git a/src/firmware-update.cpp b/src/firmware-update.cpp
index 50726e2..09f6e90 100644
--- a/src/firmware-update.cpp
+++ b/src/firmware-update.cpp
@@ -18,6 +18,7 @@
#include <fstream>
#include <iostream>
#include <ipmid/api.hpp>
+#include <ipmid/utils.hpp>
#include <map>
#include <random>
#include <sdbusplus/bus.hpp>
@@ -26,11 +27,35 @@
#include <sdbusplus/timer.hpp>
#include <sstream>
+namespace ipmi
+{
+namespace firmware
+{
+constexpr Cmd cmdGetFwVersionInfo = 0x20;
+} // namespace firmware
+} // namespace ipmi
+
#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 *versionIntf =
+ "xyz.openbmc_project.Software.Version";
+
+enum class FWDeviceIDTag : uint8_t
+{
+ bmcActiveImage = 1,
+ bmcRecoveryImage,
+};
+
+const static boost::container::flat_map<FWDeviceIDTag, const char *>
+ fwVersionIdMap{{FWDeviceIDTag::bmcActiveImage,
+ "/xyz/openbmc_project/software/bmc_active"},
+ {FWDeviceIDTag::bmcRecoveryImage,
+ "/xyz/openbmc_project/software/bmc_recovery"}};
+
#endif
static constexpr const char *secondaryFitImageStartAddr = "22480000";
@@ -954,126 +979,90 @@
return rc;
}
-struct fw_version_info
+#ifdef INTEL_PFR_ENABLED
+using fwVersionInfoType = std::tuple<uint8_t, // ID Tag
+ uint8_t, // Major Version Number
+ uint8_t, // Minor Version Number
+ uint32_t, // Build Number
+ uint32_t, // Build Timestamp
+ uint32_t>; // Update Timestamp
+ipmi::RspType<uint8_t, std::vector<fwVersionInfoType>> ipmiGetFwVersionInfo()
{
- uint8_t id_tag;
- uint8_t major;
- uint8_t minor;
- uint32_t build;
- uint32_t build_time;
- uint32_t update_time;
-} __attribute__((packed));
-
-static ipmi_ret_t ipmi_firmware_get_fw_version_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 Version 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 2:16 - Device firmare information(fwVersionInfoType)
// 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_version_info *>(ret_count + 1);
-
- for (uint8_t id_tag = 1; id_tag < 6; id_tag++)
+ std::vector<fwVersionInfoType> fwVerInfoList;
+ std::shared_ptr<sdbusplus::asio::connection> busp = getSdBus();
+ for (const auto &fwDev : fwVersionIdMap)
{
- 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;
- }
- std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
- auto method =
- bus->new_method_call(FW_UPDATE_SERVER_DBUS_NAME, fw_path,
- "org.freedesktop.DBus.Properties", "GetAll");
- method.append(FW_UPDATE_INFO_INTERFACE);
- std::vector<std::pair<std::string, ipmi::DbusVariant>> properties;
+ std::string verStr;
try
{
- auto reply = bus->call(method);
+ auto service = ipmi::getService(*busp, versionIntf, fwDev.second);
- if (reply.is_method_error())
- continue;
-
- reply.read(properties);
+ ipmi::Value result = ipmi::getDbusProperty(
+ *busp, service, fwDev.second, versionIntf, "Version");
+ verStr = std::get<std::string>(result);
}
- catch (sdbusplus::exception::SdBusError &e)
+ catch (const std::exception &e)
{
- std::cerr << "SDBus Error: " << e.what();
- return IPMI_CC_UNSPECIFIED_ERROR;
- }
- uint8_t major = 0;
- uint8_t minor = 0;
- uint32_t build = 0;
- int32_t build_time = 0;
- int32_t update_time = 0;
- for (const auto &t : properties)
- {
- auto key = t.first;
- auto value = t.second;
- if (key == "version")
- {
- auto strver = std::get<std::string>(value);
- std::stringstream ss;
- ss << std::hex << strver;
- uint32_t t;
- ss >> t;
- major = t;
- ss.ignore();
- ss >> t;
- minor = t;
- ss.ignore();
- ss >> build;
- }
- else if (key == "build_time")
- {
- build_time = std::get<int32_t>(value);
- }
- else if (key == "update_time")
- {
- update_time = std::get<int32_t>(value);
- }
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "Failed to fetch Version property",
+ phosphor::logging::entry("ERROR=%s", e.what()),
+ phosphor::logging::entry("PATH=%s", fwDev.second),
+ phosphor::logging::entry("INTERFACE=%s", versionIntf));
+ continue;
}
- info->id_tag = id_tag;
- info->major = major;
- info->minor = minor;
- info->build = build;
- info->build_time = build_time;
- info->update_time = update_time;
- count++;
- info++;
+ if (verStr.empty())
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "Version is empty.",
+ phosphor::logging::entry("PATH=%s", fwDev.second),
+ phosphor::logging::entry("INTERFACE=%s", versionIntf));
+ continue;
+ }
+
+ // BMC Version format: <major>.<minor>-<build bum>-<build hash>
+ std::vector<std::string> splitVer;
+ boost::split(splitVer, verStr, boost::is_any_of(".-"));
+ if (splitVer.size() < 3)
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "Invalid Version format.",
+ phosphor::logging::entry("Version=%s", verStr.c_str()),
+ phosphor::logging::entry("PATH=%s", fwDev.second));
+ continue;
+ }
+
+ uint8_t majorNum = 0;
+ uint8_t minorNum = 0;
+ uint32_t buildNum = 0;
+ try
+ {
+ majorNum = std::stoul(splitVer[0], nullptr, 16);
+ minorNum = std::stoul(splitVer[1], nullptr, 16);
+ buildNum = std::stoul(splitVer[2], nullptr, 16);
+ }
+ catch (const std::exception &e)
+ {
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "Failed to convert stoul.",
+ phosphor::logging::entry("ERROR=%s", e.what()));
+ continue;
+ }
+
+ // Build Timestamp - Not supported.
+ // Update Timestamp - TODO: Need to check with CPLD team.
+ fwVerInfoList.emplace_back(
+ fwVersionInfoType(static_cast<uint8_t>(fwDev.first), majorNum,
+ minorNum, buildNum, 0, 0));
}
- *ret_count = count;
- // Status code.
- ipmi_ret_t rc = IPMI_CC_OK;
- *data_len = sizeof(count) + count * sizeof(*info);
-
- return rc;
+ return ipmi::responseSuccess(fwVerInfoList.size(), fwVerInfoList);
}
+#endif
struct fw_security_revision_info
{
@@ -1678,11 +1667,15 @@
if (DEBUG)
std::cerr << "Registering firmware update commands\n";
- // get firmware version information
- ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_FW_VERSION_INFO,
- NULL, ipmi_firmware_get_fw_version_info,
- PRIVILEGE_ADMIN);
+#ifdef INTEL_PFR_ENABLED
+ // Following commands are supported only for PFR enabled platforms
+ // CMD:0x20 - Get Firmware Version Information
+ // 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,