IBM VPD main app & KeywordVPD parser:

IBM VPD main application triggers either
'IPZ/Keyword' type VPD parser. This commit also has
keyword VPD parser code.

Flag to enable IBM VPD main parser is
"--enable-ibm-parser"
Flag to debug the keyword vpd parser is
"--enable-debug-kw-vpd".

Steps to build and execute:
 ./bootstrap.sh
 ./configure ${CONFIGURE_FLAGS} --enable-ibm-parser
 make
To run test cases:
 make check

Test:
Tested on a rainier system to parse
[IPZ and Keyword] types of VPD.

Signed-off-by: PriyangaRamasamy <priyanga24@in.ibm.com>
Change-Id: Ie4466551a60acd16ad9e4852f9b4d14c51f0a44d
diff --git a/keyword_vpd_parser.cpp b/keyword_vpd_parser.cpp
new file mode 100644
index 0000000..eef17b8
--- /dev/null
+++ b/keyword_vpd_parser.cpp
@@ -0,0 +1,207 @@
+#include "keyword_vpd_parser.hpp"
+
+#include <iostream>
+#include <numeric>
+#include <string>
+
+namespace vpd
+{
+namespace keyword
+{
+namespace parser
+{
+KeywordVpdMap KeywordVpdParser::parseKwVpd()
+{
+    int kwVpdType;
+    if (keywordVpdVector.empty())
+    {
+        throw std::runtime_error("Blank Vpd Data");
+    }
+
+    validateLargeResourceIdentifierString();
+
+    kwVpdType = validateTheTypeOfKwVpd();
+
+    auto kwValMap = kwValParser();
+
+    // Donot process these two functions for bono type VPD
+    if (!kwVpdType)
+    {
+        validateSmallResourceTypeEnd();
+
+        validateChecksum();
+    }
+
+    validateSmallResourceTypeLastEnd();
+
+#ifdef DEBUG_KW_VPD
+    cerr << '\n' << " KW " << '\t' << "  VALUE " << '\n';
+    for (const auto& it : kwValMap)
+    {
+        cerr << '\n' << " " << it->first << '\t';
+        copy((it->second).begin(), (it->second).end(),
+             ostream_iterator<int>(cout << hex, " "));
+    }
+#endif
+
+    return kwValMap;
+}
+
+void KeywordVpdParser::validateLargeResourceIdentifierString()
+{
+    kwVpdIterator = keywordVpdVector.begin();
+
+    // Check for large resource type identfier string
+    if (*kwVpdIterator != KW_VPD_START_TAG)
+    {
+        throw std::runtime_error(
+            "Invalid Large resource type Identifier String");
+    }
+
+    itrOutOfBoundCheck(1);
+    advance(kwVpdIterator, sizeof(KW_VPD_START_TAG));
+}
+
+int KeywordVpdParser::validateTheTypeOfKwVpd()
+{
+    size_t dataSize = getKwDataSize();
+
+    itrOutOfBoundCheck(TWO_BYTES + dataSize);
+
+#ifdef DEBUG_KW_VPD
+    auto dsDeb = dataSize;
+    auto itDeb = kwVpdIterator + TWO_BYTES;
+    std::cout << '\n' << '\t';
+    while (dsDeb != 0)
+    {
+        std::cout << *itDeb;
+        itDeb++;
+        dsDeb--;
+    }
+    std::cout << '\n';
+#endif
+
+    // +TWO_BYTES is the description's size byte
+    std::advance(kwVpdIterator, TWO_BYTES + dataSize);
+
+    int kwVpdType = 0;
+    // Check for invalid vendor defined large resource type
+    if (*kwVpdIterator != KW_VAL_PAIR_START_TAG)
+    {
+        if (*kwVpdIterator != ALT_KW_VAL_PAIR_START_TAG)
+        {
+            throw std::runtime_error("Invalid Keyword Value Pair Start Tag");
+        }
+        // Bono vpd referred as 1
+        kwVpdType = 1;
+    }
+    return kwVpdType;
+}
+
+KeywordVpdMap KeywordVpdParser::kwValParser()
+{
+    int totalSize = 0;
+    KeywordVpdMap kwValMap;
+
+    checkSumStart = kwVpdIterator;
+
+    itrOutOfBoundCheck(1);
+    kwVpdIterator++;
+
+    // Get the total length of all keyword value pairs
+    totalSize = getKwDataSize();
+
+    if (totalSize == 0)
+    {
+        throw std::runtime_error("Badly formed keyword VPD data");
+    }
+
+    itrOutOfBoundCheck(TWO_BYTES);
+    std::advance(kwVpdIterator, TWO_BYTES);
+
+    // Parse the keyword-value and store the pairs in map
+    while (totalSize > 0)
+    {
+        std::string kwStr(kwVpdIterator, kwVpdIterator + TWO_BYTES);
+
+        totalSize -= TWO_BYTES;
+        itrOutOfBoundCheck(TWO_BYTES);
+        std::advance(kwVpdIterator, TWO_BYTES);
+
+        size_t kwSize = *kwVpdIterator;
+
+        itrOutOfBoundCheck(1);
+        kwVpdIterator++;
+
+        std::vector<uint8_t> valVec(kwVpdIterator, kwVpdIterator + kwSize);
+
+        itrOutOfBoundCheck(kwSize);
+        std::advance(kwVpdIterator, kwSize);
+
+        totalSize -= kwSize + 1;
+
+        kwValMap.emplace(std::make_pair(std::move(kwStr), std::move(valVec)));
+    }
+
+    checkSumEnd = kwVpdIterator - 1;
+
+    return kwValMap;
+}
+
+void KeywordVpdParser::validateSmallResourceTypeEnd()
+{
+    // Check for small resource type end tag
+    if (*kwVpdIterator != KW_VAL_PAIR_END_TAG)
+    {
+        throw std::runtime_error("Invalid Small resource type End");
+    }
+}
+
+void KeywordVpdParser::validateChecksum()
+{
+    uint8_t checkSum = 0;
+
+    // Checksum calculation
+    checkSum = std::accumulate(checkSumStart, checkSumEnd + 1, checkSum);
+    checkSum = ~checkSum + 1;
+
+    if (checkSum != *(kwVpdIterator + 1))
+    {
+        throw std::runtime_error("Invalid Check sum");
+    }
+#ifdef DEBUG_KW_VPD
+    std::cout << "\nCHECKSUM : " << std::hex << static_cast<int>(checkSum)
+              << std::endl;
+#endif
+
+    itrOutOfBoundCheck(TWO_BYTES);
+    std::advance(kwVpdIterator, TWO_BYTES);
+}
+
+void KeywordVpdParser::validateSmallResourceTypeLastEnd()
+{
+    // Check for small resource type last end of data
+    if (*kwVpdIterator != KW_VPD_END_TAG)
+    {
+        throw std::runtime_error(
+            "Invalid Small resource type Last End Of Data");
+    }
+}
+
+size_t KeywordVpdParser::getKwDataSize()
+{
+    return (*(kwVpdIterator + 1) << 8 | *kwVpdIterator);
+}
+
+void KeywordVpdParser::itrOutOfBoundCheck(uint8_t incVar)
+{
+
+    if ((std::distance(keywordVpdVector.begin(), kwVpdIterator + incVar)) >
+        std::distance(keywordVpdVector.begin(), keywordVpdVector.end()))
+    {
+        throw std::runtime_error("Badly formed VPD data");
+    }
+}
+} // namespace parser
+} // namespace keyword
+} // namespace vpd