1 /**
2  * Copyright © 2024 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "standard_device.hpp"
18 
19 #include "format_utils.hpp"
20 
21 #include <exception>
22 #include <format>
23 #include <span>
24 #include <stdexcept>
25 
26 namespace phosphor::power::sequencer
27 {
28 
29 std::string StandardDevice::findPgoodFault(
30     Services& services, const std::string& powerSupplyError,
31     std::map<std::string, std::string>& additionalData)
32 {
33     std::string error{};
34     try
35     {
36         prepareForPgoodFaultDetection(services);
37 
38         // Get all GPIO values (if possible) from device.  They may be slow to
39         // obtain, so obtain them once and then pass values to each Rail object.
40         std::vector<int> gpioValues = getGPIOValuesIfPossible();
41 
42         // Loop through all the rails checking if any detected a pgood fault.
43         // The rails are in power-on-sequence order.
44         for (std::unique_ptr<Rail>& rail : rails)
45         {
46             if (rail->hasPgoodFault(*this, services, gpioValues,
47                                     additionalData))
48             {
49                 services.logErrorMsg(std::format(
50                     "Pgood fault found in rail monitored by device {}", name));
51 
52                 // If this is a PSU rail and a PSU error was previously detected
53                 if (rail->isPowerSupplyRail() && !powerSupplyError.empty())
54                 {
55                     // Return power supply error as root cause
56                     error = powerSupplyError;
57                 }
58                 else
59                 {
60                     // Return pgood fault as root cause
61                     error =
62                         "xyz.openbmc_project.Power.Error.PowerSequencerVoltageFault";
63                 }
64 
65                 storePgoodFaultDebugData(services, gpioValues, additionalData);
66                 break;
67             }
68         }
69     }
70     catch (const std::exception& e)
71     {
72         throw std::runtime_error{std::format(
73             "Unable to determine if a pgood fault occurred in device {}: {}",
74             name, e.what())};
75     }
76     return error;
77 }
78 
79 std::vector<int> StandardDevice::getGPIOValuesIfPossible()
80 {
81     std::vector<int> values{};
82     try
83     {
84         values = getGPIOValues();
85     }
86     catch (...)
87     {}
88     return values;
89 }
90 
91 void StandardDevice::storePgoodFaultDebugData(
92     Services& services, const std::vector<int>& gpioValues,
93     std::map<std::string, std::string>& additionalData)
94 {
95     additionalData.emplace("DEVICE_NAME", name);
96     if (!gpioValues.empty())
97     {
98         std::string valuesStr = format_utils::toString(std::span(gpioValues));
99         services.logInfoMsg(
100             std::format("Device {} GPIO values: {}", name, valuesStr));
101         additionalData.emplace("GPIO_VALUES", valuesStr);
102     }
103 }
104 
105 } // namespace phosphor::power::sequencer
106