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