pseq: Add presence checking

As a circumvention for hardware problems, it can be necessary to check
for device presence in order to prevent calling out not present
components. Add the ability to configure presence checking in the PSEQ
configuration files and use presence, when configured, in problem
determination.

Signed-off-by: Jim Wright <jlwright@us.ibm.com>
Change-Id: Ia9d8ee57f2d643f61ece45d9c03d523b891e98b8
diff --git a/phosphor-power-sequencer/config_files/UCD90320Monitor_ibm,everest.json b/phosphor-power-sequencer/config_files/UCD90320Monitor_ibm,everest.json
index 255682b..7e50882 100644
--- a/phosphor-power-sequencer/config_files/UCD90320Monitor_ibm,everest.json
+++ b/phosphor-power-sequencer/config_files/UCD90320Monitor_ibm,everest.json
@@ -1,37 +1,102 @@
 {
     "rails": [
-        "12.0V",
-        "3V3IO",
-        "CP0_VDD0",
-        "CP0_VDD1",
-        "CP1_VDD0",
-        "CP1_VDD1",
-        "CP2_VDD0",
-        "CP2_VDD1",
-        "CP3_VDD0",
-        "CP3_VDD1",
-        "CP0_VCS0",
-        "CP0_VCS1",
-        "CP1_VCS0",
-        "CP1_VCS1",
-        "CP2_VCS0",
-        "CP2_VCS1",
-        "CP3_VCS0",
-        "CP3_VCS1",
-        "CP03_AVDD",
-        "CP12_AVDD",
-        "CP0_VDN",
-        "CP1_VDN",
-        "CP2_VDN",
-        "CP3_VDN",
-        "CP0_VIO",
-        "CP1_VIO",
-        "CP2_VIO",
-        "CP3_VIO",
-        "CP0_VPCIE",
-        "CP1_VPCIE",
-        "CP2_VPCIE",
-        "CP3_VPCIE"
+        {
+            "name": "12.0V"
+        },
+        {
+            "name": "3V3IO"
+        },
+        {
+            "name": "CP0_VDD0"
+        },
+        {
+            "name": "CP0_VDD1"
+        },
+        {
+            "name": "CP1_VDD0"
+        },
+        {
+            "name": "CP1_VDD1"
+        },
+        {
+            "name": "CP2_VDD0"
+        },
+        {
+            "name": "CP2_VDD1"
+        },
+        {
+            "name": "CP3_VDD0"
+        },
+        {
+            "name": "CP3_VDD1"
+        },
+        {
+            "name": "CP0_VCS0"
+        },
+        {
+            "name": "CP0_VCS1"
+        },
+        {
+            "name": "CP1_VCS0"
+        },
+        {
+            "name": "CP1_VCS1"
+        },
+        {
+            "name": "CP2_VCS0"
+        },
+        {
+            "name": "CP2_VCS1",
+            "presence": "/xyz/openbmc_project/inventory/system/chassis/motherboard/dcm2/cpu0"
+        },
+        {
+            "name": "CP3_VCS0"
+        },
+        {
+            "name": "CP3_VCS1"
+        },
+        {
+            "name": "CP03_AVDD"
+        },
+        {
+            "name": "CP12_AVDD"
+        },
+        {
+            "name": "CP0_VDN"
+        },
+        {
+            "name": "CP1_VDN"
+        },
+        {
+            "name": "CP2_VDN"
+        },
+        {
+            "name": "CP3_VDN"
+        },
+        {
+            "name": "CP0_VIO"
+        },
+        {
+            "name": "CP1_VIO"
+        },
+        {
+            "name": "CP2_VIO"
+        },
+        {
+            "name": "CP3_VIO"
+        },
+        {
+            "name": "CP0_VPCIE"
+        },
+        {
+            "name": "CP1_VPCIE"
+        },
+        {
+            "name": "CP2_VPCIE"
+        },
+        {
+            "name": "CP3_VPCIE"
+        }
     ],
 
     "pins": [
@@ -45,11 +110,13 @@
         },
         {
             "name": "CP2_VPCIE",
-            "line": 61
+            "line": 61,
+            "presence": "/xyz/openbmc_project/inventory/system/chassis/motherboard/dcm2/cpu0"
         },
         {
             "name": "CP3_VPCIE",
-            "line": 62
+            "line": 62,
+            "presence": "/xyz/openbmc_project/inventory/system/chassis/motherboard/dcm3/cpu0"
         },
         {
             "name": "5.0V_USB_front",
@@ -65,11 +132,13 @@
         },
         {
             "name": "CP2_VDN",
-            "line": 74
+            "line": 74,
+            "presence": "/xyz/openbmc_project/inventory/system/chassis/motherboard/dcm2/cpu0"
         },
         {
             "name": "CP3_VDN",
-            "line": 75
+            "line": 75,
+            "presence": "/xyz/openbmc_project/inventory/system/chassis/motherboard/dcm3/cpu0"
         },
         {
             "name": "CP0_VIO",
@@ -81,11 +150,13 @@
         },
         {
             "name": "CP2_VIO",
-            "line": 78
+            "line": 78,
+            "presence": "/xyz/openbmc_project/inventory/system/chassis/motherboard/dcm2/cpu0"
         },
         {
             "name": "CP3_VIO",
-            "line": 79
+            "line": 79,
+            "presence": "/xyz/openbmc_project/inventory/system/chassis/motherboard/dcm3/cpu0"
         }
     ]
 }
