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/Makefile.am b/Makefile.am
index e70b061..56a3b57 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -7,8 +7,9 @@
 	write.hpp \
 	impl.hpp \
 	args.hpp \
-	types.hpp \
-	utils.hpp
+	utils.hpp \
+        keyword_vpd_parser.hpp \
+	ibm_vpd_type_check.hpp
 
 if IBM_PARSER
 noinst_HEADERS += \
@@ -17,12 +18,14 @@
 
 bin_PROGRAMS = ibm-read-vpd
 ibm_read_vpd_SOURCES = \
-	ipz_app.cpp \
+	ibm_vpd_app.cpp \
+	ibm_vpd_type_check.cpp \
 	parser.cpp \
 	vpdecc/vpdecc.c   \
 	vpdecc/vpdecc_support.c\
 	impl.cpp \
-	utils.cpp
+	utils.cpp \
+        keyword_vpd_parser.cpp
 
 ibm_read_vpd_LDFLAGS = $(SDBUSPLUS_LIBS) $(PHOSPHOR_LOGGING_LIBS)
 ibm_read_vpd_CXXFLAGS = $(SDBUSPLUS_CFLAGS) $(PHOSPHOR_LOGGING_CFLAGS)
diff --git a/configure.ac b/configure.ac
index 5e6bb8b..6657b52 100644
--- a/configure.ac
+++ b/configure.ac
@@ -17,7 +17,6 @@
 PKG_CHECK_MODULES([SDBUSPLUS], [sdbusplus])
 PKG_CHECK_MODULES([PHOSPHOR_LOGGING], [phosphor-logging])
 
-
 # Suppress the --with-libtool-sysroot error
 LT_INIT
 
@@ -78,6 +77,19 @@
 AX_PTHREAD([GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=1"],[GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=0"])
 AC_SUBST(GTEST_CPPFLAGS)
 
+# Configure option to enable debug information for keyword VPD parser
+AC_ARG_ENABLE([debug-kw-vpd],
+              AS_HELP_STRING([--enable-debug-kw-vpd ],
+              [Debug statements for Keyword VPD Parser .
+            ]))
+AS_IF([ test "$enable_debug_kw_vpd" = "yes"],
+      [
+       AC_MSG_NOTICE([Enabling debug information for keyword vpd parser application])
+       kw_vpd_debug_flags=-DDEBUG_KW_VPD
+       AC_SUBST([KW_VPD_DEBUG_FLAGS], [$kw_vpd_debug_flags])
+      ]
+      )
+
 AC_CONFIG_HEADERS([config.h])
 AC_CONFIG_FILES([Makefile test/Makefile])
 AC_OUTPUT
