VPD ECC support

Added methods/interfaces to create ECC and verify the data using ECC

Tested: tested some of the EEPROMS on Rainier simics
root@rainier:/tmp# ./ipz-read-vpd --file /sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a480.i2c-bus/i2c-8/8-0050/8-00500/nvmem
PASSED
root@rainier:/tmp# ./ipz-read-vpd --file /sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a400.i2c-bus/i2c-7/7-0050/7-00500/nvmem
PASSED
root@rainier:/tmp# ./ipz-read-vpd --file /sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a480.i2c-bus/i2c-8/8-0051/8-00510/nvmem
PASSED
root@rainier:/tmp# ./ipz-read-vpd --file /sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a400.i2c-bus/i2c-7/7-0051/7-00510/nvmem
PASSED
root@rainier:/tmp# ./ipz-read-vpd --file /sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a080.i2c-bus/i2c-0/0-0051/0-00510/nvmem
PASSED

Signed-off-by: Alpana Kumari <alpankum@in.ibm.com>
Change-Id: I863327f504c2dfa468d5ceadce10250292a968b7
diff --git a/Makefile.am b/Makefile.am
index c7e4d9c..e70b061 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -11,10 +11,16 @@
 	utils.hpp
 
 if IBM_PARSER
+noinst_HEADERS += \
+    vpdecc/vpdecc.h\
+    vpdecc/vpdecc_support.h
+
 bin_PROGRAMS = ibm-read-vpd
 ibm_read_vpd_SOURCES = \
 	ipz_app.cpp \
 	parser.cpp \
+	vpdecc/vpdecc.c   \
+	vpdecc/vpdecc_support.c\
 	impl.cpp \
 	utils.cpp
 
diff --git a/impl.cpp b/impl.cpp
index 25da01f..f4d1e42 100644
--- a/impl.cpp
+++ b/impl.cpp
@@ -12,6 +12,8 @@
 #include <tuple>
 #include <unordered_map>
 
+#include "vpdecc/vpdecc.h"
+
 namespace openpower
 {
 namespace vpd
@@ -55,21 +57,6 @@
         {"VS", std::make_tuple(record::Keyword::VS, keyword::Encoding::ASCII)},
 };
 
-namespace
-{
-
-using RecordId = uint8_t;
-using RecordOffset = uint16_t;
-using RecordSize = uint16_t;
-using RecordType = uint16_t;
-using RecordLength = uint16_t;
-using KwSize = uint8_t;
-using PoundKwSize = uint16_t;
-using ECCOffset = uint16_t;
-using ECCLength = uint16_t;
-
-} // namespace
-
 namespace offsets
 {
 
@@ -78,6 +65,9 @@
     VHDR = 17,
     VHDR_TOC_ENTRY = 29,
     VTOC_PTR = 35,
+    VTOC_DATA = 13,
+    VHDR_ECC = 0,
+    VHDR_RECORD = 11
 };
 }
 
@@ -89,9 +79,142 @@
     RECORD_NAME = 4,
     KW_NAME = 2,
     RECORD_MIN = 44,
+    VTOC_RECORD_LENGTH = 14,
+    VHDR_ECC_LENGTH = 11,
+    VHDR_RECORD_LENGTH = 44,
 };
 }
 