diff --git a/phosphor-power-sequencer/config_files/UCD90320Monitor_ibm,rainier.json b/phosphor-power-sequencer/config_files/UCD90320Monitor_ibm,rainier.json
index 01e2986..b489db2 100644
--- a/phosphor-power-sequencer/config_files/UCD90320Monitor_ibm,rainier.json
+++ b/phosphor-power-sequencer/config_files/UCD90320Monitor_ibm,rainier.json
@@ -1,37 +1,101 @@
 {
     "rails": [
-       "12.0V",
-       "5.0V_USB",
-       "5.0V_DASD",
-       "3.3VA",
-       "3.3VB",
-       "1.5V",
-       "1.1V",
-       "VDDA_DCM0",
-       "VDDB_DCM0",
-       "VDDA_DCM1",
-       "VDDB_DCM1",
-       "12.0VCS",
-       "3.3VCS",
-       "1.1V_Current",
-       "5.0V_USB_Current",
-       "5.0V_DASD_Current",
-       "12.0VN",
-       "12.0VP",
-       "12.0VQ",
-       "12.0VR",
-       "ThermalDiode1",
-       "ThermalDiode2",
-       "ThermalDiode3",
-       "ThermalDiode4",
-       "VDN_DCM0",
-       "VDN_DCM1",
-       "VCS_DCM0",
-       "VCS_DCM1",
-       "VIO_DCM0",
-       "VIO_DCM1",
-       "VPCIE_DCM0",
-       "VPCIE_DCM1"
+        {
+           "name": "12.0V"
+        },
+        {
+           "name": "5.0V_USB"
+        },
+        {
+           "name": "5.0V_DASD"
+        },
+        {
+           "name": "3.3VA"
+        },
+        {
+           "name": "3.3VB"
+        },
+        {
+           "name": "1.5V"
+        },
+        {
+           "name": "1.1V"
+        },
+        {
+           "name": "VDDA_DCM0"
+        },
+        {
+           "name": "VDDB_DCM0"
+        },
+        {
+           "name": "VDDA_DCM1"
+        },
+        {
+           "name": "VDDB_DCM1"
+        },
+        {
+           "name": "12.0VCS"
+        },
+        {
+           "name": "3.3VCS"
+        },
+        {
+           "name": "1.1V_Current"
+        },
+        {
+           "name": "5.0V_USB_Current"
+        },
+        {
+           "name": "5.0V_DASD_Current"
+        },
+        {
+           "name": "12.0VN"
+        },
+        {
+           "name": "12.0VP"
+        },
+        {
+           "name": "12.0VQ"
+        },
+        {
+           "name": "12.0VR"
+        },
+        {
+           "name": "ThermalDiode1"
+        },
+        {
+           "name": "ThermalDiode2"
+        },
+        {
+           "name": "ThermalDiode3"
+        },
+        {
+           "name": "ThermalDiode4"
+        },
+        {
+           "name": "VDN_DCM0"
+        },
+        {
+           "name": "VDN_DCM1"
+        },
+        {
+           "name": "VCS_DCM0"
+        },
+        {
+           "name": "VCS_DCM1"
+        },
+        {
+           "name": "VIO_DCM0"
+        },
+        {
+           "name": "VIO_DCM1"
+        },
+        {
+           "name": "VPCIE_DCM0"
+        },
+        {
+           "name": "VPCIE_DCM1"
+        }
     ],
 
     "pins": [
diff --git a/phosphor-power-sequencer/src/power_control.cpp b/phosphor-power-sequencer/src/power_control.cpp
index eac5ba4..91021a4 100644
--- a/phosphor-power-sequencer/src/power_control.cpp
+++ b/phosphor-power-sequencer/src/power_control.cpp
@@ -84,7 +84,7 @@
                 name = std::get_if<std::string>(&properties[namePropertyName]);
             }
         }