diff --git a/ibm_vpd_app.cpp b/ibm_vpd_app.cpp
new file mode 100644
index 0000000..44977e0
--- /dev/null
+++ b/ibm_vpd_app.cpp
@@ -0,0 +1,259 @@
+#include "config.h"
+
+#include "defines.hpp"
+#include "ibm_vpd_type_check.hpp"
+#include "keyword_vpd_parser.hpp"
+#include "parser.hpp"
+#include "utils.hpp"
+
+#include <CLI/CLI.hpp>
+#include <exception>
+#include <fstream>
+#include <iostream>
+#include <iterator>
+#include <nlohmann/json.hpp>
+
+using namespace std;
+using namespace openpower::vpd;
+using namespace CLI;
+using namespace vpd::keyword::parser;
+using namespace vpdFormat;
+
+/** @brief Encodes a keyword for D-Bus.
+ */
+static string encodeKeyword(const string& kw, const string& encoding)
+{
+    if (encoding == "MAC")
+    {
+        string res{};
+        size_t first = kw[0];
+        res += toHex(first >> 4);
+        res += toHex(first & 0x0f);
+        for (size_t i = 1; i < kw.size(); ++i)
+        {
+            res += ":";
+            res += toHex(kw[i] >> 4);
+            res += toHex(kw[i] & 0x0f);
+        }
+        return res;
+    }
+    else // default to string encoding
+    {
+        return string(kw.begin(), kw.end());
+    }
+}
+
+/**
+ * @brief Populate FRU specific interfaces.
+ *
+ * This is a common method which handles both
+ * ipz and keyword specific interfaces thus,
+ * reducing the code redundancy.
+ * @param[in] map - Reference to the innermost keyword-value map.
+ * @param[in] preIntrStr - Reference to the interface string.
+ * @param[out] interfaces - Reference to interface map.
+ */
+template <typename T>
+static void populateFruSpecificInterfaces(const T& map,
+                                          const string& preIntrStr,
+                                          inventory::InterfaceMap& interfaces)
+{
+    inventory::PropertyMap prop;
+
+    for (const auto& kwVal : map)
+    {
+        std::vector<uint8_t> vec(kwVal.second.begin(), kwVal.second.end());
+
+        auto kw = kwVal.first;
+
+        if (kw[0] == '#')
+        {
+            kw = std::string("PD_") + kw[1];
+        }
+        prop.emplace(move(kw), move(vec));
+    }
+
+    interfaces.emplace(preIntrStr, move(prop));
+}
+
+/**
+ * @brief Populate Interfaces.
+ *
+ * This method populates common and extra interfaces to dbus.
+ * @param[in] js - json object
+ * @param[out] interfaces - Reference to interface map
+ * @param[in] vpdMap - Reference to the parsed vpd map.
+ */
+template <typename T>
+static void populateInterfaces(const nlohmann::json& js,
+                               inventory::InterfaceMap& interfaces,
+                               const T& vpdMap)
+{
+    for (const auto& ifs : js.items())
+    {
+        const string& inf = ifs.key();
+        inventory::PropertyMap props;
+
+        for (const auto& itr : ifs.value().items())
+        {
+            const string& rec = itr.value().value("recordName", "");
+            const string& kw = itr.value().value("keywordName", "");
+            const string& encoding = itr.value().value("encoding", "");
+
+            if constexpr (std::is_same<T, Parsed>::value)
+            {
+                if (!rec.empty() && !kw.empty() && vpdMap.at(rec).count(kw) &&
+                    vpdMap.count(rec))
+                {
+                    auto encoded =
+                        encodeKeyword(vpdMap.at(rec).at(kw), encoding);
+                    props.emplace(itr.key(), encoded);
+                }
+            }
+            else if constexpr (std::is_same<T, KeywordVpdMap>::value)
+            {
+                if (!kw.empty() && vpdMap.count(kw))
+                {
+                    auto prop =
+                        string(vpdMap.at(kw).begin(), vpdMap.at(kw).end());
+                    auto encoded = encodeKeyword(prop, encoding);
+                    props.emplace(itr.key(), encoded);
+                }
+            }
+        }
+        interfaces.emplace(inf, move(props));
+    }
+}
+
+/**
+ * @brief Populate Dbus.
+ *
+ * This method invokes all the populateInterface functions
+ * and notifies PIM about dbus object.
+ * @param[in] vpdMap - Either IPZ vpd map or Keyword vpd map based on the input.
+ * @param[in] js - Inventory json object
+ * @param[in] filePath - Path of the vpd file
+ * @param[in] preIntrStr - Interface string
+ */
+template <typename T>
+static void populateDbus(const T& vpdMap, nlohmann::json& js,
+                         const string& filePath, const string& preIntrStr)
+{
+    inventory::InterfaceMap interfaces;
+    inventory::ObjectMap objects;
+    inventory::PropertyMap prop;
+
+    for (const auto& item : js["frus"][filePath])
+    {
+        const auto& objectPath = item["inventoryPath"];
+        sdbusplus::message::object_path object(objectPath);
+        // Populate the VPD keywords and the common interfaces only if we
+        // are asked to inherit that data from the VPD, else only add the
+        // extraInterfaces.
+        if (item.value("inherit", true))
+        {
+            if constexpr (std::is_same<T, Parsed>::value)
+            {
+                // Each record in the VPD becomes an interface and all keyword
+                // within the record are properties under that interface.
+                for (const auto& record : vpdMap)
+                {
+                    populateFruSpecificInterfaces(
+                        record.second, preIntrStr + record.first, interfaces);
+                }
+            }
+            else if constexpr (std::is_same<T, KeywordVpdMap>::value)
+            {
+                populateFruSpecificInterfaces(vpdMap, preIntrStr, interfaces);
+            }
+        }
+
+        // Populate interfaces and properties that are common to every FRU
+        // and additional interface that might be defined on a per-FRU basis.
+
+        if (item.find("commonInterfaces") != item.end())
+        {
+            populateInterfaces(item["commonInterfaces"], interfaces, vpdMap);
+        }
+        if (item.find("extraInterfaces") != item.end())
+        {
+            populateInterfaces(item["extraInterfaces"], interfaces, vpdMap);
+        }
+        objects.emplace(move(object), move(interfaces));
+    }
+
+    // Notify PIM
+    inventory::callPIM(move(objects));
+}
+
+int main(int argc, char** argv)
+{
+    int rc = 0;
+
+    try
+    {
+        using json = nlohmann::json;
+
+        App app{"ibm-read-vpd - App to read IPZ format VPD, parse it and store "
+                "in DBUS"};
+        string file{};
+
+        app.add_option("-f, --file", file, "File containing VPD (IPZ/KEYWORD)")
+            ->required()
+            ->check(ExistingFile);
+
+        CLI11_PARSE(app, argc, argv);
+
+        // Make sure that the file path we get is for a supported EEPROM
+        ifstream inventoryJson(INVENTORY_JSON);
+        auto js = json::parse(inventoryJson);
+
+        if ((js.find("frus") == js.end()) ||
+            (js["frus"].find(file) == js["frus"].end()))
+        {
+            throw std::runtime_error("Device path missing in inventory JSON");
+        }
+
+        // Open the file in binary mode
+        ifstream vpdFile(file, ios::binary);
+        // Read the content of the binary file into a vector
+        Binary vpdVector((istreambuf_iterator<char>(vpdFile)),
+                         istreambuf_iterator<char>());
+
+        vpdType type = vpdTypeCheck(vpdVector);
+
+        switch (type)
+        {
+            case IPZ_VPD:
+            {
+                // Invoking IPZ Vpd Parser
+                auto vpdStore = parse(move(vpdVector));
+                const Parsed& vpdMap = vpdStore.getVpdMap();
+                string preIntrStr = "com.ibm.ipzvpd.";
+                // Write it to the inventory
+                populateDbus(vpdMap, js, file, preIntrStr);
+            }
+            break;
+
+            case KEYWORD_VPD:
+            {
+                // Creating Keyword Vpd Parser Object
+                KeywordVpdParser parserObj(move(vpdVector));
+                // Invoking KW Vpd Parser
+                const auto& kwValMap = parserObj.parseKwVpd();
+                string preIntrStr = "com.ibm.kwvpd.KWVPD";
+                populateDbus(kwValMap, js, file, preIntrStr);
+            }
+            break;
+            default:
+                throw std::runtime_error("Invalid VPD format");
+        }
+    }
+    catch (exception& e)
+    {
+        cerr << e.what() << "\n";
+        rc = -1;
+    }
+
+    return rc;
+}
diff --git a/ibm_vpd_type_check.cpp b/ibm_vpd_type_check.cpp
new file mode 100644
index 0000000..41303ee
--- /dev/null
+++ b/ibm_vpd_type_check.cpp
@@ -0,0 +1,25 @@
+#include "ibm_vpd_type_check.hpp"
+
+#include "keyword_vpd_types.hpp"
+
+using namespace vpd::keyword::parser;
+
+namespace vpdFormat
+{
+vpdType vpdTypeCheck(const Binary& vpdVector)
+{
+    if (vpdVector[IPZ_DATA_START] == KW_VAL_PAIR_START_TAG)
+    {
+        // IPZ VPD FORMAT
+        return vpdType::IPZ_VPD;
+    }
+    else if (vpdVector[KW_VPD_DATA_START] == KW_VPD_START_TAG)
+    {
+        // KEYWORD VPD FORMAT
+        return vpdType::KEYWORD_VPD;
+    }
+
+    // INVALID VPD FORMAT
+    return vpdType::INVALID_VPD_FORMAT;
+}
+} // namespace vpdFormat
diff --git a/ibm_vpd_type_check.hpp b/ibm_vpd_type_check.hpp
new file mode 100644
index 0000000..d122473
--- /dev/null
+++ b/ibm_vpd_type_check.hpp
@@ -0,0 +1,27 @@
+#pragma once
+#include <types.hpp>
+
+using namespace openpower::vpd;
+
+namespace vpdFormat
+{
+/**
+ * @brief Types of VPD
+ */
+enum vpdType
+{
+    IPZ_VPD,           /**< IPZ VPD type */
+    KEYWORD_VPD,       /**< Keyword VPD type */
+    INVALID_VPD_FORMAT /**< Invalid VPD type */
+};
+
+/**
+ * @brief Check the type of VPD.
+ *
+ * Checks the type of vpd based on the start tag.
+ * @param[in] vector - Vpd data in vector format
+ *
+ * @return enum of type vpdType
+ */
+vpdType vpdTypeCheck(const Binary& vector);
+} // namespace vpdFormat
diff --git a/ipz_app.cpp b/ipz_app.cpp
deleted file mode 100644
index cd5be8f..0000000
--- a/ipz_app.cpp
+++ /dev/null
@@ -1,172 +0,0 @@
-#include "config.h"
-
-#include "defines.hpp"
-#include "parser.hpp"
-#include "utils.hpp"
-
-#include <CLI/CLI.hpp>
-#include <exception>
-#include <fstream>
-#include <iostream>
-#include <iterator>
-#include <nlohmann/json.hpp>
-
-using namespace std;
-using namespace openpower::vpd;
-
-/** @brief Encodes a keyword for D-Bus.
- */
-static string encodeKeyword(const string& rec, const string& kw,
-                            const string& encoding, const Parsed& vpdMap)
-{
-    if (encoding == "MAC")
-    {
-        string res{};
-        const auto& val = vpdMap.at(rec).at(kw);
-        size_t first = val[0];
-        res += toHex(first >> 4);
-        res += toHex(first & 0x0f);
-        for (size_t i = 1; i < val.size(); ++i)
-        {
-            res += ":";
-            res += toHex(val[i] >> 4);
-            res += toHex(val[i] & 0x0f);
-        }
-        return res;
-    }
-    else // default to string encoding
-    {
-        return string(vpdMap.at(rec).at(kw).begin(),
-                      vpdMap.at(rec).at(kw).end());
-    }
-}
-
-static void populateInterfaces(const nlohmann::json& js,
-                               inventory::InterfaceMap& interfaces,
-                               const Parsed& vpdMap)
-{
-    for (const auto& ifs : js.items())
-    {
-        const string& inf = ifs.key();
-        inventory::PropertyMap props;
-
-        for (const auto& itr : ifs.value().items())
-        {
-            const string& rec = itr.value().value("recordName", "");
-            const string& kw = itr.value().value("keywordName", "");
-            const string& encoding = itr.value().value("encoding", "");
-
-            if (!rec.empty() && !kw.empty() && vpdMap.count(rec) &&
-                vpdMap.at(rec).count(kw))
-            {
-                auto encoded = encodeKeyword(rec, kw, encoding, vpdMap);
-                props.emplace(itr.key(), encoded);
-            }
-        }
-        interfaces.emplace(inf, move(props));
-    }
-}
-
-static void populateDbus(Store& vpdStore, nlohmann::json& js,
-                         const string& filePath)
-{
-    inventory::InterfaceMap interfaces;
-    inventory::ObjectMap objects;
-    const auto& vpdMap = vpdStore.getVpdMap();
-    string preIntrStr = "com.ibm.ipzvpd.";
-
-    for (const auto& item : js["frus"][filePath])
-    {
-        const auto& objectPath = item["inventoryPath"];
-        sdbusplus::message::object_path object(objectPath);
-
-        // Populate the VPD keywords and the common interfaces only if we
-        // are asked to inherit that data from the VPD, else only add the
-        // extraInterfaces.
-        if (item.value("inherit", true))
-        {
-            // Each record in the VPD becomes an interface and all keywords
-            // within the record are properties under that interface.
-            for (const auto& record : vpdMap)
-            {
-                inventory::PropertyMap prop;
-                for (auto kwVal : record.second)
-                {
-                    std::vector<uint8_t> vec(kwVal.second.begin(),
-                                             kwVal.second.end());
-                    std::string kw = kwVal.first;
-                    if (kw[0] == '#')
-                    {
-                        kw = std::string("PD_") + kw[1];
-                    }
-                    prop.emplace(move(kw), move(vec));
-                }
-                interfaces.emplace(preIntrStr + record.first, move(prop));
-            }
-
-            // Populate interfaces and properties that are common to every FRU
-            // and additional interface that might be defined on a per-FRU
-            // basis.
-            if (js.find("commonInterfaces") != js.end())
-            {
-                populateInterfaces(js["commonInterfaces"], interfaces, vpdMap);
-            }
-        }
-        if (item.find("extraInterfaces") != item.end())
-        {
-            populateInterfaces(item["extraInterfaces"], interfaces, vpdMap);
-        }
-        objects.emplace(move(object), move(interfaces));
-    }
-
-    // Notify PIM
-    inventory::callPIM(move(objects));
-}
-
-int main(int argc, char** argv)
-{
-    int rc = 0;
-
-    try
-    {
-        using namespace CLI;
-        using json = nlohmann::json;
-
-        App app{"ibm-read-vpd - App to read IPZ format VPD, parse it and store "
-                "in DBUS"};
-        string file{};
-
-        app.add_option("-f, --file", file, "File containing VPD in IPZ format")
-            ->required()
-            ->check(ExistingFile);
-
-        CLI11_PARSE(app, argc, argv);
-
-        // Make sure that the file path we get is for a supported EEPROM
-        ifstream inventoryJson(INVENTORY_JSON);
-        auto js = json::parse(inventoryJson);
-
-        if ((js.find("frus") == js.end()) ||
-            (js["frus"].find(file) == js["frus"].end()))
-        {
-            throw std::runtime_error("Device path missing in inventory JSON");
-        }
-
-        ifstream vpdFile(file, ios::binary);
-        Binary vpd((istreambuf_iterator<char>(vpdFile)),
-                   istreambuf_iterator<char>());
-
-        // Use ipz vpd Parser
-        auto vpdStore = parse(move(vpd));
-
-        // Write it to the inventory
-        populateDbus(vpdStore, js, file);
-    }
-    catch (exception& e)
-    {
-        cerr << e.what() << "\n";
-        rc = -1;
-    }
-
-    return rc;
-}
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
diff --git a/keyword_vpd_parser.hpp b/keyword_vpd_parser.hpp
new file mode 100644
index 0000000..769a83a
--- /dev/null
+++ b/keyword_vpd_parser.hpp
@@ -0,0 +1,129 @@
+#pragma once
+
+#include "keyword_vpd_types.hpp"
+
+namespace vpd
+{
+namespace keyword
+{
+namespace parser
+{
+
+/**
+ * @class KeywordVpdParser
+ * @brief Implements parser for Keyword VPD
+ *
+ * KeywordVpdParser object must be constructed by passing in
+ * Keyword VPD in binary format. To parse the VPD, call the
+ * kwVpdParser() method. The kwVpdParser() method returns
+ * a map of keyword-value pairs.
+ *
+ * Following is the algorithm used to parse Keyword VPD data:
+ * 1) Validate if the first byte is 'largeResourceIdentifierString'.
+ * 2) Validate the byte after the description is 'vendor defined large resource
+ * type tag'.
+ * 3) For each keyword-value pairs :
+ * 	3.1) Parse the 2 byte length keyword and emplace it in the map as 'key'.
+ * 	3.2) Parse over the value bytes corresponding to the keyword and
+ * 	     emplace it in the map as 'value' for the key inserted in 3.1.
+ * 4) Validate the byte before checksum byte is 'small resource type end tag'.
+ * 5) Validate the checksum.
+ * 6) Validate the 'small resource type last end tag'.
+ * 7) Return the keyword-value map.
+ */
+
+class KeywordVpdParser
+{
+  public:
+    KeywordVpdParser() = delete;
+    KeywordVpdParser(const KeywordVpdParser&) = delete;
+    KeywordVpdParser(KeywordVpdParser&&) = delete;
+    ~KeywordVpdParser() = default;
+
+    /**
+     * @brief Move Constructor
+     *
+     * Move kwVpdVector to parser object's kwVpdVector
+     */
+    KeywordVpdParser(Binary&& kwVpdVector) :
+        keywordVpdVector(std::move(kwVpdVector))
+    {
+    }
+
+    /**
+     * @brief Parse the keyword VPD binary data.
+     *
+     * Calls the sub functions to emplace the
+     * keyword-value pairs in map and to validate
+     * certain tags and checksum data.
+     *
+     * @return map of keyword:value
+     */
+    KeywordVpdMap parseKwVpd();
+
+  private:
+    Binary::iterator checkSumStart; //!< Pointer to the start byte from where
+                                    //!< the checksum need to be calculated
+    Binary::iterator checkSumEnd;   //!< Pointer to the end byte until which the
+                                    //!< checksum need to be calculated
+    Binary::iterator kwVpdIterator; //!< Iterator to parse the vector
+    Binary keywordVpdVector;        //!< Vector which stores keyword VPD data
+
+    /**
+     * @brief Validate the large resource identifier string
+     */
+    void validateLargeResourceIdentifierString();
+
+    /**
+     * @brief Validate the type of keyword VPD
+     *
+     * @return integer representing the type of kw VPD.
+     */
+    int validateTheTypeOfKwVpd();
+
+    /**
+     * @brief Parsing keyword-value pairs and emplace into Map.
+     *
+     * @return map of keyword:value
+     */
+    KeywordVpdMap kwValParser();
+
+    /**
+     * @brief Validate small resource type end tag
+     */
+    void validateSmallResourceTypeEnd();
+
+    /**
+     * @brief Validate checksum.
+     *
+     * Finding the 2's complement of sum of all the
+     * keywords,values and large resource identifier string.
+     */
+    void validateChecksum();
+
+    /**
+     * @brief Validate small resource type last end tag
+     */
+    void validateSmallResourceTypeLastEnd();
+
+    /**
+     * @brief Get the size of the keyword
+     *
+     * @return one byte length size data
+     */
+    size_t getKwDataSize();
+
+    /**
+     * @brief Check for iterator Out of Bound exception
+     *
+     * Check if no.of elements from (begining of the vector) to (iterator +
+     * incVar) is lesser than or equal to the total no.of elements in the
+     * vector. This check is performed before the advancement of the iterator.
+     *
+     * @param[incVar] - no.of positions the iterator is going to be iterated
+     */
+    void itrOutOfBoundCheck(uint8_t incVar);
+};
+} // namespace parser
+} // namespace keyword
+} // namespace vpd
diff --git a/keyword_vpd_types.hpp b/keyword_vpd_types.hpp
new file mode 100644
index 0000000..b219da7
--- /dev/null
+++ b/keyword_vpd_types.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <stdint.h>
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace vpd
+{
+namespace keyword
+{
+namespace parser
+{
+constexpr uint8_t KW_VPD_START_TAG = 0x82;
+constexpr uint8_t KW_VPD_END_TAG = 0x78;
+constexpr uint8_t KW_VAL_PAIR_START_TAG = 0x84;
+constexpr uint8_t ALT_KW_VAL_PAIR_START_TAG = 0x90;
+constexpr uint8_t KW_VAL_PAIR_END_TAG = 0x79;
+constexpr int TWO_BYTES = 2;
+constexpr int IPZ_DATA_START = 11;
+constexpr int KW_VPD_DATA_START = 0;
+
+using Binary = std::vector<uint8_t>;
+using KeywordVpdMap = std::unordered_map<std::string, std::vector<uint8_t>>;
+} // namespace parser
+} // namespace keyword
+} // namespace vpd
diff --git a/test/Makefile.am b/test/Makefile.am
index 8b93faa..adc1e10 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -32,6 +32,14 @@
 ipz_parser_test_CPPFLAGS = $(test_cppflags) -DIPZ_PARSER
 ipz_parser_test_LDFLAGS = $(test_ldflags)
 
