regulators: Support a string or vector for VPD

Add a 'byte_values' alternative to the 'value' entry in the compare VPD
action.  This is to support VPD values that are not strings, such as
'HW', a new IBM keyword that describes the version of a piece of
hardware.

To support this, the VPD class now treats all VPD keyword values as
vectors of uint8_ts, including in its data cache.  If a compare VPD
action in the JSON contains a string value, it will be converted to the
vector before the CompareVPDAction class is created.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I3fcabf896f4885feae1b07ee2c3da5929cf8bfa4
diff --git a/phosphor-regulators/src/actions/compare_vpd_action.cpp b/phosphor-regulators/src/actions/compare_vpd_action.cpp
index 1049cee..d7cccf0 100644
--- a/phosphor-regulators/src/actions/compare_vpd_action.cpp
+++ b/phosphor-regulators/src/actions/compare_vpd_action.cpp
@@ -30,7 +30,7 @@
     try
     {
         // Get actual VPD keyword value
-        std::string actualValue =
+        std::vector<uint8_t> actualValue =
             environment.getServices().getVPD().getValue(fru, keyword);
 
         // Check if actual value equals expected value
@@ -51,7 +51,13 @@
     ss << "compare_vpd: { ";
     ss << "fru: " << fru << ", ";
     ss << "keyword: " << keyword << ", ";
-    ss << "value: " << value << " }";
+    ss << "value: [ ";
+    ss << std::hex << std::uppercase;
+    for (unsigned int i = 0; i < value.size(); ++i)
+    {
+        ss << ((i > 0) ? ", " : "") << "0x" << static_cast<uint16_t>(value[i]);
+    }
+    ss << " ] }";
     return ss.str();
 }
 
diff --git a/phosphor-regulators/src/actions/compare_vpd_action.hpp b/phosphor-regulators/src/actions/compare_vpd_action.hpp
index af7e085..8825220 100644
--- a/phosphor-regulators/src/actions/compare_vpd_action.hpp
+++ b/phosphor-regulators/src/actions/compare_vpd_action.hpp
@@ -18,7 +18,9 @@
 #include "action.hpp"
 #include "action_environment.hpp"
 
+#include <cstdint>
 #include <string>
+#include <vector>
 
 namespace phosphor::power::regulators
 {
@@ -52,7 +54,7 @@
      */
     explicit CompareVPDAction(const std::string& fru,
                               const std::string& keyword,
-                              const std::string& value) :
+                              const std::vector<uint8_t>& value) :
         fru{fru},
         keyword{keyword}, value{value}
     {
@@ -96,7 +98,7 @@
      *
      * @return value
      */
-    const std::string& getValue() const
+    const std::vector<uint8_t>& getValue() const
     {
         return value;
     }
@@ -124,7 +126,7 @@
     /**
      * Expected value.
      */
-    const std::string value{};
+    const std::vector<uint8_t> value{};
 };
 
 } // namespace phosphor::power::regulators
diff --git a/phosphor-regulators/src/config_file_parser.cpp b/phosphor-regulators/src/config_file_parser.cpp
index 5238741..0b4673d 100644
--- a/phosphor-regulators/src/config_file_parser.cpp
+++ b/phosphor-regulators/src/config_file_parser.cpp
@@ -274,10 +274,26 @@
     std::string keyword = parseString(keywordElement);
     ++propertyCount;
 
-    // Required value property
-    const json& valueElement = getRequiredProperty(element, "value");
-    std::string value = parseString(valueElement);
-    ++propertyCount;
+    // Either value or byte_values required property
+    auto valueIt = element.find("value");
+    std::vector<uint8_t> value{};
+    auto byteValuesIt = element.find("byte_values");
+    if ((valueIt != element.end()) && (byteValuesIt == element.end()))
+    {
+        std::string stringValue = parseString(*valueIt);
+        value.insert(value.begin(), stringValue.begin(), stringValue.end());
+        ++propertyCount;
+    }
+    else if ((valueIt == element.end()) && (byteValuesIt != element.end()))
+    {
+        value = parseHexByteArray(*byteValuesIt);
+        ++propertyCount;
+    }
+    else
+    {
+        throw std::invalid_argument{
+            "Invalid property: Must contain either value or byte_values"};
+    }
 
     // Verify no invalid properties exist
     verifyPropertyCount(element, propertyCount);