+namespace eccStatus
+{
+enum Status
+{
+    SUCCESS = 0,
+    FAILED = -1,
+};
+}
+
+namespace
+{
+constexpr auto toHex(size_t c)
+{
+    constexpr auto map = "0123456789abcdef";
+    return map[c];
+}
+} // namespace
+
+/*readUInt16LE: Read 2 bytes LE data*/
+static LE2ByteData readUInt16LE(Binary::const_iterator iterator)
+{
+    LE2ByteData lowByte = *iterator;
+    LE2ByteData highByte = *(iterator + 1);
+    lowByte |= (highByte << 8);
+    return lowByte;
+}
+
+RecordOffset Impl::getVtocOffset() const
+{
+    auto vpdPtr = vpd.cbegin();
+    std::advance(vpdPtr, offsets::VTOC_PTR);
+    // Get VTOC Offset
+    auto vtocOffset = readUInt16LE(vpdPtr);
+
+    return vtocOffset;
+}
+
+#ifdef IPZ_PARSER
+
+int Impl::vhdrEccCheck() const
+{
+    int rc = eccStatus::SUCCESS;
+    auto vpdPtr = vpd.cbegin();
+
+    auto l_status =
+        vpdecc_check_data(const_cast<uint8_t*>(&vpdPtr[offsets::VHDR_RECORD]),
+                          lengths::VHDR_RECORD_LENGTH,
+                          const_cast<uint8_t*>(&vpdPtr[offsets::VHDR_ECC]),
+                          lengths::VHDR_ECC_LENGTH);
+    if (l_status != VPD_ECC_OK)
+    {
+        rc = eccStatus::FAILED;
+    }
+
+    return rc;
+}
+
+int Impl::vtocEccCheck() const
+{
+    int rc = eccStatus::SUCCESS;
+    // Use another pointer to get ECC information from VHDR,
+    // actual pointer is pointing to VTOC data
+
+    auto vpdPtr = vpd.cbegin();
+
+    // Get VTOC Offset
+    auto vtocOffset = getVtocOffset();
+
+    // Get the VTOC Length
+    std::advance(vpdPtr, offsets::VTOC_PTR + sizeof(RecordOffset));
+    auto vtocLength = readUInt16LE(vpdPtr);
+
+    // Get the ECC Offset
+    std::advance(vpdPtr, sizeof(RecordLength));
+    auto vtocECCOffset = readUInt16LE(vpdPtr);
+
+    // Get the ECC length
+    std::advance(vpdPtr, sizeof(ECCOffset));
+    auto vtocECCLength = readUInt16LE(vpdPtr);
+
+    // Reset pointer to start of the vpd,
+    // so that Offset will point to correct address
+    vpdPtr = vpd.cbegin();
+    auto l_status = vpdecc_check_data(
+        const_cast<uint8_t*>(&vpdPtr[vtocOffset]), vtocLength,
+        const_cast<uint8_t*>(&vpdPtr[vtocECCOffset]), vtocECCLength);
+    if (l_status != VPD_ECC_OK)
+    {
+        rc = eccStatus::FAILED;
+    }
+
+    return rc;
+}
+
+int Impl::recordEccCheck(Binary::const_iterator iterator) const
+{
+    int rc = eccStatus::SUCCESS;
+
+    auto recordOffset = readUInt16LE(iterator);
+
+    std::advance(iterator, sizeof(RecordOffset));
+    auto recordLength = readUInt16LE(iterator);
+
+    std::advance(iterator, sizeof(RecordLength));
+    auto eccOffset = readUInt16LE(iterator);
+
+    std::advance(iterator, sizeof(ECCOffset));
+    auto eccLength = readUInt16LE(iterator);
+
+    if (eccLength == 0 || eccOffset == 0 || recordOffset == 0 ||
+        recordLength == 0)
+    {
+        throw std::runtime_error("Something went wrong. Could't find Record's "
+                                 "OR its ECC's offset and Length");
+    }
+
+    auto vpdPtr = vpd.cbegin();
+
+    auto l_status = vpdecc_check_data(
+        const_cast<uint8_t*>(&vpdPtr[recordOffset]), recordLength,
+        const_cast<uint8_t*>(&vpdPtr[eccOffset]), eccLength);
+    if (l_status != VPD_ECC_OK)
+    {
+        rc = eccStatus::FAILED;
+    }
+
+    return rc;
+}
+#endif
+
 void Impl::checkHeader() const
 {
     if (vpd.empty() || (lengths::RECORD_MIN > vpd.size()))
@@ -108,6 +231,16 @@
         {
             throw std::runtime_error("VHDR record not found");
         }
+
+#ifdef IPZ_PARSER
+        // Check ECC
+        int rc = eccStatus::FAILED;
+        rc = vhdrEccCheck();
+        if (rc != eccStatus::SUCCESS)
+        {
+            throw std::runtime_error("ERROR: VHDR ECC check Failed");
+        }
+#endif
     }
 }
 
@@ -116,9 +249,7 @@
     internal::OffsetList offsets{};
 
     // The offset to VTOC could be 1 or 2 bytes long
-    RecordOffset vtocOffset = vpd.at(offsets::VTOC_PTR);
-    RecordOffset highByte = vpd.at(offsets::VTOC_PTR + 1);
-    vtocOffset |= (highByte << 8);
+    RecordOffset vtocOffset = getVtocOffset();
 
     // Got the offset to VTOC, skip past record header and keyword header
     // to get to the record name.
@@ -135,6 +266,15 @@
         throw std::runtime_error("VTOC record not found");
     }
 
+#ifdef IPZ_PARSER
+    // Check ECC
+    int rc = eccStatus::FAILED;
+    rc = vtocEccCheck();
+    if (rc != eccStatus::SUCCESS)
+    {
+        throw std::runtime_error("ERROR: VTOC ECC check Failed");
+    }
+#endif
     // VTOC record name is good, now read through the TOC, stored in the PT
     // PT keyword; vpdBuffer is now pointing at the first character of the
     // name 'VTOC', jump to PT data.
