regulators: Add VPD service

The regulators application needs to obtain VPD (Vital Product Data)
keyword values.

Sometimes regulator configuration and monitoring varies depending on
hardware type or version.  VPD keyword values can provide this
information about the hardware.

Add a new class to obtain hardware VPD from the D-Bus
xyz.openbmc_project.Inventory.Decorator.Asset interface.

Also define an abstract base class and a mock implementation to
enable use of gmock in test cases related to VPD.

Tested:
* Verified VPD values were successfully obtained from D-Bus.
* Verified VPD values were cached.
* Tested where object path was invalid.
* Tested where keyword was invalid.
* Verified cached VPD values were cleared when machine powered on.
* For the complete test plan, see
  https://gist.github.com/smccarney/519a54353361e28b1d25f5783c15f471

Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
Change-Id: Id08e8bca8db6421d46669c495e8a9432e45a1fd6
diff --git a/phosphor-regulators/src/manager.cpp b/phosphor-regulators/src/manager.cpp
index 73e49bf..778e4b4 100644
--- a/phosphor-regulators/src/manager.cpp
+++ b/phosphor-regulators/src/manager.cpp
@@ -205,8 +205,9 @@
 
 void Manager::clearHardwareData()
 {
-    // Clear any cached hardware presence data
+    // Clear any cached hardware presence data and VPD values
     services.getPresenceService().clearCache();
+    services.getVPD().clearCache();
 
     // Verify System object exists; this means config file has been loaded
     if (system)
diff --git a/phosphor-regulators/src/meson.build b/phosphor-regulators/src/meson.build
index 7b852bb..d7fc414 100644
--- a/phosphor-regulators/src/meson.build
+++ b/phosphor-regulators/src/meson.build
@@ -21,6 +21,7 @@
     'sensor_monitoring.cpp',
     'system.cpp',
     'temporary_file.cpp',
+    'vpd.cpp',
 
     'actions/compare_presence_action.cpp',
     'actions/if_action.cpp',
diff --git a/phosphor-regulators/src/services.hpp b/phosphor-regulators/src/services.hpp
index 8d6fc0d..44d438e 100644
--- a/phosphor-regulators/src/services.hpp
+++ b/phosphor-regulators/src/services.hpp
@@ -18,6 +18,7 @@
 #include "error_logging.hpp"
 #include "journal.hpp"
 #include "presence_service.hpp"
+#include "vpd.hpp"
 
 #include <sdbusplus/bus.hpp>
 
@@ -71,6 +72,13 @@
      * @return hardware presence interface
      */
     virtual PresenceService& getPresenceService() = 0;
+
+    /**
+     * Returns the interface to hardware VPD (Vital Product Data).
+     *
+     * @return hardware VPD interface
+     */
+    virtual VPD& getVPD() = 0;
 };
 
 /**
@@ -95,7 +103,7 @@
      * @param bus D-Bus bus object
      */
     explicit BMCServices(sdbusplus::bus::bus& bus) :
-        bus{bus}, errorLogging{bus}, presenceService{bus}
+        bus{bus}, errorLogging{bus}, presenceService{bus}, vpd{bus}
     {
     }
 
@@ -123,6 +131,12 @@
         return presenceService;
     }
 
+    /** @copydoc Services::getVPD() */
+    virtual VPD& getVPD() override
+    {
+        return vpd;
+    }
+
   private:
     /**
      * D-Bus bus object.
@@ -144,6 +158,11 @@
      * Implementation of the PresenceService interface using D-Bus method calls.
      */
     DBusPresenceService presenceService;
+
+    /**
+     * Implementation of the VPD interface using D-Bus method calls.
+     */
+    DBusVPD vpd;
 };
 
 } // namespace phosphor::power::regulators
