FruUtils: Add support for finding FRU at an offset

This change adds findFRUHeader which can detect non-IPMI headers to
identify the offset for IPMI FRU data. An implementation for "$TYAN$"
has been added. The new findFRUHeader is now used by readFRUContents
to generically read IPMI FRU both at offset 0 and at a offset as
determined by findFRUHeader.

Signed-off-by: Oskar Senft <osk@google.com>
Change-Id: I4325aac7737db59aaa11359ad7a0d575957f16e7
diff --git a/test/test_fru-utils.cpp b/test/test_fru-utils.cpp
index d2f0e36..2984094 100644
--- a/test/test_fru-utils.cpp
+++ b/test/test_fru-utils.cpp
@@ -1,6 +1,8 @@
 #include "FruUtils.hpp"
 
 #include <array>
+#include<algorithm>
+#include<iterator>
 
 #include "gtest/gtest.h"
 
@@ -142,3 +144,101 @@
 
     EXPECT_EQ(calculateChecksum(data), 255);
 }
+
+int64_t getDataTempl(
+    const std::vector<uint8_t>& data,
+    [[maybe_unused]] int flag,
+    [[maybe_unused]] int file,
+    [[maybe_unused]] uint16_t address,
+    uint16_t offset, uint8_t length, uint8_t* outBuf)
+{
+    if (offset >= data.size())
+    {
+        return 0;
+    }
+
+    uint16_t idx;
+    for (idx = offset;
+         idx < data.size() && idx < offset + length;
+         ++idx, ++outBuf)
+    {
+        *outBuf = data[idx];
+    }
+
+    return idx - offset;
+}
+
+TEST(FindFRUHeaderTest, InvalidHeader)
+{
+    const std::vector<uint8_t> data = {255, 16};
+    uint16_t offset = 0;
+    std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData;
+    auto getData = [&data](auto fl, auto fi, auto a, auto o, auto l,
+        auto* b) { return getDataTempl(data, fl, fi, a, o, l, b); };
+
+    EXPECT_FALSE(findFRUHeader(0, 0, 0, getData, "error", blockData, offset));
+}
+
+TEST(FindFRUHeaderTest, NoData)
+{
+    const std::vector<uint8_t> data = {};
+    uint16_t offset = 0;
+    std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData;
+    auto getData = [&data](auto fl, auto fi, auto a, auto o, auto l,
+        auto* b) { return getDataTempl(data, fl, fi, a, o, l, b); };
+
+    EXPECT_FALSE(findFRUHeader(0, 0, 0, getData, "error", blockData, offset));
+}
+
+TEST(FindFRUHeaderTest, ValidHeader)
+{
+    const std::vector<uint8_t> data = {
+        0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0xf5};
+    uint16_t offset = 0;
+    std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData;
+    auto getData = [&data](auto fl, auto fi, auto a, auto o, auto l,
+        auto* b) { return getDataTempl(data, fl, fi, a, o, l, b); };
+
+    EXPECT_TRUE(findFRUHeader(0, 0, 0, getData, "error", blockData, offset));
+    EXPECT_EQ(0, offset);
+}
+
+TEST(FindFRUHeaderTest, TyanInvalidHeader)
+{
+    std::vector<uint8_t> data = {'$', 'T', 'Y', 'A', 'N', '$', 0, 0};
+    data.resize(0x6000 + I2C_SMBUS_BLOCK_MAX);
+    uint16_t offset = 0;
+    std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData;
+    auto getData = [&data](auto fl, auto fi, auto a, auto o, auto l,
+        auto* b) { return getDataTempl(data, fl, fi, a, o, l, b); };
+
+    EXPECT_FALSE(findFRUHeader(0, 0, 0, getData, "error", blockData, offset));
+}
+
+TEST(FindFRUHeaderTest, TyanNoData)
+{
+    const std::vector<uint8_t> data = {'$', 'T', 'Y', 'A', 'N', '$', 0, 0};
+    uint16_t offset = 0;
+    std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData;
+    auto getData = [&data](auto fl, auto fi, auto a, auto o, auto l,
+        auto* b) { return getDataTempl(data, fl, fi, a, o, l, b); };
+
+    EXPECT_FALSE(findFRUHeader(0, 0, 0, getData, "error", blockData, offset));
+}
+
+TEST(FindFRUHeaderTest, TyanValidHeader)
+{
+    std::vector<uint8_t> data = {'$', 'T', 'Y', 'A', 'N', '$', 0, 0};
+    data.resize(0x6000);
+    constexpr std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> fruHeader = {
+        0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0xf5};
+    copy(fruHeader.begin(), fruHeader.end(), back_inserter(data));
+
+    uint16_t offset = 0;
+    std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData;
+    auto getData = [&data](auto fl, auto fi, auto a, auto o, auto l,
+        auto* b) { return getDataTempl(data, fl, fi, a, o, l, b); };
+
+    EXPECT_TRUE(findFRUHeader(0, 0, 0, getData, "error", blockData, offset));
+    EXPECT_EQ(0x6000, offset);
+}