regulators: Wait for compatible system types

There are two options for naming the JSON configuration file:
1. Default name
2. Name based on compatible system types provided by EntityManager

If option #2 is used, there is no guarantee that EntityManager will have
published the compatible systems types on D-Bus before a boot is
attempted.  This is not common, but the problem has occurred.

When the boot begins, the regulators application configures the voltage
regulators.  If the config file has not been found and loaded, this
configuration cannot occur.

This commit enhances the regulators application to wait for
EntityManager to publish the compatible systems information during the
boot so that the regulators can be configured.

A maximum wait time is enforced to prevent the boot from being blocked
indefinitely.  If this maximum wait time is reached, a critical error is
logged indicating that the voltage regulators could not be configured.

Tested:
* System types available before boot
* Have to wait for system types during boot
  * System types found within 5 minutes
  * System types not found within 5 minutes
* For complete test plan details, see
  https://gist.github.com/smccarney/b059a5f4da1a9138af770217cbafaea3

Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
Change-Id: I700a376b94603f36defd1fe266c0a2d7e2a9d30b
diff --git a/phosphor-regulators/src/manager.cpp b/phosphor-regulators/src/manager.cpp
index e81d7f1..1bdc9ab 100644
--- a/phosphor-regulators/src/manager.cpp
+++ b/phosphor-regulators/src/manager.cpp
@@ -29,6 +29,7 @@
 #include <exception>
 #include <functional>
 #include <map>
+#include <thread>
 #include <tuple>
 #include <utility>
 #include <variant>
@@ -43,6 +44,7 @@
 constexpr auto compatibleIntf =
     "xyz.openbmc_project.Configuration.IBMCompatibleSystem";
 constexpr auto compatibleNamesProp = "Names";
+constexpr std::chrono::minutes maxTimeToWaitForCompatTypes{5};
 
 /**
  * Default configuration file name.  This is used when the system does not
@@ -95,8 +97,11 @@
     // Clear any cached data or error history related to hardware devices
     clearHardwareData();
 
-    // Verify System object exists; this means config file has been loaded
-    if (system)
+    // Wait until the config file has been loaded or hit max wait time
+    waitUntilConfigFileLoaded();
+
+    // Verify config file has been loaded and System object is valid
+    if (isConfigFileLoaded())
     {
         // Configure the regulator devices in the system
         system->configure(services);
@@ -107,20 +112,13 @@
         services.getJournal().logError("Unable to configure regulator devices: "
                                        "Configuration file not loaded");
 
-        // TODO: Add code to wait for EntityManager to publish the compatible
-        // interface before logging this error.
-
         // Log critical error since regulators could not be configured.  Could
         // cause hardware damage if default regulator settings are very wrong.
-        /*
         services.getErrorLogging().logConfigFileError(Entry::Level::Critical,
                                                       services.getJournal());
-        */
 
         // Throw InternalFailure to propogate error status to D-Bus client
-        /*
         throw sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure{};
-        */
     }
 }
 
@@ -191,8 +189,8 @@
             timers.clear();
         */
 
-        // Verify System object exists; this means config file has been loaded
-        if (system)
+        // Verify config file has been loaded and System object is valid
+        if (isConfigFileLoaded())
         {
             // Close the regulator devices in the system.  Monitoring is
             // normally disabled because the system is being powered off.  The
@@ -222,8 +220,8 @@
     services.getPresenceService().clearCache();
     services.getVPD().clearCache();
 
-    // Verify System object exists; this means config file has been loaded
-    if (system)
+    // Verify config file has been loaded and System object is valid
+    if (isConfigFileLoaded())
     {
         // Clear any cached hardware data in the System object
         system->clearCache();
@@ -350,4 +348,33 @@
     }
 }
 
+void Manager::waitUntilConfigFileLoaded()
+{
+    // If config file not loaded and list of compatible system types is empty
+    if (!isConfigFileLoaded() && compatibleSystemTypes.empty())
+    {
+        // Loop until compatible system types found or waited max amount of time
+        auto start = std::chrono::system_clock::now();
+        std::chrono::system_clock::duration timeWaited{0};
+        while (compatibleSystemTypes.empty() &&
+               (timeWaited <= maxTimeToWaitForCompatTypes))
+        {
+            // Try to find list of compatible system types
+            findCompatibleSystemTypes();
+            if (!compatibleSystemTypes.empty())
+            {
+                // Compatible system types found; try to load config file
+                loadConfigFile();
+            }
+            else
+            {
+                // Sleep 5 seconds
+                using namespace std::chrono_literals;
+                std::this_thread::sleep_for(5s);
+            }
+            timeWaited = std::chrono::system_clock::now() - start;
+        }
+    }
+}
+
 } // namespace phosphor::power::regulators