fru-device: retry with bytewise read
certain powersupplies, e.g. SuperMicro PWS-920P-SQ
require that their FRU eeprom be read bytewise.
otherwise, only garbage is read.
Tested: on Tyan S8030 board. FRU eeprom is now read correctly.
Change-Id: I36a0ce8d8f955d4fe3867dbd63fefda63b1a9b13
Signed-off-by: Alexander Hansen <alexander.hansen@9elements.com>
diff --git a/src/fru_device.cpp b/src/fru_device.cpp
index 2045b4e..11f2872 100644
--- a/src/fru_device.cpp
+++ b/src/fru_device.cpp
@@ -262,13 +262,30 @@
return (ret == static_cast<int>(msgs.size())) ? msgs[1].len : -1;
}
-static int64_t readBlockData(bool is16bit, int file, uint16_t address,
- off_t offset, size_t len, uint8_t* buf)
+static int64_t readData(bool is16bit, bool isBytewise, int file,
+ uint16_t address, off_t offset, size_t len,
+ uint8_t* buf)
{
if (!is16bit)
{
- return i2c_smbus_read_i2c_block_data(file, static_cast<uint8_t>(offset),
- len, buf);
+ if (!isBytewise)
+ {
+ return i2c_smbus_read_i2c_block_data(
+ file, static_cast<uint8_t>(offset), len, buf);
+ }
+
+ std::span<uint8_t> bufspan{buf, len};
+ for (size_t i = 0; i < len; i++)
+ {
+ int byte = i2c_smbus_read_byte_data(
+ file, static_cast<uint8_t>(offset + i));
+ if (byte < 0)
+ {
+ return static_cast<int64_t>(byte);
+ }
+ bufspan[i] = static_cast<uint8_t>(byte);
+ }
+ return static_cast<int64_t>(len);
}
offset = htobe16(offset);
@@ -296,10 +313,11 @@
return readFromEeprom(file, offset, length, outbuf);
};
FRUReader reader(std::move(readFunc));
- std::vector<uint8_t> device = readFRUContents(reader, errorMessage);
+ std::pair<std::vector<uint8_t>, bool> pair = readFRUContents(reader,
+ errorMessage);
close(file);
- return device;
+ return pair.first;
}
std::set<int> findI2CEeproms(int i2cBus,
@@ -468,19 +486,37 @@
auto readFunc = [is16BitBool, file, ii](off_t offset, size_t length,
uint8_t* outbuf) {
- return readBlockData(is16BitBool, file, ii, offset, length,
- outbuf);
+ return readData(is16BitBool, false, file, ii, offset, length,
+ outbuf);
};
FRUReader reader(std::move(readFunc));
std::string errorMessage = "bus " + std::to_string(bus) +
" address " + std::to_string(ii);
- std::vector<uint8_t> device = readFRUContents(reader, errorMessage);
- if (device.empty())
+ std::pair<std::vector<uint8_t>, bool> pair =
+ readFRUContents(reader, errorMessage);
+ const bool foundHeader = pair.second;
+
+ if (!foundHeader && !is16BitBool)
+ {
+ // certain FRU eeproms require bytewise reading.
+ // otherwise garbage is read. e.g. SuperMicro PWS 920P-SQ
+
+ auto readFunc = [is16BitBool, file, ii](off_t offset,
+ size_t length,
+ uint8_t* outbuf) {
+ return readData(is16BitBool, true, file, ii, offset, length,
+ outbuf);
+ };
+ FRUReader readerBytewise(std::move(readFunc));
+ pair = readFRUContents(readerBytewise, errorMessage);
+ }
+
+ if (pair.first.empty())
{
continue;
}
- devices->emplace(ii, device);
+ devices->emplace(ii, pair.first);
fruAddresses[bus].insert(ii);
}
return 1;
diff --git a/src/fru_utils.cpp b/src/fru_utils.cpp
index ab3283b..0bb92b9 100644
--- a/src/fru_utils.cpp
+++ b/src/fru_utils.cpp
@@ -743,15 +743,15 @@
return false;
}
-std::vector<uint8_t> readFRUContents(FRUReader& reader,
- const std::string& errorHelp)
+std::pair<std::vector<uint8_t>, bool>
+ readFRUContents(FRUReader& reader, const std::string& errorHelp)
{
std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{};
off_t baseOffset = 0x0;
if (!findFRUHeader(reader, errorHelp, blockData, baseOffset))
{
- return {};
+ return {{}, false};
}
std::vector<uint8_t> device;
@@ -779,7 +779,7 @@
{
std::cerr << "Fru area offsets are not in required order as per "
"Section 17 of Fru specification\n";
- return {};
+ return {{}, true};
}
prevOffset = areaOffset;
@@ -797,7 +797,7 @@
{
std::cerr << "failed to read " << errorHelp << " base offset "
<< baseOffset << "\n";
- return {};
+ return {{}, true};
}
// Ignore data type (blockData is already unsigned).
@@ -827,7 +827,7 @@
{
std::cerr << "failed to read " << errorHelp << " base offset "
<< baseOffset << "\n";
- return {};
+ return {{}, true};
}
// Ok, let's check the record length, which is in bytes (unsigned,
@@ -859,7 +859,7 @@
{
std::cerr << "failed to read " << errorHelp << " base offset "
<< baseOffset << "\n";
- return {};
+ return {{}, true};
}
device.insert(device.end(), blockData.begin(),
@@ -869,7 +869,7 @@
fruLength -= std::min(requestLength, fruLength);
}
- return device;
+ return {device, true};
}
unsigned int getHeaderAreaFieldOffset(fruAreas area)
diff --git a/src/fru_utils.hpp b/src/fru_utils.hpp
index e2c6a54..c2b81cc 100644
--- a/src/fru_utils.hpp
+++ b/src/fru_utils.hpp
@@ -155,9 +155,10 @@
/// \brief Read and validate FRU contents.
/// \param reader the FRUReader to read via
/// \param errorHelp and a helper string for failures
-/// \return the FRU contents from the file
-std::vector<uint8_t> readFRUContents(FRUReader& reader,
- const std::string& errorHelp);
+/// \return the FRU contents from the file and bool indicating if the FRU Header
+/// was found
+std::pair<std::vector<uint8_t>, bool>
+ readFRUContents(FRUReader& reader, const std::string& errorHelp);
/// \brief Validate an IPMI FRU common header
/// \param blockData the bytes comprising the common header