-        catch (std::exception& e)
+        catch (const std::exception&)
         {}
     }
 
@@ -305,7 +305,7 @@
             getDeviceProperties(properties);
         }
     }
-    catch (const std::exception& e)
+    catch (const std::exception&)
     {
         // Interface or property not found. Let the Interfaces Added callback
         // process the information once the interfaces are added to D-Bus.
diff --git a/phosphor-power-sequencer/src/power_sequencer_monitor.cpp b/phosphor-power-sequencer/src/power_sequencer_monitor.cpp
index 04398eb..e11fb3f 100644
--- a/phosphor-power-sequencer/src/power_sequencer_monitor.cpp
+++ b/phosphor-power-sequencer/src/power_sequencer_monitor.cpp
@@ -45,10 +45,10 @@
                 std::pair<std::string, std::variant<std::string, uint64_t>>>());
         bus.call_noreply(method);
     }
-    catch (const sdbusplus::exception::exception& e)
+    catch (const std::exception& e)
     {
         log<level::ERR>(
-            fmt::format("Unable to create dump, error {}", e.what()).c_str());
+            fmt::format("Unable to create dump, error: {}", e.what()).c_str());
     }
 }
 
@@ -76,7 +76,7 @@
     catch (const std::exception& e)
     {
         log<level::ERR>(
-            fmt::format("Unable to log error, message: {}, error {}", message,
+            fmt::format("Unable to log error, message: {}, error: {}", message,
                         e.what())
                 .c_str());
     }
diff --git a/phosphor-power-sequencer/src/ucd90320_monitor.cpp b/phosphor-power-sequencer/src/ucd90320_monitor.cpp
index 3f431fb..050fcdc 100644
--- a/phosphor-power-sequencer/src/ucd90320_monitor.cpp
+++ b/phosphor-power-sequencer/src/ucd90320_monitor.cpp
@@ -16,6 +16,7 @@
 
 #include "ucd90320_monitor.hpp"
 
+#include "types.hpp"
 #include "utility.hpp"
 
 #include <fmt/format.h>
@@ -179,6 +180,35 @@
     }
 }
 
