FruDevice: additional diagnostics when decode FRU

FruDevice currently has kind of loyal policy for FRU decoding, so one
can have a broken FRU but doesn't notice it.
This adds more diagnostics when decode FRU binary.

Tested: 1) tested with several different good FRUs
        2) tested with modified FRU like:
           * wrong checksum in data area
           * no C1 in the end of area
           * change one of C0 to C1

Signed-off-by: Andrei Kartashev <a.kartashev@yadro.com>
Change-Id: Id703dada73eb1fa35abd531a3303b06288bd4aad
diff --git a/src/FruDevice.cpp b/src/FruDevice.cpp
index b92116e..b2072e6 100644
--- a/src/FruDevice.cpp
+++ b/src/FruDevice.cpp
@@ -129,6 +129,8 @@
     return FRU_AREA_NAMES[static_cast<unsigned int>(area)];
 }
 bool validateHeader(const std::array<uint8_t, I2C_SMBUS_BLOCK_MAX>& blockData);
+uint8_t calculateChecksum(std::vector<uint8_t>::const_iterator iter,
+                          std::vector<uint8_t>::const_iterator end);
 bool updateFRUProperty(
     const std::string& assetTag, uint32_t bus, uint32_t address,
     std::string propertyName,
@@ -966,6 +968,17 @@
     return make_pair(DecodeState::ok, value);
 }
 
+static void checkLang(uint8_t lang)
+{
+    // If Lang is not English then the encoding is defined as 2-byte UNICODE,
+    // but we don't support that.
+    if (lang && lang != 25)
+    {
+        std::cerr << "Warning: language other then English is not "
+                     "supported \n";
+    }
+}
+
 bool formatFRU(const std::vector<uint8_t>& fruBytes,
                boost::container::flat_map<std::string, std::string>& result)
 {
@@ -1009,6 +1022,14 @@
             fruBytes.begin() + offset + fruAreaSize - 1;
         ++fruBytesIter;
 
+        if (calculateChecksum(fruBytes.begin() + offset, fruBytesIterEndArea) !=
+            *fruBytesIterEndArea)
+        {
+            std::cerr << "Checksum error in FRU area " << getFruAreaName(area)
+                      << "\n";
+            return false;
+        }
+
         switch (area)
         {
             case fruAreas::fruAreaChassis:
@@ -1021,8 +1042,10 @@
             }
             case fruAreas::fruAreaBoard:
             {
+                uint8_t lang = *fruBytesIter;
                 result["BOARD_LANGUAGE_CODE"] =
-                    std::to_string(static_cast<int>(*fruBytesIter));
+                    std::to_string(static_cast<int>(lang));
+                checkLang(lang);
                 fruBytesIter += 1;
 
                 unsigned int minutes = *fruBytesIter |
@@ -1050,8 +1073,10 @@
             }
             case fruAreas::fruAreaProduct:
             {
+                uint8_t lang = *fruBytesIter;
                 result["PRODUCT_LANGUAGE_CODE"] =
-                    std::to_string(static_cast<int>(*fruBytesIter));
+                    std::to_string(static_cast<int>(lang));
+                checkLang(lang);
                 fruBytesIter += 1;
                 fruAreaFieldNames = &PRODUCT_FRU_AREAS;
                 break;
@@ -1106,7 +1131,26 @@
                     return false;
                 }
             }
+            else
+            {
+                if (fieldIndex < fruAreaFieldNames->size())
+                {
+                    std::cerr << "Mandatory fields absent in FRU area "
+                              << getFruAreaName(area) << " after " << name
+                              << "\n";
+                }
+            }
         } while (state == DecodeState::ok);
+        for (; fruBytesIter < fruBytesIterEndArea; fruBytesIter++)
+        {
+            uint8_t c = *fruBytesIter;
+            if (c)
+            {
+                std::cerr << "Non-zero byte after EndOfFields in FRU area "
+                          << getFruAreaName(area) << "\n";
+                break;
+            }
+        }
     }
 
     return true;
@@ -1530,12 +1574,18 @@
 }
 
 // Calculate new checksum for fru info area
-size_t calculateChecksum(std::vector<uint8_t>& fruAreaData)
+uint8_t calculateChecksum(std::vector<uint8_t>::const_iterator iter,
+                          std::vector<uint8_t>::const_iterator end)
 {
     constexpr int checksumMod = 256;
     constexpr uint8_t modVal = 0xFF;
-    int sum = std::accumulate(fruAreaData.begin(), fruAreaData.end(), 0);
-    return (checksumMod - sum) & modVal;
+    int sum = std::accumulate(iter, end, 0);
+    int checksum = (checksumMod - sum) & modVal;
+    return static_cast<uint8_t>(checksum);
+}
+uint8_t calculateChecksum(std::vector<uint8_t>& fruAreaData)
+{
+    return calculateChecksum(fruAreaData.begin(), fruAreaData.end());
 }
 
 // Update new fru area length &
@@ -1575,9 +1625,7 @@
     std::copy_n(fruData.begin() + fruAreaStart, checksumLoc - fruAreaStart,
                 std::back_inserter(finalFRUData));
 
-    size_t checksumVal = calculateChecksum(finalFRUData);
-
-    fruData[checksumLoc] = static_cast<uint8_t>(checksumVal);
+    fruData[checksumLoc] = calculateChecksum(finalFRUData);
     return checksumLoc;
 }