diff --git a/phosphor-regulators/src/vpd.cpp b/phosphor-regulators/src/vpd.cpp
index fc99904..5f5454e 100644
--- a/phosphor-regulators/src/vpd.cpp
+++ b/phosphor-regulators/src/vpd.cpp
@@ -22,10 +22,10 @@
 namespace phosphor::power::regulators
 {
 
-std::string DBusVPD::getValue(const std::string& inventoryPath,
-                              const std::string& keyword)
+std::vector<uint8_t> DBusVPD::getValue(const std::string& inventoryPath,
+                                       const std::string& keyword)
 {
-    std::string value{};
+    std::vector<uint8_t> value{};
 
     // Get cached keywords for the inventory path
     KeywordMap& cachedKeywords = cache[inventoryPath];
@@ -38,12 +38,28 @@
     }
     else
     {
-        // Get keyword value from D-Bus interface/property.  The property name
-        // is normally the same as the VPD keyword name.  However, the CCIN
-        // keyword is stored in the Model property.
-        std::string property{(keyword == "CCIN") ? "Model" : keyword};
-        util::getProperty(ASSET_IFACE, property, inventoryPath,
-                          INVENTORY_MGR_IFACE, bus, value);
+        if (keyword == "HW")
+        {
+            // HW is a vector<uint8_t>, the others are a string.
+            util::getProperty("com.ibm.ipzvpd.VINI", "HW", inventoryPath,
+                              INVENTORY_MGR_IFACE, bus, value);
+        }
+        else
+        {
+            // Get keyword value from D-Bus interface/property.  The property
+            // name is normally the same as the VPD keyword name.  However, the
+            // CCIN keyword is stored in the Model property.
+            std::string property{(keyword == "CCIN") ? "Model" : keyword};
+            std::string stringValue;
+            util::getProperty(ASSET_IFACE, property, inventoryPath,
+                              INVENTORY_MGR_IFACE, bus, stringValue);
+
+            if (!stringValue.empty())
+            {
+                value.insert(value.begin(), stringValue.begin(),
+                             stringValue.end());
+            }
+        }
 
         // Cache keyword value
         cachedKeywords[keyword] = value;
diff --git a/phosphor-regulators/src/vpd.hpp b/phosphor-regulators/src/vpd.hpp
index 3222d50..8ae1314 100644
--- a/phosphor-regulators/src/vpd.hpp
+++ b/phosphor-regulators/src/vpd.hpp
@@ -17,8 +17,10 @@
 
 #include <sdbusplus/bus.hpp>
 
+#include <cstdint>
 #include <map>
 #include <string>
+#include <vector>
 
 namespace phosphor::power::regulators
 {
@@ -60,8 +62,8 @@
      * @param keyword VPD keyword
      * @return VPD keyword value
      */
-    virtual std::string getValue(const std::string& inventoryPath,
-                                 const std::string& keyword) = 0;
+    virtual std::vector<uint8_t> getValue(const std::string& inventoryPath,
+                                          const std::string& keyword) = 0;
 };
 
 /**
@@ -96,14 +98,14 @@
     }
 
     /** @copydoc VPD::getValue() */
-    virtual std::string getValue(const std::string& inventoryPath,
-                                 const std::string& keyword) override;
+    virtual std::vector<uint8_t> getValue(const std::string& inventoryPath,
+                                          const std::string& keyword) override;
 
   private:
     /**
      * Type alias for map from keyword names to values.
      */
-    using KeywordMap = std::map<std::string, std::string>;
+    using KeywordMap = std::map<std::string, std::vector<uint8_t>>;
 
     /**
      * D-Bus bus object.