+check_PROGRAMS += kw_vpd_test
+kw_vpd_test_SOURCES = \
+        keyword_vpd_parser_test/kw_vpd_test.cpp \
+        ../keyword_vpd_parser.cpp
+
+kw_vpd_test_CPPFLAGS = $(test_cppflags)
+kw_vpd_test_LDFLAGS = $(test_ldflags)
+
 if !IBM_PARSER
 noinst_PROGRAMS = parser_test
 parser_test_SOURCES = \
@@ -43,4 +51,5 @@
 
 parser_test_LDFLAGS = $(SDBUSPLUS_LIBS) $(PHOSPHOR_LOGGING_LIBS)
 parser_test_CXXFLAGS = $(SDBUSPLUS_CFLAGS) $(PHOSPHOR_LOGGING_CFLAGS)
+
 endif
diff --git a/test/bono.vpd b/test/bono.vpd
new file mode 100644
index 0000000..afc1b8e
--- /dev/null
+++ b/test/bono.vpd
Binary files differ
diff --git a/test/keyword_vpd_parser_test/kw_vpd_test.cpp b/test/keyword_vpd_parser_test/kw_vpd_test.cpp
new file mode 100644
index 0000000..c09d494
--- /dev/null
+++ b/test/keyword_vpd_parser_test/kw_vpd_test.cpp
@@ -0,0 +1,199 @@
+#include "keyword_vpd_parser.hpp"
+
+#include <exception>
+#include <fstream>
+
+#include <gtest/gtest.h>
+
+using namespace vpd::keyword::parser;
+
+class KeywordVpdParserTest : public ::testing::Test
+{
+  protected:
+    Binary keywordVpdVector;
+    Binary bonoKwVpdVector;
+
+    KeywordVpdParserTest()
+    {
+        // Open the kw VPD file in binary mode
+        std::ifstream kwVpdFile("vpd.dat", std::ios::binary);
+
+        // Read the content of the binary file into a vector
+        keywordVpdVector.assign((std::istreambuf_iterator<char>(kwVpdFile)),
+                                std::istreambuf_iterator<char>());
+        // Open the BONO type kw VPD file in binary mode
+        std::ifstream bonoKwVpdFile("bono.vpd", std::ios::binary);
+
+        // Read the content of the binary file into a vector
+        bonoKwVpdVector.assign((std::istreambuf_iterator<char>(bonoKwVpdFile)),
+                               std::istreambuf_iterator<char>());
+    }
+};
+
+TEST_F(KeywordVpdParserTest, GoodTestCase)
+{
+    KeywordVpdParser parserObj1(std::move(keywordVpdVector));
+    KeywordVpdMap map1 = {
+        {"WI", {0x00}},
+        {"FL", {0x50, 0x32, 0x20, 0x20, 0x20}},
+        {"SM",
+         {0x82, 0x50, 0x32, 0x2d, 0x44, 0x34, 0x20, 0x20, 0x20, 0x20, 0x20,
+          0x20, 0x32, 0x53, 0x53, 0x43, 0x81, 0x50, 0x32, 0x2d, 0x44, 0x35,
+          0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x32, 0x53, 0x53, 0x43, 0x80,
+          0x50, 0x32, 0x2d, 0x44, 0x37, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+          0x32, 0x53, 0x53, 0x43, 0x83, 0x50, 0x32, 0x2d, 0x44, 0x38, 0x20,
+          0x20, 0x20, 0x20, 0x20, 0x20, 0x32, 0x53, 0x53, 0x43}},
+        {"B2",
+         {0x50, 0x05, 0x07, 0x60, 0x73, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00,
+          0x00, 0x00, 0x00, 0x01, 0x00}},
+        {"MF", {0x00, 0x10}},
+        {"VZ", {0x30, 0x33}},
+        {"PN", {0x30, 0x31, 0x4b, 0x55, 0x37, 0x32, 0x34}},
+        {"FN", {0x20, 0x30, 0x31, 0x4b, 0x55, 0x37, 0x32, 0x34}},
+        {"CE", {0x31}},
+        {"SN",
+         {0x59, 0x48, 0x33, 0x30, 0x42, 0x47, 0x37, 0x38, 0x42, 0x30, 0x31,
+          0x34}},
+        {"CC", {0x32, 0x44, 0x33, 0x37}}};
+
+    auto map2 = parserObj1.parseKwVpd();
+    ASSERT_EQ(1, map1 == map2);
+
+    // BONO TYPE VPD
+    KeywordVpdParser parserObj2(std::move(bonoKwVpdVector));
+    map1 = {{"B2",
+             {0x50, 0x0, 0xb3, 0xe0, 0x90, 0x0, 0x2, 0x50, 0x0, 0x0, 0x0, 0x0,
+              0x0, 0x0, 0x0, 0x0}},
+            {"CC", {0x35, 0x39, 0x33, 0x42}},
+            {"CT", {0x50, 0x37, 0x32, 0x0}},
+            {"EC", {0x50, 0x34, 0x35, 0x35, 0x33, 0x37}},
+            {"FN", {0x30, 0x32, 0x44, 0x45, 0x33, 0x36, 0x35}},
+            {"PN", {0x30, 0x32, 0x44, 0x45, 0x33, 0x36, 0x36}},
+            {"RV", {0xa1}},
+            {"SI", {0x31, 0x30, 0x31, 0x34, 0x30, 0x36, 0x37, 0x34}},
+            {"SN",
+             {0x59, 0x4c, 0x35, 0x30, 0x48, 0x54, 0x39, 0x36, 0x4a, 0x30, 0x30,
+              0x38}},
+            {"Z4", {0x30}},
+            {"Z5", {0x30}},
+            {"Z6", {0x41, 0x31, 0x38, 0x30, 0x30, 0x32, 0x30, 0x30}}};
+
+    map2 = parserObj2.parseKwVpd();
+    ASSERT_EQ(1, map1 == map2);
+}
+
+TEST_F(KeywordVpdParserTest, InvKwVpdTag)
+{
+    // Invalid Large resource type Identifier String - corrupted at index[0]
+    keywordVpdVector[0] = 0x83;
+    KeywordVpdParser parserObj1(std::move(keywordVpdVector));
+    EXPECT_THROW(parserObj1.parseKwVpd(), std::runtime_error);
+
+    // For BONO type VPD
+    bonoKwVpdVector[0] = 0x83;
+    KeywordVpdParser parserObj2(std::move(bonoKwVpdVector));
+    EXPECT_THROW(parserObj2.parseKwVpd(), std::runtime_error);
+}
+
+TEST_F(KeywordVpdParserTest, InvKwValTag)
+{
+    // Invalid Large resource type Vendor Defined - corrupted at index[19]
+    keywordVpdVector[19] = 0x85;
+    KeywordVpdParser parserObj1(std::move(keywordVpdVector));
+    EXPECT_THROW(parserObj1.parseKwVpd(), std::runtime_error);
+
+    // For BONO type VPD - corruputed at index[33]
+    bonoKwVpdVector[33] = 0x91;
+    KeywordVpdParser parserObj2(std::move(bonoKwVpdVector));
+    EXPECT_THROW(parserObj2.parseKwVpd(), std::runtime_error);
+}
+
+TEST_F(KeywordVpdParserTest, InvKwValSize)
+{
+    // Badly formed keyword VPD data - corrupted at index[20]
+    keywordVpdVector[20] = 0x00;
+    KeywordVpdParser parserObj1(std::move(keywordVpdVector));
+    EXPECT_THROW(parserObj1.parseKwVpd(), std::runtime_error);
+
+    // For BONO type VPD - corruputed at index[34]
+    bonoKwVpdVector[34] = 0x00;
+    KeywordVpdParser parserObj2(std::move(bonoKwVpdVector));
+    EXPECT_THROW(parserObj2.parseKwVpd(), std::runtime_error);
+}
+
+TEST_F(KeywordVpdParserTest, InvKwValEndTag)
+{
+    // Invalid Small resource type End - corrupted at index[177]
+    keywordVpdVector[177] = 0x80;
+    KeywordVpdParser parserObj1(std::move(keywordVpdVector));
+    EXPECT_THROW(parserObj1.parseKwVpd(), std::runtime_error);
+}
+
+TEST_F(KeywordVpdParserTest, InvChecksum)
+{
+    // Invalid Check sum - corrupted at index[178]
+    keywordVpdVector[178] = 0xb1;
+    KeywordVpdParser parserObj1(std::move(keywordVpdVector));
+    EXPECT_THROW(parserObj1.parseKwVpd(), std::runtime_error);
+}
+
+TEST_F(KeywordVpdParserTest, InvKwVpdEndTag)
+{
+    // Invalid Small resource type Last End Of Data - corrupted at index[179]
+    keywordVpdVector[179] = 0x79;
+    KeywordVpdParser parserObj1(std::move(keywordVpdVector));
+    EXPECT_THROW(parserObj1.parseKwVpd(), std::runtime_error);
+
+    // For BONO type VPD - corrupted at index[147]
+    bonoKwVpdVector[147] = 0x79;
+    KeywordVpdParser parserObj2(std::move(bonoKwVpdVector));
+    EXPECT_THROW(parserObj2.parseKwVpd(), std::runtime_error);
+}
+
+TEST_F(KeywordVpdParserTest, OutOfBoundGreaterSize)
+{
+    // Iterator Out of Bound - size is larger than the actual size - corrupted
+    // at index[24]
+    keywordVpdVector[24] = 0x32;
+    KeywordVpdParser parserObj1(std::move(keywordVpdVector));
+    EXPECT_THROW(parserObj1.parseKwVpd(), std::runtime_error);
+
+    // For BONO type VPD - corrupted at index[38]
+    bonoKwVpdVector[38] = 0x4D;
+    KeywordVpdParser parserObj2(std::move(bonoKwVpdVector));
+    EXPECT_THROW(parserObj2.parseKwVpd(), std::runtime_error);
+}
+
+TEST_F(KeywordVpdParserTest, OutOfBoundLesserSize)
+{
+    // Iterator Out of Bound - size is smaller than the actual size - corrupted
+    // at index[24]
+    keywordVpdVector[24] = 0x03;
+    KeywordVpdParser parserObj1(std::move(keywordVpdVector));
+    EXPECT_THROW(parserObj1.parseKwVpd(), std::runtime_error);
+
+    // For BONO type VPD - corrupted at index[38]
+    bonoKwVpdVector[38] = 0x04;
+    KeywordVpdParser parserObj2(std::move(bonoKwVpdVector));
+    EXPECT_THROW(parserObj2.parseKwVpd(), std::runtime_error);
+}
+
+TEST_F(KeywordVpdParserTest, BlankVpd)
+{
+    // Blank Kw Vpd
+    keywordVpdVector.clear();
+    KeywordVpdParser parserObj1(std::move(keywordVpdVector));
+    EXPECT_THROW(parserObj1.parseKwVpd(), std::runtime_error);
+
+    // Blank Bono Type Vpd
+    bonoKwVpdVector.clear();
+    KeywordVpdParser parserObj2(std::move(bonoKwVpdVector));
+    EXPECT_THROW(parserObj2.parseKwVpd(), std::runtime_error);
+}
+
+int main(int argc, char** argv)
+{
+    ::testing::InitGoogleTest(&argc, argv);
+
+    return RUN_ALL_TESTS();
+}
diff --git a/test/vpd.dat b/test/vpd.dat
new file mode 100755
index 0000000..106f6a6
--- /dev/null
+++ b/test/vpd.dat
Binary files differ