smbios-mdr: Expose Firmware Inventory Information
SMBIOS Type 45 defines the data structure to expose Firmware
Inventory information. Added patch which exposes the same
on the DBus.
Introduced fw-inventory-dbus meson option which by default,
will be disabled. When enabled fw-inventory is exposed to dbus
Introduced expose-firmware-component-name meson option which
by default, will be disabled. When enabled fw-component name is
exposed as dbus object
Tested:
1) No crash is seen when firmware is flashed with change
2) Firmware Inventory information is available under dbus tree
```
busctl tree xyz.openbmc_project.Smbios.MDR_V2
`- /xyz
`- /xyz/openbmc_project
|- /xyz/openbmc_project/Smbios
| `- /xyz/openbmc_project/Smbios/MDR_V2
`- /xyz/openbmc_project/software
`- /xyz/openbmc_project/software/UEFI
```
Change-Id: If5b367f21dedc0addef4f7b1d4c6dac6a5dc17c2
Signed-off-by: Prithvi Pai <ppai@nvidia.com>
diff --git a/src/firmware_inventory.cpp b/src/firmware_inventory.cpp
new file mode 100644
index 0000000..30053d5
--- /dev/null
+++ b/src/firmware_inventory.cpp
@@ -0,0 +1,218 @@
+#include "firmware_inventory.hpp"
+
+#include "mdrv2.hpp"
+
+#include <boost/algorithm/string.hpp>
+
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <regex>
+#include <sstream>
+
+namespace phosphor
+{
+namespace smbios
+{
+namespace utils
+{
+std::vector<std::string> getExistingVersionPaths(sdbusplus::bus_t& bus)
+{
+ std::vector<std::string> existingVersionPaths;
+
+ auto getVersionPaths = bus.new_method_call(
+ phosphor::smbios::mapperBusName, phosphor::smbios::mapperPath,
+ phosphor::smbios::mapperInterface, "GetSubTreePaths");
+ getVersionPaths.append(firmwarePath);
+ getVersionPaths.append(0);
+ getVersionPaths.append(
+ std::array<std::string, 1>({phosphor::smbios::versionInterface}));
+
+ try
+ {
+ auto reply = bus.call(getVersionPaths);
+ reply.read(existingVersionPaths);
+ }
+ catch (const sdbusplus::exception_t& e)
+ {
+ lg2::error("Failed to query version objects. ERROR={E}", "E", e.what());
+ existingVersionPaths.clear();
+ }
+
+ return existingVersionPaths;
+}
+} // namespace utils
+
+bool FirmwareInventory::getFirmwareInventoryData(uint8_t*& dataIn,
+ int inventoryIndex)
+{
+ dataIn = getSMBIOSTypePtr(dataIn, firmwareInventoryInformationType);
+ if (dataIn == nullptr)
+ {
+ return false;
+ }
+ for (uint8_t index = 0; index < inventoryIndex; index++)
+ {
+ dataIn = smbiosNextPtr(dataIn);
+ if (dataIn == nullptr)
+ {
+ return false;
+ }
+ dataIn = getSMBIOSTypePtr(dataIn, firmwareInventoryInformationType);
+ if (dataIn == nullptr)
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+void FirmwareInventory::firmwareInfoUpdate(uint8_t* smbiosTableStorage)
+{
+ uint8_t* dataIn = smbiosTableStorage;
+ if (!getFirmwareInventoryData(dataIn, firmwareInventoryIndex))
+ {
+ lg2::info("Failed to get data for firmware inventory index {I}", "I",
+ firmwareInventoryIndex);
+ return;
+ }
+
+ auto firmwareInfo = reinterpret_cast<struct FirmwareInfo*>(dataIn);
+
+ firmwareComponentName(firmwareInfo->componentName, firmwareInfo->length,
+ dataIn);
+ firmwareVersion(firmwareInfo->version, firmwareInfo->length, dataIn);
+ firmwareId(firmwareInfo->id, firmwareInfo->length, dataIn);
+ firmwareReleaseDate(firmwareInfo->releaseDate, firmwareInfo->length,
+ dataIn);
+ firmwareManufacturer(firmwareInfo->manufacturer, firmwareInfo->length,
+ dataIn);
+ present(true);
+ purpose(softwareVersion::VersionPurpose::Other);
+
+ std::vector<std::tuple<std::string, std::string, std::string>> assocs;
+ assocs.emplace_back("software_version", "functional",
+ "/xyz/openbmc_project/software");
+ association::associations(assocs);
+}
+
+std::string FirmwareInventory::checkAndCreateFirmwarePath(
+ uint8_t* dataIn, int inventoryIndex,
+ std::vector<std::string>& existingVersionPaths)
+{
+ if (!getFirmwareInventoryData(dataIn, inventoryIndex))
+ {
+ lg2::info("Failed to get data for firmware inventory index {I}", "I",
+ inventoryIndex);
+ return "";
+ }
+ auto firmwareInfo = reinterpret_cast<struct FirmwareInfo*>(dataIn);
+ std::string firmwareId =
+ positionToString(firmwareInfo->id, firmwareInfo->length, dataIn);
+ auto firmwareName = positionToString(firmwareInfo->componentName,
+ firmwareInfo->length, dataIn);
+ std::string firmwareObjPath = "";
+#ifdef EXPOSE_FW_COMPONENT_NAME
+ firmwareObjPath = firmwareName;
+#else
+ firmwareObjPath = firmwareId;
+#endif
+ if (firmwareInfo->numOfAssociatedComponents > 0)
+ {
+ for (int i = 0; i < firmwareInfo->numOfAssociatedComponents; i++)
+ {
+ auto component = smbiosHandlePtr(
+ dataIn, firmwareInfo->associatedComponentHandles[i]);
+ if (component == nullptr)
+ {
+ continue;
+ }
+
+ auto header = reinterpret_cast<struct StructureHeader*>(component);
+ switch (header->type)
+ {
+ case processorsType:
+ case systemSlots:
+ case onboardDevicesExtended:
+ {
+ auto designation = positionToString(
+ component[4], header->length, component);
+ if (!designation.empty())
+ {
+ firmwareObjPath.append("_").append(designation);
+ }
+ break;
+ }
+ case systemPowerSupply:
+ {
+ auto location = positionToString(component[5],
+ header->length, component);
+ if (!location.empty())
+ {
+ firmwareObjPath.append("_").append(location);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ if (firmwareObjPath.empty())
+ {
+ firmwareObjPath = "firmware" + std::to_string(inventoryIndex);
+ }
+ boost::algorithm::trim_right(firmwareObjPath);
+ firmwareObjPath =
+ std::regex_replace(firmwareObjPath, std::regex("[^a-zA-Z0-9_/]+"), "_");
+
+ auto eqObjName = [firmwareObjPath](std::string s) {
+ std::filesystem::path p(s);
+ return p.filename().compare(firmwareObjPath) == 0;
+ };
+ if (std::find_if(existingVersionPaths.begin(), existingVersionPaths.end(),
+ std::move(eqObjName)) != existingVersionPaths.end())
+ {
+ return "";
+ }
+ std::string path = firmwarePath;
+ path.append("/").append(firmwareObjPath);
+ return path;
+}
+
+void FirmwareInventory::firmwareComponentName(
+ const uint8_t positionNum, const uint8_t structLen, uint8_t* dataIn)
+{
+ std::string result = positionToString(positionNum, structLen, dataIn);
+ prettyName(result);
+}
+
+void FirmwareInventory::firmwareVersion(
+ const uint8_t positionNum, const uint8_t structLen, uint8_t* dataIn)
+{
+ std::string result = positionToString(positionNum, structLen, dataIn);
+ version(result);
+}
+
+void FirmwareInventory::firmwareId(const uint8_t positionNum,
+ const uint8_t structLen, uint8_t* dataIn)
+{
+ std::string result = positionToString(positionNum, structLen, dataIn);
+ extendedVersion(result);
+}
+
+void FirmwareInventory::firmwareReleaseDate(
+ const uint8_t positionNum, const uint8_t structLen, uint8_t* dataIn)
+{
+ std::string result = positionToString(positionNum, structLen, dataIn);
+ buildDate(result);
+}
+
+void FirmwareInventory::firmwareManufacturer(
+ const uint8_t positionNum, const uint8_t structLen, uint8_t* dataIn)
+{
+ std::string result = positionToString(positionNum, structLen, dataIn);
+ manufacturer(result);
+}
+} // namespace smbios
+} // namespace phosphor
diff --git a/src/mdrv2.cpp b/src/mdrv2.cpp
index f33fbc1..8395c94 100644
--- a/src/mdrv2.cpp
+++ b/src/mdrv2.cpp
@@ -651,6 +651,39 @@
}
#endif
+#ifdef FIRMWARE_INVENTORY_DBUS
+
+ auto existingVersionPaths = utils::getExistingVersionPaths(*bus);
+ num = getTotalFirmwareInventory();
+ if (!num)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "get firmware inventory failed");
+ existingVersionPaths.clear();
+ return;
+ }
+
+ // In case the new size is smaller than old, trim the vector
+ if (*num < firmwareCollection.size())
+ {
+ firmwareCollection.resize(*num);
+ }
+ for (unsigned int index = 0; index < *num; index++)
+ {
+ auto path = FirmwareInventory::checkAndCreateFirmwarePath(
+ smbiosDir.dir[smbiosDirIndex].dataStorage, index,
+ existingVersionPaths);
+ if (path.empty())
+ {
+ continue;
+ }
+ firmwareCollection.emplace_back(
+ std::make_unique<phosphor::smbios::FirmwareInventory>(
+ *bus, path, index, smbiosDir.dir[smbiosDirIndex].dataStorage));
+ }
+
+#endif
+
system.reset();
system = std::make_unique<System>(bus, smbiosInventoryPath + systemSuffix,
smbiosDir.dir[smbiosDirIndex].dataStorage,
@@ -800,6 +833,40 @@
return num;
}
+std::optional<size_t> MDRV2::getTotalFirmwareInventory()
+{
+ uint8_t* dataIn = smbiosDir.dir[smbiosDirIndex].dataStorage;
+ size_t num = 0;
+
+ if (dataIn == nullptr)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Fail to get firmware inventory - no storage data");
+ return std::nullopt;
+ }
+
+ while (1)
+ {
+ dataIn = getSMBIOSTypePtr(dataIn, firmwareInventoryInformationType);
+ if (dataIn == nullptr)
+ {
+ break;
+ }
+ num++;
+ dataIn = smbiosNextPtr(dataIn);
+ if (dataIn == nullptr)
+ {
+ break;
+ }
+ if (num >= limitEntryLen)
+ {
+ break;
+ }
+ }
+
+ return num;
+}
+
bool MDRV2::checkSMBIOSVersion(uint8_t* dataIn)
{
const std::string anchorString21 = "_SM_";
diff --git a/src/meson.build b/src/meson.build
index 3a2bc17..d94397e 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -19,6 +19,14 @@
cpp_args_smbios += ['-DTPM_DBUS']
endif
+if get_option('firmware-inventory-dbus').allowed()
+ cpp_args_smbios += ['-DFIRMWARE_INVENTORY_DBUS']
+endif
+
+if get_option('expose-firmware-component-name').allowed()
+ cpp_args_smbios += ['-DEXPOSE_FW_COMPONENT_NAME']
+endif
+
executable(
'smbiosmdrv2app',
'mdrv2.cpp',
@@ -28,6 +36,7 @@
'system.cpp',
'pcieslot.cpp',
'tpm.cpp',
+ 'firmware_inventory.cpp',
cpp_args: cpp_args_smbios,
dependencies: [
boost_dep,