diff --git a/phosphor-regulators/src/vpd.cpp b/phosphor-regulators/src/vpd.cpp
new file mode 100644
index 0000000..fc99904
--- /dev/null
+++ b/phosphor-regulators/src/vpd.cpp
@@ -0,0 +1,55 @@
+/**
+ * Copyright © 2021 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vpd.hpp"
+
+#include "types.hpp"
+#include "utility.hpp"
+
+namespace phosphor::power::regulators
+{
+
+std::string DBusVPD::getValue(const std::string& inventoryPath,
+                              const std::string& keyword)
+{
+    std::string value{};
+
+    // Get cached keywords for the inventory path
+    KeywordMap& cachedKeywords = cache[inventoryPath];
+
+    // Check if the keyword value is already cached
+    auto it = cachedKeywords.find(keyword);
+    if (it != cachedKeywords.end())
+    {
+        value = it->second;
+    }
+    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);
+
+        // Cache keyword value
+        cachedKeywords[keyword] = value;
+    }
+
+    return value;
+}
+
+} // namespace phosphor::power::regulators
diff --git a/phosphor-regulators/src/vpd.hpp b/phosphor-regulators/src/vpd.hpp
new file mode 100644
index 0000000..3222d50
--- /dev/null
+++ b/phosphor-regulators/src/vpd.hpp
@@ -0,0 +1,121 @@
+/**
+ * Copyright © 2021 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <sdbusplus/bus.hpp>
+
+#include <map>
+#include <string>
+
+namespace phosphor::power::regulators
+{
+
+/**
+ * @class VPD
+ *
+ * Abstract base class that provides an interface to hardware VPD (Vital Product
+ * Data).
+ *
+ * The interface is used to obtain VPD keyword values.
+ */
+class VPD
+{
+  public:
+    // Specify which compiler-generated methods we want
+    VPD() = default;
+    VPD(const VPD&) = delete;
+    VPD(VPD&&) = delete;
+    VPD& operator=(const VPD&) = delete;
+    VPD& operator=(VPD&&) = delete;
+    virtual ~VPD() = default;
+
+    /**
+     * Clears any cached hardware VPD values.
+     */
+    virtual void clearCache(void) = 0;
+
+    /**
+     * Returns the value of the specified VPD keyword for the specified
+     * inventory path.
+     *
+     * May return a cached value if one is available to improve performance.
+     *
+     * Throws an exception if an error occurs while obtaining the VPD
+     * value.
+     *
+     * @param inventoryPath D-Bus inventory path of the hardware
+     * @param keyword VPD keyword
+     * @return VPD keyword value
+     */
+    virtual std::string getValue(const std::string& inventoryPath,
+                                 const std::string& keyword) = 0;
+};
+
+/**
+ * @class DBusVPD
+ *
+ * Implementation of the VPD interface using D-Bus method calls.
+ */
+class DBusVPD : public VPD
+{
+  public:
+    // Specify which compiler-generated methods we want
+    DBusVPD() = delete;
+    DBusVPD(const DBusVPD&) = delete;
+    DBusVPD(DBusVPD&&) = delete;
+    DBusVPD& operator=(const DBusVPD&) = delete;
+    DBusVPD& operator=(DBusVPD&&) = delete;
+    virtual ~DBusVPD() = default;
+
+    /**
+     * Constructor.
+     *
+     * @param bus D-Bus bus object
+     */
+    explicit DBusVPD(sdbusplus::bus::bus& bus) : bus{bus}
+    {
+    }
+
+    /** @copydoc VPD::clearCache() */
+    virtual void clearCache(void) override
+    {
+        cache.clear();
+    }
+
+    /** @copydoc VPD::getValue() */
+    virtual std::string 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>;
+
+    /**
+     * D-Bus bus object.
+     */
+    sdbusplus::bus::bus& bus;
+
+    /**
+     * Cached VPD keyword values.
+     *
+     * Map from inventory paths to VPD keywords.
+     */
+    std::map<std::string, KeywordMap> cache{};
+};
+
+} // namespace phosphor::power::regulators