pseq: Standard pgood fault detection algorithm
Create a StandardDevice class for the phosphor-power-sequencer
application. This class implements the standard pgood (power good)
fault detection algorithm.
When adding support for a new power sequencer device type, a sub-class
of StandardDevice should be created if possible. This will ensure that
pgood fault detection works consistently across device types.
Tested:
* Verified all new and existing gtests ran successfully
Change-Id: I80c0b36429f1d84fa1e317889803429927797fdc
Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
diff --git a/phosphor-power-sequencer/src/meson.build b/phosphor-power-sequencer/src/meson.build
index db8112f..2d14caf 100644
--- a/phosphor-power-sequencer/src/meson.build
+++ b/phosphor-power-sequencer/src/meson.build
@@ -8,6 +8,7 @@
'config_file_parser.cpp',
'rail.cpp',
'services.cpp',
+ 'standard_device.cpp',
implicit_include_directories: false,
dependencies: [
nlohmann_json_dep,
diff --git a/phosphor-power-sequencer/src/power_sequencer_device.hpp b/phosphor-power-sequencer/src/power_sequencer_device.hpp
index f6ebb48..7a8c1b3 100644
--- a/phosphor-power-sequencer/src/power_sequencer_device.hpp
+++ b/phosphor-power-sequencer/src/power_sequencer_device.hpp
@@ -129,9 +129,13 @@
virtual double getVoutUVFaultLimit(uint8_t page) = 0;
/**
- * Returns whether a pgood fault has occurred on one of the rails being
+ * Checks whether a pgood fault has occurred on one of the rails being
* monitored by this device.
*
+ * If a pgood fault was found, this method returns a string containing the
+ * error that should be logged. If no fault was found, an empty string is
+ * returned.
+ *
* Throws an exception if an error occurs while trying to obtain the status
* of the rails.
*
@@ -141,16 +145,14 @@
* supply error occurred. This error may be the
* root cause if a pgood fault occurred on a power
* supply rail monitored by this device.
- * @param error Error that should be logged if this method returns true.
* @param additionalData Additional data to include in the error log if
- * this method returns true.
- * @return true if a pgood fault was found on a rail monitored by this
- * device, false otherwise
+ * a pgood fault was found
+ * @return error that should be logged if a pgood fault was found, or an
+ * empty string if no pgood fault was found
*/
- virtual bool
- hasPgoodFault(Services& services, const std::string& powerSupplyError,
- std::string& error,
- std::map<std::string, std::string>& additionalData) = 0;
+ virtual std::string
+ findPgoodFault(Services& services, const std::string& powerSupplyError,
+ std::map<std::string, std::string>& additionalData) = 0;
};
} // namespace phosphor::power::sequencer
diff --git a/phosphor-power-sequencer/src/standard_device.cpp b/phosphor-power-sequencer/src/standard_device.cpp
new file mode 100644
index 0000000..cc999c5
--- /dev/null
+++ b/phosphor-power-sequencer/src/standard_device.cpp
@@ -0,0 +1,105 @@
+/**
+ * Copyright © 2024 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 "standard_device.hpp"
+
+#include "format_utils.hpp"
+
+#include <exception>
+#include <format>
+#include <span>
+#include <stdexcept>
+
+namespace phosphor::power::sequencer
+{
+
+std::string StandardDevice::findPgoodFault(
+ Services& services, const std::string& powerSupplyError,
+ std::map<std::string, std::string>& additionalData)
+{
+ std::string error{};
+ try
+ {
+ prepareForPgoodFaultDetection(services);
+
+ // Get all GPIO values (if possible) from device. They may be slow to
+ // obtain, so obtain them once and then pass values to each Rail object.
+ std::vector<int> gpioValues = getGPIOValuesIfPossible();
+
+ // Loop through all the rails checking if any detected a pgood fault.
+ // The rails are in power-on-sequence order.
+ for (std::unique_ptr<Rail>& rail : rails)
+ {
+ if (rail->hasPgoodFault(*this, services, gpioValues,
+ additionalData))
+ {
+ services.logErrorMsg(std::format(
+ "Pgood fault found in rail monitored by device {}", name));
+
+ // If this is a PSU rail and a PSU error was previously detected
+ if (rail->isPowerSupplyRail() && !powerSupplyError.empty())
+ {
+ // Return power supply error as root cause
+ error = powerSupplyError;
+ }
+ else
+ {
+ // Return pgood fault as root cause
+ error =
+ "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault";
+ }
+
+ storePgoodFaultDebugData(services, gpioValues, additionalData);
+ break;
+ }
+ }
+ }
+ catch (const std::exception& e)
+ {
+ throw std::runtime_error{std::format(
+ "Unable to determine if a pgood fault occurred in device {}: {}",
+ name, e.what())};
+ }
+ return error;
+}
+
+std::vector<int> StandardDevice::getGPIOValuesIfPossible()
+{
+ std::vector<int> values{};
+ try
+ {
+ values = getGPIOValues();
+ }
+ catch (...)
+ {}
+ return values;
+}
+
+void StandardDevice::storePgoodFaultDebugData(
+ Services& services, const std::vector<int>& gpioValues,
+ std::map<std::string, std::string>& additionalData)
+{
+ additionalData.emplace("DEVICE_NAME", name);
+ if (!gpioValues.empty())
+ {
+ std::string valuesStr = format_utils::toString(std::span(gpioValues));
+ services.logInfoMsg(
+ std::format("Device {} GPIO values: {}", name, valuesStr));
+ additionalData.emplace("GPIO_VALUES", valuesStr);
+ }
+}
+
+} // namespace phosphor::power::sequencer
diff --git a/phosphor-power-sequencer/src/standard_device.hpp b/phosphor-power-sequencer/src/standard_device.hpp
new file mode 100644
index 0000000..3dbba2f
--- /dev/null
+++ b/phosphor-power-sequencer/src/standard_device.hpp
@@ -0,0 +1,143 @@
+/**
+ * Copyright © 2024 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 "power_sequencer_device.hpp"
+#include "rail.hpp"
+#include "services.hpp"
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace phosphor::power::sequencer
+{
+
+/**
+ * @class StandardDevice
+ *
+ * PowerSequencerDevice sub-class that implements the standard pgood fault
+ * detection algorithm.
+ *
+ * When adding support for a new power sequencer device type, create a sub-class
+ * of StandardDevice if possible. This will ensure that pgood fault detection
+ * works consistently across device types.
+ */
+class StandardDevice : public PowerSequencerDevice
+{
+ public:
+ // Specify which compiler-generated methods we want
+ StandardDevice() = delete;
+ StandardDevice(const StandardDevice&) = delete;
+ StandardDevice(StandardDevice&&) = delete;
+ StandardDevice& operator=(const StandardDevice&) = delete;
+ StandardDevice& operator=(StandardDevice&&) = delete;
+ virtual ~StandardDevice() = default;
+
+ /**
+ * Constructor.
+ *
+ * @param name device name
+ * @param rails voltage rails that are enabled and monitored by this device
+ */
+ explicit StandardDevice(const std::string& name,
+ std::vector<std::unique_ptr<Rail>> rails) :
+ name{name},
+ rails{std::move(rails)}
+ {}
+
+ /** @copydoc PowerSequencerDevice::getName() */
+ virtual const std::string& getName() const override
+ {
+ return name;
+ }
+
+ /** @copydoc PowerSequencerDevice::getRails() */
+ virtual const std::vector<std::unique_ptr<Rail>>& getRails() const override
+ {
+ return rails;
+ }
+
+ /** @copydoc PowerSequencerDevice::findPgoodFault()
+ *
+ * Calls prepareForPgoodFaultDetection() before starting detection. If a
+ * pgood fault is detected, calls storePgoodFaultDebugData().
+ */
+ virtual std::string findPgoodFault(
+ Services& services, const std::string& powerSupplyError,
+ std::map<std::string, std::string>& additionalData) override;
+
+ protected:
+ /**
+ * Prepare for pgood fault detection.
+ *
+ * Perform any actions that are necessary to prepare for fault detection.
+ * For example, cache information that is slow to obtain and is used
+ * multiple times during detection.
+ *
+ * Default implementation does nothing. Override in sub-classes if needed.
+ *
+ * @param services System services like hardware presence and the journal
+ */
+ virtual void
+ prepareForPgoodFaultDetection([[maybe_unused]] Services& services)
+ {}
+
+ /**
+ * Returns the GPIO values that can be read from the device, if possible.
+ *
+ * If the device does not support reading GPIO values or an error occurs, an
+ * empty vector is returned.
+ *
+ * @return GPIO values, or empty vector if values could not be read
+ */
+ virtual std::vector<int> getGPIOValuesIfPossible();
+
+ /**
+ * Store pgood fault debug data in the specified additional data map.
+ *
+ * The default implementation stores the device name and all the GPIO
+ * values. The GPIO values are stored as a simple list of integers.
+ *
+ * Sub-classes should override if needed to store device-specific data
+ * and/or a formatted representation of the GPIO values.
+ *
+ * This method should NOT throw exceptions. If debug data cannot be
+ * obtained, the error should be caught and ignored so that pgood error
+ * handling can continue.
+ *
+ * @param services System services like hardware presence and the journal
+ * @param gpioValues GPIO values obtained from the device (if any)
+ * @param additionalData Additional data to include in an error log
+ */
+ virtual void storePgoodFaultDebugData(
+ Services& services, const std::vector<int>& gpioValues,
+ std::map<std::string, std::string>& additionalData);
+
+ /**
+ * Device name.
+ */
+ std::string name{};
+
+ /**
+ * Voltage rails that are enabled and monitored by this device.
+ */
+ std::vector<std::unique_ptr<Rail>> rails{};
+};
+
+} // namespace phosphor::power::sequencer