IPZ VPD Parser support
This commit enables the app ibm-read-vpd to read the
IPZ format vpd. To build this app, the application
build must be configured with the "--enable-ibm-parser"
option.
The application populates the Inventory with the parsed
VPD data. The parser relies on a JSON config file
that maps the input VPD file path to the Inventory D-Bus
object that hosts the VPD data as properties.
The JSON file also supplies any additional interfaces
and properties that the D-Bus object should implement.
Argument required to run this application-
ibm-read-vpd --file vpd_file
Tested:
Also tested this on Rainier simulation model and verified that VPD
for FRUs is properly published as properties of D-Bus objects in the
Inventory.
Change-Id: Ic9305f25625adced7f64123589dd083e2679afbb
Signed-off-by: Alpana Kumari <alpankum@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 7759ee3..c7e4d9c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -10,6 +10,18 @@
types.hpp \
utils.hpp
+if IBM_PARSER
+bin_PROGRAMS = ibm-read-vpd
+ibm_read_vpd_SOURCES = \
+ ipz_app.cpp \
+ parser.cpp \
+ impl.cpp \
+ utils.cpp
+
+ibm_read_vpd_LDFLAGS = $(SDBUSPLUS_LIBS) $(PHOSPHOR_LOGGING_LIBS)
+ibm_read_vpd_CXXFLAGS = $(SDBUSPLUS_CFLAGS) $(PHOSPHOR_LOGGING_CFLAGS)
+
+else
# Be sure to build these before compiling
BUILT_SOURCES = \
writefru.hpp \
@@ -51,5 +63,6 @@
utils.cpp
openpower_read_vpd_LDFLAGS = $(SDBUSPLUS_LIBS) $(PHOSPHOR_LOGGING_LIBS)
openpower_read_vpd_CXXFLAGS = $(SDBUSPLUS_CFLAGS) $(PHOSPHOR_LOGGING_CFLAGS)
+endif
SUBDIRS = test
diff --git a/configure.ac b/configure.ac
index 05bf92e..5e6bb8b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -17,6 +17,7 @@
PKG_CHECK_MODULES([SDBUSPLUS], [sdbusplus])
PKG_CHECK_MODULES([PHOSPHOR_LOGGING], [phosphor-logging])
+
# Suppress the --with-libtool-sysroot error
LT_INIT
@@ -50,6 +51,33 @@
PROPGEN="$PYTHON $srcdir/extra-properties.py -e $PROP_YAML"
AC_SUBST(PROPGEN)
+# Setup option for IBM VPD formats.
+AC_ARG_ENABLE([ibm-parser],
+ AS_HELP_STRING([--enable-ibm-parser ],
+ [Enable IBM IPZ and Keyword format VPD parser support.
+ ]))
+
+AM_CONDITIONAL([IBM_PARSER], [test "x$enable_ibm_parser" = "xyes"])
+AS_IF([test "x$enable_ibm_parser" == "xyes"], [
+# Check necessary header files
+ AC_CHECK_HEADER(
+ [CLI/CLI.hpp],
+ [],
+ [AC_MSG_ERROR([Could not find CLI11 CLI.hpp])]
+ )
+ AC_CHECK_HEADER(
+ [nlohmann/json.hpp],
+ [],
+ [AC_MSG_ERROR([Could not find nlohmann/json.hpp. Require nlohmann/json package])]
+ )
+ AX_APPEND_COMPILE_FLAGS([-DIPZ_PARSER], [CXXFLAGS])
+ AC_DEFINE(INVENTORY_JSON, "/usr/share/vpd/vpd_inventory.json", [JSON file that defines inventory blueprint])
+ ])
+
+# Check/set gtest specific functions.
+AX_PTHREAD([GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=1"],[GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=0"])
+AC_SUBST(GTEST_CPPFLAGS)
+
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([Makefile test/Makefile])
AC_OUTPUT
diff --git a/examples/inventory.json b/examples/inventory.json
new file mode 100644
index 0000000..b5592cc
--- /dev/null
+++ b/examples/inventory.json
@@ -0,0 +1,34 @@
+{
+ "commonInterfaces": {
+ "xyz.openbmc_project.Inventory.Decorator.Asset": {
+ "PartNumber": {
+ "recordName": "VINI",
+ "keywordName": "PN"
+ },
+ "SerialNumber": {
+ "recordName": "VINI",
+ "keywordName": "SN"
+ }
+ },
+ "xyz.openbmc_project.Inventory.Item": {
+ "PrettyName": {
+ "recordName": "VINI",
+ "keywordName": "DR"
+ }
+ }
+ },
+ "frus": {
+ "/sys/devices/path/to/motherboard/eeeprom": {
+ "inventoryPath": "/bus/path/for/motherboardfru",
+ "extraInterfaces": {
+ "xyz.openbmc_project.Inventory.Item.Board.Motherboard": null
+ }
+ },
+ "/sys/devices/path/to/bmc/eeprom": {
+ "inventoryPath": "/bus/path/for/bmcfru",
+ "extraInterfaces": {
+ "xyz.openbmc_project.Inventory.Item.Bmc": null
+ }
+ }
+ }
+}
diff --git a/impl.cpp b/impl.cpp
index 765b1ac..35a0e0f 100644
--- a/impl.cpp
+++ b/impl.cpp
@@ -23,6 +23,7 @@
static constexpr auto MAC_ADDRESS_LEN_BYTES = 6;
static constexpr auto LAST_KW = "PF";
+static constexpr auto POUND_KW = '#';
static constexpr auto UUID_LEN_BYTES = 16;
static constexpr auto UUID_TIME_LOW_END = 8;
static constexpr auto UUID_TIME_MID_END = 13;
@@ -62,6 +63,7 @@
using RecordType = uint16_t;
using RecordLength = uint16_t;
using KwSize = uint8_t;
+using PoundKwSize = uint16_t;
using ECCOffset = uint16_t;
using ECCLength = uint16_t;
@@ -193,16 +195,26 @@
std::advance(iterator, nameOffset);
std::string name(iterator, iterator + lengths::RECORD_NAME);
+#ifndef IPZ_PARSER
if (supportedRecords.end() != supportedRecords.find(name))
{
+#endif
// If it's a record we're interested in, proceed to find
// contained keywords and their values.
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));
+#endif
auto kwMap = readKeywords(iterator);
// 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
}
std::string Impl::readKwData(const internal::KeywordInfo& keyword,
@@ -312,13 +324,35 @@
// We're done
break;
}
+ // Check if the Keyword is '#kw'
+ char kwNameStart = *iterator;
+
// Jump past keyword name
std::advance(iterator, lengths::KW_NAME);
- // Note keyword data length
- std::size_t length = *iterator;
- // Jump past keyword length
- std::advance(iterator, sizeof(KwSize));
+
+ std::size_t length;
+ std::size_t lengthHighByte;
+ if (POUND_KW == kwNameStart)
+ {
+ // Note keyword data length
+ length = *iterator;
+ lengthHighByte = *(iterator + 1);
+ length |= (lengthHighByte << 8);
+
+ // Jump past 2Byte keyword length
+ std::advance(iterator, sizeof(PoundKwSize));
+ }
+ else
+ {
+ // Note keyword data length
+ length = *iterator;
+
+ // Jump past keyword length
+ std::advance(iterator, sizeof(KwSize));
+ }
+
// Pointing to keyword data now
+#ifndef IPZ_PARSER
if (supportedKeywords.end() != supportedKeywords.find(kw))
{
// Keyword is of interest to us
@@ -326,6 +360,14 @@
length, iterator);
map.emplace(std::move(kw), std::move(data));
}
+
+#else
+ // support all the Keywords
+ auto stop = std::next(iterator, length);
+ std::string kwdata(iterator, stop);
+ map.emplace(std::move(kw), std::move(kwdata));
+
+#endif
// Jump past keyword data length
std::advance(iterator, length);
}
diff --git a/impl.hpp b/impl.hpp
index 94a958e..525ae7a 100644
--- a/impl.hpp
+++ b/impl.hpp
@@ -36,14 +36,14 @@
} // namespace internal
/** @class Impl
- * @brief Implements parser for OpenPOWER VPD
+ * @brief Implements parser for VPD
*
- * An Impl object must be constructed by passing in OpenPOWER VPD in
+ * An Impl object must be constructed by passing in VPD in
* binary format. To parse the VPD, call the run() method. The run()
* method returns an openpower::vpd::Store object, which contains
* parsed VPD, and provides access methods for the VPD.
*
- * Following is the algorithm used to parse OpenPOWER VPD:
+ * Following is the algorithm used to parse IPZ/OpenPower VPD:
* 1) Validate that the first record is VHDR, the header record.
* 2) From the VHDR record, get the offset of the VTOC record,
* which is the table of contents record.
@@ -66,13 +66,13 @@
/** @brief Construct an Impl
*
- * @param[in] vpdBuffer - Binary OpenPOWER VPD
+ * @param[in] vpdBuffer - Binary VPD
*/
explicit Impl(Binary&& vpdBuffer) : vpd(std::move(vpdBuffer)), out{}
{
}
- /** @brief Run the parser on binary OpenPOWER VPD
+ /** @brief Run the parser on binary VPD
*
* @returns openpower::vpd::Store object
*/
@@ -99,13 +99,13 @@
/** @brief Read VPD information contained within a record
*
* @param[in] recordOffset - offset to a record location
- * within the binary OpenPOWER VPD
+ * within the binary VPD
*/
void processRecord(std::size_t recordOffset);
/** @brief Read keyword data
*
- * @param[in] keyword - OpenPOWER VPD keyword
+ * @param[in] keyword - VPD keyword
* @param[in] dataLength - Length of data to be read
* @param[in] iterator - iterator pointing to a Keyword's data in
* the VPD
@@ -129,7 +129,7 @@
/** @brief Checks if the VHDR record is present in the VPD */
void checkHeader() const;
- /** @brief OpenPOWER VPD in binary format */
+ /** @brief VPD in binary format */
Binary vpd;
/** @brief parser output */
diff --git a/ipz_app.cpp b/ipz_app.cpp
new file mode 100644
index 0000000..610ac8d
--- /dev/null
+++ b/ipz_app.cpp
@@ -0,0 +1,142 @@
+#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;
+
+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", "");
+
+ if (!rec.empty() && !kw.empty() && vpdMap.count(rec) &&
+ vpdMap.at(rec).count(kw))
+ {
+ props.emplace(itr.key(), string(vpdMap.at(rec).at(kw).begin(),
+ vpdMap.at(rec).at(kw).end()));
+ }
+ }
+ interfaces.emplace(inf, move(props));
+ }
+}
+
+static void populateDbus(Store& vpdStore, nlohmann::json& js,
+ const string& objectPath, const string& filePath)
+{
+ inventory::InterfaceMap interfaces;
+ inventory::ObjectMap objects;
+ sdbusplus::message::object_path object(objectPath);
+ const auto& vpdMap = vpdStore.getVpdMap();
+ string preIntrStr = "com.ibm.ipzvpd.";
+
+ // 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 (js["frus"][filePath].find("extraInterfaces") !=
+ js["frus"][filePath].end())
+ {
+ populateInterfaces(js["frus"][filePath]["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");
+ }
+
+ const string& objectPath = js["frus"][file].value("inventoryPath", "");
+
+ if (objectPath.empty())
+ {
+ throw std::runtime_error("Could not find D-Bus object path 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, objectPath, file);
+ }
+ catch (exception& e)
+ {
+ cerr << e.what() << "\n";
+ rc = -1;
+ }
+
+ return rc;
+}
diff --git a/parser.hpp b/parser.hpp
index e131c46..097b14a 100644
--- a/parser.hpp
+++ b/parser.hpp
@@ -9,11 +9,11 @@
namespace vpd
{
-/** @brief API to parse OpenPOWER VPD
+/** @brief API to parse VPD
*
- * @param [in] vpd - OpenPOWER VPD in binary format
+ * @param [in] vpd - VPD in binary format
* @returns A Store object, which provides access to
- * the parsed VPD
+ * the parsed VPD.
*/
Store parse(Binary&& vpd);
diff --git a/store.hpp b/store.hpp
index c184e37..a8326a7 100644
--- a/store.hpp
+++ b/store.hpp
@@ -42,6 +42,15 @@
{
}
+ /** @brief Retrieves VPD from Store as a Parsed object
+ *
+ * @returns VPD as a Parsed object
+ */
+ inline const Parsed& getVpdMap() const
+ {
+ return vpd;
+ }
+
/** @brief Retrieves VPD from Store
*
* @tparam R - VPD record
diff --git a/test/Makefile.am b/test/Makefile.am
index 4f35b94..05785da 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -3,8 +3,32 @@
TESTS = $(check_PROGRAMS)
check_PROGRAMS = store_test
+
store_test_SOURCES = store/store.cpp
+check_PROGRAMS += ipz_parser_test
+
+ipz_parser_test_SOURCES = ipz_parser/parser.cpp \
+ ../impl.cpp
+test_cppflags = \
+ -Igtest \
+ $(GTEST_CPPFLAGS) \
+ $(AM_CPPFLAGS) \
+ -pthread \
+ $(PTHREAD_CFLAGS) \
+ $(SDBUSPLUS_CFLAGS)
+
+test_ldflags = \
+ -lgtest_main \
+ -lgtest \
+ -pthread \
+ $(PTHREAD_LIBS) \
+ $(SDBUSPLUS_LIBS)
+
+ipz_parser_test_CPPFLAGS = $(test_cppflags) -DIPZ_PARSER
+ipz_parser_test_LDFLAGS = $(test_ldflags)
+
+if !IBM_PARSER
noinst_PROGRAMS = parser_test
parser_test_SOURCES = \
parser/parser.cpp \
@@ -14,3 +38,4 @@
../utils.cpp
parser_test_LDFLAGS = $(SDBUSPLUS_LIBS) $(PHOSPHOR_LOGGING_LIBS)
parser_test_CXXFLAGS = $(SDBUSPLUS_CFLAGS) $(PHOSPHOR_LOGGING_CFLAGS)
+endif
diff --git a/test/ipz_parser/parser.cpp b/test/ipz_parser/parser.cpp
new file mode 100644
index 0000000..40c60e9
--- /dev/null
+++ b/test/ipz_parser/parser.cpp
@@ -0,0 +1,144 @@
+#include <cassert>
+#include <defines.hpp>
+#include <fstream>
+#include <impl.hpp>
+#include <iterator>
+#include <parser.hpp>
+#include <store.hpp>
+
+#include <gtest/gtest.h>
+
+using namespace openpower::vpd;
+
+TEST(IpzVpdParserApp, vpdGoodPath)
+{
+ // Create a vpd
+ Binary vpd = {
+ 0x00, 0x0f, 0x17, 0xba, 0x42, 0xca, 0x82, 0xd7, 0x7b, 0x77, 0x1e, 0x84,
+ 0x28, 0x00, 0x52, 0x54, 0x04, 0x56, 0x48, 0x44, 0x52, 0x56, 0x44, 0x02,
+ 0x30, 0x31, 0x50, 0x54, 0x0e, 0x56, 0x54, 0x4f, 0x43, 0xd5, 0x00, 0x37,
+ 0x00, 0x4c, 0x00, 0x97, 0x05, 0x13, 0x00, 0x50, 0x46, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x84, 0x48, 0x00, 0x52, 0x54,
+ 0x04, 0x56, 0x54, 0x4f, 0x43, 0x50, 0x54, 0x0e, 0x56, 0x49, 0x4e, 0x49,
+ 0xd5, 0x00, 0x52, 0x00, 0x90, 0x00, 0x73, 0x05, 0x24, 0x00, 0x84, 0x8c,
+ 0x00, 0x52, 0x54, 0x04, 0x56, 0x49, 0x4e, 0x49, 0x44, 0x52, 0x10, 0x41,
+ 0x50, 0x53, 0x53, 0x20, 0x26, 0x20, 0x54, 0x50, 0x4d, 0x20, 0x20, 0x43,
+ 0x41, 0x52, 0x44, 0x43, 0x45, 0x01, 0x31, 0x56, 0x5a, 0x02, 0x30, 0x31,
+ 0x46, 0x4e, 0x07, 0x30, 0x31, 0x44, 0x48, 0x32, 0x30, 0x30, 0x50, 0x4e,
+ 0x07, 0x30, 0x31, 0x44, 0x48, 0x32, 0x30, 0x31, 0x53, 0x4e, 0x0c, 0x59,
+ 0x4c, 0x33, 0x30, 0x42, 0x47, 0x37, 0x43, 0x46, 0x30, 0x33, 0x50, 0x43,
+ 0x43, 0x04, 0x36, 0x42, 0x36, 0x36, 0x50, 0x52, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x45, 0x04, 0x30, 0x30, 0x30, 0x31,
+ 0x43, 0x54, 0x04, 0x40, 0xb8, 0x02, 0x03, 0x48, 0x57, 0x02, 0x00, 0x01,
+ 0x42, 0x33, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x34, 0x01,
+ 0x00, 0x42, 0x37, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x50, 0x46, 0x02, 0x00, 0x00, 0x78, 0x84, 0xdc,
+ 0x00, 0x52, 0x54, 0x04};
+
+ // call app for this vpd
+ parser::Impl p(std::move(vpd));
+ Store vpdStore = p.run();
+
+ static const std::string record = "VINI";
+ static const std::string keyword = "DR";
+
+ // TODO 2: Move this as an utility to store.hpp
+ std::string dataFound;
+ Parsed st_bin = vpdStore.getVpdMap();
+
+ auto kw = st_bin.find(record);
+ if (st_bin.end() != kw)
+ {
+ auto value = (kw->second).find(keyword);
+ if ((kw->second).end() != value)
+ {
+ dataFound = value->second;
+ }
+ }
+
+ ASSERT_EQ(dataFound, "APSS & TPM CARD");
+}
+
+TEST(IpzVpdParserApp, vpdBadPathEmptyVPD)
+{
+ Binary vpd = {};
+
+ // VPD is empty
+ parser::Impl p(std::move(vpd));
+
+ // Expecting a throw here
+ EXPECT_THROW(p.run(), std::runtime_error);
+}
+
+TEST(IpzVpdParserApp, vpdBadPathMissingHeader)
+{
+ Binary vpd = {
+ 0x00, 0x0f, 0x17, 0xba, 0x42, 0xca, 0x82, 0xd7, 0x7b, 0x77, 0x1e, 0x84,
+ 0x28, 0x00, 0x52, 0x54, 0x04, 0x56, 0x48, 0x44, 0x52, 0x56, 0x44, 0x02,
+ 0x30, 0x31, 0x50, 0x54, 0x0e, 0x56, 0x54, 0x4f, 0x43, 0xd5, 0x00, 0x37,
+ 0x00, 0x4c, 0x00, 0x97, 0x05, 0x13, 0x00, 0x50, 0x46, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x84, 0x48, 0x00, 0x52, 0x54,
+ 0x04, 0x56, 0x54, 0x4f, 0x43, 0x50, 0x54, 0x0e, 0x56, 0x49, 0x4e, 0x49,
+ 0xd5, 0x00, 0x52, 0x00, 0x90, 0x00, 0x73, 0x05, 0x24, 0x00, 0x84, 0x8c,
+ 0x00, 0x52, 0x54, 0x04, 0x56, 0x49, 0x4e, 0x49, 0x44, 0x52, 0x10, 0x41,
+ 0x50, 0x53, 0x53, 0x20, 0x26, 0x20, 0x54, 0x50, 0x4d, 0x20, 0x20, 0x43,
+ 0x41, 0x52, 0x44, 0x43, 0x45, 0x01, 0x31, 0x56, 0x5a, 0x02, 0x30, 0x31,
+ 0x46, 0x4e, 0x07, 0x30, 0x31, 0x44, 0x48, 0x32, 0x30, 0x30, 0x50, 0x4e,
+ 0x07, 0x30, 0x31, 0x44, 0x48, 0x32, 0x30, 0x31, 0x53, 0x4e, 0x0c, 0x59,
+ 0x4c, 0x33, 0x30, 0x42, 0x47, 0x37, 0x43, 0x46, 0x30, 0x33, 0x50, 0x43,
+ 0x43, 0x04, 0x36, 0x42, 0x36, 0x36, 0x50, 0x52, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x45, 0x04, 0x30, 0x30, 0x30, 0x31,
+ 0x43, 0x54, 0x04, 0x40, 0xb8, 0x02, 0x03, 0x48, 0x57, 0x02, 0x00, 0x01,
+ 0x42, 0x33, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x34, 0x01,
+ 0x00, 0x42, 0x37, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x50, 0x46, 0x02, 0x00, 0x00, 0x78, 0x84, 0xdc,
+ 0x00, 0x52, 0x54, 0x04};
+
+ // corrupt the VHDR
+ vpd[17] = 0x00;
+
+ parser::Impl p(std::move(vpd));
+
+ // Expecting a throw here
+ EXPECT_THROW(p.run(), std::runtime_error);
+}
+
+TEST(IpzVpdParserApp, vpdBadPathMissingVTOC)
+{
+ Binary vpd = {
+ 0x00, 0x0f, 0x17, 0xba, 0x42, 0xca, 0x82, 0xd7, 0x7b, 0x77, 0x1e, 0x84,
+ 0x28, 0x00, 0x52, 0x54, 0x04, 0x56, 0x48, 0x44, 0x52, 0x56, 0x44, 0x02,
+ 0x30, 0x31, 0x50, 0x54, 0x0e, 0x56, 0x54, 0x4f, 0x43, 0xd5, 0x00, 0x37,
+ 0x00, 0x4c, 0x00, 0x97, 0x05, 0x13, 0x00, 0x50, 0x46, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x84, 0x48, 0x00, 0x52, 0x54,
+ 0x04, 0x56, 0x54, 0x4f, 0x43, 0x50, 0x54, 0x0e, 0x56, 0x49, 0x4e, 0x49,
+ 0xd5, 0x00, 0x52, 0x00, 0x90, 0x00, 0x73, 0x05, 0x24, 0x00, 0x84, 0x8c,
+ 0x00, 0x52, 0x54, 0x04, 0x56, 0x49, 0x4e, 0x49, 0x44, 0x52, 0x10, 0x41,
+ 0x50, 0x53, 0x53, 0x20, 0x26, 0x20, 0x54, 0x50, 0x4d, 0x20, 0x20, 0x43,
+ 0x41, 0x52, 0x44, 0x43, 0x45, 0x01, 0x31, 0x56, 0x5a, 0x02, 0x30, 0x31,
+ 0x46, 0x4e, 0x07, 0x30, 0x31, 0x44, 0x48, 0x32, 0x30, 0x30, 0x50, 0x4e,
+ 0x07, 0x30, 0x31, 0x44, 0x48, 0x32, 0x30, 0x31, 0x53, 0x4e, 0x0c, 0x59,
+ 0x4c, 0x33, 0x30, 0x42, 0x47, 0x37, 0x43, 0x46, 0x30, 0x33, 0x50, 0x43,
+ 0x43, 0x04, 0x36, 0x42, 0x36, 0x36, 0x50, 0x52, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x45, 0x04, 0x30, 0x30, 0x30, 0x31,
+ 0x43, 0x54, 0x04, 0x40, 0xb8, 0x02, 0x03, 0x48, 0x57, 0x02, 0x00, 0x01,
+ 0x42, 0x33, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x34, 0x01,
+ 0x00, 0x42, 0x37, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x50, 0x46, 0x02, 0x00, 0x00, 0x78, 0x84, 0xdc,
+ 0x00, 0x52, 0x54, 0x04};
+
+ // corrupt the VTOC
+ vpd[61] = 0x00;
+
+ parser::Impl p(std::move(vpd));
+
+ // Expecting a throw here
+ EXPECT_THROW(p.run(), std::runtime_error);
+}
+
+int main(int argc, char** argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+
+ return RUN_ALL_TESTS();
+}
diff --git a/types.hpp b/types.hpp
index 196e0fd..172d0c1 100644
--- a/types.hpp
+++ b/types.hpp
@@ -22,7 +22,7 @@
using Path = std::string;
using Property = std::string;
-using Value = sdbusplus::message::variant<bool, int64_t, std::string>;
+using Value = sdbusplus::message::variant<bool, int64_t, std::string, Binary>;
using PropertyMap = std::map<Property, Value>;
using Interface = std::string;