frudevice: consolidate FRU reading
Consolidate FRU reading and validation into one method shared by eeproms
and raw i2c devices.
Tested: Verified this work for devices read via i2c and via eeprom
sysfs.
Signed-off-by: Patrick Venture <venture@google.com>
Change-Id: I3144692f91d0f77c6ca3953860e0dfa9c212ccc9
diff --git a/src/FruDevice.cpp b/src/FruDevice.cpp
index 492e7ff..7a46311 100644
--- a/src/FruDevice.cpp
+++ b/src/FruDevice.cpp
@@ -28,6 +28,7 @@
#include <ctime>
#include <filesystem>
#include <fstream>
+#include <functional>
#include <future>
#include <iomanip>
#include <iostream>
@@ -80,6 +81,91 @@
auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
auto objServer = sdbusplus::asio::object_server(systemBus);
+bool validateHeader(const std::array<uint8_t, I2C_SMBUS_BLOCK_MAX>& blockData);
+
+using ReadBlockFunc = std::function<int64_t(int flag, int file, uint16_t offset,
+ uint8_t length, uint8_t* outBuf)>;
+
+// Read and validate FRU contents, given the flag required for raw i2c, the open
+// file handle, a read method, and a helper string for failures.
+std::vector<char> readFruContents(int flag, int file, ReadBlockFunc readBlock,
+ const std::string& errorHelp)
+{
+ std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData;
+
+ if (readBlock(flag, file, 0x0, 0x8, blockData.data()) < 0)
+ {
+ std::cerr << "failed to read " << errorHelp << "\n";
+ return {};
+ }
+
+ // check the header checksum
+ if (!validateHeader(blockData))
+ {
+ if (DEBUG)
+ {
+ std::cerr << "Illegal header " << errorHelp << "\n";
+ }
+
+ return {};
+ }
+
+ std::vector<char> device;
+ device.insert(device.end(), blockData.begin(), blockData.begin() + 8);
+
+ int fruLength = 0;
+ for (size_t jj = 1; jj <= FRU_AREAS.size(); jj++)
+ {
+ // TODO: offset can be 255, device is holding "chars" that's not
+ // good.
+ int areaOffset = device[jj];
+ if (areaOffset == 0)
+ {
+ continue;
+ }
+
+ areaOffset *= 8;
+
+ if (readBlock(flag, file, static_cast<uint16_t>(areaOffset), 0x2,
+ blockData.data()) < 0)
+ {
+ std::cerr << "failed to read " << errorHelp << "\n";
+ return {};
+ }
+
+ // Ignore data type.
+ int length = blockData[1] * 8;
+ areaOffset += length;
+ fruLength = (areaOffset > fruLength) ? areaOffset : fruLength;
+ }
+
+ // You already copied these first 8 bytes (the ipmi fru header size)
+ fruLength -= 8;
+
+ int readOffset = 8;
+
+ while (fruLength > 0)
+ {
+ int requestLength = std::min(I2C_SMBUS_BLOCK_MAX, fruLength);
+
+ if (readBlock(flag, file, static_cast<uint16_t>(readOffset),
+ static_cast<uint8_t>(requestLength),
+ blockData.data()) < 0)
+ {
+ std::cerr << "failed to read " << errorHelp << "\n";
+ return {};
+ }
+
+ device.insert(device.end(), blockData.begin(),
+ blockData.begin() + requestLength);
+
+ readOffset += requestLength;
+ fruLength -= requestLength;
+ }
+
+ return device;
+}
+
// Given a bus/address, produce the path in sysfs for an eeprom.
static std::string getEepromPath(size_t bus, size_t address)
{
@@ -240,96 +326,19 @@
// with some tweaks.
static std::vector<char> processEeprom(int bus, int address)
{
- std::vector<char> device;
- std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData;
-
auto path = getEepromPath(bus, address);
int file = open(path.c_str(), O_RDONLY);
if (file < 0)
{
std::cerr << "Unable to open eeprom file: " << path << "\n";
- return device;
+ return {};
}
- auto readBytes = readFromEeprom(0, file, 0, 0x8, blockData.data());
- if (readBytes < 0)
- {
- std::cerr << "failed to read eeprom at " << bus << " address "
- << address << "\n";
- close(file);
- return device;
- }
-
- if (!validateHeader(blockData))
- {
- if (DEBUG)
- {
- std::cerr << "Illegal header at bus " << bus << " address "
- << address << "\n";
- }
-
- close(file);
- return device;
- }
-
- // Copy the IPMI Fru Header
- device.insert(device.end(), blockData.begin(), blockData.begin() + 8);
-
- int fruLength = 0;
- for (size_t jj = 1; jj <= FRU_AREAS.size(); jj++)
- {
- // TODO: offset can be 255, device is holding "chars" that's not good.
- int areaOffset = device[jj];
- if (areaOffset == 0)
- {
- continue;
- }
-
- areaOffset *= 8;
-
- if (readFromEeprom(0, file, static_cast<uint16_t>(areaOffset), 0x2,
- blockData.data()) < 0)
- {
- std::cerr << "failed to read bus " << bus << " address " << address
- << "\n";
- device.clear();
- close(file);
- return device;
- }
-
- // Ignore data type.
- int length = blockData[1] * 8;
- areaOffset += length;
- fruLength = (areaOffset > fruLength) ? areaOffset : fruLength;
- }
-
- // You already copied these first 8 bytes (the ipmi fru header size)
- fruLength -= 8;
-
- int readOffset = 8;
-
- while (fruLength > 0)
- {
- int requestLength = std::min(I2C_SMBUS_BLOCK_MAX, fruLength);
-
- if (readFromEeprom(0, file, static_cast<uint16_t>(readOffset),
- static_cast<uint8_t>(requestLength),
- blockData.data()) < 0)
- {
- std::cerr << "failed to read bus " << bus << " address " << address
- << "\n";
- device.clear();
- close(file);
- return device;
- }
-
- device.insert(device.end(), blockData.begin(),
- blockData.begin() + requestLength);
-
- readOffset += requestLength;
- fruLength -= requestLength;
- }
+ std::string errorMessage = "eeprom at " + std::to_string(bus) +
+ " address " + std::to_string(address);
+ std::vector<char> device =
+ readFruContents(0, file, readFromEeprom, errorMessage);
close(file);
return device;
@@ -401,8 +410,6 @@
{
std::future<int> future = std::async(std::launch::async, [&]() {
- std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData;
-
// NOTE: When reading the devices raw on the bus, it can interfere with
// the driver's ability to operate, therefore read eeproms first before
// scanning for devices without drivers. Several experiments were run
@@ -452,79 +459,15 @@
continue;
}
- if (readBlockData(flag, file, 0x0, 0x8, blockData.data()) < 0)
+ std::string errorMessage =
+ "bus " + std::to_string(bus) + " address " + std::to_string(ii);
+ std::vector<char> device =
+ readFruContents(flag, file, readBlockData, errorMessage);
+ if (device.empty())
{
- std::cerr << "failed to read bus " << bus << " address " << ii
- << "\n";
continue;
}
- // check the header checksum
- if (!validateHeader(blockData))
- {
- if (DEBUG)
- {
- std::cerr << "Illegal header at bus " << bus << " address "
- << ii << "\n";
- }
- continue;
- }
-
- std::vector<char> device;
- device.insert(device.end(), blockData.begin(),
- blockData.begin() + 8);
-
- int fruLength = 0;
- for (size_t jj = 1; jj <= FRU_AREAS.size(); jj++)
- {
- // TODO: offset can be 255, device is holding "chars" that's not
- // good.
- int areaOffset = device[jj];
- if (areaOffset == 0)
- {
- continue;
- }
-
- areaOffset *= 8;
-
- if (readBlockData(flag, file, static_cast<uint16_t>(areaOffset),
- 0x2, blockData.data()) < 0)
- {
- std::cerr << "failed to read bus " << bus << " address "
- << ii << "\n";
- return -1;
- }
-
- // Ignore data type.
- int length = blockData[1] * 8;
- areaOffset += length;
- fruLength = (areaOffset > fruLength) ? areaOffset : fruLength;
- }
-
- // You already copied these first 8 bytes (the ipmi fru header size)
- fruLength -= 8;
-
- int readOffset = 8;
-
- while (fruLength > 0)
- {
- int requestLength = std::min(I2C_SMBUS_BLOCK_MAX, fruLength);
-
- if (readBlockData(flag, file, static_cast<uint16_t>(readOffset),
- static_cast<uint8_t>(requestLength),
- blockData.data()) < 0)
- {
- std::cerr << "failed to read bus " << bus << " address "
- << ii << "\n";
- return -1;
- }
-
- device.insert(device.end(), blockData.begin(),
- blockData.begin() + requestLength);
-
- readOffset += requestLength;
- fruLength -= requestLength;
- }
devices->emplace(ii, device);
}
return 1;