+bool UCD90320Monitor::isPresent(const std::string& inventoryPath)
+{
+    // Empty path indicates no presence check is needed
+    if (inventoryPath.empty())
+    {
+        return true;
+    }
+
+    // Get presence from D-Bus interface/property
+    try
+    {
+        bool present{true};
+        util::getProperty(INVENTORY_IFACE, PRESENT_PROP, inventoryPath,
+                          INVENTORY_MGR_IFACE, bus, present);
+        log<level::INFO>(
+            fmt::format("Presence, path: {}, value: {}", inventoryPath, present)
+                .c_str());
+        return present;
+    }
+    catch (const std::exception& e)
+    {
+        log<level::INFO>(
+            fmt::format("Error getting presence property, path: {}, error: {}",
+                        inventoryPath, e.what())
+                .c_str());
+        return false;
+    }
+}
+
 void UCD90320Monitor::parseConfigFile(const std::filesystem::path& pathName)
 {
     try
@@ -192,8 +222,34 @@
         {
             for (const auto& railElement : *railsIterator)
             {
-                std::string rail = railElement.get<std::string>();
-                rails.emplace_back(std::move(rail));
+                auto nameIterator = railElement.find("name");
+
+                if (nameIterator != railElement.end())
+                {
+                    Rail rail;
+                    rail.name = (*nameIterator).get<std::string>();
+
+                    // Presence element is optional
+                    auto presenceIterator = railElement.find("presence");
+                    if (presenceIterator != railElement.end())
+                    {
+                        rail.presence = (*presenceIterator).get<std::string>();
+                    }
+
+                    log<level::DEBUG>(
+                        fmt::format("Adding rail, name: {}, presence: {}",
+                                    rail.name, rail.presence)
+                            .c_str());
+                    rails.emplace_back(std::move(rail));
+                }
+                else
+                {
+                    log<level::ERR>(
+                        fmt::format(
+                            "No name found within rail in configuration file: {}",
+                            pathName.string())
+                            .c_str());
+                }
             }
         }
         else
@@ -203,7 +259,8 @@
                             pathName.string())
                     .c_str());
         }
-        log<level::DEBUG>(fmt::format("Found rails: {}", rails).c_str());
+        log<level::DEBUG>(
+            fmt::format("Found number of rails: {}", rails.size()).c_str());
 
         // Parse pin information from config file
         auto pinsIterator = rootElement.find("pins");
@@ -217,12 +274,22 @@
                 if (nameIterator != pinElement.end() &&
                     lineIterator != pinElement.end())
                 {
-                    std::string name = (*nameIterator).get<std::string>();
-                    unsigned int line = (*lineIterator).get<unsigned int>();
-
                     Pin pin;
-                    pin.name = name;
-                    pin.line = line;
+                    pin.name = (*nameIterator).get<std::string>();
+                    pin.line = (*lineIterator).get<unsigned int>();
+
+                    // Presence element is optional
+                    auto presenceIterator = pinElement.find("presence");
+                    if (presenceIterator != pinElement.end())
+                    {
+                        pin.presence = (*presenceIterator).get<std::string>();
+                    }
+
+                    log<level::DEBUG>(
+                        fmt::format(
+                            "Adding pin, name: {}, line: {}, presence: {}",
+                            pin.name, pin.line, pin.presence)
+                            .c_str());
                     pins.emplace_back(std::move(pin));
                 }
                 else
@@ -247,10 +314,9 @@
     }
     catch (const std::exception& e)
     {
-        // Log error message in journal
-        log<level::ERR>(std::string("Exception parsing configuration file: " +
-                                    std::string(e.what()))
-                            .c_str());
+        log<level::ERR>(
+            fmt::format("Error parsing configuration file, error: {}", e.what())
+                .c_str());
     }
 }
 
@@ -269,12 +335,12 @@
         onFailureCheckRails(message, additionalData, powerSupplyError);
         onFailureCheckPins(message, additionalData);
     }
-    catch (device_error::ReadFailure& e)
+    catch (const std::exception& e)
     {
         log<level::ERR>(
-            fmt::format("ReadFailure when collecting metadata, error {}",
-                        e.what())
+            fmt::format("Error when collecting metadata, error: {}", e.what())
                 .c_str());
+        additionalData.emplace("ERROR", e.what());
     }
 
     if (message.empty())