@@ -165,13 +305,22 @@
         std::advance(iterator, lengths::RECORD_NAME + sizeof(RecordType));
 
         // Get record offset
-        RecordOffset offset = *iterator;
-        RecordOffset highByte = *(iterator + 1);
-        offset |= (highByte << 8);
+        auto offset = readUInt16LE(iterator);
         offsets.push_back(offset);
 
+#ifdef IPZ_PARSER
+        // Verify the ECC for this Record
+        int rc = recordEccCheck(iterator);
+
+        if (rc != eccStatus::SUCCESS)
+        {
+            throw std::runtime_error(
+                "ERROR: ECC check for one of the Record did not Pass.");
+        }
+#endif
+
         // Jump record size, record length, ECC offset and ECC length
-        std::advance(iterator, sizeof(RecordSize) + sizeof(RecordLength) +
+        std::advance(iterator, sizeof(RecordOffset) + sizeof(RecordLength) +
                                    sizeof(ECCOffset) + sizeof(ECCLength));
     }
 
@@ -190,6 +339,7 @@
     std::advance(iterator, nameOffset);
 
     std::string name(iterator, iterator + lengths::RECORD_NAME);
+
 #ifndef IPZ_PARSER
     if (supportedRecords.end() != supportedRecords.find(name))
     {
@@ -199,6 +349,7 @@
         std::advance(iterator, lengths::RECORD_NAME);
 
 #ifdef IPZ_PARSER
+
         // Reverse back to RT Kw, in ipz vpd, to Read RT KW & value
         std::advance(iterator, -(lengths::KW_NAME + sizeof(KwSize) +
                                  lengths::RECORD_NAME));
@@ -207,6 +358,7 @@
         // Add entry for this record (and contained keyword:value pairs)
         // to the parsed vpd output.
         out.emplace(std::move(name), std::move(kwMap));
+
 #ifndef IPZ_PARSER
     }
 #endif
@@ -383,7 +535,6 @@
     {
         processRecord(offset);
     }
-
     // Return a Store object, which has interfaces to
     // access parsed VPD by record:keyword
     return Store(std::move(out));
diff --git a/impl.hpp b/impl.hpp
index 525ae7a..8d3b59e 100644
--- a/impl.hpp
+++ b/impl.hpp
@@ -35,6 +35,22 @@
 
 } // namespace internal
 
+namespace
+{
+
+using RecordId = uint8_t;
+using RecordOffset = uint16_t;
+using RecordSize = uint16_t;
+using RecordType = uint16_t;
+using RecordLength = uint16_t;
+using KwSize = uint8_t;
+using PoundKwSize = uint16_t;
+using ECCOffset = uint16_t;
+using ECCLength = uint16_t;
+using LE2ByteData = uint16_t;
+
+} // namespace
+
 /** @class Impl
  *  @brief Implements parser for VPD
  *
@@ -129,6 +145,28 @@
     /** @brief Checks if the VHDR record is present in the VPD */
     void checkHeader() const;
 
+    /** @brief Checks the ECC for VHDR Record.
+     *  @returns Success(0) OR corrupted data(-1)
+     */
+    int vhdrEccCheck() const;
+
+    /** @brief Checks the ECC for VTOC Record.
+     *  @returns Success(0) OR corrupted data(-1)
+     */
+    int vtocEccCheck() const;
+
+    /** @brief Checks the ECC for the given record.
+     *
+     * @param[in] iterator - iterator pointing to a record in the VPD
+     * @returns Success(0) OR corrupted data(-1)
+     */
+    int recordEccCheck(Binary::const_iterator iterator) const;
+
+    /** @brief This interface collects Offset of VTOC
+     *  @returns VTOC Offset
+     */
+    RecordOffset getVtocOffset() const;
+
     /** @brief VPD in binary format */
     Binary vpd;
 
diff --git a/test/Makefile.am b/test/Makefile.am
index 05785da..8b93faa 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -8,8 +8,12 @@
 
 check_PROGRAMS += ipz_parser_test
 
-ipz_parser_test_SOURCES = ipz_parser/parser.cpp \
-                          ../impl.cpp
+ipz_parser_test_SOURCES = \
+    ipz_parser/parser.cpp \
+    ../impl.cpp           \
+    ../vpdecc/vpdecc.c  \
+    ../vpdecc/vpdecc_support.c
+
 test_cppflags = \
 	-Igtest \
 	$(GTEST_CPPFLAGS) \
@@ -32,10 +36,11 @@
 noinst_PROGRAMS = parser_test
 parser_test_SOURCES = \
 	parser/parser.cpp \
