bugfix: frudevice: read entire fru contents

The FRU sections do not need to be contiguous, therefore compute the
overall length first to copy it out in its entirety.

Tested: Verified on a FRU that had gaps in the contents that it now
properly parsed the contents after copying it out.

Tested: Verified on a FRU that did not have gaps that it continued to
correctly parse the contents after copying it out.

Signed-off-by: Patrick Venture <venture@google.com>
Change-Id: I60eac117c479d72f88a7e8e46884dd858681784b
diff --git a/src/FruDevice.cpp b/src/FruDevice.cpp
index a830c94..7648c4a 100644
--- a/src/FruDevice.cpp
+++ b/src/FruDevice.cpp
@@ -241,18 +241,23 @@
         return device;
     }
 
+    // Copy the IPMI Fru Header
     device.insert(device.end(), block_data.begin(), block_data.begin() + 8);
 
+    int fruLength = 0;
     for (size_t jj = 1; jj <= FRU_AREAS.size(); jj++)
     {
-        auto area_offset = device[jj];
+        // TODO: offset can be 255, device is holding "chars" that's not good.
+        int area_offset = device[jj];
         if (area_offset == 0)
         {
             continue;
         }
 
-        area_offset = static_cast<char>(area_offset * 8);
-        if (readFromEeprom(file, area_offset, 0x8, block_data.data()) < 0)
+        area_offset *= 8;
+
+        if (readFromEeprom(file, static_cast<uint16_t>(area_offset), 0x2,
+                           block_data.data()) < 0)
         {
             std::cerr << "failed to read bus " << bus << " address " << address
                       << "\n";
@@ -261,30 +266,36 @@
             return device;
         }
 
+        // Ignore data type.
         int length = block_data[1] * 8;
-        device.insert(device.end(), block_data.begin(), block_data.begin() + 8);
-        length -= 8;
-        area_offset = static_cast<char>(area_offset + 8);
+        area_offset += length;
+        fruLength = (area_offset > fruLength) ? area_offset : fruLength;
+    }
 
-        while (length > 0)
+    // You already copied these first 8 bytes (the ipmi fru header size)
+    fruLength -= 8;
+
+    int readOffset = 8;
+
+    while (fruLength > 0)
+    {
+        int to_get = std::min(I2C_SMBUS_BLOCK_MAX, fruLength);
+
+        if (readFromEeprom(file, static_cast<uint16_t>(readOffset),
+                           static_cast<uint8_t>(to_get), block_data.data()) < 0)
         {
-            auto to_get = std::min(I2C_SMBUS_BLOCK_MAX, length);
-
-            if (readFromEeprom(file, area_offset, static_cast<uint8_t>(to_get),
-                               block_data.data()) < 0)
-            {
-                std::cerr << "failed to read bus " << bus << " address "
-                          << address << "\n";
-                device.clear();
-                close(file);
-                return device;
-            }
-
-            device.insert(device.end(), block_data.begin(),
-                          block_data.begin() + to_get);
-            area_offset = static_cast<char>(area_offset + to_get);
-            length -= to_get;
+            std::cerr << "failed to read bus " << bus << " address " << address
+                      << "\n";
+            device.clear();
+            close(file);
+            return device;
         }
+
+        device.insert(device.end(), block_data.begin(),
+                      block_data.begin() + to_get);
+
+        readOffset += to_get;
+        fruLength -= to_get;
     }
 
     close(file);
@@ -428,45 +439,57 @@
             device.insert(device.end(), block_data.begin(),
                           block_data.begin() + 8);
 
+            int fruLength = 0;
             for (size_t jj = 1; jj <= FRU_AREAS.size(); jj++)
             {
-                auto area_offset = device[jj];
+                // TODO: offset can be 255, device is holding "chars" that's not
+                // good.
+                int area_offset = device[jj];
                 if (area_offset == 0)
                 {
                     continue;
                 }
 
-                area_offset = static_cast<char>(area_offset * 8);
-                if (read_block_data(flag, file, area_offset, 0x8,
+                area_offset *= 8;
+
+                if (read_block_data(flag, file,
+                                    static_cast<uint16_t>(area_offset), 0x2,
                                     block_data.data()) < 0)
                 {
                     std::cerr << "failed to read bus " << bus << " address "
                               << ii << "\n";
                     return -1;
                 }
+
+                // Ignore data type.
                 int length = block_data[1] * 8;
-                device.insert(device.end(), block_data.begin(),
-                              block_data.begin() + 8);
-                length -= 8;
-                area_offset = static_cast<char>(area_offset + 8);
+                area_offset += length;
+                fruLength = (area_offset > fruLength) ? area_offset : fruLength;
+            }
 
-                while (length > 0)
+            // You already copied these first 8 bytes (the ipmi fru header size)
+            fruLength -= 8;
+
+            int readOffset = 8;
+
+            while (fruLength > 0)
+            {
+                int to_get = std::min(I2C_SMBUS_BLOCK_MAX, fruLength);
+
+                if (read_block_data(
+                        flag, file, static_cast<uint16_t>(readOffset),
+                        static_cast<uint8_t>(to_get), block_data.data()) < 0)
                 {
-                    auto to_get = std::min(I2C_SMBUS_BLOCK_MAX, length);
-
-                    if (read_block_data(flag, file, area_offset,
-                                        static_cast<uint8_t>(to_get),
-                                        block_data.data()) < 0)
-                    {
-                        std::cerr << "failed to read bus " << bus << " address "
-                                  << ii << "\n";
-                        return -1;
-                    }
-                    device.insert(device.end(), block_data.begin(),
-                                  block_data.begin() + to_get);
-                    area_offset = static_cast<char>(area_offset + to_get);
-                    length -= to_get;
+                    std::cerr << "failed to read bus " << bus << " address "
+                              << ii << "\n";
+                    return -1;
                 }
+
+                device.insert(device.end(), block_data.begin(),
+                              block_data.begin() + to_get);
+
+                readOffset += to_get;
+                fruLength -= to_get;
             }
             devices->emplace(ii, device);
         }