writefrudata: validate multi-record data

It is fail to parse FRU data with multi-record area available as its
header data is different from other areas.
Support validate multi-record data so that can parse FRU data with
multi-record enabled.

Tested:
1. Check if the FRU with multi-record can be parsed.
2. Check IPMI fru print command.
  FRU Device Description : Builtin FRU Device (ID 0)
   Chassis Type          : Rack Mount Chassis
   Chassis Serial        : A1A1-A000-0000000000123
   Board Mfg Date        : Tue Feb  4 00:00:00 2020 UTC
   Board Mfg             : AmpereComputing(R)
   Board Product         : MT.JADE
   Board Serial          : 9510002CJ0LA
   Board Part Number     : B81.03010.0001
   Product Manufacturer  : AmpereComputing(R)
   Product Name          : MT.JADE
   Product Part Number   : AC82830AC10C
   Product Version       : PR010
   Product Serial        : AMPX3A1-A000-0000000000123

Signed-off-by: Thang Q. Nguyen <thang@os.amperecomputing.com>
Change-Id: I163c5c7bff2f7315e9c6b610f5074c37bc167ccf
diff --git a/writefrudata.cpp b/writefrudata.cpp
index 7507230..eca66d6 100644
--- a/writefrudata.cpp
+++ b/writefrudata.cpp
@@ -362,6 +362,48 @@
 }
 
 /**
+ * Validates the data for multirecord fields and CRC if selected
+ *
+ * @param[in] data - the data to verify
+ * @param[in] len - the length of the region to verify
+ * @param[in] validateCrc - whether to validate the CRC
+ * @return non-zero on failure
+ */
+int verifyFruMultiRecData(const uint8_t* data, const size_t len,
+                          bool validateCrc)
+{
+    uint8_t checksum = 0;
+    int rc = -1;
+
+    if (!validateCrc)
+    {
+        // There's nothing else to do for this area.
+        return EXIT_SUCCESS;
+    }
+
+    // As per the IPMI platform spec, byte[3] is the record checksum.
+    checksum = calculateCRC(data, len);
+    if (checksum != data[3])
+    {
+#ifdef __IPMI_DEBUG__
+        log<level::ERR>(
+            "Checksum mismatch",
+            entry("Calculated=0x%X", static_cast<uint32_t>(checksum)),
+            entry("Embedded=0x%X", static_cast<uint32_t>(data[3])));
+#endif
+        return rc;
+    }
+#ifdef __IPMI_DEBUG__
+    else
+    {
+        log<level::DEBUG>("Checksum matches");
+    }
+#endif
+
+    return EXIT_SUCCESS;
+}
+
+/**
  * Validates the data for mandatory fields and CRC if selected.
  *
  * @param[in] data - the data to verify
@@ -466,13 +508,22 @@
         }
         else if (areaOffset)
         {
-            // Read 2 bytes to know the actual size of area.
-            uint8_t areaHeader[2] = {0};
+            // Read 3 bytes to know the actual size of area.
+            uint8_t areaHeader[3] = {0};
             std::memcpy(areaHeader, &((uint8_t*)fruData)[areaOffset],
                         sizeof(areaHeader));
 
             // Size of this area will be the 2nd byte in the FRU area header.
-            size_t areaLen = areaHeader[1] * IPMI_EIGHT_BYTES;
+            size_t areaLen;
+            if (fruEntry == IPMI_FRU_MULTI_OFFSET)
+            {
+                areaLen = areaHeader[2] + IPMI_FRU_MULTIREC_HDR_BYTES;
+            }
+            else
+            {
+                areaLen = areaHeader[1] * IPMI_EIGHT_BYTES;
+            }
+
             uint8_t areaData[areaLen] = {0};
 
             log<level::DEBUG>("FRU Data", entry("SIZE=%d", dataLen),
@@ -495,7 +546,16 @@
             // contents beyond the first byte are not defined in the spec and
             // it may not end with a CRC byte.
             bool validateCrc = fruEntry != IPMI_FRU_INTERNAL_OFFSET;
-            rc = verifyFruData(areaData, areaLen, validateCrc);
+
+            if (fruEntry == IPMI_FRU_MULTI_OFFSET)
+            {
+                rc = verifyFruMultiRecData(areaData, areaLen, validateCrc);
+            }
+            else
+            {
+                rc = verifyFruData(areaData, areaLen, validateCrc);
+            }
+
             if (rc < 0)
             {
                 log<level::ERR>("Err validating FRU area",
diff --git a/writefrudata.hpp b/writefrudata.hpp
index cc5cef6..0628cdf 100644
--- a/writefrudata.hpp
+++ b/writefrudata.hpp
@@ -40,6 +40,7 @@
 #define IPMI_FRU_MULTI_OFFSET offsetof(struct common_header, multi_offset)
 #define IPMI_FRU_HDR_CRC_OFFSET offsetof(struct common_header, crc)
 #define IPMI_EIGHT_BYTES 8
+#define IPMI_FRU_MULTIREC_HDR_BYTES 5
 
 /**
  * Validate a FRU.