-	../impl.cpp \
-	../parser.cpp \
-	../write.cpp \
-	../utils.cpp
+    ../impl.cpp \
+    ../parser.cpp \
+    ../write.cpp \
+    ../utils.cpp
+
 parser_test_LDFLAGS = $(SDBUSPLUS_LIBS) $(PHOSPHOR_LOGGING_LIBS)
 parser_test_CXXFLAGS = $(SDBUSPLUS_CFLAGS) $(PHOSPHOR_LOGGING_CFLAGS)
 endif
diff --git a/vpdecc/vpdecc.c b/vpdecc/vpdecc.c
new file mode 100644
index 0000000..f7cf7eb
--- /dev/null
+++ b/vpdecc/vpdecc.c
@@ -0,0 +1,21 @@
+#include "vpdecc.h"
+
+#include <string.h>
+
+int vpdecc_create_ecc(const unsigned char* data, size_t data_length,
+                      unsigned char* ecc, size_t* ecc_buffersize)
+{
+    int i, vRet = 0;
+
+    memset(ecc, 0, *ecc_buffersize);
+
+    return vRet;
+}
+
+int vpdecc_check_data(unsigned char* data, size_t data_length,
+                      const unsigned char* ecc, size_t ecc_length)
+{
+    int vRet = 0;
+
+    return vRet;
+}
diff --git a/vpdecc/vpdecc.h b/vpdecc/vpdecc.h
new file mode 100644
index 0000000..6e7605f
--- /dev/null
+++ b/vpdecc/vpdecc.h
@@ -0,0 +1,87 @@
+/******************************************************************************

+ *

+ * IBM Confidential

+ *

+ * Licensed Internal Code Source Materials

+ *

+ * IBM Flexible Support Processor Licensed Internal Code

+ *

+ * (c) Copyright IBM Corp. 2004

+ *

+ * The source code is for this program is not published or otherwise divested

+ * of its trade secrets, irrespective of what has been deposited with the

+ * U.S. Copyright Office.

+ *

+ *****************************************************************************/

+

+#ifndef _VPDECC_H_

+#define _VPDECC_H_

+

+#include <stdlib.h>

+

+#define VPD_ECC_OK 0

+#define VPD_ECC_NOT_ENOUGH_BUFFER 1

+#define VPD_ECC_WRONG_ECC_SIZE 2

+#define VPD_ECC_WRONG_BUFFER_SIZE 9

+#define VPD_ECC_UNCORRECTABLE_DATA 90

+#define VPD_ECC_CORRECTABLE_DATA 91

+

+#ifdef __cplusplus

+extern "C" {

+#endif

+

+/* TODO  doxygen !!!!!!!! */

+

+/******************************************************************************/

+/* vpdecc_create_ecc                                                          */

+/*                                                                            */

+/* For a given data block (together with the length of the data block         */

+/* this function creates the ECC                                              */

+/*                                                                            */

+/* @param     pData           In-Buffer containing the raw VPD data           */

+/*                                            (wont't be changed)             */

+/*                                                                            */

+/* @param     vDataLength     In        should contain the length of the Data */

+/*                                      in the buffer given to vData          */

+/*                                                                            */

+/* @param     pEcc            Out-Buffer after execution this will be the     */

+/*                                      buffer for the calculated Ecc         */

+/*                                                                            */

+/* @param     pEccLenght      In/Out    In : size of buffer                   */

+/*                                      Out: contains the length of the Ecc   */

+/*                                                                            */

+/* @return Error returncode                                                   */

+/******************************************************************************/

+int vpdecc_create_ecc(const unsigned char* data, size_t datalength,

+                      unsigned char* ecc, size_t* ecc_buffersize);

+

+/******************************************************************************/

+/* vpdecc_check_data                                                          */

+/*                                                                            */

+/* For a given data block (together with the ecc)                             */

+/* this function checks the data for validness                                */

+/*                                                                            */

+/* @param     pData           In-Buffer containing the raw VPD data           */

+/*                            Out-Buffer containing the raw VPD data          */

+/*                                      No error           : data unchanged   */

+/*                                      Correctable error  : data corrected   */

+/*                                      Uncorrectable error: data unchanged   */

+/*                                                                            */

+/* @param     vDataLength     In        should contain the length of the Data */

+/*                                      in the buffer given to vData          */

+/*                                                                            */

+/* @param     pEcc            In-Buffer should contain the Ecc for the data   */

+/*                                                                            */

+/*                                                                            */

+/* @param     vEccLenght      In        should contain the length of the Ecc  */

+/*                                                                            */

+/* @return Error returncode                                                   */

+/******************************************************************************/

+int vpdecc_check_data(unsigned char* data, size_t data_length,

+                      const unsigned char* ecc, size_t ecc_length);

+

+#ifdef __cplusplus

+} /* end extern "C" */