@@ -319,14 +385,14 @@
     catch (const std::exception& e)
     {
         log<level::ERR>(
-            fmt::format("Error reading device GPIOs, error {}", e.what())
+            fmt::format("Error reading device GPIOs, error: {}", e.what())
                 .c_str());
         additionalData.emplace("GPIO_ERROR", e.what());
     }
 
     // Add GPIO values to additional data, device has 84 GPIO pins so that value
     // is expected
-    if (numberLines == 84)
+    if (numberLines == 84 && values.size() >= 84)
     {
         log<level::INFO>(fmt::format("MAR01-24 GPIO values: {}",
                                      std::span{values}.subspan(0, 24))
@@ -374,7 +440,8 @@
             if (line < values.size())
             {
                 int value = values[line];
-                if (value == 0)
+
+                if ((value == 0) && isPresent(pins[pin].presence))
                 {
                     additionalData.emplace("INPUT_NUM",
                                            fmt::format("{}", line));
@@ -399,12 +466,12 @@
         additionalData.emplace("MFR_STATUS",
                                fmt::format("{:#014x}", readMFRStatus()));
     }
-    catch (device_error::ReadFailure& e)
+    catch (const std::exception& e)
     {
         log<level::ERR>(
-            fmt::format("ReadFailure when collecting MFR_STATUS, error {}",
-                        e.what())
+            fmt::format("Error when collecting MFR_STATUS, error: {}", e.what())
                 .c_str());
+        additionalData.emplace("ERROR", e.what());
     }
 
     // The status_word register has a summary bit to tell us if each page even
@@ -434,14 +501,13 @@
                             fmt::format("STATUS{}_VOUT", page),
                             fmt::format("{:#04x}", vout));
 
-                        // Base the callouts on the first vout failure found
-                        if (message.empty())
+                        // Base the callouts on the first present vout failure
+                        // found
+                        if (message.empty() && (page < rails.size()) &&
+                            isPresent(rails[page].presence))
                         {
-                            if (page < rails.size())
-                            {
-                                additionalData.emplace("RAIL_NAME",
-                                                       rails[page]);
-                            }
+                            additionalData.emplace("RAIL_NAME",
+                                                   rails[page].name);
 
                             // Use power supply error if set and 12v rail has
                             // failed, else use voltage error
diff --git a/phosphor-power-sequencer/src/ucd90320_monitor.hpp b/phosphor-power-sequencer/src/ucd90320_monitor.hpp
index 7f1d137..59e784c 100644
--- a/phosphor-power-sequencer/src/ucd90320_monitor.hpp
+++ b/phosphor-power-sequencer/src/ucd90320_monitor.hpp
@@ -17,6 +17,13 @@
 {
     std::string name;
     unsigned int line;
+    std::string presence;
+};
+
+struct Rail
+{
+    std::string name;
+    std::string presence;
 };
 
 /**
@@ -69,9 +76,9 @@
     pmbus::PMBus pmbusInterface;
 
     /**
-     * List of rail names
+     * List of rails
      */
-    std::vector<std::string> rails;
+    std::vector<Rail> rails;
 
     /**
      * Finds the list of compatible system types using D-Bus methods.
@@ -91,6 +98,17 @@
     void findConfigFile(const std::vector<std::string>& compatibleSystemTypes);
 
     /**
+     * Returns whether the hardware with the specified inventory path is
+     * present.
+     * If an error occurs while obtaining the presence value, presence is
+     * assumed to be false. An empty string path indicates no presence check is
+     * needed.
+     * @param inventoryPath D-Bus inventory path of the hardware
+     * @return true if hardware is present, false otherwise
+     */
+    bool isPresent(const std::string& inventoryPath);
+
+    /**
      * Analyzes the device pins for errors when the device is known to be in an
      * error state.
      * @param message Message property of the error log entry