+#endif

+

+#endif /* endif _VPDECC_H_ */

diff --git a/vpdecc/vpdecc_support.c b/vpdecc/vpdecc_support.c
new file mode 100644
index 0000000..674f236
--- /dev/null
+++ b/vpdecc/vpdecc_support.c
@@ -0,0 +1,75 @@
+
+#include "vpdecc_support.h"
+
+#include <string.h>
+
+/******************************************************************************/
+/* seepromGetEcc                                                              */
+/*                                                                            */
+/* Calculates the 7 bit ECC code of a 32 bit data word and returns it         */
+/*                                                                            */
+/******************************************************************************/
+inline unsigned char seepromGetEcc(const unsigned char* data)
+{
+    unsigned char vResult = 0x00;
+    return vResult;
+}
+
+/******************************************************************************/
+/*                                                                            */
+/******************************************************************************/
+int seepromScramble(const int bitOffset, const unsigned char* cleanData,
+                    size_t cleanSize, unsigned char* scrambledData,
+                    size_t scrambledSize)
+{
+    int vRet = 0;
+    return vRet;
+}
+
+/******************************************************************************/
+/*                                                                            */
+/******************************************************************************/
+int seepromUnscramble(const int bitOffset, const unsigned char* scrambledData,
+                      size_t scrambledSize, unsigned char* cleanData,
+                      size_t cleanSize)
+{
+    int vRet = 0;
+    return vRet;
+}
+
+/******************************************************************************/
+/* seepromGenCsDecode                                                         */
+/*                                                                            */
+/*                                                                            */
+/******************************************************************************/
+void seepromGenCsDecode(const unsigned char numBits,
+                        const unsigned char syndrom,
+                        const unsigned char* csdSyndroms,
+                        unsigned char* vResult)
+{
+}
+
+/******************************************************************************/
+/* seepromGenerateCheckSyndromDecode                                          */
+/*                                                                            */
+/*                                                                            */
+/******************************************************************************/
+void seepromGenerateCheckSyndromDecode(const unsigned char checkSyndrom,
+                                       unsigned char* csdData,
+                                       unsigned char* csdEcc)
+{
+}
+
+/******************************************************************************/
+/* seepromEccCheck                                                            */
+/*                                                                            */
+/* Checks the data integrety and correct it if possible                       */
+/*                                                                            */
+/******************************************************************************/
+
+int seepromEccCheck(unsigned char* vData, unsigned char* vEcc,
+                    size_t numOfWords)
+{
+    int vRet = 0;
+    return vRet;
+}
diff --git a/vpdecc/vpdecc_support.h b/vpdecc/vpdecc_support.h
new file mode 100644
index 0000000..6f71c03
--- /dev/null
+++ b/vpdecc/vpdecc_support.h
@@ -0,0 +1,45 @@
+#include "vpdecc.h"
+
+/******************************************************************************/
+unsigned char seepromGetEcc(const unsigned char* data);
+
+/******************************************************************************/
+/* seepromScramble                                                            */
+/******************************************************************************/
+
+int seepromScramble(const int bitOffset, const unsigned char* cleanData,
+                    size_t cleanSize, unsigned char* scrambledData,
+                    size_t scrambledSize);
+
+/******************************************************************************/
+/******************************************************************************/
+int seepromUnscramble(const int bitOffset, const unsigned char* scrambledData,
+                      size_t scrambledSize, unsigned char* cleanData,
+                      size_t cleanSize);
+
+/******************************************************************************/
+/******************************************************************************/
+void seepromGenCsDecode(const unsigned char numBits,
+                        const unsigned char syndrom,
+                        const unsigned char* csdSyndroms,
+                        unsigned char* vResult);
+
+/******************************************************************************/
+/* seepromGenerateCheckSyndromDecode                                          */
+/******************************************************************************/
+void seepromGenerateCheckSyndromDecode(const unsigned char checkSyndrom,
+                                       unsigned char* csdData,
+                                       unsigned char* csdEcc);
+
+/******************************************************************************/
+/******************************************************************************/
+int seepromEccCheck(unsigned char* vData, unsigned char* vEcc,
+                    size_t numOfDataBytes);
+
+/******************************************************************************/
+/******************************************************************************/
+/*int seepromCheckData(unsigned char* seepromData);*/
+
+/******************************************************************************/
+/******************************************************************************/
+/*int seepromCreateEcc(unsigned char* seepromData);*/